diff options
Diffstat (limited to 'lib')
1121 files changed, 46680 insertions, 18657 deletions
diff --git a/lib/.gitignore b/lib/.gitignore index 56b1ed2b84..4125111ebd 100644 --- a/lib/.gitignore +++ b/lib/.gitignore @@ -7,6 +7,7 @@ /common_test/doc/src/ct_rpc.xml /common_test/doc/src/ct_snmp.xml /common_test/doc/src/ct_ssh.xml +/common_test/doc/src/ct_netconfc.xml /common_test/doc/src/ct_telnet.xml /common_test/doc/src/unix_telnet.xml diff --git a/lib/Makefile b/lib/Makefile index aa4e074830..3753bd165b 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2011. All Rights Reserved. +# Copyright Ericsson AB 1996-2012. 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 @@ -31,7 +31,7 @@ ifdef BUILD_ALL cosTransactions cosEvent cosTime cosNotification \ cosProperty cosFileTransfer cosEventDomain et megaco webtool \ xmerl edoc eunit ssh inviso typer erl_docgen \ - percept dialyzer hipe + percept eldap dialyzer hipe EXTRA_FILE := $(wildcard EXTRA-APPLICATIONS) EXTRA_APPLICATIONS := $(if $(EXTRA_FILE),$(shell cat $(EXTRA_FILE))) endif diff --git a/lib/appmon/doc/src/Makefile b/lib/appmon/doc/src/Makefile index 743f123c06..2bf560ee6b 100644 --- a/lib/appmon/doc/src/Makefile +++ b/lib/appmon/doc/src/Makefile @@ -107,14 +107,14 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/appmon/doc/src/appmon.xml b/lib/appmon/doc/src/appmon.xml index 5af50daa53..6e04f8503f 100644 --- a/lib/appmon/doc/src/appmon.xml +++ b/lib/appmon/doc/src/appmon.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/appmon/doc/src/appmon_chapter.xml b/lib/appmon/doc/src/appmon_chapter.xml index 3f642ad002..7ce22caaad 100644 --- a/lib/appmon/doc/src/appmon_chapter.xml +++ b/lib/appmon/doc/src/appmon_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2010</year> + <year>2000</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/appmon/doc/src/notes.xml b/lib/appmon/doc/src/notes.xml index c469880abd..04b2b0d8ba 100644 --- a/lib/appmon/doc/src/notes.xml +++ b/lib/appmon/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2010</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -30,6 +30,26 @@ </header> <p>This document describes the changes made to the Appmon application.</p> +<section><title>Appmon 2.1.14.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>The GS applications is now deprecated and will be + removed in the R16 release. The following GS-based + applications have been superseded by the Observer + application and will removed in R16: Appmon, Pman, + Tv.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9907</p> + </item> + </list> + </section> + +</section> + <section><title>Appmon 2.1.14</title> <section><title>Improvements and New Features</title> diff --git a/lib/appmon/priv/Makefile b/lib/appmon/priv/Makefile index 9af4fbd228..8920923c98 100644 --- a/lib/appmon/priv/Makefile +++ b/lib/appmon/priv/Makefile @@ -57,8 +57,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(HELP_FILES) $(TOOLBAR_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(HELP_FILES) $(TOOLBAR_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/appmon/src/Makefile b/lib/appmon/src/Makefile index 43f4f085b8..9ee7c92a4d 100644 --- a/lib/appmon/src/Makefile +++ b/lib/appmon/src/Makefile @@ -92,10 +92,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/appmon/src/appmon.erl b/lib/appmon/src/appmon.erl index 7a6d86c9ab..ba98a24400 100644 --- a/lib/appmon/src/appmon.erl +++ b/lib/appmon/src/appmon.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/appmon/src/appmon_a.erl b/lib/appmon/src/appmon_a.erl index 2c8185a85d..6838a7a275 100644 --- a/lib/appmon/src/appmon_a.erl +++ b/lib/appmon/src/appmon_a.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/appmon/src/appmon_lb.erl b/lib/appmon/src/appmon_lb.erl index 55fda83027..3ab6ce58e6 100644 --- a/lib/appmon/src/appmon_lb.erl +++ b/lib/appmon/src/appmon_lb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 diff --git a/lib/appmon/src/appmon_txt.erl b/lib/appmon/src/appmon_txt.erl index 8647bc9890..056265af90 100644 --- a/lib/appmon/src/appmon_txt.erl +++ b/lib/appmon/src/appmon_txt.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/appmon/vsn.mk b/lib/appmon/vsn.mk index 047f1eadc1..0654468b42 100644 --- a/lib/appmon/vsn.mk +++ b/lib/appmon/vsn.mk @@ -1 +1 @@ -APPMON_VSN = 2.1.14 +APPMON_VSN = 2.1.14.1 diff --git a/lib/asn1/c_src/Makefile b/lib/asn1/c_src/Makefile index 8c06be56f8..903cf32838 100644 --- a/lib/asn1/c_src/Makefile +++ b/lib/asn1/c_src/Makefile @@ -110,10 +110,10 @@ $(NIF_SHARED_OBJ_FILE): $(NIF_OBJ_FILES) include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_PROGRAM) $(NIF_SHARED_OBJ_FILE) $(RELSYSDIR)/priv/lib - $(INSTALL_DIR) $(RELSYSDIR)/c_src - $(INSTALL_DATA) *.c $(RELSYSDIR)/c_src + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_PROGRAM) $(NIF_SHARED_OBJ_FILE) "$(RELSYSDIR)/priv/lib" + $(INSTALL_DIR) "$(RELSYSDIR)/c_src" + $(INSTALL_DATA) *.c "$(RELSYSDIR)/c_src" release_docs_spec: diff --git a/lib/asn1/doc/src/Makefile b/lib/asn1/doc/src/Makefile index 20bd00a3b8..9ef5750353 100644 --- a/lib/asn1/doc/src/Makefile +++ b/lib/asn1/doc/src/Makefile @@ -119,13 +119,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/asn1/doc/src/notes.xml b/lib/asn1/doc/src/notes.xml index 9b6c482c0a..b5422c9083 100644 --- a/lib/asn1/doc/src/notes.xml +++ b/lib/asn1/doc/src/notes.xml @@ -31,6 +31,32 @@ <p>This document describes the changes made to the asn1 application.</p> +<section><title>Asn1 1.7</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Some ASN.1 INTEGER type and SEQUENCE constructor variants + previously not handled by the ASN.1 compiler are now + correctly handled</p> + <p> + Own Id: OTP-9688</p> + </item> + <item> + <p> + An INTEGER with a value constraint where unions are used + e.g. X1 ::= INTEGER (1..4 | 6 | 8 | 10 | 20) is not + handled correctly. For PER the value is encoded in wrong + number of bits.</p> + <p> + Own Id: OTP-9946</p> + </item> + </list> + </section> + +</section> + <section><title>Asn1 1.6.19</title> <section><title>Improvements and New Features</title> diff --git a/lib/asn1/src/Makefile b/lib/asn1/src/Makefile index 5614cbea91..4bd49aa93b 100644 --- a/lib/asn1/src/Makefile +++ b/lib/asn1/src/Makefile @@ -153,16 +153,16 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(APP_SRC) $(APPUP_SRC) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(APP_SRC) $(APPUP_SRC) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLES) "$(RELSYSDIR)/examples" # there are no include files to be used by the user -#$(INSTALL_DIR) $(RELSYSDIR)/include -#$(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include +#$(INSTALL_DIR) "$(RELSYSDIR)/include" +#$(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/asn1/src/asn1_db.erl b/lib/asn1/src/asn1_db.erl index 0862ef6e02..843fc66c9c 100644 --- a/lib/asn1/src/asn1_db.erl +++ b/lib/asn1/src/asn1_db.erl @@ -48,11 +48,19 @@ dbstop() -> Resp = req(stop), erase(?MODULE), Resp. %% Internal functions req(Request) -> - get(?MODULE) ! {self(), Request}, - receive {?MODULE, Reply} -> Reply after 5000 -> exit(db_timeout) end. + DbPid = get(?MODULE), + Ref = erlang:monitor(process,DbPid), + get(?MODULE) ! {{Ref, self()}, Request}, + receive + {{Ref,?MODULE}, Reply} -> + erlang:demonitor(Ref,[flush]), + Reply; + {'DOWN',Ref,_,_,Info} -> + exit({db_error,Info}) + end. -reply(From, Response) -> - From ! {?MODULE, Response}. +reply({Ref,From}, Response) -> + From ! {{Ref,?MODULE}, Response}. init(Parent, Includes) -> MRef = erlang:monitor(process, Parent), diff --git a/lib/asn1/src/asn1ct.erl b/lib/asn1/src/asn1ct.erl index 2d17f73a2c..99755a95a6 100644 --- a/lib/asn1/src/asn1ct.erl +++ b/lib/asn1/src/asn1ct.erl @@ -816,7 +816,9 @@ check({true,M},File,OutFile,Includes,EncodingRule,DbFile,Options,InputMods) -> asn1_db:dbsave(DbFile,M#module.name), verbose("--~p--~n",[{generated,DbFile}],Options), {true,{M,NewM,GenTypeOrVal}} - end + end; + ErrorList = {error,_} -> + {false,ErrorList} end; check({false,M},_,_,_,_,_,_,_) -> {false,M}. diff --git a/lib/asn1/src/asn1ct_check.erl b/lib/asn1/src/asn1ct_check.erl index c223561f12..59e82b7a57 100644 --- a/lib/asn1/src/asn1ct_check.erl +++ b/lib/asn1/src/asn1ct_check.erl @@ -4177,7 +4177,7 @@ check_constraint(S,{'SizeConstraint',Lb}) -> check_constraint(S,{'SingleValue', L}) when is_list(L) -> F = fun(A) -> resolv_value(S,A) end, - {'SingleValue',lists:map(F,L)}; + {'SingleValue',lists:sort(lists:map(F,L))}; check_constraint(S,{'SingleValue', V}) when is_integer(V) -> Val = resolv_value(S,V), @@ -4399,22 +4399,22 @@ constraint_union(_S,C) -> constraint_union1(S,[A={'ValueRange',_},union,B={'ValueRange',_}|Rest],Acc) -> AunionB = constraint_union_vr([A,B]), - constraint_union1(S,Rest,AunionB++Acc); + constraint_union1(S,Rest,Acc ++ AunionB); constraint_union1(S,[A={'SingleValue',_},union,B={'SingleValue',_}|Rest],Acc) -> AunionB = constraint_union_sv(S,[A,B]), - constraint_union1(S,Rest,AunionB++Acc); + constraint_union1(S,Rest,Acc ++ AunionB); constraint_union1(S,[A={'SingleValue',_},union,B={'ValueRange',_}|Rest],Acc) -> AunionB = union_sv_vr(S,A,B), - constraint_union1(S,Rest,AunionB++Acc); + constraint_union1(S,Rest,Acc ++ AunionB); constraint_union1(S,[A={'ValueRange',_},union,B={'SingleValue',_}|Rest],Acc) -> AunionB = union_sv_vr(S,B,A), - constraint_union1(S,Rest,AunionB++Acc); + constraint_union1(S,Rest,Acc ++ AunionB); constraint_union1(S,[union|Rest],Acc) -> %skip when unsupported constraints constraint_union1(S,Rest,Acc); constraint_union1(S,[A|Rest],Acc) -> constraint_union1(S,Rest,[A|Acc]); constraint_union1(_S,[],Acc) -> - lists:reverse(Acc). + Acc. constraint_union_sv(_S,SV) -> Values=lists:map(fun({_,V})->V end,SV), @@ -4467,63 +4467,33 @@ constraint_union_vr([{_,{_,Ub2}}|Rest],A=[{_,{_,Ub1}}|_Acc]) when Ub2=<Ub1-> constraint_union_vr([VR|Rest],Acc) -> constraint_union_vr(Rest,[VR|Acc]). -union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',VR={Lb,Ub}}) +union_sv_vr(_S,{'SingleValue',SV},VR) when is_integer(SV) -> - case is_int_in_vr(SV,C2) of - true -> [C2]; - _ -> - case VR of - {'MIN',Ub} when SV==Ub+1 -> [{'ValueRange',{'MIN',SV}}]; - {Lb,'MAX'} when SV==Lb-1 -> [{'ValueRange',{SV,'MAX'}}]; - {Lb,Ub} when SV==Ub+1 -> [{'ValueRange',{Lb,SV}}]; - {Lb,Ub} when SV==Lb-1 -> [{'ValueRange',{SV,Ub}}]; - _ -> - [C1,C2] - end - end; -union_sv_vr(_S,C1={'SingleValue',SV},C2={'ValueRange',{_Lb,_Ub}}) + union_sv_vr(_S,{'SingleValue',[SV]},VR); +union_sv_vr(_S,{'SingleValue',SV},{'ValueRange',{VLb,VUb}}) when is_list(SV) -> - case lists:filter(fun(X)->is_int_in_vr(X,C2) end,SV) of - [] -> [C2]; - L -> - case expand_vr(L,C2) of - {[],C3} -> [C3]; - {L,C2} -> [C1,C2]; - {[Val],C3} -> [{'SingleValue',Val},C3]; - {L2,C3} -> [{'SingleValue',L2},C3] - end - end. - -expand_vr(L,VR={_,{Lb,Ub}}) -> - case lower_Lb(L,Lb) of - false -> - case higher_Ub(L,Ub) of - false -> - {L,VR}; - {L1,UbNew} -> - expand_vr(L1,{'ValueRange',{Lb,UbNew}}) - end; - {L1,LbNew} -> - expand_vr(L1,{'ValueRange',{LbNew,Ub}}) - end. - -lower_Lb(_,'MIN') -> - false; -lower_Lb(L,Lb) -> - remove_val_from_list(Lb - 1,L). - -higher_Ub(_,'MAX') -> - false; -higher_Ub(L,Ub) -> - remove_val_from_list(Ub + 1,L). + L = lists:sort(SV++[VLb,VUb]), + {Lb,L1} = case lists:member('MIN',L) of + true -> {'MIN',L--['MIN']}; % remove 'MIN' so it does not disturb + false -> {hd(L),tl(L)} + end, + Ub = case lists:member('MAX',L1) of + true -> 'MAX'; + false -> lists:last(L1) + end, + case SV of + [H] -> H; + _ -> SV + end, + %% for now we through away the Singlevalues so that they don't disturb + %% in the code generating phase (the effective Valuerange is already + %% calculated. If we want to keep the Singlevalues as well for + %% use in code gen phases we need to introduce a new representation + %% like {'ValueRange',{Lb,Ub},[ListOfRanges|AntiValues|Singlevalues] + %% These could be used to generate guards which allows only the specific + %% values , not the full range + [{'ValueRange',{Lb,Ub}}]. -remove_val_from_list(Val,List) -> - case lists:member(Val,List) of - true -> - {lists:delete(Val,List),Val}; - false -> - false - end. %% get_constraints/2 %% Arguments are a list of constraints, which has the format {key,value}, @@ -6021,17 +5991,25 @@ generate_automatic_tags1([H|T],[TagNo|TagNos]) when is_record(H,'ComponentType') type={default,'IMPLICIT'}, form= 0 }]}, % PRIMITIVE [H#'ComponentType'{typespec=NewTs}|generate_automatic_tags1(T,[TagNo+1|TagNos])]; -generate_automatic_tags1([ExtMark|T],[_TagNo|TagNos]) -> % EXTENSIONMARK +generate_automatic_tags1([ExtMark = #'EXTENSIONMARK'{}|T],[_TagNo|TagNos]) -> [ExtMark | generate_automatic_tags1(T,TagNos)]; +generate_automatic_tags1([H|T],TagList) -> % ExtensionAdditionGroup etc are just ignored + [H | generate_automatic_tags1(T,TagList)]; generate_automatic_tags1([],_) -> []. -any_manual_tag([#'ComponentType'{typespec=#type{tag=[]}}|Rest]) -> - any_manual_tag(Rest); -any_manual_tag([#'EXTENSIONMARK'{}|Rest]) -> - any_manual_tag(Rest); -any_manual_tag([_|_Rest]) -> +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Returns true if there is at least one ComponentType with a manually +%% specified tag. No manual tag is indicated by typespec=#type{tag=[]} +%% so we check if we find a tag =/= [] and return true in that case +%% all other things in the componentlist like (EXTENSIONMARK, +%% ExtensionAdditionGroup,...) except ComponentType is simply +%% ignored/skipped +any_manual_tag([#'ComponentType'{typespec=#type{tag=Tag}}|_Rest]) + when Tag =/= []-> true; +any_manual_tag([_|Rest]) -> + any_manual_tag(Rest); any_manual_tag([]) -> false. diff --git a/lib/asn1/src/asn1ct_constructed_per.erl b/lib/asn1/src/asn1ct_constructed_per.erl index e07680f10b..5f5138ef23 100644 --- a/lib/asn1/src/asn1ct_constructed_per.erl +++ b/lib/asn1/src/asn1ct_constructed_per.erl @@ -100,18 +100,26 @@ gen_encode_constructed(Erule,Typename,D) when is_record(D,type) -> case Ext of {ext,_,NumExt} when NumExt > 0 -> case extgroup_pos_and_length(CompList) of - {extgrouppos,ExtGroupPos,ExtGroupLen} -> - Elements = make_elements(ExtGroupPos+1, - "Val1",lists:seq(1,ExtGroupLen)), - emit([ - {next,val}," = case [X || X <- [",Elements, - "],X =/= asn1_NOVALUE] of",nl, - "[] -> ",{curr,val},";",nl, - "_ -> setelement(",{asis,ExtGroupPos+1},",", - {curr,val},",", - "{extaddgroup,", Elements,"})",nl, - "end,",nl]), - asn1ct_name:new(val); + {extgrouppos,[]} -> % no extenstionAdditionGroup + ok; + {extgrouppos,ExtGroupPosLenList} -> + ExtGroupFun = + fun({ExtActualGroupPos,ExtGroupVirtualPos,ExtGroupLen}) -> + Elements = + make_elements(ExtGroupVirtualPos+1, + "Val1", + lists:seq(1,ExtGroupLen)), + emit([ + {next,val}," = case [X || X <- [",Elements, + "],X =/= asn1_NOVALUE] of",nl, + "[] -> ",{curr,val},";",nl, + "_ -> setelement(",{asis,ExtActualGroupPos+1},",", + {curr,val},",", + "{extaddgroup,", Elements,"})",nl, + "end,",nl]), + asn1ct_name:new(val) + end, + lists:foreach(ExtGroupFun,ExtGroupPosLenList); _ -> % no extensionAdditionGroup ok end, @@ -279,9 +287,9 @@ gen_decode_constructed(Erules,Typename,D) when is_record(D,type) -> {false,false,false} end end, - NewCompList = wrap_compList(CompList), +%% NewCompList = wrap_compList(CompList), {AccTerm,AccBytes} = - gen_dec_components_call(Erules,Typename,NewCompList,MaybeComma2,DecObjInf,Ext,length(Optionals)), + gen_dec_components_call(Erules,Typename,CompList,MaybeComma2,DecObjInf,Ext,length(Optionals)), case asn1ct_name:all(term) of [] -> emit(MaybeComma2); % no components at all _ -> emit({com,nl}) @@ -689,24 +697,28 @@ ext_length([],_,Acc) -> Acc. extgroup_pos_and_length(CompList) when is_list(CompList) -> - noextgroup; + {extgrouppos,[]}; extgroup_pos_and_length({RootList,ExtList}) -> - extgrouppos(ExtList,length(RootList)+1); -extgroup_pos_and_length({Rl1,Ext,_Rl2}) -> - extgrouppos(Ext,length(Rl1)+1). - -extgrouppos([{'ExtensionAdditionGroup',_Num}|T],Pos) -> - extgrouppos(T,Pos,0); -extgrouppos([_|T],Pos) -> - extgrouppos(T,Pos+1); -extgrouppos([],_) -> - noextgroup. - -extgrouppos(['ExtensionAdditionGroupEnd'|_T],Pos,Len) -> - {extgrouppos,Pos,Len}; -extgrouppos([_|T],Pos,Len) -> - extgrouppos(T,Pos,Len+1). - + ActualPos = length(RootList) +1, + %% position to get and deliver data in the record to the user + VirtualPos = ActualPos, + %% position to encode/decode the extaddgroup as an opentype sequence + extgrouppos(ExtList,ActualPos,VirtualPos,[]); +extgroup_pos_and_length({RootList,ExtList,_Rl2}) -> + extgroup_pos_and_length({RootList,ExtList}). + +extgrouppos([{'ExtensionAdditionGroup',_Num}|T],ActualPos,VirtualPos,Acc) -> + extgrouppos(T,ActualPos,VirtualPos,0,Acc); +extgrouppos([_|T],ActualPos,VirtualPos,Acc) -> + extgrouppos(T,ActualPos+1,VirtualPos+1,Acc); +extgrouppos([],_,_,Acc) -> + {extgrouppos,lists:reverse(Acc)}. + +extgrouppos(['ExtensionAdditionGroupEnd'|T],ActualPos,VirtualPos,Len,Acc) -> + extgrouppos(T,ActualPos+1,VirtualPos+Len,[{ActualPos,VirtualPos,Len}|Acc]); +extgrouppos([_|T],ActualPos,VirtualPos,Len,Acc) -> + extgrouppos(T,ActualPos,VirtualPos,Len+1,Acc). + gen_dec_extension_value(_) -> @@ -817,19 +829,21 @@ add_textual_order1(Cs,NumIn) -> end, NumIn,Cs). -gen_enc_components_call(Erule,TopType,{Root1,ExtList,Root2},MaybeComma,DynamicEnc,Ext) -> - gen_enc_components_call(Erule,TopType,{Root1++Root2,ExtList},MaybeComma,DynamicEnc,Ext); -gen_enc_components_call(Erule,TopType,{CompList,ExtList},MaybeComma,DynamicEnc,Ext) -> +gen_enc_components_call(Erule,TopType,{Root,ExtList},MaybeComma,DynamicEnc,Ext) -> + gen_enc_components_call(Erule,TopType,{Root,ExtList,[]},MaybeComma,DynamicEnc,Ext); +gen_enc_components_call(Erule,TopType,CL={Root,ExtList,Root2},MaybeComma,DynamicEnc,Ext) -> %% The type has extensionmarker - Rpos = gen_enc_components_call1(Erule,TopType,CompList,1,MaybeComma,DynamicEnc,noext), + Rpos = gen_enc_components_call1(Erule,TopType,Root++Root2,1,MaybeComma,DynamicEnc,noext), case Ext of {ext,_,ExtNum} when ExtNum > 0 -> emit([nl, ",Extensions",nl]); + _ -> true end, %handle extensions - NewExtList = wrap_extensionAdditionGroups(ExtList), + {extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL), + NewExtList = wrap_extensionAdditionGroups(ExtList,ExtGroupPosLen), gen_enc_components_call1(Erule,TopType,NewExtList,Rpos,MaybeComma,DynamicEnc,Ext); gen_enc_components_call(Erule,TopType, CompList, MaybeComma, DynamicEnc, Ext) -> %% The type has no extensionmarker @@ -938,7 +952,7 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> Atype = case Type of #type{def=#'ObjectClassFieldType'{type=InnerType}} -> - InnerType; + InnerType; _ -> asn1ct_gen:get_inner(Type#type.def) end, @@ -948,6 +962,7 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> emit(["?RT_PER:encode_open_type(dummy,?RT_PER:complete("]); _ -> true end, + case Atype of {typefield,_} -> case DynamicEnc of @@ -1023,20 +1038,22 @@ gen_enc_line(Erule,TopType,Cname,Type,Element, _Pos,DynamicEnc,Ext) -> emit("))"); _ -> true end. -gen_dec_components_call(Erule,TopType,{Root1,ExtList,Root2},MaybeComma,DecInfObj,Ext,NumberOfOptionals) -> - gen_dec_components_call(Erule,TopType,{Root1++Root2,ExtList},MaybeComma,DecInfObj,Ext,NumberOfOptionals); -gen_dec_components_call(Erule,TopType,{CompList,ExtList},MaybeComma, +gen_dec_components_call(Erule,TopType,{Root,ExtList},MaybeComma, DecInfObj,Ext,NumberOfOptionals) -> + gen_dec_components_call(Erule,TopType,{Root,ExtList,[]},MaybeComma,DecInfObj,Ext,NumberOfOptionals); +gen_dec_components_call(Erule,TopType,CL={Root1,ExtList,Root2},MaybeComma,DecInfObj,Ext,NumberOfOptionals) -> %% The type has extensionmarker - OptTable = create_optionality_table(CompList), + + OptTable = create_optionality_table(Root1++Root2), {Rpos,AccTerm,AccBytes} = - gen_dec_components_call1(Erule,TopType, CompList, 1, OptTable, + gen_dec_components_call1(Erule,TopType, Root1++Root2, 1, OptTable, MaybeComma,DecInfObj,noext,[],[], NumberOfOptionals), emit([",",nl,"{Extensions,",{next,bytes},"} = "]), emit(["?RT_PER:getextension(Ext,",{curr,bytes},"),",nl]), asn1ct_name:new(bytes), - NewExtList = wrap_extensionAdditionGroups(ExtList), + {extgrouppos,ExtGroupPosLen} = extgroup_pos_and_length(CL), + NewExtList = wrap_extensionAdditionGroups(ExtList,ExtGroupPosLen), {_Epos,AccTermE,AccBytesE} = gen_dec_components_call1(Erule,TopType,NewExtList,Rpos, OptTable, "",DecInfObj,Ext,[],[],NumberOfOptionals), @@ -1233,8 +1250,7 @@ gen_dec_line(Erule,TopType,Cname,Type,Pos,DecInfObj,Ext,Prop) -> "} = ?RT_PER:decode_open_type(",{curr,bytes}, ", []),",nl]), emit([indent(2),"case (catch ObjFun(", - {asis,Name}, - ",",{curr,tmpterm},",telltype,", + {asis,Name},",",{curr,tmpterm},",telltype,", {asis,RestFieldNames},")) of", nl]), emit([indent(4),"{'EXIT',",{curr,reason},"} ->",nl]), emit([indent(6),"exit({'Type not ", @@ -1596,42 +1612,44 @@ flat_complist({Rl1,El,Rl2}) -> Rl1 ++ El ++ Rl2; flat_complist({Rl,El}) -> Rl ++ El; flat_complist(CompList) -> CompList. -wrap_compList({Root1,Ext,Root2}) -> - {Root1,wrap_extensionAdditionGroups(Ext),Root2}; -wrap_compList({Root1,Ext}) -> - {Root1,wrap_extensionAdditionGroups(Ext)}; -wrap_compList(CompList) -> - CompList. +%%wrap_compList({Root1,Ext,Root2}) -> +%% {Root1,wrap_extensionAdditionGroups(Ext),Root2}; +%%wrap_compList({Root1,Ext}) -> +%% {Root1,wrap_extensionAdditionGroups(Ext)}; +%%wrap_compList(CompList) -> +%% CompList. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Will convert all componentTypes following 'ExtensionAdditionGroup' %% up to the matching 'ExtensionAdditionGroupEnd' into one componentType %% of type SEQUENCE with the componentTypes as components %% -wrap_extensionAdditionGroups(ExtCompList) -> - wrap_extensionAdditionGroups(ExtCompList,[],0). +wrap_extensionAdditionGroups(ExtCompList,ExtGroupPosLen) -> + wrap_extensionAdditionGroups(ExtCompList,ExtGroupPosLen,[],0,0). -wrap_extensionAdditionGroups([{'ExtensionAdditionGroup',_Number}|Rest],Acc,0) -> - {ExtGroupCompList= - [#'ComponentType'{textual_order=TextPos}|_], - ['ExtensionAdditionGroupEnd'|Rest2]} = +wrap_extensionAdditionGroups([{'ExtensionAdditionGroup',_Number}|Rest], + [{ActualPos,_,_}|ExtGroupPosLenRest],Acc,_ExtAddGroupDiff,ExtGroupNum) -> + {ExtGroupCompList,['ExtensionAdditionGroupEnd'|Rest2]} = lists:splitwith(fun(#'ComponentType'{}) -> true; (_) -> false end, Rest), - wrap_extensionAdditionGroups(Rest2, + wrap_extensionAdditionGroups(Rest2,ExtGroupPosLenRest, [#'ComponentType'{ - name='ExtAddGroup', % FIXME: handles ony one ExtAddGroup - typespec=#type{def=#'SEQUENCE'{ - extaddgroup=1,% FIXME: handles only one + name=list_to_atom("ExtAddGroup"++ + integer_to_list(ExtGroupNum+1)), + typespec=#type{def=#'SEQUENCE'{ + extaddgroup=ExtGroupNum+1, components=ExtGroupCompList}}, - textual_order = TextPos, - prop='OPTIONAL'}|Acc],length(ExtGroupCompList)-1); -wrap_extensionAdditionGroups([H=#'ComponentType'{textual_order=Tord}|T],Acc,ExtAddGroupDiff) when is_integer(Tord) -> - wrap_extensionAdditionGroups(T,[H#'ComponentType'{ - textual_order=Tord - ExtAddGroupDiff}|Acc],ExtAddGroupDiff); -wrap_extensionAdditionGroups([H|T],Acc,ExtAddGroupDiff) -> - wrap_extensionAdditionGroups(T,[H|Acc],ExtAddGroupDiff); -wrap_extensionAdditionGroups([],Acc,_) -> + textual_order = ActualPos, + prop='OPTIONAL'}|Acc],length(ExtGroupCompList)-1, + ExtGroupNum+1); +wrap_extensionAdditionGroups([H=#'ComponentType'{textual_order=Tord}|T], + ExtAddGrpLenPos,Acc,ExtAddGroupDiff,ExtGroupNum) when is_integer(Tord) -> + wrap_extensionAdditionGroups(T,ExtAddGrpLenPos,[H#'ComponentType'{ + textual_order=Tord - ExtAddGroupDiff}|Acc],ExtAddGroupDiff,ExtGroupNum); +wrap_extensionAdditionGroups([H|T],ExtAddGrpLenPos,Acc,ExtAddGroupDiff,ExtGroupNum) -> + wrap_extensionAdditionGroups(T,ExtAddGrpLenPos,[H|Acc],ExtAddGroupDiff,ExtGroupNum); +wrap_extensionAdditionGroups([],_,Acc,_,_) -> lists:reverse(Acc). diff --git a/lib/asn1/src/asn1ct_gen.erl b/lib/asn1/src/asn1ct_gen.erl index fda4e1c6d9..64a3555f62 100644 --- a/lib/asn1/src/asn1ct_gen.erl +++ b/lib/asn1/src/asn1ct_gen.erl @@ -129,28 +129,39 @@ pgen_types(Rtmod,Erules,N2nConvEnums,Module,[H|T]) -> end, pgen_types(Rtmod,Erules,N2nConvEnums,Module,T). +%% Enumerated type with extension marker pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',{NN1,NN2}}}}) -> NN = NN1 ++ NN2, - pgen_name2numfunc(TypeName,NN), - pgen_num2namefunc(TypeName,NN); + pgen_name2numfunc(TypeName,NN, extension_marker), + pgen_num2namefunc(TypeName,NN, extension_marker); +%% Without extension marker +pgen_n2nconversion(_Erules,#typedef{name=TypeName,typespec=#type{def={'ENUMERATED',NN}}}) -> + pgen_name2numfunc(TypeName,NN, no_extension_marker), + pgen_num2namefunc(TypeName,NN, no_extension_marker); pgen_n2nconversion(_Erules,_) -> true. -pgen_name2numfunc(_TypeName,[]) -> +pgen_name2numfunc(_TypeName,[], _) -> true; -pgen_name2numfunc(TypeName,[{Atom,Number}]) -> +pgen_name2numfunc(TypeName,[{Atom,Number}], extension_marker) -> + emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]), + emit(["name2num_",TypeName,"({asn1_enum, Num}) -> Num.",nl,nl]); +pgen_name2numfunc(TypeName,[{Atom,Number}], _) -> emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,".",nl,nl]); -pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest]) -> +pgen_name2numfunc(TypeName,[{Atom,Number}|NNRest], EM) -> emit(["name2num_",TypeName,"(",{asis,Atom},") ->",Number,";",nl]), - pgen_name2numfunc(TypeName,NNRest). + pgen_name2numfunc(TypeName,NNRest, EM). -pgen_num2namefunc(_TypeName,[]) -> +pgen_num2namefunc(_TypeName,[], _) -> true; -pgen_num2namefunc(TypeName,[{Atom,Number}]) -> +pgen_num2namefunc(TypeName,[{Atom,Number}], extension_marker) -> + emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]), + emit(["num2name_",TypeName,"(ExtensionNum) -> {asn1_enum, ExtensionNum}.",nl,nl]); +pgen_num2namefunc(TypeName,[{Atom,Number}], _) -> emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},".",nl,nl]); -pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest]) -> +pgen_num2namefunc(TypeName,[{Atom,Number}|NNRest], EM) -> emit(["num2name_",TypeName,"(",Number,") ->",{asis,Atom},";",nl]), - pgen_num2namefunc(TypeName,NNRest). + pgen_num2namefunc(TypeName,NNRest, EM). pgen_objects(_,_,_,[]) -> true; diff --git a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl index 597fb0030b..3ccfca3784 100644 --- a/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl +++ b/lib/asn1/src/asn1ct_gen_ber_bin_v2.erl @@ -419,7 +419,7 @@ gen_decode_selected(Erules,Type,FuncName) -> " {Tlv,_} = ?RT_BER:decode(Bin2",asn1ct_gen:nif_parameter(),"),",nl]), emit("{ok,"), gen_decode_selected_type(Erules,Type), - emit(["};",nl," Err -> exit({error,{selctive_decode,Err}})",nl, + emit(["};",nl," Err -> exit({error,{selective_decode,Err}})",nl, " end.",nl]). gen_decode_selected_type(_Erules,TypeDef) -> diff --git a/lib/asn1/src/asn1ct_gen_per.erl b/lib/asn1/src/asn1ct_gen_per.erl index 8fd7a69a19..bd5b81991d 100644 --- a/lib/asn1/src/asn1ct_gen_per.erl +++ b/lib/asn1/src/asn1ct_gen_per.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -321,19 +321,13 @@ effective_constr(_,[]) -> []; effective_constr('SingleValue',List) -> SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)), - % sort and remove duplicates - SortedSVList = lists:sort(SVList), - RemoveDup = fun([],_) ->[]; - ([H],_) -> [H]; - ([H,H|T],F) -> F([H|T],F); - ([H|T],F) -> [H|F(T,F)] - end, - - case RemoveDup(SortedSVList,RemoveDup) of + %% Sort and remove duplicates before generating SingleValue or ValueRange + %% In case of ValueRange, also check for 'MIN and 'MAX' + case lists:usort(SVList) of [N] -> [{'SingleValue',N}]; - L when is_list(L) -> - [{'ValueRange',{hd(L),lists:last(L)}}] + L when is_list(L) -> + [{'ValueRange',{least_Lb(L),greatest_Ub(L)}}] end; effective_constr('ValueRange',List) -> LBs = lists:map(fun({_,{Lb,_}})-> Lb end,List), @@ -1405,19 +1399,21 @@ get_object_field(Name,ObjectFields) -> %% have been specified within a SEQUENCE, therefore we construct a fake sequence type here %% so that we can generate code for it extaddgroup2sequence(ExtList) -> - extaddgroup2sequence(ExtList,[]). + extaddgroup2sequence(ExtList,0,[]). -extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],Acc) -> +extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],ExtNum,Acc) -> Number = case Number0 of undefined -> 1; _ -> Number0 end, {ExtGroupComps,['ExtensionAdditionGroupEnd'|T2]} = lists:splitwith(fun(Elem) -> is_record(Elem,'ComponentType') end,T), - extaddgroup2sequence(T2,[#'ComponentType'{ - name='ExtAddGroup', - typespec=#type{def=#'SEQUENCE'{ - extaddgroup=Number, - components=ExtGroupComps}}, - prop='OPTIONAL'}|Acc]); -extaddgroup2sequence([C|T],Acc) -> - extaddgroup2sequence(T,[C|Acc]); -extaddgroup2sequence([],Acc) -> + extaddgroup2sequence(T2,ExtNum+1, + [#'ComponentType'{ + name=list_to_atom("ExtAddGroup"++ + integer_to_list(ExtNum+1)), + typespec=#type{def=#'SEQUENCE'{ + extaddgroup=Number, + components=ExtGroupComps}}, + prop='OPTIONAL'}|Acc]); +extaddgroup2sequence([C|T],ExtNum,Acc) -> + extaddgroup2sequence(T,ExtNum,[C|Acc]); +extaddgroup2sequence([],_,Acc) -> lists:reverse(Acc). diff --git a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl index fb9613c18d..16eec92847 100644 --- a/lib/asn1/src/asn1ct_gen_per_rt2ct.erl +++ b/lib/asn1/src/asn1ct_gen_per_rt2ct.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 @@ -670,18 +670,13 @@ effective_constr(_,[]) -> []; effective_constr('SingleValue',List) -> SVList = lists:flatten(lists:map(fun(X)->element(2,X)end,List)), - % sort and remove duplicates - RemoveDup = fun([],_) ->[]; - ([H],_) -> [H]; - ([H,H|T],F) -> F([H|T],F); - ([H|T],F) -> [H|F(T,F)] - end, - - case RemoveDup(SVList,RemoveDup) of + %% Sort and remove duplicates before generating SingleValue or ValueRange + %% In case of ValueRange, also check for 'MIN and 'MAX' + case lists:usort(SVList) of [N] -> [{'SingleValue',N}]; - L when is_list(L) -> - [{'ValueRange',{hd(L),lists:last(L)}}] + L when is_list(L) -> + [{'ValueRange',{least_Lb(L),greatest_Ub(L)}}] end; effective_constr('ValueRange',List) -> LBs = lists:map(fun({_,{Lb,_}})-> Lb end,List), @@ -1808,19 +1803,21 @@ dec_enumerated_cases([],_,_) -> %% have been specified within a SEQUENCE, therefore we construct a fake sequence type here %% so that we can generate code for it extaddgroup2sequence(ExtList) -> - extaddgroup2sequence(ExtList,[]). + extaddgroup2sequence(ExtList,0,[]). -extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],Acc) -> +extaddgroup2sequence([{'ExtensionAdditionGroup',Number0}|T],ExtNum,Acc) -> Number = case Number0 of undefined -> 1; _ -> Number0 end, {ExtGroupComps,['ExtensionAdditionGroupEnd'|T2]} = lists:splitwith(fun(Elem) -> is_record(Elem,'ComponentType') end,T), - extaddgroup2sequence(T2,[#'ComponentType'{ - name='ExtAddGroup', - typespec=#type{def=#'SEQUENCE'{ - extaddgroup=Number, - components=ExtGroupComps}}, - prop='OPTIONAL'}|Acc]); -extaddgroup2sequence([C|T],Acc) -> - extaddgroup2sequence(T,[C|Acc]); -extaddgroup2sequence([],Acc) -> + extaddgroup2sequence(T2,ExtNum+1, + [#'ComponentType'{ + name=list_to_atom("ExtAddGroup"++ + integer_to_list(ExtNum+1)), + typespec=#type{def=#'SEQUENCE'{ + extaddgroup=Number, + components=ExtGroupComps}}, + prop='OPTIONAL'}|Acc]); +extaddgroup2sequence([C|T],ExtNum,Acc) -> + extaddgroup2sequence(T,ExtNum,[C|Acc]); +extaddgroup2sequence([],_,Acc) -> lists:reverse(Acc). diff --git a/lib/asn1/src/asn1ct_parser2.erl b/lib/asn1/src/asn1ct_parser2.erl index 007d390a1b..7301f49085 100644 --- a/lib/asn1/src/asn1ct_parser2.erl +++ b/lib/asn1/src/asn1ct_parser2.erl @@ -769,9 +769,11 @@ resolve_module(_Type, Current, undefined) -> Current; resolve_module(Type, Current, Imports) -> case [Mod || #'SymbolsFromModule'{symbols = S, module = Mod} <- Imports, - #'Externaltypereference'{type = T} <- S, + #'Externaltypereference'{type = T} <- S, Type == T] of - [#'Externaltypereference'{type = Mod}] -> Mod; + [#'Externaltypereference'{type = Mod}|_] -> Mod; + %% This allows the same symbol to be imported several times + %% which ought to be checked elsewhere and flagged as an error [] -> Current end. diff --git a/lib/asn1/src/asn1rt_per_bin.erl b/lib/asn1/src/asn1rt_per_bin.erl index a124c7553d..85988aa21d 100644 --- a/lib/asn1/src/asn1rt_per_bin.erl +++ b/lib/asn1/src/asn1rt_per_bin.erl @@ -18,7 +18,6 @@ %% %% -module(asn1rt_per_bin). - %% encoding / decoding of PER aligned -include("asn1_records.hrl"). @@ -57,7 +56,7 @@ encode_NumericString/2, decode_NumericString/2, encode_ObjectDescriptor/2, decode_ObjectDescriptor/1 ]). --export([complete_bytes/1, getbits/2, getoctets/2]). +-export([complete_bytes/1, getbits/2, getoctets/2, minimum_bits/1]). -define('16K',16384). -define('32K',32768). @@ -695,21 +694,28 @@ encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val -> {octets,[Val2]}; Range =< 65536 -> {octets,<<Val2:16>>}; - Range =< 16#1000000 -> - Octs = eint_positive(Val2), - [{bits,2,length(Octs)-1},{octets,Octs}]; - Range =< 16#100000000 -> - Octs = eint_positive(Val2), - [{bits,2,length(Octs)-1},{octets,Octs}]; - Range =< 16#10000000000 -> - Octs = eint_positive(Val2), - [{bits,3,length(Octs)-1},{octets,Octs}]; + Range =< (1 bsl (255*8)) -> + Octs = binary:encode_unsigned(Val2), + RangeOcts = binary:encode_unsigned(Range - 1), + OctsLen = erlang:byte_size(Octs), + RangeOctsLen = erlang:byte_size(RangeOcts), + LengthBitsNeeded = minimum_bits(RangeOctsLen - 1), + [{bits, LengthBitsNeeded, OctsLen - 1}, {octets, Octs}]; true -> exit({not_supported,{integer_range,Range}}) end; encode_constrained_number(Range,Val) -> exit({error,{asn1,{integer_range,Range,value,Val}}}). +%% For some reason the minimum bits needed in the length field in encoding of +%% constrained whole numbers must always be atleast 2? +minimum_bits(N) when N < 4 -> 2; +minimum_bits(N) when N < 8 -> 3; +minimum_bits(N) when N < 16 -> 4; +minimum_bits(N) when N < 32 -> 5; +minimum_bits(N) when N < 64 -> 6; +minimum_bits(N) when N < 128 -> 7; +minimum_bits(_N) -> 8. decode_constrained_number(Buffer,{Lb,Ub}) -> Range = Ub - Lb + 1, @@ -738,18 +744,12 @@ decode_constrained_number(Buffer,{Lb,Ub}) -> getoctets(Buffer,1); Range =< 65536 -> getoctets(Buffer,2); - Range =< 16#1000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,3}), - {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; - Range =< 16#100000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,4}), - {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; - Range =< 16#10000000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,5}), - {Octs,Bytes3} = getoctets_as_list(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; + Range =< (1 bsl (255*8)) -> + OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)), + RangeOctLen = length(OList), + {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}), + {Octs, RestBytes} = getoctets_as_list(Bytes, Len), + {binary:decode_unsigned(binary:list_to_bin(Octs)), RestBytes}; true -> exit({not_supported,{integer_range,Range}}) end, diff --git a/lib/asn1/src/asn1rt_per_bin_rt2ct.erl b/lib/asn1/src/asn1rt_per_bin_rt2ct.erl index 750b59aba6..46d4bcb065 100644 --- a/lib/asn1/src/asn1rt_per_bin_rt2ct.erl +++ b/lib/asn1/src/asn1rt_per_bin_rt2ct.erl @@ -18,7 +18,6 @@ %% %% -module(asn1rt_per_bin_rt2ct). - %% encoding / decoding of PER aligned -include("asn1_records.hrl"). @@ -605,19 +604,13 @@ encode_constrained_number({Lb,Ub},Val) when Val >= Lb, Ub >= Val -> Range =< 65536 -> % Size = {octets,<<Val2:16>>}; [20,2,<<Val2:16>>]; - Range =< 16#1000000 -> - Octs = eint_positive(Val2), -% [{bits,2,length(Octs)-1},{octets,Octs}]; - Len = length(Octs), - [10,2,Len-1,20,Len,Octs]; - Range =< 16#100000000 -> - Octs = eint_positive(Val2), - Len = length(Octs), - [10,2,Len-1,20,Len,Octs]; - Range =< 16#10000000000 -> - Octs = eint_positive(Val2), - Len = length(Octs), - [10,3,Len-1,20,Len,Octs]; + Range =< (1 bsl (255*8)) -> + Octs = binary:encode_unsigned(Val2), + RangeOcts = binary:encode_unsigned(Range - 1), + OctsLen = erlang:byte_size(Octs), + RangeOctsLen = erlang:byte_size(RangeOcts), + LengthBitsNeeded = asn1rt_per_bin:minimum_bits(RangeOctsLen - 1), + [10,LengthBitsNeeded,OctsLen-1,20,OctsLen,Octs]; true -> exit({not_supported,{integer_range,Range}}) end; @@ -661,18 +654,12 @@ decode_constrained_number(Buffer,{Lb,_Ub},Range) -> getoctets(Buffer,1); Range =< 65536 -> getoctets(Buffer,2); - Range =< 16#1000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,3}), - {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; - Range =< 16#100000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,4}), - {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; - Range =< 16#10000000000 -> - {Len,Bytes2} = decode_length(Buffer,{1,5}), - {Octs,Bytes3} = getoctets_as_bin(Bytes2,Len), - {dec_pos_integer(Octs),Bytes3}; + Range =< (1 bsl (255*8)) -> + OList = binary:bin_to_list(binary:encode_unsigned(Range - 1)), + RangeOctLen = length(OList), + {Len, Bytes} = decode_length(Buffer, {1, RangeOctLen}), + {Octs, RestBytes} = getoctets_as_bin(Bytes, Len), + {binary:decode_unsigned(Octs), RestBytes}; true -> exit({not_supported,{integer_range,Range}}) end, diff --git a/lib/asn1/test/Makefile b/lib/asn1/test/Makefile index 1621abbec9..6e6374baf1 100644 --- a/lib/asn1/test/Makefile +++ b/lib/asn1/test/Makefile @@ -156,13 +156,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/asn1_SUITE_data - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) asn1.spec asn1.cover $(INSTALL_PROGS) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - cd asn1_SUITE_data; tar cfh $(RELSYSDIR)/asn1_SUITE_data.tar * - cd $(RELSYSDIR)/asn1_SUITE_data; tar xf $(RELSYSDIR)/asn1_SUITE_data.tar - rm $(RELSYSDIR)/asn1_SUITE_data.tar + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/asn1_SUITE_data" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) asn1.spec asn1.cover $(INSTALL_PROGS) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + cd asn1_SUITE_data; tar cfh "$(RELSYSDIR)/asn1_SUITE_data.tar" * + cd "$(RELSYSDIR)/asn1_SUITE_data"; tar xf "$(RELSYSDIR)/asn1_SUITE_data.tar" + rm "$(RELSYSDIR)/asn1_SUITE_data.tar" release_docs_spec: diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 3b9a7532c0..56f31de638 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -773,6 +773,7 @@ per_open_type(Config, Rule, Opts) -> testConstraints(Config) -> test(Config, fun testConstraints/3). testConstraints(Config, Rule, Opts) -> asn1_test_lib:compile("Constraints", Config, [Rule|Opts]), + asn1_test_lib:compile("LargeConstraints", Config, [Rule|Opts]), testConstraints:int_constraints(Rule). @@ -1236,6 +1237,27 @@ testName2Number(Config) -> 0 = 'S1AP-IEs':name2num_CauseMisc('control-processing-overload'), 'unknown-PLMN' = 'S1AP-IEs':num2name_CauseMisc(5), + + %% OTP-10144 + %% Test that n2n option generates name2num and num2name functions supporting + %% values not within the extension root if the enumeration type has an + %% extension marker. + N2NOptionsExt = [{n2n, 'NoExt'}, {n2n, 'Ext'}, {n2n, 'Ext2'}], + asn1_test_lib:compile("EnumN2N", Config, N2NOptionsExt), + %% Previously, name2num and num2name was not generated if the type didn't + %% have an extension marker: + 0 = 'EnumN2N':name2num_NoExt('blue'), + 2 = 'EnumN2N':name2num_NoExt('green'), + blue = 'EnumN2N':num2name_NoExt(0), + green = 'EnumN2N':num2name_NoExt(2), + + %% Test enumeration extension: + 7 = 'EnumN2N':name2num_Ext2('orange'), + orange = 'EnumN2N':num2name_Ext2(7), + %% 7 is not defined in Ext, only in Ext2. + {asn1_enum, 7} = 'EnumN2N':num2name_Ext(7), + 7 = 'EnumN2N':name2num_Ext({asn1_enum, 7}), + 42 = 'EnumN2N':name2num_Ext2({asn1_enum, 42}), ok. ticket_7407(Config) -> diff --git a/lib/asn1/test/asn1_SUITE_data/Constraints.py b/lib/asn1/test/asn1_SUITE_data/Constraints.py index b18c29bd89..87243121f7 100644 --- a/lib/asn1/test/asn1_SUITE_data/Constraints.py +++ b/lib/asn1/test/asn1_SUITE_data/Constraints.py @@ -4,9 +4,14 @@ BEGIN -- Single Value SingleValue ::= INTEGER (1) SingleValue2 ::= INTEGER (1..20) +predefined INTEGER ::= 1 +SingleValue3 ::= INTEGER (predefined | 5 | 10) Range2to19 ::= INTEGER (1<..<20) Range10to20 ::= INTEGER (10..20) ContainedSubtype ::= INTEGER (INCLUDES Range10to20) +-- Some ranges for additional constrained number testing. +LongLong ::= INTEGER (0..18446744073709551615) +Range256to65536 ::= INTEGER (256..65536) FixedSize ::= OCTET STRING (SIZE(10)) FixedSize2 ::= OCTET STRING (SIZE(10|20)) VariableSize ::= OCTET STRING (SIZE(1..10)) @@ -51,6 +56,7 @@ Thing ::= INTEGER {fred (0),fred2 (1),fred3 (2)} AnotherThing ::= Thing (fred | fred2) I ::= INTEGER (0|15..269) -- OTP-5457 +X1 ::= INTEGER (1..4 | 8 | 10 | 20) -- OTP-9946 -- OTP-5511 diff --git a/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1 b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1 new file mode 100644 index 0000000000..a724f2f3f5 --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/EnumN2N.asn1 @@ -0,0 +1,25 @@ +EnumN2N DEFINITIONS AUTOMATIC TAGS ::= +BEGIN + +NoExt ::= ENUMERATED { + blue(0), + red(1), + green(2) +} + +Ext ::= ENUMERATED { + blue(0), + red(1), + green(2), + ... +} + +Ext2 ::= ENUMERATED { + blue(0), + red(1), + green(2), + ..., + orange(7) +} + +END diff --git a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn index fc244c30a2..cacef8b922 100644 --- a/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn +++ b/lib/asn1/test/asn1_SUITE_data/Extension-Addition-Group.asn @@ -79,4 +79,22 @@ Ax3 ::= SEQUENCE { } -- { a 253, b TRUE, s {sa 17, sb TRUE, sextaddgroup 11}} + +-- This is to test the case with more than one ExtensionAdditionGroup +-- which did not work before + +AS-Config ::= SEQUENCE { + a INTEGER, + b BOOLEAN, + c OCTET STRING, + ..., + [[ sourceSystemInformationBlockType1Ext OCTET STRING OPTIONAL, + sourceOtherConfig-r9 INTEGER + ]], + [[ sourceSCellConfigList-r10 OCTET STRING OPTIONAL + ]] +} + + + END diff --git a/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py b/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py new file mode 100644 index 0000000000..68c7616b62 --- /dev/null +++ b/lib/asn1/test/asn1_SUITE_data/LargeConstraints.py @@ -0,0 +1,9 @@ +LargeConstraints DEFINITIONS ::= +BEGIN + +-- Maximum number that can be encoded as a constrained whole number: 1 bsl (255*8) +-- The number of octets needed to represent a number cannot be more than 255 +-- As the length field is encoded as a 8-bit bitfield. +RangeMax ::= INTEGER (1..126238304966058622268417487065116999845484776053576109500509161826268184136202698801551568013761380717534054534851164138648904527931605160527688095259563605939964364716019515983399209962459578542172100149937763938581219604072733422507180056009672540900709554109516816573779593326332288314873251559077853068444977864803391962580800682760017849589281937637993445539366428356761821065267423102149447628375691862210717202025241630303118559188678304314076943801692528246980959705901641444238894928620825482303431806955690226308773426829503900930529395181208739591967195841536053143145775307050594328881077553168201547776) + +END diff --git a/lib/asn1/test/testConstraints.erl b/lib/asn1/test/testConstraints.erl index cb15a24359..543c106e8a 100644 --- a/lib/asn1/test/testConstraints.erl +++ b/lib/asn1/test/testConstraints.erl @@ -52,8 +52,6 @@ int_constraints(Rules) -> ?line {error,_Reason2} = asn1_wrapper:encode('Constraints','SingleValue',1000) end, - - %%========================================================== %% SingleValue2 ::= INTEGER (1..20) @@ -86,7 +84,21 @@ int_constraints(Rules) -> asn1_wrapper:encode('Constraints','SingleValue',1000) end, + %%========================================================== + %% SingleValue3 ::= INTEGER (Predefined | 5 | 10) + %% Testcase for OTP-10139. A single value subtyping of an integer type + %% where one value is predefined. + %%========================================================== + ?line {ok,BytesSV3} = asn1_wrapper:encode('Constraints','SingleValue3',1), + ?line {ok,1} = asn1_wrapper:decode('Constraints','SingleValue3', + lists:flatten(BytesSV3)), + ?line {ok,BytesSV3_2} = asn1_wrapper:encode('Constraints','SingleValue3',5), + ?line {ok,5} = asn1_wrapper:decode('Constraints','SingleValue3', + lists:flatten(BytesSV3_2)), + ?line {ok,BytesSV3_3} = asn1_wrapper:encode('Constraints','SingleValue3',10), + ?line {ok,10} = asn1_wrapper:decode('Constraints','SingleValue3', + lists:flatten(BytesSV3_3)), %%========================================================== %% Range2to19 ::= INTEGER (1<..<20) @@ -116,7 +128,65 @@ int_constraints(Rules) -> ?line {error,_Reason6} = asn1_wrapper:encode('Constraints','Range2to19',20) end, + + %%========================================================== + %% Tests for Range above 16^4 up to maximum supported by asn1 assuming the + %% octet length field is encoded on max 8 bits + %%========================================================== + LastNumWithoutLengthEncoding = 65536, + ?line {ok,BytesFoo} = asn1_wrapper:encode('Constraints','Range256to65536', + LastNumWithoutLengthEncoding), + ?line {ok,LastNumWithoutLengthEncoding} = + asn1_wrapper:decode('Constraints','Range256to65536',lists:flatten(BytesFoo)), + + FirstNumWithLengthEncoding = 65537, + ?line {ok,BytesBar} = asn1_wrapper:encode('LargeConstraints','RangeMax', + FirstNumWithLengthEncoding), + ?line {ok,FirstNumWithLengthEncoding} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBar)), + + FirstNumOver16_6 = 16777217, + ?line {ok, BytesBaz} = + asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_6), + ?line {ok, FirstNumOver16_6} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBaz)), + FirstNumOver16_8 = 4294967297, + ?line {ok, BytesQux} = + asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_8), + ?line {ok, FirstNumOver16_8} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesQux)), + + FirstNumOver16_10 = 1099511627776, + ?line {ok, BytesBur} = + asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10), + ?line {ok, FirstNumOver16_10} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)), + + FirstNumOver16_10 = 1099511627776, + ?line {ok, BytesBur} = + asn1_wrapper:encode('LargeConstraints','RangeMax', FirstNumOver16_10), + ?line {ok, FirstNumOver16_10} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesBur)), + + HalfMax = 1 bsl (128*8), + ?line {ok, BytesHalfMax} = + asn1_wrapper:encode('LargeConstraints','RangeMax', HalfMax), + ?line {ok, HalfMax} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesHalfMax)), + + Max = 1 bsl (255*8), + ?line {ok, BytesMax} = + asn1_wrapper:encode('LargeConstraints','RangeMax', Max), + ?line {ok, Max} = + asn1_wrapper:decode('LargeConstraints','RangeMax',lists:flatten(BytesMax)), + + %% Random number within longlong range + LongLong = 12672809400538808320, + ?line {ok, BytesLongLong} = + asn1_wrapper:encode('Constraints','LongLong', LongLong), + ?line {ok, LongLong} = + asn1_wrapper:decode('Constraints','LongLong',lists:flatten(BytesLongLong)), %%========================================================== %% Constraint Combinations (Duboisson p. 285) @@ -127,17 +197,26 @@ int_constraints(Rules) -> ?line {ok,0} = asn1_wrapper:decode('Constraints','I',Bytes12), ?line {ok,Bytes13} = asn1_wrapper:encode('Constraints','I',20), ?line {ok,20} = asn1_wrapper:decode('Constraints','I',Bytes13), - + + %%========================================================== + %% Constraint Combinations (Duboisson p. 285) + %% X1 ::= INTEGER (1..4|8|10|20) + %%========================================================== + + ?line {ok,Bytes14} = asn1_wrapper:encode('Constraints','X1',1), + ?line {ok,1} = asn1_wrapper:decode('Constraints','X1',Bytes14), + ?line {ok,Bytes15} = asn1_wrapper:encode('Constraints','X1',20), + ?line {ok,20} = asn1_wrapper:decode('Constraints','X1',Bytes15), %%========================================================== %% SIZE Constraint (Duboisson p. 268) %% T ::= IA5String (SIZE (1|2, ..., SIZE (1|2|3))) %% T2 ::= IA5String (SIZE (1|2, ..., 3)) %%========================================================== - ?line {ok,Bytes14} = asn1_wrapper:encode('Constraints','T',"IA"), - ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T',Bytes14), - ?line {ok,Bytes15} = asn1_wrapper:encode('Constraints','T2',"IA"), - ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T2',Bytes15). + ?line {ok,Bytes16} = asn1_wrapper:encode('Constraints','T',"IA"), + ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T',Bytes16), + ?line {ok,Bytes17} = asn1_wrapper:encode('Constraints','T2',"IA"), + ?line {ok,"IA"} = asn1_wrapper:decode('Constraints','T2',Bytes17). refed_NNL_name(_Erule) -> diff --git a/lib/asn1/vsn.mk b/lib/asn1/vsn.mk index ae92fcb11b..04f4b22421 100644 --- a/lib/asn1/vsn.mk +++ b/lib/asn1/vsn.mk @@ -1,2 +1,2 @@ -#next version number to use is 1.6.15 | 1.7 | 2.0 -ASN1_VSN = 1.6.19 +#next version number to use is 2.0 +ASN1_VSN = 1.7 diff --git a/lib/common_test/doc/src/Makefile b/lib/common_test/doc/src/Makefile index d9651f13b0..99161ce68a 100644 --- a/lib/common_test/doc/src/Makefile +++ b/lib/common_test/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2011. All Rights Reserved. +# Copyright Ericsson AB 2003-2012. 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 @@ -46,7 +46,8 @@ CT_MODULES = \ ct_rpc \ ct_snmp \ unix_telnet \ - ct_slave + ct_slave \ + ct_netconfc CT_XML_FILES = $(CT_MODULES:=.xml) @@ -123,7 +124,7 @@ $(HTMLDIR)/%.gif: %.gif docs: pdf html man -$(CT_XML_FILES): +$(CT_XML_FILES): %.xml: ../../src/%.erl escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -preprocess true -i $(XMERL_DIR)/include \ -i ../../../test_server/include -i ../../include \ -i ../../../../erts/lib/kernel/include -i ../../../../lib/kernel/include \ @@ -158,18 +159,18 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man1 - $(INSTALL_DATA) $(MAN1DIR)/* $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/common_test/doc/src/basics_chapter.xml b/lib/common_test/doc/src/basics_chapter.xml index 20141d2561..ff6ea6c557 100644 --- a/lib/common_test/doc/src/basics_chapter.xml +++ b/lib/common_test/doc/src/basics_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2009</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -141,8 +141,9 @@ individual test case. </p> <p> - The test suite module must conform to a callback interface specified - by the CT test server. See the + The test suite module must conform to a + <seealso marker="common_test">callback interface</seealso> + specified by the CT test server. See the <seealso marker="write_test_chapter#intro">Writing Test Suites</seealso> chapter for more information. </p> diff --git a/lib/common_test/doc/src/common_test_app.xml b/lib/common_test/doc/src/common_test_app.xml index c7f6c7ce5c..6babdb93af 100644 --- a/lib/common_test/doc/src/common_test_app.xml +++ b/lib/common_test/doc/src/common_test_app.xml @@ -187,9 +187,13 @@ test case is allowed to execute (including <c>init_per_testcase/2</c> and <c>end_per_testcase/2</c>). If the timetrap time is exceeded, the test case fails with reason - <c>timetrap_timeout</c>. If a <c>TimeFunc</c> function is specified, - it will be called initially and must return a value on - <c>TimeVal</c> format.</p> + <c>timetrap_timeout</c>. A <c>TimeFunc</c> function can be used to + set a new timetrap by returning a <c>TimeVal</c>. It may also be + used to trigger a timetrap timeout by, at some point, returning a + value other than a <c>TimeVal</c>. (See the + <seealso marker="write_test_chapter#timetraps">User's Guide</seealso> + for details). + </p> <p>The <c>require</c> tag specifies configuration variables that are required by test cases (and/or configuration functions) @@ -313,9 +317,12 @@ test case is allowed to execute (including <c>init_per_testcase/2</c> and <c>end_per_testcase/2</c>). If the timetrap time is exceeded, the test case fails with reason - <c>timetrap_timeout</c>. If a <c>TimeFunc</c> function is specified, - it will be called initially and must return a value on - <c>TimeVal</c> format.</p> + <c>timetrap_timeout</c>. A <c>TimeFunc</c> function can be used to + set a new timetrap by returning a <c>TimeVal</c>. It may also be + used to trigger a timetrap timeout by, at some point, returning a + value other than a <c>TimeVal</c>. (See the + <seealso marker="write_test_chapter#timetraps">User's Guide</seealso> + for details).</p> <p>The <c>require</c> tag specifies configuration variables that are required by test cases (and/or configuration functions) @@ -494,9 +501,12 @@ exceeded, the test case fails with reason <c>timetrap_timeout</c>. <c>init_per_testcase/2</c> and <c>end_per_testcase/2</c> are included in the - timetrap time. If a <c>TimeFunc</c> function is specified, - it will be called before the test case (or <c>init_per_testcase/2</c>) - and must return a value on <c>TimeVal</c> format.</p> + timetrap time. A <c>TimeFunc</c> function can be used to + set a new timetrap by returning a <c>TimeVal</c>. It may also be + used to trigger a timetrap timeout by, at some point, returning a + value other than a <c>TimeVal</c>. (See the + <seealso marker="write_test_chapter#timetraps">User's Guide</seealso> + for details).</p> <p>The <c>require</c> tag specifies configuration variables that are required by the test case (and/or <c>init/end_per_testcase/2</c>). diff --git a/lib/common_test/doc/src/ct_hooks_chapter.xml b/lib/common_test/doc/src/ct_hooks_chapter.xml index 8505ee8469..014507c886 100644 --- a/lib/common_test/doc/src/ct_hooks_chapter.xml +++ b/lib/common_test/doc/src/ct_hooks_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2011</year><year>2011</year> + <year>2011</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -189,6 +189,22 @@ it.</p> </section> + <section> + <title>External configuration data and Logging</title> + <p>It's possible in the CTH to read configuration data values + by calling <c>ct:get_config/1/2/3</c> (as explained in the + <seealso marker="config_file_chapter#require_config_data"> + External configuration data</seealso> + chapter). The config variables in question must, as always, first have been + <c>required</c> by means of a suite-, group-, or test case info function, + or the <c>ct:require/1/2</c> function. Note that the latter can also be used + in CT hook functions.</p> + <p>The CT hook functions may call any of the logging functions available + in the <c>ct</c> interface to print information to the log files, or to + add comments in the suite overview page. + </p> + </section> + </section> <marker id="manipulating"/> @@ -201,11 +217,13 @@ functions for a CTH follow a common interface, this interface is described below.</p> - <p>It is only possible to hook into test function which exists in the test - suite. So in order for a CTH to hook in before - <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso>, - the <seealso marker="common_test#Module:init_per_suite-1">init_per_suite</seealso> - function must exist in the test suite.</p> + <p>Common Test will always call all available hook functions, even pre- and post + hooks for configuration functions that are not implemented in the suite. + For example, <c>pre_init_per_suite(x_SUITE, ...)</c> and + <c>post_init_per_suite(x_SUITE, ...)</c> will be called for test suite + <c>x_SUITE</c>, even if it doesn't export <c>init_per_suite/1</c>. This feature + makes it possible to use hooks as configuration fallbacks, or even + completely replace all configuration functions with hook functions.</p> <marker id="pre"/> <section> @@ -429,6 +447,16 @@ terminate(State) -> <seealso marker="sasl:sasl_app">SASL</seealso> events report using the normal SASL mechanisms. </cell> </row> + <row> + <cell>cth_surefire</cell> + <cell>no</cell> + <cell>Captures all test results and outputs them as surefire XML into + a file. The file which is created is by default called junit_report.xml. + The name can be by setting the path option for this hook. e.g. + <code>-ct_hooks cth_surefire [{path,"/tmp/report.xml"}]</code> + Surefire XML can forinstance be used by Jenkins to display test + results.</cell> + </row> </table> </section> diff --git a/lib/common_test/doc/src/ct_run.xml b/lib/common_test/doc/src/ct_run.xml index b01ab3667d..df1defa664 100644 --- a/lib/common_test/doc/src/ct_run.xml +++ b/lib/common_test/doc/src/ct_run.xml @@ -4,7 +4,7 @@ <comref> <header> <copyright> - <year>2007</year><year>2011</year> + <year>2007</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -88,11 +88,13 @@ [-step [config | keep_inactive]] [-config ConfigFile1 ConfigFile2 .. ConfigFileN] [-userconfig CallbackModule1 ConfigString1 and CallbackModule2 - ConfigString2 and .. and CallbackModuleN ConfigStringN] + ConfigString2 and .. CallbackModuleN ConfigStringN] [-decrypt_key Key] | [-decrypt_file KeyFile] [-label Label] [-logdir LogDir] [-logopts LogOpts] + [-verbosity GenVLevel | [Category1 VLevel1 and + Category2 VLevel2 and .. CategoryN VLevelN]] [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]] [-stylesheet CSSFile] [-cover CoverCfgFile] @@ -103,10 +105,14 @@ [-no_auto_compile] [-muliply_timetraps Multiplier] [-scale_timetraps] + [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc] [-repeat N [-force_stop]] | [-duration HHMMSS [-force_stop]] | [-until [YYMoMoDD]HHMMSS [-force_stop]] - [-basic_html]</pre> + [-basic_html] + [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and .. + CTHModuleN CTHOptsN] + </pre> </section> <section> <title>Run tests using test specification</title> @@ -119,6 +125,8 @@ [-label Label] [-logdir LogDir] [-logopts LogOpts] + [-verbosity GenVLevel | [Category1 VLevel1 and + Category2 VLevel2 and .. CategoryN VLevelN]] [-allow_user_terms] [-silent_connections [ConnType1 ConnType2 .. ConnTypeN]] [-stylesheet CSSFile] @@ -130,10 +138,14 @@ [-no_auto_compile] [-muliply_timetraps Multiplier] [-scale_timetraps] + [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc] [-repeat N [-force_stop]] | [-duration HHMMSS [-force_stop]] | [-until [YYMoMoDD]HHMMSS [-force_stop]] - [-basic_html]</pre> + [-basic_html] + [-ct_hooks CTHModule1 CTHOpts1 and CTHModule2 CTHOpts2 and .. + CTHModuleN CTHOptsN] + </pre> </section> <section> <title>Run tests in web based GUI</title> @@ -145,11 +157,14 @@ [-userconfig CallbackModule1 ConfigString1 and CallbackModule2 ConfigString2 and .. and CallbackModuleN ConfigStringN] [-logopts LogOpts] + [-verbosity GenVLevel | [Category1 VLevel1 and + Category2 VLevel2 and .. CategoryN VLevelN]] [-decrypt_key Key] | [-decrypt_file KeyFile] [-include InclDir1 InclDir2 .. InclDirN] [-no_auto_compile] [-muliply_timetraps Multiplier] [-scale_timetraps] + [-create_priv_dir auto_per_run | auto_per_tc | manual_per_tc] [-basic_html]</pre> </section> <section> diff --git a/lib/common_test/doc/src/getting_started_chapter.xml b/lib/common_test/doc/src/getting_started_chapter.xml index 7de0912036..039578dd2e 100644 --- a/lib/common_test/doc/src/getting_started_chapter.xml +++ b/lib/common_test/doc/src/getting_started_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2007</year><year>2010</year> + <year>2007</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -90,7 +90,7 @@ <p>As you can understand from the illustration above, Common Test requires that a test case generates a runtime error to indicate failure (e.g. by causing a bad match error or by calling <c>exit/1</c>, preferrably - through the <c>ct:fail/1/2</c> help function). A succesful execution is + through the <c>ct:fail/1,2</c> help function). A succesful execution is indicated by means of a normal return from the test case function. </p> </section> @@ -98,7 +98,8 @@ <section> <title>A simple test suite</title> <p>As you've seen in the basics chapter, the test suite module implements - callback functions (mandatory or optional) for various purposes, e.g: + <seealso marker="common_test">callback functions</seealso> + (mandatory or optional) for various purposes, e.g: <list> <item>Init/end configuration function for the test suite</item> <item>Init/end configuration function for a test case</item> @@ -197,7 +198,7 @@ <pre> 1> ct:run_test([{suite, "check_log_SUITE"}]).</pre> <p> - The result from running our test is printed in log files on HTML format + The result from running our test is printed in log files in HTML format (stored in unique log directories on different level). This illustration shows the log file structure: </p> diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index da0b6b2d65..050b8774ca 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -32,6 +32,308 @@ <file>notes.xml</file> </header> +<section><title>Common_Test 1.6.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Common Test adds the test suite directories to the code + path before executing the tests. These directories should + also be removed from the code path at the end of the test + run, which, prior to this fix, was not performed.</p> + <p> + Own Id: OTP-9595</p> + </item> + <item> + <p> + An entry is now created in the index.html file (i.e. the + overview file for the test run) for each repeated test + during a test run. This was previously not the case. Note + that in the top level (logdir) index file, however, only + the last test result is listed. For example, given the + test spec: + [{merge_tests,false},{dirs,"test1"},{dirs,"test1"}]. In + the index file for the test run (under + Logdir/ct_run.Node.Date.Time), both tests are listed. In + the top level index file (under Logdir), only the last + test is listed (one has to find the previous results + through the all_runs.html file).</p> + <p> + Own Id: OTP-9634 Aux Id: seq11924 </p> + </item> + <item> + <p> + After a test case timeout or abortion, the + end_per_testcase function executes on a new dedicated + process. The group leader for this process should be set + to the IO server for the test case, which was not done + properly. The result of this error was that no warnings + about end_per_testcase failing or timing out were ever + printed in the test case log. Also, help functions such + as e.g. test_server:stop_node/1, attempting to + synchronize with the IO server, would hang. The fault has + been corrected.</p> + <p> + Own Id: OTP-9666</p> + </item> + <item> + <p> + The ct:get_status/0 function would cause the calling + process to receive 'DOWN' messages if no tests were + running at the time of the call. This bug has been fixed.</p> + <p> + Own Id: OTP-9830 Aux Id: seq11975 </p> + </item> + <item> + <p> + A deadlock situation could occur if Common Test is + forwarding error_handler printouts to Test Server at the + same time a new test case is starting. This error has + been fixed.</p> + <p> + Own Id: OTP-9894</p> + </item> + <item> + <p> + A link to the ct_run program is now created, as expected, + in the installation bin directory (default + /usr/local/bin) during 'make install'.</p> + <p> + Own Id: OTP-9898</p> + </item> + <item> + <p> + Using the repeat, duration or until option with + ct:run_test/1, would cause an infinite loop. This has + been fixed.</p> + <p> + Own Id: OTP-9899</p> + </item> + <item> + <p> + Two or more test cases executing in parallel and printing + to screen at the same time with ct:pal/2/3 or + ct:print/2/3 could write into each other's "slots" and + create a mess of mixed strings. In order to avoid this, + only a single IO message is now ever sent per printout + call.</p> + <p> + Own Id: OTP-9900 Aux Id: OTP-9904 </p> + </item> + <item> + <p> + When a test case was killed because of a timetrap + timeout, the current location (suite, case and line) was + not printed correctly in the log files. This has been + corrected.</p> + <p> + Own Id: OTP-9930 Aux Id: seq12002 </p> + </item> + <item> + <p> + The wrong exit location was printed in the log file when + ct:fail/1 or ct_fail/2 was called.</p> + <p> + Own Id: OTP-9933 Aux Id: seq12002 </p> + </item> + <item> + <p> + Test Server and Common Test would add new error handlers + with each test run and fail to remove previously added + ones. In the case of Test Server, this would only happen + if SASL was not running on the test node. This has been + fixed.</p> + <p> + Own Id: OTP-9941 Aux Id: seq12009 </p> + </item> + <item> + <p> + If a test case process was terminated due to an exit + signal from a linked process, Test Server failed to + report the correct name of the suite and case to the + framework. This has been corrected.</p> + <p> + Own Id: OTP-9958 Aux Id: OTP-9855 </p> + </item> + <item> + <p> + When starting a test with ct_run and adding a directory + to the code path using -pa or -pz (preceding -erl_args), + Common Test would delete any existing directory in the + code path with the same base name (see + filename:basename/1) as the directory being added. This + has been fixed.</p> + <p> + Own Id: OTP-9964</p> + </item> + <item> + <p> + If passing two or more directories with the same base + name (see filename:basename/1) to Common Test with ct_run + -pa, only one of the directories would actually be added.</p> + <p> + Own Id: OTP-9975 Aux Id: seq12019 </p> + </item> + <item> + <p> + Configuration data required by the group info function + was deleted before the call to post_end_per_group, which + made it impossible for the hook function to read and use + the data in question. This has been fixed.</p> + <p> + Own Id: OTP-9989</p> + </item> + <item> + <p> + Disabling built-in hooks in a test specification was + ignored, this has now been fixed.</p> + <p> + Own Id: OTP-10009</p> + </item> + <item> + <p> + Various typographical errors corrected in documentation + for common_test, driver, erl_driver and windows + installation instructions. (Thanks to Tuncer Ayaz)</p> + <p> + Own Id: OTP-10037</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + A new optional feature has been introduced that enables + Common Test to generate priv_dir directory names that are + unique for each test case or config function. The name of + the option/flag is 'create_priv_dir' and it can be set to + value 'auto_per_run' (which is the default, existing, + behaviour), or 'auto_per_tc' or 'manual_per_tc'. If + 'auto_per_tc' is used, Test Server creates a dedicated + priv_dir automatically for each test case (which can be + very expensive in case of many and/or repeated cases). If + 'manual_per_tc' is used, the user needs to create the + priv_dir explicitly by calling the new function + ct:make_priv_dir/0.</p> + <p> + Own Id: OTP-9659 Aux Id: seq11930 </p> + </item> + <item> + <p> + A column for test case group name has been added to the + suite overview HTML log file.</p> + <p> + Own Id: OTP-9730 Aux Id: seq11952 </p> + </item> + <item> + <p> + It is now possible to use the post_end_per_testcase CT + hook function to print a comment for a test case in the + overview log file, even if the test case gets killed by a + timetrap or unknown exit signal, or if the + end_per_testcase function times out.</p> + <p> + Own Id: OTP-9855 Aux Id: seq11979 </p> + </item> + <item> + <p> + The pre- and post CT hook functions are now always called + for all configuration functions, even for configuration + functions that are not implemented in the test suite.</p> + <p> + Own Id: OTP-9880 Aux Id: seq11993 </p> + </item> + <item> + <p> + Common Test will now print error information (with a time + stamp) in the test case log file immediately when a test + case fails. This makes it easier to see when, in time, + the fault actually occured, and aid the job of locating + relevant trace and debug printouts in the log.</p> + <p> + Own Id: OTP-9904 Aux Id: seq11985, OTP-9900 </p> + </item> + <item> + <p> + Test Server has been modified to check the SASL + errlog_type parameter when receiving an error logger + event, so that it doesn't print reports of type that the + user has disabled.</p> + <p> + Own Id: OTP-9955 Aux Id: seq12013 </p> + </item> + <item> + <p> + The test specification term 'skip_groups' was implemented + in Common Test v1.6. It was never documented however, + which has now been attended to. Please see the Test + Specifications chapter in the User's Guide for + information.</p> + <p> + Own Id: OTP-9972</p> + </item> + <item> + <p> + The Common Test Master has been updated to use a CSS + style sheet for the html log files.</p> + <p> + Own Id: OTP-9973</p> + </item> + <item> + <p> + If the init_per_group/2 and end_per_group/2 functions are + not implemented in the test suite, Common Test calls it's + own local init- and end functions - previously named + ct_init_per_group/2 and ct_end_per_group/2 - when a group + is executed. These functions have been renamed + init_per_group/2 and end_per_group/2 respectively. Note + that this may affect any user event handler identifying + events by the old names.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9986 Aux Id: OTP-9992 </p> + </item> + <item> + <p> + By specifying a user defined function ({M,F,A} or fun) as + timetrap value, either by means of an info function or by + calling ct:timetrap/1, it is now possible to set a + timetrap that will be triggered when the user function + returns.</p> + <p> + Own Id: OTP-9988 Aux Id: OTP-9501, seq11894 </p> + </item> + <item> + <p> + If the optional configuration functions init_per_suite/1 + and end_per_suite/1 are not implemented in the test + suite, local Common Test versions of these functions are + called instead, and will be displayed in the overview log + file. Any printouts made by the pre- or + post_init_per_suite and pre- or post_end_per_suite hook + functions are saved in the log files for these functions.</p> + <p> + Own Id: OTP-9992</p> + </item> + <item> + <p> + A hook has been added to common test which outputs + surefire XML for usage together with CI tools such as + Jenkins. To enable the hook pass '-ct_hooks cth_surefire' + to ct_run. See the CTH documentation for more details.</p> + <p> + Own Id: OTP-9995</p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.6</title> <section><title>Improvements and New Features</title> diff --git a/lib/common_test/doc/src/ref_man.xml b/lib/common_test/doc/src/ref_man.xml index a9fdef7359..6fede88434 100644 --- a/lib/common_test/doc/src/ref_man.xml +++ b/lib/common_test/doc/src/ref_man.xml @@ -4,7 +4,7 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2003</year><year>2011</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -71,6 +71,7 @@ <xi:include href="ct_cover.xml"/> <xi:include href="ct_ftp.xml"/> <xi:include href="ct_ssh.xml"/> + <xi:include href="ct_netconfc.xml"/> <xi:include href="ct_rpc.xml"/> <xi:include href="ct_snmp.xml"/> <xi:include href="ct_telnet.xml"/> diff --git a/lib/common_test/doc/src/run_test_chapter.xml b/lib/common_test/doc/src/run_test_chapter.xml index 848f278fa6..bc0af63790 100644 --- a/lib/common_test/doc/src/run_test_chapter.xml +++ b/lib/common_test/doc/src/run_test_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2011</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -425,7 +425,7 @@ <p>Below is the test specification syntax. Test specifications can be used to run tests both in a single test host environment and in a distributed Common Test environment (Large Scale - Testing). The node parameters in the init term are only + Testing). The node parameters in the <c>init</c> term are only relevant in the latter (see the <seealso marker="ct_master_chapter#test_specifications">Large Scale Testing</seealso> chapter for information). For details on @@ -466,6 +466,9 @@ {logdir, LogDir}. {logdir, NodeRefs, LogDir}. + + {create_priv_dir, PrivDirOption}. + {create_priv_dir, NodeRefs, PrivDirOption}. {event_handler, EventHandlers}. {event_handler, NodeRefs, EventHandlers}. @@ -493,6 +496,9 @@ {skip_suites, DirRef, Suites, Comment}. {skip_suites, NodeRefs, DirRef, Suites, Comment}. + + {skip_groups, DirRef, Suite, GroupNames, Comment}. + {skip_groups, NodeRefs, DirRef, Suite, GroupNames, Comment}. {skip_cases, DirRef, Suite, Cases, Comment}. {skip_cases, NodeRefs, DirRef, Suite, Cases, Comment}. @@ -512,6 +518,7 @@ DirAlias = atom() Dir = string() LogDir = string() + PrivDirOption = auto_per_run | auto_per_tc | manual_per_tc EventHandlers = atom() | [atom()] InitArgs = [term()] CTHModules = [CTHModule | {CTHModule, CTHInitArgs} | {CTHModule, CTHInitArgs, CTHPriority}] @@ -521,8 +528,9 @@ Suites = atom() | [atom()] | all Suite = atom() Groups = GroupSpec | [GroupSpec] | all - GroupSpec = Group | {Group,Properties} | {Group,Properties,GroupSpec} - Group = atom() + GroupSpec = GroupName | {GroupName,Properties} | {GroupName,Properties,GroupSpec} + GroupName = atom() + GroupNames = GroupName | [GroupName] Cases = atom() | [atom()] | all Comment = string() | "" </pre> @@ -567,9 +575,9 @@ <item>Lastly, all suites for systems t3 are to be completely skipped and this should be explicitly noted in the log files.</item> </list> - <p>It is possible to specify initialization options for nodes defined in the - test specification. Currently, there are options to start the node and/or to - evaluate any function on the node. + <p>With the <c>init</c> term it's possible to specify initialization options + for nodes defined in the test specification. Currently, there are options + to start the node and/or to evaluate any function on the node. See the <seealso marker="ct_master_chapter#ct_slave">Automatic startup of the test target nodes</seealso> chapter for details.</p> <p>It is possible for the user to provide a test specification that diff --git a/lib/common_test/doc/src/write_test_chapter.xml b/lib/common_test/doc/src/write_test_chapter.xml index c0ec26ddcc..7b7e7af8ea 100644 --- a/lib/common_test/doc/src/write_test_chapter.xml +++ b/lib/common_test/doc/src/write_test_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2003</year><year>2011</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -29,7 +29,6 @@ <file>write_test_chapter.xml</file> </header> - <section> <marker id="intro"></marker> <title>Support for test suite authors</title> @@ -63,7 +62,8 @@ function in CT will not be able to locate it (at least not per default). </p> - <p>The <c>ct.hrl</c> header file must be included in all test suite files. + <p>It is also recommended that the <c>ct.hrl</c> header file is included + in all test suite modules. </p> <p>Each test suite module must export the function <c>all/0</c> @@ -71,6 +71,12 @@ to be executed in that module. </p> + <p>The callback functions that the test suite should implement, and + which will be described in more detail below, are + all listed in the <seealso marker="common_test">common_test + reference manual page</seealso>. + </p> + </section> <section> @@ -113,6 +119,14 @@ suite will be skipped automatically (so called <em>auto skipped</em>), including <c>end_per_suite</c>. </p> + + <p>Note that if <c>init_per_suite</c> and <c>end_per_suite</c> do not exist + in the suite, Common Test calls dummy functions (with the same names) + instead, so that output generated by hook functions may be saved to the log + files for these dummies + (see the <seealso marker="ct_hooks_chapter#manipulating">Common Test Hooks</seealso> + chapter for more information). + </p> </section> <marker id="per_testcase"/> @@ -513,8 +527,7 @@ execution is immediately stopped and the rest of the cases skipped.</p> <p>Before execution of a group begins, the configuration function - <c>init_per_group(GroupName, Config)</c> is called (the function is - mandatory if one or more test case groups are defined). The list of tuples + <c>init_per_group(GroupName, Config)</c> is called. The list of tuples returned from this function is passed to the test cases in the usual manner by means of the <c>Config</c> argument. <c>init_per_group/2</c> is meant to be used for initializations common for the test cases in the @@ -522,6 +535,14 @@ <c>end_per_group(GroupName, Config</c> function is called. This function is meant to be used for cleaning up after <c>init_per_group/2</c>.</p> + <p>Whenever a group is executed, if <c>init_per_group</c> and + <c>end_per_group</c> do not exist in the suite, Common Test calls + dummy functions (with the same names) instead. Output generated by + hook functions will be saved to the log files for these dummies + (see the <seealso marker="ct_hooks_chapter#manipulating">Common Test + Hooks</seealso> chapter for more information). + </p> + <note><p><c>init_per_testcase/2</c> and <c>end_per_testcase/2</c> are always called for each individual test case, no matter if the case belongs to a group or not.</p></note> @@ -611,6 +632,25 @@ </section> <section> + <title>Parallel test cases and IO</title> + <p>A parallel test case has a private IO server as its group leader. + (Please see the Erlang Run-Time System Application documentation for + a description of the group leader concept). The + central IO server process that handles the output from regular test + cases and configuration functions, does not respond to IO messages + during execution of parallel groups. This is important to understand + in order to avoid certain traps, like this one:</p> + <p>If a process, <c>P</c>, is spawned during execution of e.g. + <c>init_per_suite/1</c>, it will inherit the group leader of the + <c>init_per_suite</c> process. This group leader is the central IO server + process mentioned above. If, at a later time, <em>during parallel test case + execution</em>, some event triggers process <c>P</c> to call + <c>io:format/1/2</c>, that call will never return (since the group leader + is in a non-responsive state) and cause <c>P</c> to hang. + </p> + </section> + + <section> <title>Repeated groups</title> <marker id="repeated_groups"></marker> <p>A test case group may be repeated a certain number of times @@ -745,7 +785,7 @@ <marker id="data_priv_dir"></marker> <title>Data and Private Directories</title> - <p>The data directory (<c>data_dir</c>) is the directory where the + <p>The data directory, <c>data_dir</c>, is the directory where the test module has its own files needed for the testing. The name of the <c>data_dir</c> is the the name of the test suite followed by <c>"_data"</c>. For example, @@ -769,12 +809,39 @@ </p> --> <p> - The <c>priv_dir</c> is the test suite's private directory. This - directory should be used when a test case needs to write to - files. The name of the private directory is generated by the test - server, which also creates the directory. + <c>priv_dir</c> is the private directory for the test cases. + This directory may be used whenever a test case (or configuration function) + needs to write something to file. The name of the private directory is + generated by Common Test, which also creates the directory. </p> - + <p>By default, Common Test creates one central private directory + per test run that all test cases share. This may not always be suitable, + especially if the same test cases are executed multiple times during + a test run (e.g. if they belong to a test case group with repeat + property), and there's a risk that files in the private directory get + overwritten. Under these circumstances, it's possible to configure + Common Test to create one dedicated private directory per + test case and execution instead. This is accomplished by means of + the flag/option: <c>create_priv_dir</c> (to be used with the + <c>ct_run</c> program, the <c>ct:run_test/1</c> function, or + as test specification term). There are three possible values + for this option: + <list> + <item><c>auto_per_run</c></item> + <item><c>auto_per_tc</c></item> + <item><c>manual_per_tc</c></item> + </list> + The first value indicates the default priv_dir behaviour, i.e. + one private directory created per test run. The two latter + values tell Common Test to generate a unique test directory name + per test case and execution. If the auto version is used, <em>all</em> + private directories will be created automatically. This can obviously + become very inefficient for test runs with many test cases and/or + repetitions. Therefore, in case the manual version is instead used, the + test case must tell Common Test to create priv_dir when it needs it. + It does this by calling the function <c>ct:make_priv_dir/0</c>. + </p> + <note><p>You should not depend on current working directory for reading and writing data files since this is not portable. All scratch files are to be written in the <c>priv_dir</c> and all @@ -805,34 +872,63 @@ <marker id="timetraps"></marker> <title>Timetrap timeouts</title> <p>The default time limit for a test case is 30 minutes, unless a - <c>timetrap</c> is specified either by the suite info function - or a test case info function. The timetrap timeout value defined - in <c>suite/0</c> is the value that will be used for each test case - in the suite (as well as for the configuration functions - <c>init_per_suite/1</c> and <c>end_per_suite</c>). A timetrap timeout - value set with the test case info function will override the value set - by <c>suite/0</c>, but only for that particular test case.</p> - <p>It is also possible to set/reset a timetrap during test case (or - configuration function) execution. This is done by calling - <c>ct:timetrap/1</c>. This function will cancel the current timetrap - and start a new one.</p> + <c>timetrap</c> is specified either by the suite-, group-, + or test case info function. The timetrap timeout value defined by + <c>suite/0</c> is the value that will be used for each test case + in the suite (as well as for the configuration functions + <c>init_per_suite/1</c>, <c>end_per_suite/1</c>, <c>init_per_group/2</c>, + and <c>end_per_group/2</c>). A timetrap value defined by + <c>group(GroupName)</c> overrides one defined by <c>suite()</c> + and will be used for each test case in group <c>GroupName</c>, and any + of its sub-groups. If a timetrap value is defined by <c>group/1</c> + for a sub-group, it overrides that of its higher level groups. Timetrap + values set by individual test cases (by means of the test case info + function) overrides both group- and suite- level timetraps.</p> + + <p>It is also possible to dynamically set/reset a timetrap during the + excution of a test case, or configuration function. This is done by calling + <c>ct:timetrap/1</c>. This function cancels the current timetrap + and starts a new one (that stays active until timeout, or end of the + current function).</p> + <p>Timetrap values can be extended with a multiplier value specified at - startup with the <c>multiply_timetraps</c> option. It is also possible - to let Test Server decide to scale up timetrap timeout values - automatically, e.g. if tools such as cover or trace are running during - the test. This feature is disabled by default and can be enabled with - the <c>scale_timetraps</c> start option.</p> + startup with the <c>multiply_timetraps</c> option. It is also possible + to let the test server decide to scale up timetrap timeout values + automatically, e.g. if tools such as cover or trace are running during + the test. This feature is disabled by default and can be enabled with + the <c>scale_timetraps</c> start option.</p> + <p>If a test case needs to suspend itself for a time that also gets - multipled by <c>multiply_timetraps</c>, and possibly scaled up if - <c>scale_timetraps</c> is enabled, the function <c>ct:sleep/1</c> - may be called.</p> - <p>A function (<c>fun</c> or <c>MFA</c>) may be specified as timetrap value - in the suite- and test case info function, e.g:</p> - <p><c>{timetrap,{test_utils,get_timetrap_value,[?MODULE,system_start]}}</c></p> - <p>The function will be called initially by Common Test (before execution - of the suite or the test case) and must return a time value such as an - integer (millisec), or a <c>{SecMinOrHourTag,Time}</c> tuple. More - information can be found in the <c>common_test</c> reference manual.</p> + multipled by <c>multiply_timetraps</c> (and possibly also scaled up if + <c>scale_timetraps</c> is enabled), the function <c>ct:sleep/1</c> + may be used (instead of e.g. <c>timer:sleep/1</c>).</p> + + <p>A function (<c>fun/0</c> or <c>MFA</c>) may be specified as + timetrap value in the suite-, group- and test case info function, as + well as argument to the <c>ct:timetrap/1</c> function. Examples:</p> + + <p><c>{timetrap,{my_test_utils,timetrap,[?MODULE,system_start]}}</c></p> + <p><c>ct:timetrap(fun() -> my_timetrap(TestCaseName, Config) end)</c></p> + + <p>The user timetrap function may be used for two things:</p> + <list> + <item>To act as a timetrap - the timeout is triggered when the + function returns.</item> + <item>To return a timetrap time value (other than a function).</item> + </list> + <p>Before execution of the timetrap function (which is performed + on a parallel, dedicated timetrap process), Common Test cancels + any previously set timer for the test case or configuration function. + When the timetrap function returns, the timeout is triggered, <em>unless</em> + the return value is a valid timetrap time, such as an integer, + or a <c>{SecMinOrHourTag,Time}</c> tuple (see the + <seealso marker="common_test">common_test reference manual</seealso> for + details). If a time value is returned, a new timetrap is started + to generate a timeout after the specified time.</p> + + <p>The user timetrap function may of course return a time value after a delay, + and if so, the effective timetrap time is the delay time <em>plus</em> the + returned time.</p> </section> <section> diff --git a/lib/common_test/include/ct.hrl b/lib/common_test/include/ct.hrl index 5a77108e1a..43b1b1c11f 100644 --- a/lib/common_test/include/ct.hrl +++ b/lib/common_test/include/ct.hrl @@ -19,3 +19,16 @@ -include_lib("test_server/include/test_server.hrl"). +%% the log level is used as argument to any CT logging function +-define(MIN_IMPORTANCE, 0 ). +-define(LOW_IMPORTANCE, 25). +-define(STD_IMPORTANCE, 50). +-define(HI_IMPORTANCE, 75). +-define(MAX_IMPORTANCE, 99). + +%% verbosity thresholds to filter out logging printouts +-define(MIN_VERBOSITY, 0 ). %% turn logging off +-define(LOW_VERBOSITY, 25 ). +-define(STD_VERBOSITY, 50 ). +-define(HI_VERBOSITY, 75 ). +-define(MAX_VERBOSITY, 100). diff --git a/lib/common_test/priv/Makefile.in b/lib/common_test/priv/Makefile.in index d9033f6ef1..2f06f7df65 100644 --- a/lib/common_test/priv/Makefile.in +++ b/lib/common_test/priv/Makefile.in @@ -60,6 +60,7 @@ FILES = vts.tool SCRIPTS = IMAGES = tile1.jpg CSS = ct_default.css +JS = jquery-latest.js jquery.tablesorter.min.js # # Rules @@ -85,12 +86,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk ifeq ($(XNIX),true) release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv" else release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv" endif release_docs_spec: @@ -107,6 +108,7 @@ else FILES = vts.tool IMAGES = tile1.jpg CSS = ct_default.css +JS = jquery-latest.js jquery.tablesorter.min.js # # Rules @@ -125,8 +127,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(FILES) $(IMAGES) $(CSS) $(JS) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/common_test/priv/ct_default.css b/lib/common_test/priv/ct_default.css index 8ae6990cd8..1188f8f676 100644 --- a/lib/common_test/priv/ct_default.css +++ b/lib/common_test/priv/ct_default.css @@ -136,7 +136,18 @@ th { } thead th { - background: #2C5755; text-align: center; + background: #3F3F3F; color: #fff; + font-family: arial, sans-serif; font-size: 120%; + letter-spacing: -0.5px; + font-weight: bold; text-align: center; + padding-right: .5em; vertical-align: top; + text-decoration: underline; +} + +tfoot td { + font-family: arial, sans-serif; font-size: 110%; + letter-spacing: -0.5px; + font-weight: bold; } .odd td { @@ -167,10 +178,6 @@ th a, td a:active { color: #85ABD5; } -tfoot th, tfoot td { - background: #3F3F3F; color: #fff; -} - th + td { padding-left: .5em; } diff --git a/lib/common_test/priv/jquery-latest.js b/lib/common_test/priv/jquery-latest.js new file mode 100644 index 0000000000..ac7e7009dc --- /dev/null +++ b/lib/common_test/priv/jquery-latest.js @@ -0,0 +1,154 @@ +/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML=" <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
diff --git a/lib/common_test/priv/jquery.tablesorter.min.js b/lib/common_test/priv/jquery.tablesorter.min.js new file mode 100644 index 0000000000..b8605df1e7 --- /dev/null +++ b/lib/common_test/priv/jquery.tablesorter.min.js @@ -0,0 +1,4 @@ + +(function($){$.extend({tablesorter:new +function(){var parsers=[],widgets=[];this.defaults={cssHeader:"header",cssAsc:"headerSortUp",cssDesc:"headerSortDown",cssChildRow:"expand-child",sortInitialOrder:"asc",sortMultiSortKey:"shiftKey",sortForce:null,sortAppend:null,sortLocaleCompare:true,textExtraction:"simple",parsers:{},widgets:[],widgetZebra:{css:["even","odd"]},headers:{},widthFixed:false,cancelSelection:true,sortList:[],headerList:[],dateFormat:"us",decimal:'/\.|\,/g',onRenderHeader:null,selectorHeaders:'thead th',debug:false};function benchmark(s,d){log(s+","+(new Date().getTime()-d.getTime())+"ms");}this.benchmark=benchmark;function log(s){if(typeof console!="undefined"&&typeof console.debug!="undefined"){console.log(s);}else{alert(s);}}function buildParserCache(table,$headers){if(table.config.debug){var parsersDebug="";}if(table.tBodies.length==0)return;var rows=table.tBodies[0].rows;if(rows[0]){var list=[],cells=rows[0].cells,l=cells.length;for(var i=0;i<l;i++){var p=false;if($.metadata&&($($headers[i]).metadata()&&$($headers[i]).metadata().sorter)){p=getParserById($($headers[i]).metadata().sorter);}else if((table.config.headers[i]&&table.config.headers[i].sorter)){p=getParserById(table.config.headers[i].sorter);}if(!p){p=detectParserForColumn(table,rows,-1,i);}if(table.config.debug){parsersDebug+="column:"+i+" parser:"+p.id+"\n";}list.push(p);}}if(table.config.debug){log(parsersDebug);}return list;};function detectParserForColumn(table,rows,rowIndex,cellIndex){var l=parsers.length,node=false,nodeValue=false,keepLooking=true;while(nodeValue==''&&keepLooking){rowIndex++;if(rows[rowIndex]){node=getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex);nodeValue=trimAndGetNodeText(table.config,node);if(table.config.debug){log('Checking if value was empty on row:'+rowIndex);}}else{keepLooking=false;}}for(var i=1;i<l;i++){if(parsers[i].is(nodeValue,table,node)){return parsers[i];}}return parsers[0];}function getNodeFromRowAndCellIndex(rows,rowIndex,cellIndex){return rows[rowIndex].cells[cellIndex];}function trimAndGetNodeText(config,node){return $.trim(getElementText(config,node));}function getParserById(name){var l=parsers.length;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==name.toLowerCase()){return parsers[i];}}return false;}function buildCache(table){if(table.config.debug){var cacheTime=new Date();}var totalRows=(table.tBodies[0]&&table.tBodies[0].rows.length)||0,totalCells=(table.tBodies[0].rows[0]&&table.tBodies[0].rows[0].cells.length)||0,parsers=table.config.parsers,cache={row:[],normalized:[]};for(var i=0;i<totalRows;++i){var c=$(table.tBodies[0].rows[i]),cols=[];if(c.hasClass(table.config.cssChildRow)){cache.row[cache.row.length-1]=cache.row[cache.row.length-1].add(c);continue;}cache.row.push(c);for(var j=0;j<totalCells;++j){cols.push(parsers[j].format(getElementText(table.config,c[0].cells[j]),table,c[0].cells[j]));}cols.push(cache.normalized.length);cache.normalized.push(cols);cols=null;};if(table.config.debug){benchmark("Building cache for "+totalRows+" rows:",cacheTime);}return cache;};function getElementText(config,node){var text="";if(!node)return"";if(!config.supportsTextContent)config.supportsTextContent=node.textContent||false;if(config.textExtraction=="simple"){if(config.supportsTextContent){text=node.textContent;}else{if(node.childNodes[0]&&node.childNodes[0].hasChildNodes()){text=node.childNodes[0].innerHTML;}else{text=node.innerHTML;}}}else{if(typeof(config.textExtraction)=="function"){text=config.textExtraction(node);}else{text=$(node).text();}}return text;}function appendToTable(table,cache){if(table.config.debug){var appendTime=new Date()}var c=cache,r=c.row,n=c.normalized,totalRows=n.length,checkCell=(n[0].length-1),tableBody=$(table.tBodies[0]),rows=[];for(var i=0;i<totalRows;i++){var pos=n[i][checkCell];rows.push(r[pos]);if(!table.config.appender){var l=r[pos].length;for(var j=0;j<l;j++){tableBody[0].appendChild(r[pos][j]);}}}if(table.config.appender){table.config.appender(table,rows);}rows=null;if(table.config.debug){benchmark("Rebuilt table:",appendTime);}applyWidget(table);setTimeout(function(){$(table).trigger("sortEnd");},0);};function buildHeaders(table){if(table.config.debug){var time=new Date();}var meta=($.metadata)?true:false;var header_index=computeTableHeaderCellIndexes(table);$tableHeaders=$(table.config.selectorHeaders,table).each(function(index){this.column=header_index[this.parentNode.rowIndex+"-"+this.cellIndex];this.order=formatSortingOrder(table.config.sortInitialOrder);this.count=this.order;if(checkHeaderMetadata(this)||checkHeaderOptions(table,index))this.sortDisabled=true;if(checkHeaderOptionsSortingLocked(table,index))this.order=this.lockedOrder=checkHeaderOptionsSortingLocked(table,index);if(!this.sortDisabled){var $th=$(this).addClass(table.config.cssHeader);if(table.config.onRenderHeader)table.config.onRenderHeader.apply($th);}table.config.headerList[index]=this;});if(table.config.debug){benchmark("Built headers:",time);log($tableHeaders);}return $tableHeaders;};function computeTableHeaderCellIndexes(t){var matrix=[];var lookup={};var thead=t.getElementsByTagName('THEAD')[0];var trs=thead.getElementsByTagName('TR');for(var i=0;i<trs.length;i++){var cells=trs[i].cells;for(var j=0;j<cells.length;j++){var c=cells[j];var rowIndex=c.parentNode.rowIndex;var cellId=rowIndex+"-"+c.cellIndex;var rowSpan=c.rowSpan||1;var colSpan=c.colSpan||1 +var firstAvailCol;if(typeof(matrix[rowIndex])=="undefined"){matrix[rowIndex]=[];}for(var k=0;k<matrix[rowIndex].length+1;k++){if(typeof(matrix[rowIndex][k])=="undefined"){firstAvailCol=k;break;}}lookup[cellId]=firstAvailCol;for(var k=rowIndex;k<rowIndex+rowSpan;k++){if(typeof(matrix[k])=="undefined"){matrix[k]=[];}var matrixrow=matrix[k];for(var l=firstAvailCol;l<firstAvailCol+colSpan;l++){matrixrow[l]="x";}}}}return lookup;}function checkCellColSpan(table,rows,row){var arr=[],r=table.tHead.rows,c=r[row].cells;for(var i=0;i<c.length;i++){var cell=c[i];if(cell.colSpan>1){arr=arr.concat(checkCellColSpan(table,headerArr,row++));}else{if(table.tHead.length==1||(cell.rowSpan>1||!r[row+1])){arr.push(cell);}}}return arr;};function checkHeaderMetadata(cell){if(($.metadata)&&($(cell).metadata().sorter===false)){return true;};return false;}function checkHeaderOptions(table,i){if((table.config.headers[i])&&(table.config.headers[i].sorter===false)){return true;};return false;}function checkHeaderOptionsSortingLocked(table,i){if((table.config.headers[i])&&(table.config.headers[i].lockedOrder))return table.config.headers[i].lockedOrder;return false;}function applyWidget(table){var c=table.config.widgets;var l=c.length;for(var i=0;i<l;i++){getWidgetById(c[i]).format(table);}}function getWidgetById(name){var l=widgets.length;for(var i=0;i<l;i++){if(widgets[i].id.toLowerCase()==name.toLowerCase()){return widgets[i];}}};function formatSortingOrder(v){if(typeof(v)!="Number"){return(v.toLowerCase()=="desc")?1:0;}else{return(v==1)?1:0;}}function isValueInArray(v,a){var l=a.length;for(var i=0;i<l;i++){if(a[i][0]==v){return true;}}return false;}function setHeadersCss(table,$headers,list,css){$headers.removeClass(css[0]).removeClass(css[1]);var h=[];$headers.each(function(offset){if(!this.sortDisabled){h[this.column]=$(this);}});var l=list.length;for(var i=0;i<l;i++){h[list[i][0]].addClass(css[list[i][1]]);}}function fixColumnWidth(table,$headers){var c=table.config;if(c.widthFixed){var colgroup=$('<colgroup>');$("tr:first td",table.tBodies[0]).each(function(){colgroup.append($('<col>').css('width',$(this).width()));});$(table).prepend(colgroup);};}function updateHeaderSortCount(table,sortList){var c=table.config,l=sortList.length;for(var i=0;i<l;i++){var s=sortList[i],o=c.headerList[s[0]];o.count=s[1];o.count++;}}function multisort(table,sortList,cache){if(table.config.debug){var sortTime=new Date();}var dynamicExp="var sortWrapper = function(a,b) {",l=sortList.length;for(var i=0;i<l;i++){var c=sortList[i][0];var order=sortList[i][1];var s=(table.config.parsers[c].type=="text")?((order==0)?makeSortFunction("text","asc",c):makeSortFunction("text","desc",c)):((order==0)?makeSortFunction("numeric","asc",c):makeSortFunction("numeric","desc",c));var e="e"+i;dynamicExp+="var "+e+" = "+s;dynamicExp+="if("+e+") { return "+e+"; } ";dynamicExp+="else { ";}var orgOrderCol=cache.normalized[0].length-1;dynamicExp+="return a["+orgOrderCol+"]-b["+orgOrderCol+"];";for(var i=0;i<l;i++){dynamicExp+="}; ";}dynamicExp+="return 0; ";dynamicExp+="}; ";if(table.config.debug){benchmark("Evaling expression:"+dynamicExp,new Date());}eval(dynamicExp);cache.normalized.sort(sortWrapper);if(table.config.debug){benchmark("Sorting on "+sortList.toString()+" and dir "+order+" time:",sortTime);}return cache;};function makeSortFunction(type,direction,index){var a="a["+index+"]",b="b["+index+"]";if(type=='text'&&direction=='asc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+a+" < "+b+") ? -1 : 1 )));";}else if(type=='text'&&direction=='desc'){return"("+a+" == "+b+" ? 0 : ("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : ("+b+" < "+a+") ? -1 : 1 )));";}else if(type=='numeric'&&direction=='asc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+a+" - "+b+"));";}else if(type=='numeric'&&direction=='desc'){return"("+a+" === null && "+b+" === null) ? 0 :("+a+" === null ? Number.POSITIVE_INFINITY : ("+b+" === null ? Number.NEGATIVE_INFINITY : "+b+" - "+a+"));";}};function makeSortText(i){return"((a["+i+"] < b["+i+"]) ? -1 : ((a["+i+"] > b["+i+"]) ? 1 : 0));";};function makeSortTextDesc(i){return"((b["+i+"] < a["+i+"]) ? -1 : ((b["+i+"] > a["+i+"]) ? 1 : 0));";};function makeSortNumeric(i){return"a["+i+"]-b["+i+"];";};function makeSortNumericDesc(i){return"b["+i+"]-a["+i+"];";};function sortText(a,b){if(table.config.sortLocaleCompare)return a.localeCompare(b);return((a<b)?-1:((a>b)?1:0));};function sortTextDesc(a,b){if(table.config.sortLocaleCompare)return b.localeCompare(a);return((b<a)?-1:((b>a)?1:0));};function sortNumeric(a,b){return a-b;};function sortNumericDesc(a,b){return b-a;};function getCachedSortType(parsers,i){return parsers[i].type;};this.construct=function(settings){return this.each(function(){if(!this.tHead||!this.tBodies)return;var $this,$document,$headers,cache,config,shiftDown=0,sortOrder;this.config={};config=$.extend(this.config,$.tablesorter.defaults,settings);$this=$(this);$.data(this,"tablesorter",config);$headers=buildHeaders(this);this.config.parsers=buildParserCache(this,$headers);cache=buildCache(this);var sortCSS=[config.cssDesc,config.cssAsc];fixColumnWidth(this);$headers.click(function(e){var totalRows=($this[0].tBodies[0]&&$this[0].tBodies[0].rows.length)||0;if(!this.sortDisabled&&totalRows>0){$this.trigger("sortStart");var $cell=$(this);var i=this.column;this.order=this.count++%2;if(this.lockedOrder)this.order=this.lockedOrder;if(!e[config.sortMultiSortKey]){config.sortList=[];if(config.sortForce!=null){var a=config.sortForce;for(var j=0;j<a.length;j++){if(a[j][0]!=i){config.sortList.push(a[j]);}}}config.sortList.push([i,this.order]);}else{if(isValueInArray(i,config.sortList)){for(var j=0;j<config.sortList.length;j++){var s=config.sortList[j],o=config.headerList[s[0]];if(s[0]==i){o.count=s[1];o.count++;s[1]=o.count%2;}}}else{config.sortList.push([i,this.order]);}};setTimeout(function(){setHeadersCss($this[0],$headers,config.sortList,sortCSS);appendToTable($this[0],multisort($this[0],config.sortList,cache));},1);return false;}}).mousedown(function(){if(config.cancelSelection){this.onselectstart=function(){return false};return false;}});$this.bind("update",function(){var me=this;setTimeout(function(){me.config.parsers=buildParserCache(me,$headers);cache=buildCache(me);},1);}).bind("updateCell",function(e,cell){var config=this.config;var pos=[(cell.parentNode.rowIndex-1),cell.cellIndex];cache.normalized[pos[0]][pos[1]]=config.parsers[pos[1]].format(getElementText(config,cell),cell);}).bind("sorton",function(e,list){$(this).trigger("sortStart");config.sortList=list;var sortList=config.sortList;updateHeaderSortCount(this,sortList);setHeadersCss(this,$headers,sortList,sortCSS);appendToTable(this,multisort(this,sortList,cache));}).bind("appendCache",function(){appendToTable(this,cache);}).bind("applyWidgetId",function(e,id){getWidgetById(id).format(this);}).bind("applyWidgets",function(){applyWidget(this);});if($.metadata&&($(this).metadata()&&$(this).metadata().sortlist)){config.sortList=$(this).metadata().sortlist;}if(config.sortList.length>0){$this.trigger("sorton",[config.sortList]);}applyWidget(this);});};this.addParser=function(parser){var l=parsers.length,a=true;for(var i=0;i<l;i++){if(parsers[i].id.toLowerCase()==parser.id.toLowerCase()){a=false;}}if(a){parsers.push(parser);};};this.addWidget=function(widget){widgets.push(widget);};this.formatFloat=function(s){var i=parseFloat(s);return(isNaN(i))?0:i;};this.formatInt=function(s){var i=parseInt(s);return(isNaN(i))?0:i;};this.isDigit=function(s,config){return/^[-+]?\d*$/.test($.trim(s.replace(/[,.']/g,'')));};this.clearTableBody=function(table){if($.browser.msie){function empty(){while(this.firstChild)this.removeChild(this.firstChild);}empty.apply(table.tBodies[0]);}else{table.tBodies[0].innerHTML="";}};}});$.fn.extend({tablesorter:$.tablesorter.construct});var ts=$.tablesorter;ts.addParser({id:"text",is:function(s){return true;},format:function(s){return $.trim(s.toLocaleLowerCase());},type:"text"});ts.addParser({id:"digit",is:function(s,table){var c=table.config;return $.tablesorter.isDigit(s,c);},format:function(s){return $.tablesorter.formatFloat(s);},type:"numeric"});ts.addParser({id:"currency",is:function(s){return/^[£$€?.]/.test(s);},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/[£$€]/g),""));},type:"numeric"});ts.addParser({id:"ipAddress",is:function(s){return/^\d{2,3}[\.]\d{2,3}[\.]\d{2,3}[\.]\d{2,3}$/.test(s);},format:function(s){var a=s.split("."),r="",l=a.length;for(var i=0;i<l;i++){var item=a[i];if(item.length==2){r+="0"+item;}else{r+=item;}}return $.tablesorter.formatFloat(r);},type:"numeric"});ts.addParser({id:"url",is:function(s){return/^(https?|ftp|file):\/\/$/.test(s);},format:function(s){return jQuery.trim(s.replace(new RegExp(/(https?|ftp|file):\/\//),''));},type:"text"});ts.addParser({id:"isoDate",is:function(s){return/^\d{4}[\/-]\d{1,2}[\/-]\d{1,2}$/.test(s);},format:function(s){return $.tablesorter.formatFloat((s!="")?new Date(s.replace(new RegExp(/-/g),"/")).getTime():"0");},type:"numeric"});ts.addParser({id:"percent",is:function(s){return/\%$/.test($.trim(s));},format:function(s){return $.tablesorter.formatFloat(s.replace(new RegExp(/%/g),""));},type:"numeric"});ts.addParser({id:"usLongDate",is:function(s){return s.match(new RegExp(/^[A-Za-z]{3,10}\.? [0-9]{1,2}, ([0-9]{4}|'?[0-9]{2}) (([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(AM|PM)))$/));},format:function(s){return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"shortDate",is:function(s){return/\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4}/.test(s);},format:function(s,table){var c=table.config;s=s.replace(/\-/g,"/");if(c.dateFormat=="us"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$1/$2");}else if(c.dateFormat=="uk"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})/,"$3/$2/$1");}else if(c.dateFormat=="dd/mm/yy"||c.dateFormat=="dd-mm-yy"){s=s.replace(/(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{2})/,"$1/$2/$3");}return $.tablesorter.formatFloat(new Date(s).getTime());},type:"numeric"});ts.addParser({id:"time",is:function(s){return/^(([0-2]?[0-9]:[0-5][0-9])|([0-1]?[0-9]:[0-5][0-9]\s(am|pm)))$/.test(s);},format:function(s){return $.tablesorter.formatFloat(new Date("2000/01/01 "+s).getTime());},type:"numeric"});ts.addParser({id:"metadata",is:function(s){return false;},format:function(s,table,cell){var c=table.config,p=(!c.parserMetadataName)?'sortValue':c.parserMetadataName;return $(cell).metadata()[p];},type:"numeric"});ts.addWidget({id:"zebra",format:function(table){if(table.config.debug){var time=new Date();}var $tr,row=-1,odd;$("tr:visible",table.tBodies[0]).each(function(i){$tr=$(this);if(!$tr.hasClass(table.config.cssChildRow))row++;odd=(row%2==0);$tr.removeClass(table.config.widgetZebra.css[odd?0:1]).addClass(table.config.widgetZebra.css[odd?1:0])});if(table.config.debug){$.tablesorter.benchmark("Applying Zebra widget",time);}}});})(jQuery);
\ No newline at end of file diff --git a/lib/common_test/src/Makefile b/lib/common_test/src/Makefile index 125aa828fb..f7dce195d7 100644 --- a/lib/common_test/src/Makefile +++ b/lib/common_test/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2011. All Rights Reserved. +# Copyright Ericsson AB 2003-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -69,14 +69,19 @@ MODULES= \ ct_slave \ ct_hooks\ ct_hooks_lock\ - cth_log_redirect + cth_log_redirect\ + cth_surefire \ + ct_netconfc \ + ct_conn_log_h \ + cth_conn_log TARGET_MODULES= $(MODULES:%=$(EBIN)/%) BEAM_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) ERL_FILES= $(MODULES:=.erl) HRL_FILES = \ - ct_util.hrl + ct_util.hrl \ + ct_netconfc.hrl EXTERNAL_HRL_FILES = \ ../include/ct.hrl \ ../include/ct_event.hrl @@ -132,12 +137,12 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) "$(RELSYSDIR)/include" release_tests_spec: opt $(INSTALL_DIR) $(RELEASE_PATH)/common_test_test diff --git a/lib/common_test/src/common_test.app.src b/lib/common_test/src/common_test.app.src index 7fba484b18..18c1dec784 100644 --- a/lib/common_test/src/common_test.app.src +++ b/lib/common_test/src/common_test.app.src @@ -1,7 +1,7 @@ % This is an -*- erlang -*- file. %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,12 +25,16 @@ ct_framework, ct_ftp, ct_gen_conn, + ct_hooks, + ct_hooks_lock, ct_logs, ct_make, ct_master, ct_master_event, ct_master_logs, ct_master_status, + ct_netconfc, + ct_conn_log_h, ct_repeat, ct_rpc, ct_run, @@ -45,7 +49,10 @@ ct_config, ct_config_plain, ct_config_xml, - ct_slave + ct_slave, + cth_log_redirect, + cth_conn_log, + cth_surefire ]}, {registered, [ct_logs, ct_util_server, diff --git a/lib/common_test/src/ct.erl b/lib/common_test/src/ct.erl index 63a8adbc63..8d4721ab63 100644 --- a/lib/common_test/src/ct.erl +++ b/lib/common_test/src/ct.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -51,6 +51,8 @@ -module(ct). +-include("ct.hrl"). + %% Command line user interface for running tests -export([install/1, run/1, run/2, run/3, run_test/1, run_testspec/1, step/3, step/4, @@ -60,13 +62,15 @@ -export([require/1, require/2, get_config/1, get_config/2, get_config/3, reload_config/1, - log/1, log/2, log/3, - print/1, print/2, print/3, - pal/1, pal/2, pal/3, + log/1, log/2, log/3, log/4, + print/1, print/2, print/3, print/4, + pal/1, pal/2, pal/3, pal/4, capture_start/0, capture_stop/0, capture_get/0, capture_get/1, fail/1, fail/2, comment/1, comment/2, make_priv_dir/0, testcases/2, userdata/2, userdata/3, - timetrap/1, get_timetrap_info/0, sleep/1]). + timetrap/1, get_timetrap_info/0, sleep/1, + notify/2, sync_notify/2, + break/1, break/2, continue/0, continue/1]). %% New API for manipulating with config handlers -export([add_config/2, remove_config/2]). @@ -146,11 +150,14 @@ run(TestDirs) -> %%% {silent_connections,Conns} | {stylesheet,CSSFile} | %%% {cover,CoverSpecFile} | {step,StepOpts} | %%% {event_handler,EventHandlers} | {include,InclDirs} | -%%% {auto_compile,Bool} | {multiply_timetraps,M} | {scale_timetraps,Bool} | +%%% {auto_compile,Bool} | {create_priv_dir,CreatePrivDir} | +%%% {multiply_timetraps,M} | {scale_timetraps,Bool} | %%% {repeat,N} | {duration,DurTime} | {until,StopTime} | %%% {force_stop,Bool} | {decrypt,DecryptKeyOrFile} | -%%% {refresh_logs,LogDir} | {logopts,LogOpts} | {basic_html,Bool} | -%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} +%%% {refresh_logs,LogDir} | {logopts,LogOpts} | +%%% {verbosity,VLevels} | {basic_html,Bool} | +%%% {ct_hooks, CTHs} | {enable_builtin_hooks,Bool} | +%%% {noinput,Bool} %%% TestDirs = [string()] | string() %%% Suites = [string()] | [atom()] | string() | atom() %%% Cases = [atom()] | atom() @@ -171,6 +178,7 @@ run(TestDirs) -> %%% EH = atom() | {atom(),InitArgs} | {[atom()],InitArgs} %%% InitArgs = [term()] %%% InclDirs = [string()] | string() +%%% CreatePrivDir = auto_per_run | auto_per_tc | manual_per_tc %%% M = integer() %%% N = integer() %%% DurTime = string(HHMMSS) @@ -180,6 +188,9 @@ run(TestDirs) -> %%% DecryptFile = string() %%% LogOpts = [LogOpt] %%% LogOpt = no_nl | no_src +%%% VLevels = VLevel | [{Category,VLevel}] +%%% VLevel = integer() +%%% Category = atom() %%% CTHs = [CTHModule | {CTHModule, CTHInitArgs}] %%% CTHModule = atom() %%% CTHInitArgs = term() @@ -419,25 +430,41 @@ reload_config(Required)-> %%%----------------------------------------------------------------- %%% @spec log(Format) -> ok -%%% @equiv log(default,Format,[]) +%%% @equiv log(default,50,Format,[]) log(Format) -> - log(default,Format,[]). + log(default,?STD_IMPORTANCE,Format,[]). %%%----------------------------------------------------------------- %%% @spec log(X1,X2) -> ok -%%% X1 = Category | Format +%%% X1 = Category | Importance | Format %%% X2 = Format | Args -%%% @equiv log(Category,Format,Args) +%%% @equiv log(Category,Importance,Format,Args) log(X1,X2) -> - {Category,Format,Args} = - if is_atom(X1) -> {X1,X2,[]}; - is_list(X1) -> {default,X1,X2} + {Category,Importance,Format,Args} = + if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]}; + is_integer(X1) -> {default,X1,X2,[]}; + is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2} end, - log(Category,Format,Args). + log(Category,Importance,Format,Args). %%%----------------------------------------------------------------- -%%% @spec log(Category,Format,Args) -> ok +%%% @spec log(X1,X2,X3) -> ok +%%% X1 = Category | Importance +%%% X2 = Importance | Format +%%% X3 = Format | Args +%%% @equiv log(Category,Importance,Format,Args) +log(X1,X2,X3) -> + {Category,Importance,Format,Args} = + if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]}; + is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3}; + is_integer(X1) -> {default,X1,X2,X3} + end, + log(Category,Importance,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec log(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -446,30 +473,52 @@ log(X1,X2) -> %%% <p>This function is meant for printing a string directly from a %%% test case to the test case log file.</p> %%% -%%% <p>Default <code>Category</code> is <code>default</code> and -%%% default <code>Args</code> is <code>[]</code>.</p> -log(Category,Format,Args) -> - ct_logs:tc_log(Category,Format,Args). +%%% <p>Default <code>Category</code> is <code>default</code>, +%%% default <code>Importance</code> is <code>?STD_IMPORTANCE</code>, +%%% and default value for <code>Args</code> is <code>[]</code>.</p> +%%% <p>Please see the User's Guide for details on <code>Category</code> +%%% and <code>Importance</code>.</p> +log(Category,Importance,Format,Args) -> + ct_logs:tc_log(Category,Importance,Format,Args). %%%----------------------------------------------------------------- %%% @spec print(Format) -> ok -%%% @equiv print(default,Format,[]) +%%% @equiv print(default,50,Format,[]) print(Format) -> - print(default,Format,[]). + print(default,?STD_IMPORTANCE,Format,[]). %%%----------------------------------------------------------------- -%%% @equiv print(Category,Format,Args) +%%% @spec print(X1,X2) -> ok +%%% X1 = Category | Importance | Format +%%% X2 = Format | Args +%%% @equiv print(Category,Importance,Format,Args) print(X1,X2) -> - {Category,Format,Args} = - if is_atom(X1) -> {X1,X2,[]}; - is_list(X1) -> {default,X1,X2} + {Category,Importance,Format,Args} = + if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]}; + is_integer(X1) -> {default,X1,X2,[]}; + is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2} end, - print(Category,Format,Args). + print(Category,Importance,Format,Args). %%%----------------------------------------------------------------- -%%% @spec print(Category,Format,Args) -> ok +%%% @spec print(X1,X2,X3) -> ok +%%% X1 = Category | Importance +%%% X2 = Importance | Format +%%% X3 = Format | Args +%%% @equiv print(Category,Importance,Format,Args) +print(X1,X2,X3) -> + {Category,Importance,Format,Args} = + if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]}; + is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3}; + is_integer(X1) -> {default,X1,X2,X3} + end, + print(Category,Importance,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec print(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -478,33 +527,52 @@ print(X1,X2) -> %%% <p>This function is meant for printing a string from a test case %%% to the console.</p> %%% -%%% <p>Default <code>Category</code> is <code>default</code> and -%%% default <code>Args</code> is <code>[]</code>.</p> -print(Category,Format,Args) -> - ct_logs:tc_print(Category,Format,Args). +%%% <p>Default <code>Category</code> is <code>default</code>, +%%% default <code>Importance</code> is <code>?STD_IMPORTANCE</code>, +%%% and default value for <code>Args</code> is <code>[]</code>.</p> +%%% <p>Please see the User's Guide for details on <code>Category</code> +%%% and <code>Importance</code>.</p> +print(Category,Importance,Format,Args) -> + ct_logs:tc_print(Category,Importance,Format,Args). %%%----------------------------------------------------------------- %%% @spec pal(Format) -> ok -%%% @equiv pal(default,Format,[]) +%%% @equiv pal(default,50,Format,[]) pal(Format) -> - pal(default,Format,[]). + pal(default,?STD_IMPORTANCE,Format,[]). %%%----------------------------------------------------------------- %%% @spec pal(X1,X2) -> ok -%%% X1 = Category | Format +%%% X1 = Category | Importance | Format %%% X2 = Format | Args -%%% @equiv pal(Category,Format,Args) +%%% @equiv pal(Category,Importance,Format,Args) pal(X1,X2) -> - {Category,Format,Args} = - if is_atom(X1) -> {X1,X2,[]}; - is_list(X1) -> {default,X1,X2} + {Category,Importance,Format,Args} = + if is_atom(X1) -> {X1,?STD_IMPORTANCE,X2,[]}; + is_integer(X1) -> {default,X1,X2,[]}; + is_list(X1) -> {default,?STD_IMPORTANCE,X1,X2} end, - pal(Category,Format,Args). + pal(Category,Importance,Format,Args). %%%----------------------------------------------------------------- -%%% @spec pal(Category,Format,Args) -> ok +%%% @spec pal(X1,X2,X3) -> ok +%%% X1 = Category | Importance +%%% X2 = Importance | Format +%%% X3 = Format | Args +%%% @equiv pal(Category,Importance,Format,Args) +pal(X1,X2,X3) -> + {Category,Importance,Format,Args} = + if is_atom(X1), is_integer(X2) -> {X1,X2,X3,[]}; + is_atom(X1), is_list(X2) -> {X1,?STD_IMPORTANCE,X2,X3}; + is_integer(X1) -> {default,X1,X2,X3} + end, + pal(Category,Importance,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec pal(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -513,10 +581,13 @@ pal(X1,X2) -> %%% <p>This function is meant for printing a string from a test case, %%% both to the test case log file and to the console.</p> %%% -%%% <p>Default <code>Category</code> is <code>default</code> and -%%% default <code>Args</code> is <code>[]</code>.</p> -pal(Category,Format,Args) -> - ct_logs:tc_pal(Category,Format,Args). +%%% <p>Default <code>Category</code> is <code>default</code>, +%%% default <code>Importance</code> is <code>?STD_IMPORTANCE</code>, +%%% and default value for <code>Args</code> is <code>[]</code>.</p> +%%% <p>Please see the User's Guide for details on <code>Category</code> +%%% and <code>Importance</code>.</p> +pal(Category,Importance,Format,Args) -> + ct_logs:tc_pal(Category,Importance,Format,Args). %%%----------------------------------------------------------------- %%% @spec capture_start() -> ok @@ -838,8 +909,9 @@ userdata(TestDir, Suite, Case) when is_atom(Case) -> %%%----------------------------------------------------------------- %%% @spec get_status() -> TestStatus | {error,Reason} | no_tests_running %%% TestStatus = [StatusElem] -%%% StatusElem = {current,{Suite,TestCase}} | {successful,Successful} | +%%% StatusElem = {current,TestCaseInfo} | {successful,Successful} | %%% {failed,Failed} | {skipped,Skipped} | {total,Total} +%%% TestCaseInfo = {Suite,TestCase} | [{Suite,TestCase}] %%% Suite = atom() %%% TestCase = atom() %%% Successful = integer() @@ -851,7 +923,8 @@ userdata(TestDir, Suite, Case) when is_atom(Case) -> %%% Reason = term() %%% %%% @doc Returns status of ongoing test. The returned list contains info about -%%% which test case is currently executing, as well as counters for +%%% which test case is currently executing (a list of cases when a +%%% parallel test case group is executing), as well as counters for %%% successful, failed, skipped, and total test cases so far. get_status() -> case get_testdata(curr_tc) of @@ -876,6 +949,8 @@ get_testdata(Key) -> Error; {'EXIT',_Reason} -> no_tests_running; + [CurrTC] when Key == curr_tc -> + {ok,CurrTC}; Data -> {ok,Data} end. @@ -993,13 +1068,21 @@ remove_config(Callback, Config) -> %%%----------------------------------------------------------------- %%% @spec timetrap(Time) -> ok -%%% Time = {hours,Hours} | {minutes,Mins} | {seconds,Secs} | Millisecs | infinity +%%% Time = {hours,Hours} | {minutes,Mins} | {seconds,Secs} | Millisecs | infinity | Func %%% Hours = integer() %%% Mins = integer() %%% Secs = integer() %%% Millisecs = integer() | float() -%%% -%%% @doc <p>Use this function to set a new timetrap for the running test case.</p> +%%% Func = {M,F,A} | fun() +%%% M = atom() +%%% F = atom() +%%% A = list() +%%% +%%% @doc <p>Use this function to set a new timetrap for the running test case. +%%% If the argument is <code>Func</code>, the timetrap will be triggered +%%% when this function returns. <code>Func</code> may also return a new +%%% <code>Time</code> value, which in that case will be the value for the +%%% new timetrap.</p> timetrap(Time) -> test_server:timetrap_cancel(), test_server:timetrap(Time). @@ -1037,3 +1120,98 @@ sleep({seconds,Ss}) -> sleep(trunc(Ss * 1000)); sleep(Time) -> test_server:adjusted_sleep(Time). + +%%%----------------------------------------------------------------- +%%% @spec notify(Name,Data) -> ok +%%% Name = atom() +%%% Data = term() +%%% +%%% @doc <p>Sends a asynchronous notification of type <c>Name</c> with +%%% <c>Data</c>to the common_test event manager. This can later be +%%% caught by any installed event manager. </p> +%%% @see //stdlib/gen_event +notify(Name,Data) -> + ct_event:notify(Name, Data). + +%%%----------------------------------------------------------------- +%%% @spec sync_notify(Name,Data) -> ok +%%% Name = atom() +%%% Data = term() +%%% +%%% @doc <p>Sends a synchronous notification of type <c>Name</c> with +%%% <c>Data</c>to the common_test event manager. This can later be +%%% caught by any installed event manager. </p> +%%% @see //stdlib/gen_event +sync_notify(Name,Data) -> + ct_event:sync_notify(Name, Data). + +%%%----------------------------------------------------------------- +%%% @spec break(Comment) -> ok | {error,Reason} +%%% Comment = string() +%%% Reason = {multiple_cases_running,TestCases} +%%% TestCases = [atom()] +%%% +%%% @doc <p>This function will cancel all timetraps and pause the +%%% execution of the current test case until the user calls the +%%% <c>continue/0</c> function. It gives the user the opportunity +%%% to interact with the erlang node running the tests, e.g. for +%%% debugging purposes or for manually executing a part of the +%%% test case. If a parallel group is executing, <c>break/2</c> +%%% should be called instead.</p> +break(Comment) -> + case get_testdata(curr_tc) of + {ok,{_,TestCase}} -> + test_server:break(?MODULE, Comment); + {ok,Cases} when is_list(Cases) -> + {error,{multiple_cases_running, + [TC || {_,TC} <- Cases]}}; + Error -> + {error,Error} + end. + +%%%----------------------------------------------------------------- +%%% @spec break(TestCase, Comment) -> ok | {error,Reason} +%%% TestCase = atom() +%%% Comment = string() +%%% Reason = test_case_not_running +%%% +%%% @doc <p>This function works the same way as <c>break/1</c>, +%%% only the <c>TestCase</c> argument makes it possible to +%%% pause a test case executing in a parallel group. The +%%% <c>continue/1</c> function should be used to resume +%%% execution of <c>TestCase</c>.</p> +break(TestCase, Comment) -> + case get_testdata(curr_tc) of + {ok,Cases} when is_list(Cases) -> + case lists:keymember(TestCase, 2, Cases) of + true -> + test_server:break(?MODULE, TestCase, Comment); + false -> + {error,test_case_not_running} + end; + {ok,{_,TestCase}} -> + test_server:break(?MODULE, TestCase, Comment); + Error -> + {error,Error} + end. + +%%%----------------------------------------------------------------- +%%% @spec continue() -> ok +%%% +%%% @doc <p>This function must be called in order to continue after a +%%% test case (not executing in a parallel group) has called +%%% <c>break/1</c>.</p> +continue() -> + test_server:continue(). + +%%%----------------------------------------------------------------- +%%% @spec continue(TestCase) -> ok +%%% TestCase = atom() +%%% +%%% @doc <p>This function must be called in order to continue after a +%%% test case has called <c>break/2</c>. If the paused test case, +%%% <c>TestCase</c>, executes in a parallel group, this +%%% function - rather than <c>continue/0</c> - must be used +%%% in order to let the test case proceed.</p> +continue(TestCase) -> + test_server:continue(TestCase). diff --git a/lib/common_test/src/ct_config.erl b/lib/common_test/src/ct_config.erl index 9277af5bc1..c585fe0995 100644 --- a/lib/common_test/src/ct_config.erl +++ b/lib/common_test/src/ct_config.erl @@ -760,13 +760,13 @@ check_config_files(Configs) -> end, lists:keysearch(error, 1, lists:flatten(lists:map(ConfigChecker, Configs))). -prepare_user_configs([ConfigString|UserConfigs], Acc, new) -> +prepare_user_configs([CallbackMod|UserConfigs], Acc, new) -> prepare_user_configs(UserConfigs, - [{list_to_atom(ConfigString), []}|Acc], + [{list_to_atom(CallbackMod),[]}|Acc], cur); prepare_user_configs(["and"|UserConfigs], Acc, _) -> prepare_user_configs(UserConfigs, Acc, new); -prepare_user_configs([ConfigString|UserConfigs], [{LastMod, LastList}|Acc], cur) -> +prepare_user_configs([ConfigString|UserConfigs], [{LastMod,LastList}|Acc], cur) -> prepare_user_configs(UserConfigs, [{LastMod, [ConfigString|LastList]}|Acc], cur); diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl new file mode 100644 index 0000000000..f3b6781971 --- /dev/null +++ b/lib/common_test/src/ct_conn_log_h.erl @@ -0,0 +1,230 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(ct_conn_log_h). + +%%% +%%% A handler that can be connected to the error_logger event +%%% handler. Writes all ct connection events. See comments in +%%% cth_conn_log for more information. +%%% + +-include("ct_util.hrl"). + +-export([init/1, + handle_event/2, handle_call/2, handle_info/2, + terminate/2]). + +-record(state, {group_leader,logs=[]}). + +-define(WIDTH,80). + +%%%----------------------------------------------------------------- +%%% Callbacks +init({GL,Logs}) -> + open_files(Logs,#state{group_leader=GL}). + +open_files([{ConnMod,{LogType,Logs}}|T],State) -> + case do_open_files(Logs,[]) of + {ok,Fds} -> + open_files(T,State#state{logs=[{ConnMod,{LogType,Fds}} | + State#state.logs]}); + Error -> + Error + end; +open_files([],State) -> + {ok,State}. + + +do_open_files([{Tag,File}|Logs],Acc) -> + case file:open(File, [write]) of + {ok,Fd} -> + do_open_files(Logs,[{Tag,Fd}|Acc]); + {error,Reason} -> + {error,{could_not_open_log,File,Reason}} + end; +do_open_files([],Acc) -> + {ok,lists:reverse(Acc)}. + +handle_event({_Type, GL, _Msg}, State) when node(GL) /= node() -> + {ok, State}; +handle_event({_Type,_GL,{Pid,{ct_connection,Action,ConnName},Report}},State) -> + Info = conn_info(Pid,#conn_log{name=ConnName,action=Action}), + write_report(now(),Info,Report,State), + {ok, State}; +handle_event({_Type,_GL,{Pid,Info=#conn_log{},Report}},State) -> + write_report(now(),conn_info(Pid,Info),Report,State), + {ok, State}; +handle_event({error_report,_,{Pid,_,[{ct_connection,ConnName}|R]}},State) -> + %% Error reports from connection + write_error(now(),conn_info(Pid,#conn_log{name=ConnName}),R,State), + {ok, State}; +handle_event(_, State) -> + {ok, State}. + +handle_info(_, State) -> + {ok, State}. + +handle_call(_Query, State) -> + {ok, {error, bad_query}, State}. + +terminate(_,#state{logs=Logs}) -> + [file:close(Fd) || {_,_,Fds} <- Logs, Fd <- Fds], + ok. + + +%%%----------------------------------------------------------------- +%%% Writing reports +write_report(Time,#conn_log{module=ConnMod}=Info,Data,State) -> + {LogType,Fd} = get_log(Info,State), + io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time), + format_title(LogType,Info), + format_data(ConnMod,LogType,Data)]). + +write_error(Time,#conn_log{module=ConnMod}=Info,Report,State) -> + case get_log(Info,State) of + {html,_} -> + %% The error will anyway be written in the html log by the + %% sasl error handler, so don't write it again. + ok; + {LogType,Fd} -> + io:format(Fd,"~n~s~s~s",[format_head(ConnMod,LogType,Time," ERROR"), + format_title(LogType,Info), + format_error(LogType,Report)]) + end. + +get_log(Info,State) -> + case proplists:get_value(Info#conn_log.module,State#state.logs) of + {html,_} -> + {html,State#state.group_leader}; + {LogType,Fds} -> + {LogType,get_fd(Info,Fds)}; + undefined -> + {html,State#state.group_leader} + end. + +get_fd(#conn_log{name=undefined},Fds) -> + proplists:get_value(default,Fds); +get_fd(#conn_log{name=ConnName},Fds) -> + case proplists:get_value(ConnName,Fds) of + undefined -> + proplists:get_value(default,Fds); + Fd -> + Fd + end. + +%%%----------------------------------------------------------------- +%%% Formatting +format_head(ConnMod,LogType,Time) -> + format_head(ConnMod,LogType,Time,""). + +format_head(ConnMod,raw,Time,Text) -> + io_lib:format("~n~p, ~p~s, ",[now_to_time(Time),ConnMod,Text]); +format_head(ConnMod,_,Time,Text) -> + Head = pad_char_end(?WIDTH,pretty_head(now_to_time(Time),ConnMod,Text),$=), + io_lib:format("~n~s",[Head]). + +format_title(raw,#conn_log{client=Client}=Info) -> + io_lib:format("Client ~p ~s ~s",[Client,actionstr(Info),serverstr(Info)]); +format_title(_,Info) -> + Title = pad_char_end(?WIDTH,pretty_title(Info),$=), + io_lib:format("~n~s", [Title]). + +format_data(_,_,NoData) when NoData == ""; NoData == <<>> -> + ""; +format_data(ConnMod,LogType,Data) -> + ConnMod:format_data(LogType,Data). + +format_error(raw,Report) -> + io_lib:format("~n~p~n",[Report]); +format_error(pretty,Report) -> + [io_lib:format("~n ~p: ~p",[K,V]) || {K,V} <- Report]. + + + + +%%%----------------------------------------------------------------- +%%% Helpers +conn_info(LoggingProc, #conn_log{client=undefined} = ConnInfo) -> + conn_info(ConnInfo#conn_log{client=LoggingProc}); +conn_info(_, ConnInfo) -> + conn_info(ConnInfo). + +conn_info(#conn_log{client=Client, module=undefined} = ConnInfo) -> + case ets:lookup(ct_connections,Client) of + [#conn{address=Address,callback=Callback}] -> + ConnInfo#conn_log{address=Address,module=Callback}; + [] -> + ConnInfo + end; +conn_info(ConnInfo) -> + ConnInfo. + + +now_to_time({_,_,MicroS}=Now) -> + {calendar:now_to_local_time(Now),MicroS}. + +pretty_head({{{Y,Mo,D},{H,Mi,S}},MicroS},ConnMod,Text0) -> + Text = string:to_upper(atom_to_list(ConnMod) ++ Text0), + io_lib:format("= ~s ==== ~s-~s-~p::~s:~s:~s,~s ", + [Text,t(D),month(Mo),Y,t(H),t(Mi),t(S), + micro2milli(MicroS)]). + +pretty_title(#conn_log{client=Client}=Info) -> + io_lib:format("= Client ~p ~s Server ~s ", + [Client,actionstr(Info),serverstr(Info)]). + +actionstr(#conn_log{action=send}) -> "----->"; +actionstr(#conn_log{action=recv}) -> "<-----"; +actionstr(#conn_log{action=open}) -> "opened session to"; +actionstr(#conn_log{action=close}) -> "closed session to"; +actionstr(_) -> "<---->". + +serverstr(#conn_log{name=undefined,address=Address}) -> + io_lib:format("~p",[Address]); +serverstr(#conn_log{name=Alias,address=Address}) -> + io_lib:format("~p(~p)",[Alias,Address]). + +month(1) -> "Jan"; +month(2) -> "Feb"; +month(3) -> "Mar"; +month(4) -> "Apr"; +month(5) -> "May"; +month(6) -> "Jun"; +month(7) -> "Jul"; +month(8) -> "Aug"; +month(9) -> "Sep"; +month(10) -> "Oct"; +month(11) -> "Nov"; +month(12) -> "Dec". + +micro2milli(X) -> + pad0(3,integer_to_list(X div 1000)). + +t(X) -> + pad0(2,integer_to_list(X)). + +pad0(N,Str) -> + M = length(Str), + lists:duplicate(N-M,$0) ++ Str. + +pad_char_end(N,Str,Char) -> + case length(lists:flatten(Str)) of + M when M<N -> Str ++ lists:duplicate(N-M,Char); + _ -> Str + end. diff --git a/lib/common_test/src/ct_event.erl b/lib/common_test/src/ct_event.erl index 3e79898ad1..998be35fda 100644 --- a/lib/common_test/src/ct_event.erl +++ b/lib/common_test/src/ct_event.erl @@ -31,7 +31,7 @@ %% API -export([start_link/0, add_handler/0, add_handler/1, stop/0]). --export([notify/1, sync_notify/1]). +-export([notify/1, notify/2, sync_notify/1,sync_notify/2]). -export([is_alive/0]). %% gen_event callbacks @@ -90,6 +90,13 @@ notify(Event) -> end. %%-------------------------------------------------------------------- +%% Function: notify(Name,Data) -> ok +%% Description: Asynchronous notification to event manager. +%%-------------------------------------------------------------------- +notify(Name, Data) -> + notify(#event{ name = Name, data = Data}). + +%%-------------------------------------------------------------------- %% Function: sync_notify(Event) -> ok %% Description: Synchronous notification to event manager. %%-------------------------------------------------------------------- @@ -102,6 +109,13 @@ sync_notify(Event) -> end. %%-------------------------------------------------------------------- +%% Function: sync_notify(Name,Data) -> ok +%% Description: Synchronous notification to event manager. +%%-------------------------------------------------------------------- +sync_notify(Name,Data) -> + sync_notify(#event{ name = Name, data = Data}). + +%%-------------------------------------------------------------------- %% Function: is_alive() -> true | false %% Description: Check if Event Manager is alive. %%-------------------------------------------------------------------- diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl index cdd8a6a596..e8d5d4708b 100644 --- a/lib/common_test/src/ct_framework.erl +++ b/lib/common_test/src/ct_framework.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -27,9 +27,10 @@ -export([init_tc/3, end_tc/3, end_tc/4, get_suite/2, get_all_cases/1]). -export([report/2, warn/1, error_notification/4]). --export([get_logopts/0, format_comment/1, get_html_wrapper/3]). +-export([get_logopts/0, format_comment/1, get_html_wrapper/4]). --export([error_in_suite/1, ct_init_per_group/2, ct_end_per_group/2]). +-export([error_in_suite/1, init_per_suite/1, end_per_suite/1, + init_per_group/2, end_per_group/2]). -export([make_all_conf/3, make_conf/5]). @@ -54,6 +55,7 @@ init_tc(Mod,Func,Config) -> %% in case Mod == ct_framework, lookup the suite name Suite = get_suite_name(Mod, Config), + %% check if previous testcase was interpreted and has left %% a "dead" trace window behind - if so, kill it case ct_util:get_testdata(interpret) of @@ -63,50 +65,46 @@ init_tc(Mod,Func,Config) -> _ -> ok end, - %% check if we need to add defaults explicitly because - %% there's no init_per_suite exported from Mod - {InitFailed,DoInit} = - case ct_util:get_testdata(curr_tc) of - {Suite,{suite0_failed,_}=Failure} -> - {Failure,false}; - {?MODULE,_} -> % should not really happen - {false,false}; - {Suite,_} -> % Func is not 1st case in suite - {false,false}; - _ when Func == init_per_suite -> % defaults will be added anyway - {false,false}; - _ -> % first case in suite - {false,true} - end, - case InitFailed of - false -> - ct_util:set_testdata({curr_tc,{Suite,Func}}), + + case ct_util:get_testdata(curr_tc) of + {Suite,{suite0_failed,{require,Reason}}} -> + {skip,{require_failed_in_suite0,Reason}}; + {Suite,{suite0_failed,_}=Failure} -> + {skip,Failure}; + CurrTC -> + case CurrTC of + undefined -> + ct_util:set_testdata({curr_tc,[{Suite,Func}]}); + _ when is_list(CurrTC) -> + ct_util:update_testdata(curr_tc, + fun(Running) -> + [{Suite,Func}|Running] + end) + end, case ct_util:read_suite_data({seq,Suite,Func}) of undefined -> - init_tc1(Mod,Func,Config,DoInit); + init_tc1(Mod,Suite,Func,Config); Seq when is_atom(Seq) -> case ct_util:read_suite_data({seq,Suite,Seq}) of [Func|TCs] -> % this is the 1st case in Seq - %% make sure no cases in this seq are marked as failed - %% from an earlier execution in the same suite + %% make sure no cases in this seq are + %% marked as failed from an earlier execution + %% in the same suite lists:foreach( fun(TC) -> - ct_util:save_suite_data({seq,Suite,TC},Seq) + ct_util:save_suite_data({seq,Suite,TC}, + Seq) end, TCs); _ -> ok end, - init_tc1(Mod,Func,Config,DoInit); + init_tc1(Mod,Suite,Func,Config); {failed,Seq,BadFunc} -> {skip,{sequence_failed,Seq,BadFunc}} - end; - {_,{require,Reason}} -> - {skip,{require_failed_in_suite0,Reason}}; - _ -> - {skip,InitFailed} + end end. -init_tc1(?MODULE,error_in_suite,[Config0],_) when is_list(Config0) -> +init_tc1(?MODULE,_,error_in_suite,[Config0]) when is_list(Config0) -> ct_logs:init_tc(false), ct_event:notify(#event{name=tc_start, node=node(), @@ -117,14 +115,16 @@ init_tc1(?MODULE,error_in_suite,[Config0],_) when is_list(Config0) -> Reason -> {skip,Reason} end; -init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) -> + +init_tc1(Mod,Suite,Func,[Config0]) when is_list(Config0) -> Config1 = case ct_util:read_suite_data(last_saved_config) of - {{Mod,LastFunc},SavedConfig} -> % last testcase + {{Suite,LastFunc},SavedConfig} -> % last testcase [{saved_config,{LastFunc,SavedConfig}} | lists:keydelete(saved_config,1,Config0)]; - {{LastSuite,InitOrEnd},SavedConfig} when InitOrEnd == init_per_suite ; - InitOrEnd == end_per_suite -> + {{LastSuite,InitOrEnd}, + SavedConfig} when InitOrEnd == init_per_suite ; + InitOrEnd == end_per_suite -> %% last suite [{saved_config,{LastSuite,SavedConfig}} | lists:keydelete(saved_config,1,Config0)]; @@ -133,13 +133,14 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) -> end, ct_util:delete_suite_data(last_saved_config), Config = lists:keydelete(watchdog,1,Config1), - if Func /= init_per_suite, DoInit /= true -> - ok; - true -> + + if Func == init_per_suite -> %% delete all default values used in previous suite ct_config:delete_default_config(suite), %% release all name -> key bindings (once per suite) - ct_config:release_allocated() + ct_config:release_allocated(); + Func /= init_per_suite -> + ok end, GroupPath = ?val(tc_group_path, Config, []), @@ -154,9 +155,7 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) -> true -> ct_config:delete_default_config(testcase) end, - %% in case Mod == ct_framework, lookup the suite name - Suite = get_suite_name(Mod, Config), - case add_defaults(Mod,Func,AllGroups,DoInit) of + case add_defaults(Mod,Func,AllGroups) of Error = {suite0_failed,_} -> ct_logs:init_tc(false), ct_event:notify(#event{name=tc_start, @@ -166,35 +165,25 @@ init_tc1(Mod,Func,[Config0],DoInit) when is_list(Config0) -> {error,Error}; {SuiteInfo,MergeResult} -> case MergeResult of - {error,Reason} when DoInit == false -> + {error,Reason} -> ct_logs:init_tc(false), ct_event:notify(#event{name=tc_start, node=node(), data={Mod,FuncSpec}}), {skip,Reason}; _ -> - init_tc2(Mod,Func,SuiteInfo,MergeResult, - Config,DoInit) + init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) end end; -init_tc1(_Mod,_Func,Args,_DoInit) -> - {ok,Args}. -init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> - %% if first testcase fails when there's no init_per_suite - %% we must do suite/0 configurations before skipping test - MergedInfo = - case MergeResult of - {error,_} when DoInit == true -> - SuiteInfo; - _ -> - MergeResult - end, +init_tc1(_Mod,_Suite,_Func,Args) -> + {ok,Args}. +init_tc2(Mod,Suite,Func,SuiteInfo,MergeResult,Config) -> %% timetrap must be handled before require - MergedInfo1 = timetrap_first(MergedInfo, [], []), + MergedInfo = timetrap_first(MergeResult, [], []), %% tell logger to use specified style sheet - case lists:keysearch(stylesheet,1,MergedInfo++Config) of + case lists:keysearch(stylesheet,1,MergeResult++Config) of {value,{stylesheet,SSFile}} -> ct_logs:set_stylesheet(Func,add_data_dir(SSFile,Config)); _ -> @@ -209,7 +198,7 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> %% list of {Type,Bool} tuples, e.g. {telnet,true}), case ct_util:get_overridden_silenced_connections() of undefined -> - case lists:keysearch(silent_connections,1,MergedInfo++Config) of + case lists:keysearch(silent_connections,1,MergeResult++Config) of {value,{silent_connections,Conns}} -> ct_util:silence_connections(Conns); _ -> @@ -218,61 +207,67 @@ init_tc2(Mod,Func,SuiteInfo,MergeResult,Config,DoInit) -> Conns -> ct_util:silence_connections(Conns) end, - if Func /= init_per_suite, DoInit /= true -> - ct_logs:init_tc(false); - true -> - ct_logs:init_tc(true) - end, + ct_logs:init_tc(Func == init_per_suite), FuncSpec = group_or_func(Func,Config), ct_event:notify(#event{name=tc_start, node=node(), data={Mod,FuncSpec}}), - case catch configure(MergedInfo1,MergedInfo1,SuiteInfo, - {FuncSpec,DoInit},Config) of + case catch configure(MergedInfo,MergedInfo,SuiteInfo, + FuncSpec,[],Config) of {suite0_failed,Reason} -> - ct_util:set_testdata({curr_tc,{Mod,{suite0_failed,{require,Reason}}}}), + ct_util:set_testdata({curr_tc,{Mod,{suite0_failed, + {require,Reason}}}}), {skip,{require_failed_in_suite0,Reason}}; {error,Reason} -> {auto_skip,{require_failed,Reason}}; {'EXIT',Reason} -> {auto_skip,Reason}; - {ok,FinalConfig} -> - case MergeResult of - {error,Reason} -> - %% suite0 configure finished now, report that - %% first test case actually failed - {skip,Reason}; - _ -> - case get('$test_server_framework_test') of - undefined -> - ct_suite_init(Mod, FuncSpec, FinalConfig); - Fun -> - case Fun(init_tc, FinalConfig) of - NewConfig when is_list(NewConfig) -> - {ok,NewConfig}; - Else -> - Else - end + {ok,PostInitHook,Config1} -> + case get('$test_server_framework_test') of + undefined -> + ct_suite_init(Suite, FuncSpec, PostInitHook, Config1); + Fun -> + PostInitHookResult = do_post_init_hook(PostInitHook, + Config1), + case Fun(init_tc, [PostInitHookResult ++ Config1]) of + NewConfig when is_list(NewConfig) -> + {ok,NewConfig}; + Else -> + Else end end end. -ct_suite_init(Mod, Func, [Config]) when is_list(Config) -> - case ct_hooks:init_tc( Mod, Func, Config) of +ct_suite_init(Suite, Func, PostInitHook, Config) when is_list(Config) -> + case ct_hooks:init_tc(Suite, Func, Config) of NewConfig when is_list(NewConfig) -> - {ok, [NewConfig]}; + PostInitHookResult = do_post_init_hook(PostInitHook, NewConfig), + {ok, [PostInitHookResult ++ NewConfig]}; Else -> Else end. -add_defaults(Mod,Func, GroupPath, DoInit) -> +do_post_init_hook(PostInitHook, Config) -> + lists:flatmap(fun({Tag,Fun}) -> + case lists:keysearch(Tag,1,Config) of + {value,_} -> + []; + false -> + case Fun() of + {error,_} -> []; + Result -> [{Tag,Result}] + end + end + end, PostInitHook). + +add_defaults(Mod,Func, GroupPath) -> Suite = get_suite_name(Mod, GroupPath), case (catch Suite:suite()) of {'EXIT',{undef,_}} -> SuiteInfo = merge_with_suite_defaults(Suite,[]), SuiteInfoNoCTH = [I || I <- SuiteInfo, element(1,I) =/= ct_hooks], - case add_defaults1(Mod,Func, GroupPath, SuiteInfoNoCTH, DoInit) of + case add_defaults1(Mod,Func, GroupPath, SuiteInfoNoCTH) of Error = {error,_} -> {SuiteInfo,Error}; MergedInfo -> {SuiteInfo,MergedInfo} end; @@ -292,7 +287,7 @@ add_defaults(Mod,Func, GroupPath, DoInit) -> SuiteInfoNoCTH = [I || I <- SuiteInfo1, element(1,I) =/= ct_hooks], case add_defaults1(Mod,Func, GroupPath, - SuiteInfoNoCTH, DoInit) of + SuiteInfoNoCTH) of Error = {error,_} -> {SuiteInfo1,Error}; MergedInfo -> {SuiteInfo1,MergedInfo} end; @@ -313,7 +308,7 @@ add_defaults(Mod,Func, GroupPath, DoInit) -> {suite0_failed,bad_return_value} end. -add_defaults1(Mod,Func, GroupPath, SuiteInfo, DoInit) -> +add_defaults1(Mod,Func, GroupPath, SuiteInfo) -> Suite = get_suite_name(Mod, GroupPath), %% GroupPathInfo (for subgroup on level X) = %% [LevelXGroupInfo, LevelX-1GroupInfo, ..., TopLevelGroupInfo] @@ -325,8 +320,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo, DoInit) -> _ -> [] end end, GroupPath), - Args = if Func == init_per_group; Func == ct_init_per_group; - Func == end_per_group; Func == ct_end_per_group -> + Args = if Func == init_per_group ; Func == end_per_group -> [?val(name, hd(GroupPath))]; true -> [] @@ -347,7 +341,7 @@ add_defaults1(Mod,Func, GroupPath, SuiteInfo, DoInit) -> (default_config == element(1,SDDef)))], case check_for_clashes(TestCaseInfo, GroupPathInfo, SuiteReqs) of [] -> - add_defaults2(Mod,Func, TCAndGroupInfo,SuiteInfo,SuiteReqs, DoInit); + add_defaults2(Mod,Func, TCAndGroupInfo,SuiteInfo,SuiteReqs); Clashes -> {error,{config_name_already_in_use,Clashes}} end. @@ -372,16 +366,12 @@ get_suite_name(Mod, _) -> Mod. %% Check that alias names are not already in use -check_for_clashes(TCInfo, GrPathInfo, SuiteInfo) -> - {CurrGrInfo,SearchIn} = case GrPathInfo of - [] -> {[],[SuiteInfo]}; - [Curr|Path] -> {Curr,[SuiteInfo|Path]} - end, +check_for_clashes(TCInfo, [CurrGrInfo|Path], SuiteInfo) -> ReqNames = fun(Info) -> [element(2,R) || R <- Info, size(R) == 3, require == element(1,R)] end, - ExistingNames = lists:flatten([ReqNames(L) || L <- SearchIn]), + ExistingNames = lists:flatten([ReqNames(L) || L <- [SuiteInfo|Path]]), CurrGrReqNs = ReqNames(CurrGrInfo), GrClashes = [Name || Name <- CurrGrReqNs, true == lists:member(Name, ExistingNames)], @@ -421,34 +411,25 @@ keysmember([Key,Pos|Next], List) -> keysmember([], _) -> true. -add_defaults2(Mod,init_per_suite, IPSInfo, SuiteInfo,SuiteReqs, false) -> - add_defaults2(Mod,init_per_suite, IPSInfo, SuiteInfo,SuiteReqs, true); +add_defaults2(_Mod,init_per_suite, IPSInfo, SuiteInfo,SuiteReqs) -> + Info = lists:flatten([IPSInfo, SuiteReqs]), + lists:flatten([Info,remove_info_in_prev(Info, [SuiteInfo])]); -add_defaults2(_Mod,IPG, IPGAndGroupInfo, SuiteInfo,SuiteReqs, DoInit) when - IPG == init_per_group ; IPG == ct_init_per_group -> - %% If DoInit == true, we have to process the suite() list, otherwise - %% it has already been handled (see clause for init_per_suite) - case DoInit of - true -> - %% note: we know for sure this is a top level group - Info = lists:flatten([IPGAndGroupInfo, SuiteReqs]), - Info ++ remove_info_in_prev(Info, [SuiteInfo]); - false -> - SuiteInfo1 = - remove_info_in_prev(lists:flatten([IPGAndGroupInfo, - SuiteReqs]), [SuiteInfo]), - %% don't require terms in prev groups (already processed) - case IPGAndGroupInfo of - [IPGInfo] -> - lists:flatten([IPGInfo,SuiteInfo1]); - [IPGInfo | [CurrGroupInfo | PrevGroupInfo]] -> - PrevGroupInfo1 = delete_require_terms(PrevGroupInfo), - lists:flatten([IPGInfo,CurrGroupInfo,PrevGroupInfo1, - SuiteInfo1]) - end +add_defaults2(_Mod,init_per_group, IPGAndGroupInfo, SuiteInfo,SuiteReqs) -> + SuiteInfo1 = + remove_info_in_prev(lists:flatten([IPGAndGroupInfo, + SuiteReqs]), [SuiteInfo]), + %% don't require terms in prev groups (already processed) + case IPGAndGroupInfo of + [IPGInfo] -> + lists:flatten([IPGInfo,SuiteInfo1]); + [IPGInfo | [CurrGroupInfo | PrevGroupInfo]] -> + PrevGroupInfo1 = delete_require_terms(PrevGroupInfo), + lists:flatten([IPGInfo,CurrGroupInfo,PrevGroupInfo1, + SuiteInfo1]) end; -add_defaults2(_Mod,_Func, TCAndGroupInfo, SuiteInfo,SuiteReqs, false) -> +add_defaults2(_Mod,_Func, TCAndGroupInfo, SuiteInfo,SuiteReqs) -> %% Include require elements from test case info and current group, %% but not from previous groups or suite/0 (since we've already required %% those vars). Let test case info elements override group and suite @@ -463,14 +444,7 @@ add_defaults2(_Mod,_Func, TCAndGroupInfo, SuiteInfo,SuiteReqs, false) -> PrevGroupInfo1 = delete_require_terms(PrevGroupInfo), lists:flatten([TCInfo,CurrGroupInfo,PrevGroupInfo1, SuiteInfo1]) - end; - -add_defaults2(_Mod,_Func, TCInfo, SuiteInfo,SuiteReqs, true) -> - %% Here we have to process the suite info list also (no call to - %% init_per_suite before this first test case). This TC can't belong - %% to a group, or the clause for (ct_)init_per_group would've caught this. - Info = lists:flatten([TCInfo, SuiteReqs]), - lists:flatten([Info,remove_info_in_prev(Info, [SuiteInfo])]). + end. delete_require_terms([Info | Prev]) -> Info1 = [T || T <- Info, @@ -506,15 +480,16 @@ timetrap_first([],Info,[]) -> timetrap_first([],Info,Found) -> ?rev(Found) ++ ?rev(Info). -configure([{require,Required}|Rest],Info,SuiteInfo,Scope,Config) -> +configure([{require,Required}|Rest], + Info,SuiteInfo,Scope,PostInitHook,Config) -> case ct:require(Required) of ok -> - configure(Rest,Info,SuiteInfo,Scope,Config); + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); Error = {error,Reason} -> case required_default('_UNDEF',Required,Info, SuiteInfo,Scope) of ok -> - configure(Rest,Info,SuiteInfo,Scope,Config); + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); _ -> case lists:keymember(Required,2,SuiteInfo) of true -> @@ -524,14 +499,15 @@ configure([{require,Required}|Rest],Info,SuiteInfo,Scope,Config) -> end end end; -configure([{require,Name,Required}|Rest],Info,SuiteInfo,Scope,Config) -> +configure([{require,Name,Required}|Rest], + Info,SuiteInfo,Scope,PostInitHook,Config) -> case ct:require(Name,Required) of ok -> - configure(Rest,Info,SuiteInfo,Scope,Config); + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); Error = {error,Reason} -> case required_default(Name,Required,Info,SuiteInfo,Scope) of ok -> - configure(Rest,Info,SuiteInfo,Scope,Config); + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); _ -> case lists:keymember(Name,2,SuiteInfo) of true -> @@ -541,35 +517,33 @@ configure([{require,Name,Required}|Rest],Info,SuiteInfo,Scope,Config) -> end end end; -configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,Config) -> - configure(Rest,Info,SuiteInfo,Scope,Config); -configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,Config) -> - Dog = test_server:timetrap(Time), - configure(Rest,Info,SuiteInfo,Scope,[{watchdog,Dog}|Config]); -configure([{ct_hooks, Hook} | Rest], Info, SuiteInfo, Scope, Config) -> - configure(Rest, Info, SuiteInfo, Scope, [{ct_hooks, Hook} | Config]); -configure([_|Rest],Info,SuiteInfo,Scope,Config) -> - configure(Rest,Info,SuiteInfo,Scope,Config); -configure([],_,_,_,Config) -> - {ok,[Config]}. +configure([{timetrap,off}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); +configure([{timetrap,Time}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> + PostInitHook1 = + [{watchdog,fun() -> case test_server:get_timetrap_info() of + undefined -> + test_server:timetrap(Time); + _ -> + {error,already_set} + end + end} | PostInitHook], + configure(Rest,Info,SuiteInfo,Scope,PostInitHook1,Config); +configure([{ct_hooks,Hook}|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,[{ct_hooks,Hook}|Config]); +configure([_|Rest],Info,SuiteInfo,Scope,PostInitHook,Config) -> + configure(Rest,Info,SuiteInfo,Scope,PostInitHook,Config); +configure([],_,_,_,PostInitHook,Config) -> + {ok,PostInitHook,Config}. %% the require element in Info may come from suite/0 and %% should be scoped 'suite', or come from the group info %% function and be scoped 'group', or come from the testcase %% info function and then be scoped 'testcase' -required_default(Name,Key,Info,SuiteInfo,{FuncSpec,true}) -> - case try_set_default(Name,Key,SuiteInfo,suite) of - ok -> - ok; - _ -> - required_default(Name,Key,Info,[],{FuncSpec,false}) - end; -required_default(Name,Key,Info,_,{init_per_suite,_}) -> +required_default(Name,Key,Info,_,init_per_suite) -> try_set_default(Name,Key,Info,suite); -required_default(Name,Key,Info,_,{{init_per_group,GrName,_},_}) -> - try_set_default(Name,Key,Info,{group,GrName}); -required_default(Name,Key,Info,_,{{ct_init_per_group,GrName,_},_}) -> +required_default(Name,Key,Info,_,{init_per_group,GrName,_}) -> try_set_default(Name,Key,Info,{group,GrName}); required_default(Name,Key,Info,_,_FuncSpec) -> try_set_default(Name,Key,Info,testcase). @@ -621,10 +595,11 @@ end_tc(Mod,Func,{Result,[Args]}, Return) -> end_tc(Mod,Func,self(),Result,Args,Return). end_tc(Mod,Func,TCPid,Result,Args,Return) -> - case lists:keysearch(watchdog,1,Args) of - {value,{watchdog,Dog}} -> test_server:timetrap_cancel(Dog); - false -> ok - end, + %% in case Mod == ct_framework, lookup the suite name + Suite = get_suite_name(Mod, Args), + + test_server:timetrap_cancel(), + %% save the testcase process pid so that it can be used %% to look up the attached trace window later case ct_util:get_testdata(interpret) of @@ -636,61 +611,76 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) -> end, ct_util:delete_testdata(comment), ct_util:delete_suite_data(last_saved_config), - FuncSpec = - case group_or_func(Func,Args) of - {_,GroupName,_Props} = Group -> - if Func == end_per_group; Func == ct_end_per_group -> - ct_config:delete_default_config({group,GroupName}); - true -> ok - end, - case lists:keysearch(save_config,1,Args) of - {value,{save_config,SaveConfig}} -> - ct_util:save_suite_data( - last_saved_config, - {Mod,{group,GroupName}}, - SaveConfig), - Group; - false -> - Group - end; - _ -> - case lists:keysearch(save_config,1,Args) of - {value,{save_config,SaveConfig}} -> - ct_util:save_suite_data(last_saved_config, - {Mod,Func},SaveConfig), - Func; - false -> - Func - end - end, - ct_util:reset_silent_connections(), + + FuncSpec = case group_or_func(Func,Args) of + {_,_GroupName,_} = Group -> Group; + _ -> Func + end, case get('$test_server_framework_test') of undefined -> {FinalResult,FinalNotify} = case ct_hooks:end_tc( - Mod, FuncSpec, Args, Result, Return) of + Suite, FuncSpec, Args, Result, Return) of '$ct_no_change' -> {ok,Result}; FinalResult1 -> {FinalResult1,FinalResult1} end, - % send sync notification so that event handlers may print - % in the log file before it gets closed + %% send sync notification so that event handlers may print + %% in the log file before it gets closed ct_event:sync_notify(#event{name=tc_done, node=node(), data={Mod,FuncSpec, tag_cth(FinalNotify)}}); Fun -> - % send sync notification so that event handlers may print - % in the log file before it gets closed + %% send sync notification so that event handlers may print + %% in the log file before it gets closed ct_event:sync_notify(#event{name=tc_done, node=node(), data={Mod,FuncSpec,tag(Result)}}), FinalResult = Fun(end_tc, Return) + end, + + case FuncSpec of + {_,GroupName,_Props} -> + if Func == end_per_group -> + ct_config:delete_default_config({group,GroupName}); + true -> ok + end, + case lists:keysearch(save_config,1,Args) of + {value,{save_config,SaveConfig}} -> + ct_util:save_suite_data(last_saved_config, + {Suite,{group,GroupName}}, + SaveConfig); + false -> + ok + end; + _ -> + case lists:keysearch(save_config,1,Args) of + {value,{save_config,SaveConfig}} -> + ct_util:save_suite_data(last_saved_config, + {Suite,Func},SaveConfig); + false -> + ok + end end, - + ct_util:reset_silent_connections(), + + %% reset the curr_tc state, or delete this TC from the list of + %% executing cases (if in a parallel group) + ClearCurrTC = fun(Running = [_,_|_]) -> + lists:delete({Mod,Func},Running); + ({_,{suite0_failed,_}}) -> + undefined; + ([{_,CurrTC}]) when CurrTC == Func -> + undefined; + (undefined) -> + undefined + end, + ct_util:update_testdata(curr_tc,ClearCurrTC), + case FinalResult of {skip,{sequence_failed,_,_}} -> %% ct_logs:init_tc is never called for a skipped test case @@ -706,7 +696,7 @@ end_tc(Mod,Func,TCPid,Result,Args,Return) -> end, case Func of end_per_suite -> - ct_util:match_delete_suite_data({seq,Mod,'_'}); + ct_util:match_delete_suite_data({seq,Suite,'_'}); _ -> ok end, @@ -864,9 +854,7 @@ mark_as_failed1(_,_,_,[]) -> ok. group_or_func(Func, Config) when Func == init_per_group; - Func == end_per_group; - Func == ct_init_per_group; - Func == ct_end_per_group -> + Func == end_per_group -> case ?val(tc_group_properties, Config) of undefined -> {Func,unknown,[]}; @@ -1209,8 +1197,8 @@ make_conf(Mod, Name, Props, TestSpec) -> "end_per_group/2 missing for group " "~p in ~p, using default.", [Name,Mod]), - {{?MODULE,ct_init_per_group}, - {?MODULE,ct_end_per_group}, + {{?MODULE,init_per_group}, + {?MODULE,end_per_group}, [{suite,Mod}]} end, {conf,[{name,Name}|Props++ExtraProps],InitConf,TestSpec,EndConf}. @@ -1470,22 +1458,31 @@ error_in_suite(Config) -> Reason = test_server:lookup_config(error,Config), exit(Reason). +%% if init_per_suite and end_per_suite are missing in the suite, +%% these will be called instead (without any trace of them in the +%% log files), only so that it's possible to call hook functions +%% for configuration +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + %% if the group config functions are missing in the suite, %% use these instead -ct_init_per_group(GroupName, Config) -> +init_per_group(GroupName, Config) -> ct:comment(io_lib:format("start of ~p", [GroupName])), ct_logs:log("TEST INFO", "init_per_group/2 for ~w missing " "in suite, using default.", [GroupName]), Config. -ct_end_per_group(GroupName, _) -> +end_per_group(GroupName, _) -> ct:comment(io_lib:format("end of ~p", [GroupName])), ct_logs:log("TEST INFO", "end_per_group/2 for ~w missing " "in suite, using default.", [GroupName]), ok. - %%%----------------------------------------------------------------- %%% @spec report(What,Data) -> ok @@ -1562,10 +1559,6 @@ report(What,Data) -> ok; {end_per_group,_} -> ok; - {ct_init_per_group,_} -> - ok; - {ct_end_per_group,_} -> - ok; {_,ok} -> add_to_stats(ok); {_,{skipped,{failed,{_,init_per_testcase,_}}}} -> @@ -1602,8 +1595,7 @@ report(What,Data) -> data=Data}), ct_hooks:on_tc_skip(What, Data), if Case /= end_per_suite, - Case /= end_per_group, - Case /= ct_end_per_group -> + Case /= end_per_group -> add_to_stats(auto_skipped); true -> ok @@ -1689,5 +1681,5 @@ format_comment(Comment) -> %%%----------------------------------------------------------------- %%% @spec get_html_wrapper(TestName, PrintLabel, Cwd) -> Header -get_html_wrapper(TestName, PrintLabel, Cwd) -> - ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd). +get_html_wrapper(TestName, PrintLabel, Cwd, TableCols) -> + ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols). diff --git a/lib/common_test/src/ct_gen_conn.erl b/lib/common_test/src/ct_gen_conn.erl index 5aab4dd2dd..1f01d84601 100644 --- a/lib/common_test/src/ct_gen_conn.erl +++ b/lib/common_test/src/ct_gen_conn.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -27,7 +27,7 @@ -compile(export_all). -export([start/4, stop/1]). --export([call/2, do_within_time/2]). +-export([call/2, call/3, return/2, do_within_time/2]). -ifdef(debug). -define(dbg,true). @@ -39,17 +39,24 @@ name, address, init_data, + reconnect = true, + forward = false, + use_existing = true, + old = false, conn_pid, cb_state, ct_util_server}). %%%----------------------------------------------------------------- -%%% @spec start(Name,Address,InitData,CallbackMod) -> +%%% @spec start(Address,InitData,CallbackMod,Opts) -> %%% {ok,Handle} | {error,Reason} %%% Name = term() %%% CallbackMod = atom() %%% InitData = term() %%% Address = term() +%%% Opts = [Opt] +%%% Opt = {name,Name} | {use_existing_connection,boolean()} | +%%% {reconnect,boolean()} | {forward_messages,boolean()} %%% %%% @doc Open a connection and start the generic connection owner process. %%% @@ -60,50 +67,67 @@ %%% <code>InitData</code> and returna %%% <code>{ok,ConnectionPid,State}</code> or %%% <code>{error,Reason}</code>.</p> +%%% +%%% If no name is given, the <code>Name</code> argument in init/3 will +%%% have the value <code>undefined</code>. +%%% +%%% The callback modules must also export +%%% ``` +%%% handle_msg(Msg,From,State) -> {reply,Reply,State} | +%%% {noreply,State} | +%%% {stop,Reply,State} +%%% terminate(ConnectionPid,State) -> term() +%%% close(Handle) -> term() +%%% ''' +%%% +%%% The <code>close/1</code> callback function is actually a callback +%%% for ct_util, for closing registered connections when +%%% ct_util_server is terminated. <code>Handle</code> is the Pid of +%%% the ct_gen_conn process. +%%% +%%% If option <code>reconnect</code> is <code>true</code>, then the +%%% callback must also export +%%% ``` +%%% reconnect(Address,State) -> {ok,ConnectionPid,State} +%%% ''' +%%% +%%% If option <code>forward_messages</code> is <ocde>true</code>, then +%%% the callback must also export +%%% ``` +%%% handle_msg(Msg,State) -> {noreply,State} | {stop,State} +%%% ''' +%%% +%%% An old interface still exists. This is used by ct_telnet, ct_ftp +%%% and ct_ssh. The start function then has an explicit +%%% <code>Name</code> argument, and no <code>Opts</code> argument. The +%%% callback must export: +%%% +%%% ``` +%%% init(Name,Address,InitData) -> {ok,ConnectionPid,State} +%%% handle_msg(Msg,State) -> {Reply,State} +%%% reconnect(Address,State) -> {ok,ConnectionPid,State} +%%% terminate(ConnectionPid,State) -> term() +%%% close(Handle) -> term() +%%% ''' +%%% +start(Address,InitData,CallbackMod,Opts) when is_list(Opts) -> + do_start(Address,InitData,CallbackMod,Opts); start(Name,Address,InitData,CallbackMod) -> - case ct_util:does_connection_exist(Name,Address,CallbackMod) of - {ok,Pid} -> - log("ct_gen_conn:start","Using existing connection!\n",[]), - {ok,Pid}; - false -> - Self = self(), - Pid = spawn(fun() -> - init_gen(Self, #gen_opts{callback=CallbackMod, - name=Name, - address=Address, - init_data=InitData}) - end), - MRef = erlang:monitor(process,Pid), - receive - {connected,Pid} -> - erlang:demonitor(MRef, [flush]), - ct_util:register_connection(Name,Address,CallbackMod,Pid), - {ok,Pid}; - {Error,Pid} -> - receive {'DOWN',MRef,process,_,_} -> ok end, - Error; - {'DOWN',MRef,process,_,Reason} -> - log("ct_gen_conn:start", - "Connection process died: ~p\n", - [Reason]), - {error,{connection_process_died,Reason}} - end - end. - + do_start(Address,InitData,CallbackMod,[{name,Name},{old,true}]). %%%----------------------------------------------------------------- %%% @spec stop(Handle) -> ok %%% Handle = handle() %%% -%%% @doc Close the telnet connection and stop the process managing it. +%%% @doc Close the connection and stop the process managing it. stop(Pid) -> - call(Pid,stop). + call(Pid,stop,5000). %%%----------------------------------------------------------------- %%% @spec log(Heading,Format,Args) -> ok %%% %%% @doc Log activities on the current connection (tool-internal use only). -%%% @see ct_logs:log/3 +%%% @see ct_logs:log/3 log(Heading,Format,Args) -> log(log,[Heading,Format,Args]). @@ -111,7 +135,7 @@ log(Heading,Format,Args) -> %%% @spec start_log(Heading) -> ok %%% %%% @doc Log activities on the current connection (tool-internal use only). -%%% @see ct_logs:start_log/1 +%%% @see ct_logs:start_log/1 start_log(Heading) -> log(start_log,[Heading]). @@ -119,7 +143,7 @@ start_log(Heading) -> %%% @spec cont_log(Format,Args) -> ok %%% %%% @doc Log activities on the current connection (tool-internal use only). -%%% @see ct_logs:cont_log/2 +%%% @see ct_logs:cont_log/2 cont_log(Format,Args) -> log(cont_log,[Format,Args]). @@ -127,7 +151,7 @@ cont_log(Format,Args) -> %%% @spec end_log() -> ok %%% %%% @doc Log activities on the current connection (tool-internal use only). -%%% @see ct_logs:end_log/0 +%%% @see ct_logs:end_log/0 end_log() -> log(end_log,[]). @@ -148,10 +172,10 @@ do_within_time(Fun,Timeout) -> Silent = get(silent), TmpPid = spawn_link(fun() -> put(silent,Silent), R = Fun(), - Self ! {self(),R} + Self ! {self(),R} end), ConnPid = get(conn_pid), - receive + receive {TmpPid,Result} -> Result; {'EXIT',ConnPid,_Reason}=M -> @@ -159,7 +183,7 @@ do_within_time(Fun,Timeout) -> exit(TmpPid,kill), self() ! M, {error,connection_closed} - after + after Timeout -> exit(TmpPid,kill), receive @@ -176,12 +200,66 @@ do_within_time(Fun,Timeout) -> %%%================================================================= %%% Internal functions -call(Pid,Msg) -> +do_start(Address,InitData,CallbackMod,Opts0) -> + Opts = check_opts(Opts0,#gen_opts{callback=CallbackMod, + address=Address, + init_data=InitData}), + case ct_util:does_connection_exist(Opts#gen_opts.name, + Address,CallbackMod) of + {ok,Pid} when Opts#gen_opts.use_existing -> + log("ct_gen_conn:start","Using existing connection!\n",[]), + {ok,Pid}; + {ok,Pid} when not Opts#gen_opts.use_existing -> + {error,{connection_exists,Pid}}; + false -> + do_start(Opts) + end. + +do_start(Opts) -> + Self = self(), + Pid = spawn(fun() -> init_gen(Self, Opts) end), + MRef = erlang:monitor(process,Pid), + receive + {connected,Pid} -> + erlang:demonitor(MRef, [flush]), + ct_util:register_connection(Opts#gen_opts.name, Opts#gen_opts.address, + Opts#gen_opts.callback, Pid), + {ok,Pid}; + {Error,Pid} -> + receive {'DOWN',MRef,process,_,_} -> ok end, + Error; + {'DOWN',MRef,process,_,Reason} -> + log("ct_gen_conn:start", + "Connection process died: ~p\n", + [Reason]), + {error,{connection_process_died,Reason}} + end. + +check_opts(Opts0) -> + check_opts(Opts0,#gen_opts{}). + +check_opts([{name,Name}|T],Opts) -> + check_opts(T,Opts#gen_opts{name=Name}); +check_opts([{reconnect,Bool}|T],Opts) -> + check_opts(T,Opts#gen_opts{reconnect=Bool}); +check_opts([{forward_messages,Bool}|T],Opts) -> + check_opts(T,Opts#gen_opts{forward=Bool}); +check_opts([{use_existing_connection,Bool}|T],Opts) -> + check_opts(T,Opts#gen_opts{use_existing=Bool}); +check_opts([{old,Bool}|T],Opts) -> + check_opts(T,Opts#gen_opts{old=Bool}); +check_opts([],Opts) -> + Opts. + +call(Pid, Msg) -> + call(Pid, Msg, infinity). + +call(Pid, Msg, Timeout) -> MRef = erlang:monitor(process,Pid), Ref = make_ref(), Pid ! {Msg,{self(),Ref}}, receive - {Ref, Result} -> + {Ref, Result} -> erlang:demonitor(MRef, [flush]), case Result of {retry,_Data} -> @@ -189,8 +267,15 @@ call(Pid,Msg) -> Other -> Other end; - {'DOWN',MRef,process,_,Reason} -> + {'DOWN',MRef,process,_,Reason} -> {error,{process_down,Pid,Reason}} + after Timeout -> + erlang:demonitor(MRef, [flush]), + log("ct_gen_conn", + "Connection process ~p not responding. Killing now!", + [Pid]), + exit(Pid, kill), + {error,{process_down,Pid,forced_termination}} end. return({To,Ref},Result) -> @@ -198,36 +283,47 @@ return({To,Ref},Result) -> init_gen(Parent,Opts) -> process_flag(trap_exit,true), - CtUtilServer = whereis(ct_util_server), - link(CtUtilServer), put(silent,false), - case catch (Opts#gen_opts.callback):init(Opts#gen_opts.name, - Opts#gen_opts.address, - Opts#gen_opts.init_data) of + try (Opts#gen_opts.callback):init(Opts#gen_opts.name, + Opts#gen_opts.address, + Opts#gen_opts.init_data) of {ok,ConnPid,State} when is_pid(ConnPid) -> link(ConnPid), put(conn_pid,ConnPid), + CtUtilServer = whereis(ct_util_server), + link(CtUtilServer), Parent ! {connected,self()}, loop(Opts#gen_opts{conn_pid=ConnPid, cb_state=State, ct_util_server=CtUtilServer}); {error,Reason} -> Parent ! {{error,Reason},self()} + catch + throw:{error,Reason} -> + Parent ! {{error,Reason},self()} end. loop(Opts) -> receive {'EXIT',Pid,Reason} when Pid==Opts#gen_opts.conn_pid -> - log("Connection down!\nOpening new!","Reason: ~p\nAddress: ~p\n", - [Reason,Opts#gen_opts.address]), - case reconnect(Opts) of - {ok, NewPid, NewState} -> - link(NewPid), - put(conn_pid,NewPid), - loop(Opts#gen_opts{conn_pid=NewPid,cb_state=NewState}); - Error -> + case Opts#gen_opts.reconnect of + true -> + log("Connection down!\nOpening new!", + "Reason: ~p\nAddress: ~p\n", + [Reason,Opts#gen_opts.address]), + case reconnect(Opts) of + {ok, NewPid, NewState} -> + link(NewPid), + put(conn_pid,NewPid), + loop(Opts#gen_opts{conn_pid=NewPid,cb_state=NewState}); + Error -> + ct_util:unregister_connection(self()), + log("Reconnect failed. Giving up!","Reason: ~p\n", + [Error]) + end; + false -> ct_util:unregister_connection(self()), - log("Reconnect failed. Giving up!","Reason: ~p\n",[Error]) + log("Connection closed!","Reason: ~p\n",[Reason]) end; {'EXIT',Pid,Reason} -> case Opts#gen_opts.ct_util_server of @@ -252,24 +348,40 @@ loop(Opts) -> loop(Opts); {{retry,{_Error,_Name,_CPid,Msg}}, From} -> log("Rerunning command","Connection reestablished. Rerunning command...",[]), - {Return,NewState} = + {Return,NewState} = (Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state), return(From, Return), - loop(Opts#gen_opts{cb_state=NewState}); - {Msg,From={Pid,_Ref}} when is_pid(Pid) -> - {Return,NewState} = + loop(Opts#gen_opts{cb_state=NewState}); + {Msg,From={Pid,_Ref}} when is_pid(Pid), Opts#gen_opts.old==true -> + {Return,NewState} = (Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state), return(From, Return), - loop(Opts#gen_opts{cb_state=NewState}) + loop(Opts#gen_opts{cb_state=NewState}); + {Msg,From={Pid,_Ref}} when is_pid(Pid) -> + case (Opts#gen_opts.callback):handle_msg(Msg,From, + Opts#gen_opts.cb_state) of + {reply,Reply,NewState} -> + return(From,Reply), + loop(Opts#gen_opts{cb_state=NewState}); + {noreply,NewState} -> + loop(Opts#gen_opts{cb_state=NewState}); + {stop,Reply,NewState} -> + ct_util:unregister_connection(self()), + (Opts#gen_opts.callback):terminate(Opts#gen_opts.conn_pid, + NewState), + return(From,Reply) + end; + Msg when Opts#gen_opts.forward==true -> + case (Opts#gen_opts.callback):handle_msg(Msg,Opts#gen_opts.cb_state) of + {noreply,NewState} -> + loop(Opts#gen_opts{cb_state=NewState}); + {stop,NewState} -> + ct_util:unregister_connection(self()), + (Opts#gen_opts.callback):terminate(Opts#gen_opts.conn_pid, + NewState) + end end. -nozero({ok,S}) when is_list(S) -> - {ok,[C || C <- S, - C=/=0, - C=/=13]}; -nozero(M) -> - M. - reconnect(Opts) -> (Opts#gen_opts.callback):reconnect(Opts#gen_opts.address, Opts#gen_opts.cb_state). @@ -277,10 +389,8 @@ reconnect(Opts) -> log(Func,Args) -> case get(silent) of - true when not ?dbg-> + true when not ?dbg-> ok; _ -> apply(ct_logs,Func,Args) end. - - diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index c42adbbdd9..d0432b604d 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -70,8 +70,7 @@ terminate(Hooks) -> {skip, Reason :: term()} | {auto_skip, Reason :: term()} | {fail, Reason :: term()}. -init_tc(ct_framework, _Func, Args) -> - Args; + init_tc(Mod, init_per_suite, Config) -> Info = try proplists:get_value(ct_hooks, Mod:suite(),[]) of List when is_list(List) -> @@ -104,27 +103,21 @@ init_tc(_Mod, TC, Config) -> {auto_skip, Reason :: term()} | {fail, Reason :: term()} | ok | '$ct_no_change'. -end_tc(ct_framework, _Func, _Args, Result, _Return) -> - Result; end_tc(Mod, init_per_suite, Config, _Result, Return) -> call(fun call_generic/3, Return, [post_init_per_suite, Mod, Config], '$ct_no_change'); - end_tc(Mod, end_per_suite, Config, Result, _Return) -> call(fun call_generic/3, Result, [post_end_per_suite, Mod, Config], '$ct_no_change'); - end_tc(_Mod, {init_per_group, GroupName, _}, Config, _Result, Return) -> call(fun call_generic/3, Return, [post_init_per_group, GroupName, Config], '$ct_no_change'); - end_tc(Mod, {end_per_group, GroupName, Opts}, Config, Result, _Return) -> Res = call(fun call_generic/3, Result, [post_end_per_group, GroupName, Config], '$ct_no_change'), maybe_stop_locker(Mod, GroupName,Opts), Res; - end_tc(_Mod, TC, Config, Result, _Return) -> call(fun call_generic/3, Result, [post_end_per_testcase, TC, Config], '$ct_no_change'). @@ -199,12 +192,12 @@ call([{Hook, call_id, NextFun} | Rest], Config, Meta, Hooks) -> case lists:keyfind(NewId, #ct_hook_config.id, Hooks) of false when NextFun =:= undefined -> {Hooks ++ [NewHook], - [{NewId, call_init} | Rest]}; + Rest ++ [{NewId, call_init}]}; ExistingHook when is_tuple(ExistingHook) -> {Hooks, Rest}; _ -> {Hooks ++ [NewHook], - [{NewId, call_init}, {NewId,NextFun} | Rest]} + Rest ++ [{NewId, call_init}, {NewId,NextFun}]} end, call(resort(NewRest,NewHooks,Meta), Config, Meta, NewHooks) catch Error:Reason -> @@ -360,11 +353,10 @@ pos(Id,[_|Rest],Num) -> pos(Id,Rest,Num+1). - catch_apply(M,F,A, Default) -> try apply(M,F,A) - catch error:Reason -> + catch _:Reason -> case erlang:get_stacktrace() of %% Return the default if it was the CTH module which did not have the function. [{M,F,A,_}|_] when Reason == undef -> diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 0cd9b5f7cb..0b7a8bb075 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,22 +28,25 @@ -module(ct_logs). --export([init/1,close/2,init_tc/1,end_tc/1]). --export([get_log_dir/0,get_log_dir/1]). --export([log/3,start_log/1,cont_log/2,end_log/0]). --export([set_stylesheet/2,clear_stylesheet/1]). --export([add_external_logs/1,add_link/3]). +-export([init/2, close/2, init_tc/1, end_tc/1]). +-export([get_log_dir/0, get_log_dir/1]). +-export([log/3, start_log/1, cont_log/2, end_log/0]). +-export([set_stylesheet/2, clear_stylesheet/1]). +-export([add_external_logs/1, add_link/3]). -export([make_last_run_index/0]). -export([make_all_suites_index/1,make_all_runs_index/1]). --export([get_ts_html_wrapper/3]). +-export([get_ts_html_wrapper/4]). +-export([xhtml/2, locate_priv_file/1, make_relative/1]). +-export([insert_javascript/1]). %% Logging stuff directly from testcase --export([tc_log/3,tc_log/4,tc_log_async/3,tc_print/3,tc_pal/3,ct_log/3, - basic_html/0]). +-export([tc_log/3, tc_log/4, tc_log_async/3, tc_print/3, tc_print/4, + tc_pal/3, tc_pal/4, ct_log/3, basic_html/0]). %% Simulate logger process for use without ct environment running -export([simulate/0]). +-include("ct.hrl"). -include("ct_event.hrl"). -include("ct_util.hrl"). -include_lib("kernel/include/file.hrl"). @@ -55,7 +58,6 @@ -define(all_runs_name, "all_runs.html"). -define(index_name, "index.html"). -define(totals_name, "totals.info"). --define(css_default, "ct_default.css"). -define(table_color1,"#ADD8E6"). -define(table_color2,"#E4F0FE"). @@ -78,9 +80,9 @@ %%% started. A new directory named ct_run.<timestamp> is created %%% and all logs are stored under this directory.</p> %%% -init(Mode) -> +init(Mode, Verbosity) -> Self = self(), - Pid = spawn_link(fun() -> logger(Self,Mode) end), + Pid = spawn_link(fun() -> logger(Self, Mode, Verbosity) end), MRef = erlang:monitor(process,Pid), receive {started,Pid,Result} -> @@ -239,7 +241,7 @@ end_tc(TCPid) -> %%% activity it is. <code>Format</code> and <code>Args</code> is the %%% data to log (as in <code>io:format(Format,Args)</code>).</p> log(Heading,Format,Args) -> - cast({log,sync,self(),group_leader(), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, [{int_header(),[log_timestamp(now()),Heading]}, {Format,Args}, {int_footer(),[]}]}), @@ -261,7 +263,7 @@ log(Heading,Format,Args) -> %%% @see cont_log/2 %%% @see end_log/0 start_log(Heading) -> - cast({log,sync,self(),group_leader(), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, [{int_header(),[log_timestamp(now()),Heading]}]}), ok. @@ -276,7 +278,8 @@ cont_log([],[]) -> ok; cont_log(Format,Args) -> maybe_log_timestamp(), - cast({log,sync,self(),group_leader(),[{Format,Args}]}), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, + [{Format,Args}]}), ok. %%%----------------------------------------------------------------- @@ -287,7 +290,8 @@ cont_log(Format,Args) -> %%% @see start_log/1 %%% @see cont_log/2 end_log() -> - cast({log,sync,self(),group_leader(),[{int_footer(), []}]}), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, + [{int_footer(), []}]}), ok. @@ -320,10 +324,16 @@ add_link(Heading,File,Type) -> [filename:join("log_private",File),Type,File]). - %%%----------------------------------------------------------------- %%% @spec tc_log(Category,Format,Args) -> ok +%%% @equiv tc_log(Category,?STD_IMPORTANCE,Format,Args) +tc_log(Category,Format,Args) -> + tc_log(Category,?STD_IMPORTANCE,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec tc_log(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -332,19 +342,26 @@ add_link(Heading,File,Type) -> %%% <p>This function is called by <code>ct</code> when logging %%% stuff directly from a testcase (i.e. not from within the CT %%% framework).</p> -tc_log(Category,Format,Args) -> - tc_log(Category,"User",Format,Args). +tc_log(Category,Importance,Format,Args) -> + tc_log(Category,Importance,"User",Format,Args). -tc_log(Category,Printer,Format,Args) -> - cast({log,sync,self(),group_leader(),[{div_header(Category,Printer),[]}, - {Format,Args}, - {div_footer(),[]}]}), +tc_log(Category,Importance,Printer,Format,Args) -> + cast({log,sync,self(),group_leader(),Category,Importance, + [{div_header(Category,Printer),[]}, + {Format,Args}, + {div_footer(),[]}]}), ok. - %%%----------------------------------------------------------------- %%% @spec tc_log_async(Category,Format,Args) -> ok +%%% @equiv tc_log_async(Category,?STD_IMPORTANCE,Format,Args) +tc_log_async(Category,Format,Args) -> + tc_log_async(Category,?STD_IMPORTANCE,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec tc_log_async(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -355,40 +372,66 @@ tc_log(Category,Printer,Format,Args) -> %%% to avoid deadlocks when e.g. the hook that handles SASL printouts %%% prints to the test case log file at the same time test server %%% asks ct_logs for an html wrapper.</p> -tc_log_async(Category,Format,Args) -> - cast({log,async,self(),group_leader(),[{div_header(Category),[]}, - {Format,Args}, - {div_footer(),[]}]}), +tc_log_async(Category,Importance,Format,Args) -> + cast({log,async,self(),group_leader(),Category,Importance, + [{div_header(Category),[]}, + {Format,Args}, + {div_footer(),[]}]}), ok. +%%%----------------------------------------------------------------- +%%% @spec tc_print(Category,Format,Args) +%%% @equiv tc_print(Category,?STD_IMPORTANCE,Format,Args) +tc_print(Category,Format,Args) -> + tc_print(Category,?STD_IMPORTANCE,Format,Args). %%%----------------------------------------------------------------- -%%% @spec tc_print(Category,Format,Args) -> ok +%%% @spec tc_print(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% %%% @doc Console printout from a testcase. %%% %%% <p>This function is called by <code>ct</code> when printing -%%% stuff a testcase on the user console.</p> -tc_print(Category,Format,Args) -> - Head = get_heading(Category), - io:format(user, lists:concat([Head,Format,"\n\n"]), Args), - ok. +%%% stuff from a testcase on the user console.</p> +tc_print(Category,Importance,Format,Args) -> + VLvl = case ct_util:get_testdata({verbosity,Category}) of + undefined -> + ct_util:get_testdata({verbosity,'$unspecified'}); + {error,bad_invocation} -> + ?MAX_VERBOSITY; + Val -> + Val + end, + if Importance >= (100-VLvl) -> + Head = get_heading(Category), + io:format(user, lists:concat([Head,Format,"\n\n"]), Args), + ok; + true -> + ok + end. get_heading(default) -> - io_lib:format("-----------------------------" + io_lib:format("\n-----------------------------" "-----------------------\n~s\n", [log_timestamp(now())]); get_heading(Category) -> - io_lib:format("-----------------------------" + io_lib:format("\n-----------------------------" "-----------------------\n~s ~w\n", [log_timestamp(now()),Category]). %%%----------------------------------------------------------------- %%% @spec tc_pal(Category,Format,Args) -> ok +%%% @equiv tc_pal(Category,?STD_IMPORTANCE,Format,Args) -> ok +tc_pal(Category,Format,Args) -> + tc_pal(Category,?STD_IMPORTANCE,Format,Args). + +%%%----------------------------------------------------------------- +%%% @spec tc_pal(Category,Importance,Format,Args) -> ok %%% Category = atom() +%%% Importance = integer() %%% Format = string() %%% Args = list() %%% @@ -397,16 +440,17 @@ get_heading(Category) -> %%% <p>This function is called by <code>ct</code> when logging %%% stuff directly from a testcase. The info is written both in the %%% log and on the console.</p> -tc_pal(Category,Format,Args) -> - tc_print(Category,Format,Args), - cast({log,sync,self(),group_leader(),[{div_header(Category),[]}, - {Format,Args}, - {div_footer(),[]}]}), +tc_pal(Category,Importance,Format,Args) -> + tc_print(Category,Importance,Format,Args), + cast({log,sync,self(),group_leader(),Category,Importance, + [{div_header(Category),[]}, + {Format,Args}, + {div_footer(),[]}]}), ok. %%%----------------------------------------------------------------- -%%% @spec tc_pal(Category,Format,Args) -> ok +%%% @spec ct_pal(Category,Format,Args) -> ok %%% Category = atom() %%% Format = string() %%% Args = list() @@ -444,7 +488,7 @@ maybe_log_timestamp() -> {MS,S,_} -> ok; _ -> - cast({log,sync,self(),group_leader(), + cast({log,sync,self(),group_leader(),ct_internal,?MAX_IMPORTANCE, [{"<i>~s</i>",[log_timestamp({MS,S,US})]}]}) end. @@ -468,7 +512,7 @@ log_timestamp({MS,S,US}) -> stylesheet, async_print_jobs}). -logger(Parent,Mode) -> +logger(Parent, Mode, Verbosity) -> register(?MODULE,self()), %%! Below is a temporary workaround for the limitation of @@ -501,26 +545,27 @@ logger(Parent,Mode) -> %% dir) so logs are independent of Common Test installation {ok,Cwd} = file:get_cwd(), CTPath = code:lib_dir(common_test), - CSSFileSrc = filename:join(filename:join(CTPath, "priv"), - ?css_default), - CSSFileDestTop = filename:join(Cwd, ?css_default), - CSSFileDestRun = filename:join(AbsDir, ?css_default), - case file:copy(CSSFileSrc, CSSFileDestTop) of - {error,Reason0} -> + PrivFiles = [?css_default,?jquery_script,?tablesorter_script], + PrivFilesSrc = [filename:join(filename:join(CTPath, "priv"), F) || + F <- PrivFiles], + PrivFilesDestTop = [filename:join(Cwd, F) || F <- PrivFiles], + PrivFilesDestRun = [filename:join(AbsDir, F) || F <- PrivFiles], + case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of + {error,Src1,Dest1,Reason1} -> io:format(user, "ERROR! "++ - "CSS file ~p could not be copied to ~p. "++ - "Reason: ~p~n", - [CSSFileSrc,CSSFileDestTop,Reason0]), - exit({css_file_error,CSSFileDestTop}); - _ -> - case file:copy(CSSFileSrc, CSSFileDestRun) of - {error,Reason1} -> + "Priv file ~p could not be copied to ~p. "++ + "Reason: ~p~n", + [Src1,Dest1,Reason1]), + exit({priv_file_error,Dest1}); + ok -> + case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of + {error,Src2,Dest2,Reason2} -> io:format(user, "ERROR! "++ - "CSS file ~p could not be copied to ~p. "++ - "Reason: ~p~n", - [CSSFileSrc,CSSFileDestRun,Reason1]), - exit({css_file_error,CSSFileDestRun}); - _ -> + "Priv file ~p could not be copied to ~p. "++ + "Reason: ~p~n", + [Src2,Dest2,Reason2]), + exit({priv_file_error,Dest2}); + ok -> ok end end @@ -540,6 +585,23 @@ logger(Parent,Mode) -> [log_timestamp(now()),"Common Test Logger started"]), Parent ! {started,self(),{Time,filename:absname("")}}, set_evmgr_gl(CtLogFd), + + %% save verbosity levels in dictionary for fast lookups + io:format(CtLogFd, "\nVERBOSITY LEVELS:\n", []), + case proplists:get_value('$unspecified', Verbosity) of + undefined -> ok; + GenLvl -> io:format(CtLogFd, "~-25s~3w~n", + ["general level",GenLvl]) + end, + [begin put({verbosity,Cat},VLvl), + if Cat == '$unspecified' -> + ok; + true -> + io:format(CtLogFd, "~-25w~3w~n", [Cat,VLvl]) + end + end || {Cat,VLvl} <- Verbosity], + io:nl(CtLogFd), + logger_loop(#logger_state{parent=Parent, log_dir=AbsDir, start_time=Time, @@ -548,31 +610,58 @@ logger(Parent,Mode) -> tc_groupleaders=[], async_print_jobs=[]}). +copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) -> + case file:copy(SrcF, DestF) of + {error,Reason} -> + {error,SrcF,DestF,Reason}; + _ -> + copy_priv_files(SrcFs, DestFs) + end; +copy_priv_files([], []) -> + ok. + logger_loop(State) -> receive - {log,SyncOrAsync,Pid,GL,List} -> - case get_groupleader(Pid, GL, State) of - {tc_log,TCGL,TCGLs} -> - case erlang:is_process_alive(TCGL) of - true -> - State1 = print_to_log(SyncOrAsync, Pid, TCGL, - List, State), - logger_loop(State1#logger_state{tc_groupleaders = - TCGLs}); - false -> - %% Group leader is dead, so write to the - %% CtLog instead - Fd = State#logger_state.ct_log_fd, - [begin io:format(Fd,Str,Args),io:nl(Fd) end || + {log,SyncOrAsync,Pid,GL,Category,Importance,List} -> + VLvl = case Category of + ct_internal -> + ?MAX_VERBOSITY; + _ -> + case get({verbosity,Category}) of + undefined -> get({verbosity,'$unspecified'}); + Val -> Val + end + end, + if Importance >= (100-VLvl) -> + case get_groupleader(Pid, GL, State) of + {tc_log,TCGL,TCGLs} -> + case erlang:is_process_alive(TCGL) of + true -> + State1 = print_to_log(SyncOrAsync, Pid, + TCGL, List, State), + logger_loop(State1#logger_state{ + tc_groupleaders = TCGLs}); + false -> + %% Group leader is dead, so write to the + %% CtLog instead + Fd = State#logger_state.ct_log_fd, + [begin io:format(Fd,Str,Args), + io:nl(Fd) end || {Str,Args} <- List], + logger_loop(State) + end; + {ct_log,Fd,TCGLs} -> + [begin io:format(Fd,Str,Args),io:nl(Fd) end || {Str,Args} <- List], - logger_loop(State) + logger_loop(State#logger_state{ + tc_groupleaders = TCGLs}) end; - {ct_log,Fd,TCGLs} -> - [begin io:format(Fd,Str,Args),io:nl(Fd) end || - {Str,Args} <- List], - logger_loop(State#logger_state{tc_groupleaders = TCGLs}) - end; + true -> + logger_loop(State) + end; {{init_tc,TCPid,GL,RefreshLog},From} -> + %% make sure no IO for this test case from the + %% CT logger gets rejected + test_server:permit_io(GL, self()), print_style(GL, State#logger_state.stylesheet), set_evmgr_gl(GL), TCGLs = add_tc_gl(TCPid,GL,State), @@ -658,13 +747,24 @@ create_io_fun(FromPid, State) -> print_to_log(sync, FromPid, TCGL, List, State) -> IoFun = create_io_fun(FromPid, State), - io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)]), + %% in some situations (exceptions), the printout is made from the + %% test server IO process and there's no valid group leader to send to + IoProc = if FromPid /= TCGL -> TCGL; + true -> State#logger_state.ct_log_fd + end, + io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)]), State; print_to_log(async, FromPid, TCGL, List, State) -> IoFun = create_io_fun(FromPid, State), + %% in some situations (exceptions), the printout is made from the + %% test server IO process and there's no valid group leader to send to + IoProc = if FromPid /= TCGL -> TCGL; + true -> State#logger_state.ct_log_fd + end, Printer = fun() -> - io:format(TCGL, "~s", [lists:foldl(IoFun, [], List)]) + test_server:permit_io(IoProc, self()), + io:format(IoProc, "~s", [lists:foldl(IoFun, [], List)]) end, case State#logger_state.async_print_jobs of [] -> @@ -769,7 +869,7 @@ set_evmgr_gl(GL) -> open_ctlog() -> {ok,Fd} = file:open(?ct_log_name,[write]), - io:format(Fd, header("Common Test Framework Log"), []), + io:format(Fd, header("Common Test Framework Log", {[],[1,2],[]}), []), case file:consult(ct_run:variables_file_name("../")) of {ok,Vars} -> io:format(Fd, config_table(Vars), []); @@ -1079,14 +1179,14 @@ total_row(Success, Fail, UserSkip, AutoSkip, NotBuilt, All) -> integer_to_list(UserSkip),integer_to_list(AutoSkip)} end, [xhtml("<tr valign=top>\n", - ["<tr class=\"",odd_or_even(),"\">\n"]), + ["</tbody>\n<tfoot>\n<tr class=\"",odd_or_even(),"\">\n"]), "<td><b>Total</b></td>\n", Label, TimestampCell, "<td align=right><b>",integer_to_list(Success),"<b></td>\n", "<td align=right><b>",integer_to_list(Fail),"<b></td>\n", "<td align=right>",integer_to_list(AllSkip), " (",UserSkipStr,"/",AutoSkipStr,")</td>\n", "<td align=right><b>",integer_to_list(NotBuilt),"<b></td>\n", - AllInfo, "</tr>\n"]. + AllInfo, "</tr>\n</tfoot>\n"]. not_built(_BaseName,_LogDir,_All,[]) -> 0; @@ -1143,10 +1243,12 @@ index_header(Label, StartTime) -> Head = case Label of undefined -> - header("Test Results", format_time(StartTime)); + header("Test Results", format_time(StartTime), + {[],[1],[2,3,4,5]}); _ -> header("Test Results for '" ++ Label ++ "'", - format_time(StartTime)) + format_time(StartTime), + {[],[1],[2,3,4,5]}) end, [Head | ["<center>\n", @@ -1158,15 +1260,17 @@ index_header(Label, StartTime) -> "\">COMMON TEST FRAMEWORK LOG</a>\n</div>"]), xhtml("<br>\n", "<br /><br /><br />\n"), xhtml(["<table border=\"3\" cellpadding=\"5\" " - "bgcolor=\"",?table_color3,"\">\n"], "<table>\n"), + "bgcolor=\"",?table_color3,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), "<th><b>Test Name</b></th>\n", xhtml(["<th><font color=\"",?table_color3,"\">_</font>Ok" "<font color=\"",?table_color3,"\">_</font></th>\n"], "<th>Ok</th>\n"), "<th>Failed</th>\n", "<th>Skipped", xhtml("<br>", "<br />"), "(User/Auto)</th>\n" - "<th>Missing", xhtml("<br>", "<br />"), "Suites</th>\n" - "\n"]]. + "<th>Missing", xhtml("<br>", "<br />"), "Suites</th>\n", + xhtml("", "</tr>\n</thead>\n<tbody>\n")]]. all_suites_index_header() -> {ok,Cwd} = file:get_cwd(), @@ -1179,12 +1283,14 @@ all_suites_index_header(IndexDir) -> AllRunsLink = xhtml(["<a href=\"",?all_runs_name,"\">",AllRuns,"</a>\n"], ["<div id=\"button_holder\" class=\"btn\">\n" "<a href=\"",?all_runs_name,"\">",AllRuns,"</a>\n</div>"]), - [header("Test Results") | + [header("Test Results", {[3],[1,2,8,9,10],[4,5,6,7]}) | ["<center>\n", AllRunsLink, xhtml("<br><br>\n", "<br /><br />\n"), xhtml(["<table border=\"3\" cellpadding=\"5\" " - "bgcolor=\"",?table_color2,"\">\n"], "<table>\n"), + "bgcolor=\"",?table_color2,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), "<th>Test Name</th>\n", "<th>Label</th>\n", "<th>Test Run Started</th>\n", @@ -1197,7 +1303,7 @@ all_suites_index_header(IndexDir) -> "<th>Node</th>\n", "<th>CT Log</th>\n", "<th>Old Runs</th>\n", - "\n"]]. + xhtml("", "</tr>\n</thead>\n<tbody>\n")]]. all_runs_header() -> {ok,Cwd} = file:get_cwd(), @@ -1209,10 +1315,12 @@ all_runs_header() -> "<a href=\"",?index_name, "\">TEST INDEX PAGE</a>\n</div>"]), xhtml("<br>\n", "<br /><br />\n")], - [header(Title) | + [header(Title, {[1],[2,3,5],[4,6,7,8,9,10]}) | ["<center>\n", IxLink, xhtml(["<table border=\"3\" cellpadding=\"5\" " - "bgcolor=\"",?table_color1,"\">\n"], "<table>\n"), + "bgcolor=\"",?table_color1,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), "<th><b>History</b></th>\n" "<th><b>Node</b></th>\n" "<th><b>Label</b></th>\n" @@ -1224,45 +1332,65 @@ all_runs_header() -> "<th>Ok</th>\n"), "<th>Failed</th>\n" "<th>Skipped<br>(User/Auto)</th>\n" - "<th>Missing<br>Suites</th>\n" - "\n"]]. + "<th>Missing<br>Suites</th>\n", + xhtml("", "</tr>\n</thead>\n<tbody>\n")]]. -header(Title) -> - header1(Title, ""). -header(Title, SubTitle) -> - header1(Title, SubTitle). +header(Title, TableCols) -> + header1(Title, "", TableCols). +header(Title, SubTitle, TableCols) -> + header1(Title, SubTitle, TableCols). -header1(Title, SubTitle) -> +header1(Title, SubTitle, TableCols) -> SubTitleHTML = if SubTitle =/= "" -> ["<center>\n", "<h3>" ++ SubTitle ++ "</h3>\n", xhtml("</center>\n<br>\n", "</center>\n<br />\n")]; - true -> xhtml("<br>\n", "<br />\n") + true -> xhtml("<br>", "<br />") end, CSSFile = xhtml(fun() -> "" end, - fun() -> make_relative(locate_default_css_file()) end), + fun() -> make_relative(locate_priv_file(?css_default)) end), + JQueryFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?jquery_script)) end), + TableSorterFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?tablesorter_script)) end), [xhtml(["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", "<html>\n"], ["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n", "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n", "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"]), - "<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n", - "<head>\n", - "<title>" ++ Title ++ " " ++ SubTitle ++ "</title>\n", - "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", - xhtml("", - ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]), - "</head>\n", - body_tag(), - "<center>\n", - "<h1>" ++ Title ++ "</h1>\n", - "</center>\n", - SubTitleHTML,"\n"]. + "<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n", + "<head>\n", + "<title>" ++ Title ++ " " ++ SubTitle ++ "</title>\n", + "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", + xhtml("", + ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">\n"]), + xhtml("", + ["<script type=\"text/javascript\" src=\"",JQueryFile, + "\"></script>\n"]), + xhtml("", + ["<script type=\"text/javascript\" src=\"",TableSorterFile, + "\"></script>\n"]), + xhtml(fun() -> "" end, + fun() -> insert_javascript({tablesorter,?sortable_table_name, + TableCols}) + end), + "</head>\n", + body_tag(), + "<center>\n", + "<h1>" ++ Title ++ "</h1>\n", + "</center>\n", + SubTitleHTML,"\n"]. index_footer() -> ["</table>\n" "</center>\n" | footer()]. +all_runs_index_footer() -> + ["</tbody>\n</table>\n" + "</center>\n" | footer()]. + footer() -> ["<center>\n", xhtml("<br><br>\n<hr>\n", "<br /><br />\n"), @@ -1274,7 +1402,8 @@ footer() -> xhtml("<br>\n", "<br />\n"), xhtml("</font></p>\n", "</div>\n"), "</center>\n" - "</body>\n"]. + "</body>\n" + "</html>\n"]. body_tag() -> @@ -1290,7 +1419,7 @@ current_time() -> format_time({{Y, Mon, D}, {H, Min, S}}) -> Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)), - lists:flatten(io_lib:format("~s ~s ~p ~w ~2.2.0w:~2.2.0w:~2.2.0w", + lists:flatten(io_lib:format("~s ~s ~2.2.0w ~w ~2.2.0w:~2.2.0w:~2.2.0w", [Weekday, month(Mon), D, Y, H, Min, S])). weekday(1) -> "Mon"; @@ -1416,8 +1545,12 @@ config_table_header() -> [ xhtml(["<h2>Configuration</h2>\n" "<table border=\"3\" cellpadding=\"5\" bgcolor=\"",?table_color1,"\"\n"], - "<h4>CONFIGURATION</h4>\n<table>\n"), - "<tr><th>Key</th><th>Value</th></tr>\n"]. + ["<h4>CONFIGURATION</h4>\n", + "<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n"]), + "<tr><th>Key</th><th>Value</th></tr>\n", + xhtml("", "</thead>\n<tbody>\n") + ]. config_table1([{Key,Value}|Vars]) -> [xhtml(["<tr><td>", atom_to_list(Key), "</td>\n", @@ -1427,7 +1560,7 @@ config_table1([{Key,Value}|Vars]) -> "<td>", io_lib:format("~p",[Value]), "</td>\n</tr>\n"]) | config_table1(Vars)]; config_table1([]) -> - ["</table>\n"]. + ["</tbody>\n</table>\n"]. make_all_runs_index(When) -> @@ -1441,7 +1574,8 @@ make_all_runs_index(When) -> DirsSorted = (catch sort_all_runs(Dirs)), Header = all_runs_header(), Index = [runentry(Dir) || Dir <- DirsSorted], - Result = file:write_file(AbsName,Header++Index++index_footer()), + Result = file:write_file(AbsName,Header++Index++ + all_runs_index_footer()), if When == start -> ok; true -> io:put_chars("done\n") end, @@ -1980,7 +2114,7 @@ simulate() -> simulate_logger_loop() -> receive - {log,_,_,_,List} -> + {log,_,_,_,_,_,List} -> S = [[io_lib:format(Str,Args),io_lib:nl()] || {Str,Args} <- List], io:format("~s",[S]), simulate_logger_loop(); @@ -2077,34 +2211,34 @@ basic_html() -> end. %%%----------------------------------------------------------------- -%%% @spec locate_default_css_file() -> CSSFile +%%% @spec locate_priv_file(FileName) -> PrivFile %%% %%% @doc %%% -locate_default_css_file() -> +locate_priv_file(FileName) -> {ok,CWD} = file:get_cwd(), - CSSFileInCwd = filename:join(CWD, ?css_default), - case filelib:is_file(CSSFileInCwd) of + PrivFileInCwd = filename:join(CWD, FileName), + case filelib:is_file(PrivFileInCwd) of true -> - CSSFileInCwd; + PrivFileInCwd; false -> - CSSResultFile = + PrivResultFile = case {whereis(?MODULE),self()} of {Self,Self} -> %% executed on the ct_logs process - filename:join(get(ct_run_dir), ?css_default); + filename:join(get(ct_run_dir), FileName); _ -> %% executed on other process than ct_logs {ok,RunDir} = get_log_dir(true), - filename:join(RunDir, ?css_default) + filename:join(RunDir, FileName) end, - case filelib:is_file(CSSResultFile) of + case filelib:is_file(PrivResultFile) of true -> - CSSResultFile; + PrivResultFile; false -> %% last resort, try use css file in CT installation CTPath = code:lib_dir(common_test), - filename:join(filename:join(CTPath, "priv"), ?css_default) + filename:join(filename:join(CTPath, "priv"), FileName) end end. @@ -2143,7 +2277,7 @@ make_relative1(DirTs, CwdTs) -> %%% %%% @doc %%% -get_ts_html_wrapper(TestName, PrintLabel, Cwd) -> +get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols) -> TestName1 = if is_list(TestName) -> lists:flatten(TestName); true -> @@ -2203,17 +2337,36 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd) -> "Open Telecom Platform</a><br />\n", "Updated: <!date>", current_time(), "<!/date>", "<br />\n</div>\n"], - CSSFile = xhtml(fun() -> "" end, - fun() -> make_relative(locate_default_css_file(), Cwd) end), + CSSFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?css_default), + Cwd) + end), + JQueryFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?jquery_script), + Cwd) + end), + TableSorterFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?tablesorter_script), + Cwd) + end), + TableSorterScript = + xhtml(fun() -> "" end, + fun() -> insert_javascript({tablesorter, + ?sortable_table_name, + TableCols}) end), {xhtml, ["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n", "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n", "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n", "<head>\n<title>", TestName1, "</title>\n", "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", - "<link rel=\"stylesheet\" href=\"", CSSFile, "\" type=\"text/css\">", - "</head>\n","<body>\n", - LabelStr, "\n"], + "<link rel=\"stylesheet\" href=\"", CSSFile, "\" type=\"text/css\">\n", + "<script type=\"text/javascript\" src=\"", JQueryFile, "\"></script>\n", + "<script type=\"text/javascript\" src=\"", TableSorterFile, "\"></script>\n"] ++ + TableSorterScript ++ ["</head>\n","<body>\n", LabelStr, "\n"], ["<center>\n<br /><hr /><p>\n", "<a href=\"", AllRuns, "\">Test run history\n</a> | ", @@ -2221,3 +2374,89 @@ get_ts_html_wrapper(TestName, PrintLabel, Cwd) -> "\">Top level test index\n</a>\n</p>\n", Copyright,"</center>\n</body>\n</html>\n"]} end. + +insert_javascript({tablesorter,_TableName,undefined}) -> + []; + +insert_javascript({tablesorter,TableName, + {DateCols,TextCols,ValCols}}) -> + Headers = + lists:flatten( + lists:sort( + lists:flatmap(fun({Sorter,Cols}) -> + [lists:flatten( + io_lib:format(" ~w: " + "{ sorter: '~s' },\n", + [Col-1,Sorter])) || Col<-Cols] + end, [{"CTDateSorter",DateCols}, + {"CTTextSorter",TextCols}, + {"CTValSorter",ValCols}]))), + Headers1 = string:substr(Headers, 1, length(Headers)-2), + + ["<script type=\"text/javascript\">\n", + "// Parser for date format, e.g: Wed Jul 4 2012 11:24:15\n", + "var monthNames = {};\n", + "monthNames[\"Jan\"] = \"01\"; monthNames[\"Feb\"] = \"02\";\n", + "monthNames[\"Mar\"] = \"03\"; monthNames[\"Apr\"] = \"04\";\n", + "monthNames[\"May\"] = \"05\"; monthNames[\"Jun\"] = \"06\";\n", + "monthNames[\"Jul\"] = \"07\"; monthNames[\"Aug\"] = \"08\";\n", + "monthNames[\"Sep\"] = \"09\"; monthNames[\"Oct\"] = \"10\";\n", + "monthNames[\"Nov\"] = \"11\"; monthNames[\"Dec\"] = \"12\";\n", + "$.tablesorter.addParser({\n", + " id: 'CTDateSorter',\n", + " is: function(s) {\n", + " return false; },\n", + " format: function(s) {\n", + %% place empty cells, "-" and "?" at the bottom + " if (s.length < 2) return 999999999;\n", + " else {\n", + %% match out each date element + " var date = s.match(/(\\w{3})\\s(\\w{3})\\s(\\d{2})\\s(\\d{4})\\s(\\d{2}):(\\d{2}):(\\d{2})/);\n", + " var y = date[4]; var mo = monthNames[date[2]]; var d = String(date[3]);\n", + " var h = String(date[5]); var mi = String(date[6]); var sec = String(date[7]);\n", + " return (parseInt('' + y + mo + d + h + mi + sec)); }},\n", + " type: 'numeric' });\n", + + "// Parser for general text format\n", + "$.tablesorter.addParser({\n", + " id: 'CTTextSorter',\n", + " is: function(s) {\n", + " return false; },\n", + " format: function(s) {\n", + %% place empty cells, "?" and "-" at the bottom + " if (s.length < 1) return 'zzzzzzzz';\n", + " else if (s == \"?\") return 'zzzzzzz';\n", + " else if (s == \"-\") return 'zzzzzz';\n", + " else if (s == \"FAILED\") return 'A';\n", + " else if (s == \"SKIPPED\") return 'B';\n", + " else if (s == \"OK\") return 'C';\n", + " else return '' + s; },\n", + " type: 'text' });\n", + + "// Parser for numerical values\n", + "$.tablesorter.addParser({\n", + " id: 'CTValSorter',\n", + " is: function(s) {\n", + " return false; },\n", + " format: function(s) {\n" + %% place empty cells and "?" at the bottom + " if (s.length < 1) return '-2';\n", + " else if (s == \"?\") return '-1';\n", + %% look for skip value, eg "3 (2/1)" + " else if ((s.search(/(\\d{1,})\\s/)) >= 0) {\n", + " var num = s.match(/(\\d{1,})\\s/);\n", + %% return only the total skip value for sorting + " return (parseInt('' + num[1])); }\n", + " else if ((s.search(/(\\d{1,})\\.(\\d{3})s/)) >= 0) {\n", + " var num = s.match(/(\\d{1,})\\.(\\d{3})/);\n", + " if (num[1] == \"0\") return (parseInt('' + num[2]));\n", + " else return (parseInt('' + num[1] + num[2])); }\n", + " else return '' + s; },\n", + " type: 'numeric' });\n", + + "$(document).ready(function() {\n", + " $(\"#",TableName,"\").tablesorter({\n", + " headers: { \n", Headers1, "\n }\n });\n", + " $(\"#",TableName,"\").trigger(\"update\");\n", + " $(\"#",TableName,"\").trigger(\"appendCache\");\n", + "});\n</script>\n"]. diff --git a/lib/common_test/src/ct_master.erl b/lib/common_test/src/ct_master.erl index 2ea2ba106a..042c5ba267 100644 --- a/lib/common_test/src/ct_master.erl +++ b/lib/common_test/src/ct_master.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,6 +25,7 @@ -export([run/1,run/3,run/4]). -export([run_on_node/2,run_on_node/3]). -export([run_test/1,run_test/2]). +-export([basic_html/1]). -export([abort/0,abort/1,progress/0]). @@ -277,7 +278,17 @@ abort(Node) when is_atom(Node) -> progress() -> call(progress). - +%%%----------------------------------------------------------------- +%%% @spec basic_html(Bool) -> ok +%%% Bool = true | false +%%% +%%% @doc If set to true, the ct_master logs will be written on a +%%% primitive html format, not using the Common Test CSS style +%%% sheet. +basic_html(Bool) -> + application:set_env(common_test_master, basic_html, Bool), + ok. + %%%----------------------------------------------------------------- %%% MASTER, runs on central controlling node. %%%----------------------------------------------------------------- diff --git a/lib/common_test/src/ct_master_logs.erl b/lib/common_test/src/ct_master_logs.erl index 244faace06..9e61d5b16f 100644 --- a/lib/common_test/src/ct_master_logs.erl +++ b/lib/common_test/src/ct_master_logs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,7 +23,10 @@ %%% node.</p> -module(ct_master_logs). --export([start/2, make_all_runs_index/0, log/3, nodedir/2, stop/0]). +-export([start/2, make_all_runs_index/0, log/3, nodedir/2, + stop/0]). + +-include("ct_util.hrl"). -record(state, {log_fd, start_time, logdir, rundir, nodedir_ix_fd, nodes, nodedirs=[]}). @@ -87,6 +90,41 @@ init(Parent,LogDir,Nodes) -> RunDirAbs = filename:join(LogDir,RunDir), file:make_dir(RunDirAbs), write_details_file(RunDirAbs,{node(),Nodes}), + + case basic_html() of + true -> + put(basic_html, true); + BasicHtml -> + put(basic_html, BasicHtml), + %% copy priv files to log dir (both top dir and test run + %% dir) so logs are independent of Common Test installation + CTPath = code:lib_dir(common_test), + PrivFiles = [?css_default,?jquery_script,?tablesorter_script], + PrivFilesSrc = [filename:join(filename:join(CTPath, "priv"), F) || + F <- PrivFiles], + PrivFilesDestTop = [filename:join(LogDir, F) || F <- PrivFiles], + PrivFilesDestRun = [filename:join(RunDirAbs, F) || F <- PrivFiles], + case copy_priv_files(PrivFilesSrc, PrivFilesDestTop) of + {error,Src1,Dest1,Reason1} -> + io:format(user, "ERROR! "++ + "Priv file ~p could not be copied to ~p. "++ + "Reason: ~p~n", + [Src1,Dest1,Reason1]), + exit({priv_file_error,Dest1}); + ok -> + case copy_priv_files(PrivFilesSrc, PrivFilesDestRun) of + {error,Src2,Dest2,Reason2} -> + io:format(user, "ERROR! "++ + "Priv file ~p could not be copied to ~p. "++ + "Reason: ~p~n", + [Src2,Dest2,Reason2]), + exit({priv_file_error,Dest2}); + ok -> + ok + end + end + end, + make_all_runs_index(LogDir), CtLogFd = open_ct_master_log(RunDirAbs), NodeStr = @@ -110,6 +148,16 @@ init(Parent,LogDir,Nodes) -> {N,""} end,Nodes)}). +copy_priv_files([SrcF | SrcFs], [DestF | DestFs]) -> + case file:copy(SrcF, DestF) of + {error,Reason} -> + {error,SrcF,DestF,Reason}; + _ -> + copy_priv_files(SrcFs, DestFs) + end; +copy_priv_files([], []) -> + ok. + loop(State) -> receive {log,_From,List} -> @@ -154,7 +202,7 @@ loop(State) -> open_ct_master_log(Dir) -> FullName = filename:join(Dir,?ct_master_log_name), {ok,Fd} = file:open(FullName,[write]), - io:format(Fd,header("Common Test Master Log"),[]), + io:format(Fd,header("Common Test Master Log", {[],[1,2],[]}),[]), %% maybe add config info here later io:format(Fd, config_table([]), []), io:format(Fd, @@ -164,8 +212,9 @@ open_ct_master_log(Dir) -> "</style>\n", []), io:format(Fd, - "<br><h2>Progress Log</h2>\n" - "<pre>\n",[]), + xhtml("<br><h2>Progress Log</h2>\n<pre>\n", + "<br /><h2>Progress Log</h2>\n<pre>\n"), + []), Fd. close_ct_master_log(Fd) -> @@ -178,20 +227,15 @@ config_table(Vars) -> config_table_header() -> ["<h2>Configuration</h2>\n", - "<table border=\"3\" cellpadding=\"5\" bgcolor=\"",?table_color, - "\"\n", - "<tr><th>Key</th><th>Value</th></tr>\n"]. - -%% -%% keep for possible later use -%% -%%config_table1([{Key,Value}|Vars]) -> -%% ["<tr><td>", atom_to_list(Key), "</td>\n", -%% "<td><pre>",io_lib:format("~p",[Value]),"</pre></td></tr>\n" | -%% config_table1(Vars)]; + xhtml(["<table border=\"3\" cellpadding=\"5\" " + "bgcolor=\"",?table_color,"\"\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n"]), + "<tr><th>Key</th><th>Value</th></tr>\n", + xhtml("", "</thead>\n<tbody>\n")]. config_table1([]) -> - ["</table>\n"]. + ["</tbody>\n</table>\n"]. int_header() -> "<div class=\"ct_internal\"><b>*** CT MASTER ~s *** ~s</b>". @@ -210,10 +254,10 @@ open_nodedir_index(Dir,StartTime) -> print_nodedir(Node,RunDir,Fd) -> Index = filename:join(RunDir,"index.html"), io:format(Fd, - ["<TR>\n" - "<TD ALIGN=center>",atom_to_list(Node),"</TD>\n", - "<TD ALIGN=left><A HREF=\"",Index,"\">",Index,"</A></TD>\n", - "</TR>\n"],[]), + ["<tr>\n" + "<td align=center>",atom_to_list(Node),"</td>\n", + "<td align=left><a href=\"",Index,"\">",Index,"</a></td>\n", + "</tr>\n"],[]), ok. close_nodedir_index(Fd) -> @@ -221,14 +265,16 @@ close_nodedir_index(Fd) -> file:close(Fd). nodedir_index_header(StartTime) -> - [header("Log Files " ++ format_time(StartTime)) | - ["<CENTER>\n", - "<P><A HREF=\"",?ct_master_log_name,"\">Common Test Master Log</A></P>", - "<TABLE border=\"3\" cellpadding=\"5\" ", - "BGCOLOR=\"",?table_color,"\">\n", - "<th><B>Node</B></th>\n", - "<th><B>Log</B></th>\n", - "\n"]]. + [header("Log Files " ++ format_time(StartTime), {[],[1,2],[]}) | + ["<center>\n", + "<p><a href=\"",?ct_master_log_name,"\">Common Test Master Log</a></p>", + xhtml(["<table border=\"3\" cellpadding=\"5\" " + "bgcolor=\"",?table_color,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), + "<th><b>Node</b></th>\n", + "<th><b>Log</b></th>\n", + xhtml("", "</tr>\n</thead>\n<tbody>\n")]]. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% All Run Index functions %%% @@ -279,21 +325,23 @@ runentry(Dir) -> {"unknown",""} end, Index = filename:join(Dir,?nodedir_index_name), - ["<TR>\n" - "<TD ALIGN=center><A HREF=\"",Index,"\">",timestamp(Dir),"</A></TD>\n", - "<TD ALIGN=center>",MasterStr,"</TD>\n", - "<TD ALIGN=center>",NodesStr,"</TD>\n", - "</TR>\n"]. + ["<tr>\n" + "<td align=center><a href=\"",Index,"\">",timestamp(Dir),"</a></td>\n", + "<td align=center>",MasterStr,"</td>\n", + "<td align=center>",NodesStr,"</td>\n", + "</tr>\n"]. all_runs_header() -> - [header("Master Test Runs") | - ["<CENTER>\n", - "<TABLE border=\"3\" cellpadding=\"5\" " - "BGCOLOR=\"",?table_color,"\">\n" - "<th><B>History</B></th>\n" - "<th><B>Master Host</B></th>\n" - "<th><B>Test Nodes</B></th>\n" - "\n"]]. + [header("Master Test Runs", {[1],[2,3],[]}) | + ["<center>\n", + xhtml(["<table border=\"3\" cellpadding=\"5\" " + "bgcolor=\"",?table_color,"\">\n"], + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n<tr>\n"]), + "<th><b>History</b></th>\n" + "<th><b>Master Host</b></th>\n" + "<th><b>Test Nodes</b></th>\n", + xhtml("", "</tr></thead>\n<tbody>\n")]]. timestamp(Dir) -> [S,Min,H,D,M,Y|_] = lists:reverse(string:tokens(Dir,".-_")), @@ -317,52 +365,71 @@ read_details_file(Dir) -> %%% Internal functions %%%-------------------------------------------------------------------- -header(Title) -> - ["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n" - "<!-- autogenerated by '"++atom_to_list(?MODULE)++"'. -->\n" - "<HTML>\n", - "<HEAD>\n", - - "<TITLE>" ++ Title ++ "</TITLE>\n", - "<META HTTP-EQUIV=\"CACHE-CONTROL\" CONTENT=\"NO-CACHE\">\n", - - "</HEAD>\n", - +header(Title, TableCols) -> + CSSFile = xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?css_default)) end), + JQueryFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?jquery_script)) end), + TableSorterFile = + xhtml(fun() -> "" end, + fun() -> make_relative(locate_priv_file(?tablesorter_script)) end), + + [xhtml(["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", + "<html>\n"], + ["<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\"\n", + "\"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\">\n", + "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"]), + "<!-- autogenerated by '"++atom_to_list(?MODULE)++"' -->\n", + "<head>\n", + "<title>" ++ Title ++ "</title>\n", + "<meta http-equiv=\"cache-control\" content=\"no-cache\">\n", + xhtml("", + ["<link rel=\"stylesheet\" href=\"",CSSFile,"\" type=\"text/css\">"]), + xhtml("", + ["<script type=\"text/javascript\" src=\"",JQueryFile, + "\"></script>\n"]), + xhtml("", + ["<script type=\"text/javascript\" src=\"",TableSorterFile, + "\"></script>\n"]), + xhtml(fun() -> "" end, + fun() -> ct_logs:insert_javascript({tablesorter, + ?sortable_table_name, + TableCols}) end), + "</head>\n", body_tag(), - - "<!-- ---- DOCUMENT TITLE ---- -->\n", - - "<CENTER>\n", - "<H1>" ++ Title ++ "</H1>\n", - "</CENTER>\n", - - "<!-- ---- CONTENT ---- -->\n"]. + "<center>\n", + "<h1>" ++ Title ++ "</h1>\n", + "</center>\n"]. index_footer() -> - ["</TABLE>\n" - "</CENTER>\n" | footer()]. + ["</tbody>\n</table>\n" + "</center>\n" | footer()]. footer() -> - ["<P><CENTER>\n" - "<HR>\n" - "<P><FONT SIZE=-1>\n" + ["<center>\n", + xhtml("<br><hr>\n", "<br />\n"), + xhtml("<p><font size=\"-1\">\n", "<div class=\"copyright\">"), "Copyright © ", year(), - " <A HREF=\"http://erlang.ericsson.se\">Open Telecom Platform</A><BR>\n" - "Updated: <!date>", current_time(), "<!/date><BR>\n" - "</FONT>\n" - "</CENTER>\n" + " <a href=\"http://www.erlang.org\">Open Telecom Platform</a>", + xhtml("<br>\n", "<br />\n"), + "Updated: <!date>", current_time(), "<!/date>", + xhtml("<br>\n", "<br />\n"), + xhtml("</font></p>\n", "</div>\n"), + "</center>\n" "</body>\n"]. body_tag() -> - "<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\"" - "vlink=\"#800080\" alink=\"#FF0000\">\n". + xhtml("<body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" " + "vlink=\"#800080\" alink=\"#FF0000\">\n", + "<body>\n"). current_time() -> format_time(calendar:local_time()). format_time({{Y, Mon, D}, {H, Min, S}}) -> Weekday = weekday(calendar:day_of_the_week(Y, Mon, D)), - lists:flatten(io_lib:format("~s ~s ~p ~w ~2.2.0w:~2.2.0w:~2.2.0w", + lists:flatten(io_lib:format("~s ~s ~2.2.0w ~w ~2.2.0w:~2.2.0w:~2.2.0w", [Weekday, month(Mon), D, Y, H, Min, S])). weekday(1) -> "Mon"; @@ -404,6 +471,23 @@ log_timestamp(Now) -> lists:flatten(io_lib:format("~2.2.0w:~2.2.0w:~2.2.0w", [H,M,S])). +basic_html() -> + case application:get_env(common_test_master, basic_html) of + {ok,true} -> + true; + _ -> + false + end. + +xhtml(HTML, XHTML) -> + ct_logs:xhtml(HTML, XHTML). + +locate_priv_file(File) -> + ct_logs:locate_priv_file(File). + +make_relative(Dir) -> + ct_logs:make_relative(Dir). + force_write_file(Name,Contents) -> force_delete(Name), file:write_file(Name,Contents). @@ -452,3 +536,4 @@ cast(Msg) -> _Pid -> ?MODULE ! Msg end. + diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl new file mode 100644 index 0000000000..d9c4a962dc --- /dev/null +++ b/lib/common_test/src/ct_netconfc.erl @@ -0,0 +1,1828 @@ +%%---------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%%---------------------------------------------------------------------- +%% File: ct_netconfc.erl +%% +%% Description: +%% This file contains the Netconf client interface +%% +%% @author Support +%% +%% @doc Netconf client module. +%% +%% <p>The Netconf client is compliant with RFC4741 and RFC4742.</p> +%% +%% <p> For each server to test against, the following entry can be +%% added to a configuration file:</p> +%% +%% <p>`{server_id(),options()}.'</p> +%% +%% <p> The `server_id()' or an associated `target_name()' (see +%% {@link ct}) shall then be used in calls to {@link open/2}.</p> +%% +%% <p>If no configuration exists for a server, a session can still be +%% opened by calling {@link open/2} with all necessary options given +%% in the call. The first argument to {@link open/2} can then be any +%% atom.</p> +%% +%% == Logging == +%% +%% The netconf server uses the `error_logger' for logging of netconf +%% traffic. A special purpose error handler is implemented in +%% `ct_conn_log_h'. To use this error handler, add the `cth_conn_log' +%% hook in your test suite, e.g. +%% +%% ``` +%% suite() -> +%% [{ct_hooks, [{cth_conn_log, [{conn_mod(),hook_options()}]}]}]. +%%''' +%% +%% The `conn_mod()' is the name of the common_test module implementing +%% the connection protocol, e.g. `ct_netconfc'. +%% +%% The hook option `log_type' specifies the type of logging: +%% +%% <dl> +%% <dt>`raw'</dt> +%% <dd>The sent and received netconf data is logged to a separate +%% text file as is without any formatting. A link to the file is +%% added to the test case HTML log.</dd> +%% +%% <dt>`pretty'</dt> +%% <dd>The sent and received netconf data is logged to a separate +%% text file with XML data nicely indented. A link to the file is +%% added to the test case HTML log.</dd> +%% +%% <dt>`html (default)'</dt> +%% <dd>The sent and received netconf traffic is pretty printed +%% directly in the test case HTML log.</dd> +%% +%% <dt>`silent'</dt> +%% <dd>Netconf traffic is not logged.</dd> +%% </dl> +%% +%% By default, all netconf traffic is logged in one single log +%% file. However, it is possible to have different connections logged +%% in separate files. To do this, use the hook option `hosts' and +%% list the names of the servers/connections that will be used in the +%% suite. Note that the connections must be named for this to work, +%% i.e. they must be opened with {@link open/2}. +%% +%% The `hosts' option has no effect if `log_type' is set to `html' or +%% `silent'. +%% +%% The hook options can also be specified in a configuration file with +%% the configuration variable `ct_conn_log': +%% +%% ``` +%% {ct_conn_log,[{conn_mod(),hook_options()}]}. +%% ''' +%% +%% For example: +%% +%% ``` +%% {ct_conn_log,[{ct_netconfc,[{log_type,pretty}, +%% {hosts,[key_or_name()]}]}]} +%% ''' +%% +%% <b>Note</b> that hook options specified in a configuration file +%% will overwrite the hardcoded hook options in the test suite. +%% +%% === Logging example 1 === +%% +%% The following `ct_hooks' statement will cause pretty printing of +%% netconf traffic to separate logs for the connections named +%% `nc_server1' and `nc_server2'. Any other connections will be logged +%% to default netconf log. +%% +%% ``` +%% suite() -> +%% [{ct_hooks, [{cth_conn_log, [{ct_netconfc,[{log_type,pretty}}, +%% {hosts,[nc_server1,nc_server2]}]} +%% ]}]}]. +%%''' +%% +%% Connections must be opened like this: +%% +%% ``` +%% open(nc_server1,[...]), +%% open(nc_server2,[...]). +%% ''' +%% +%% === Logging example 2 === +%% +%% The following configuration file will cause raw logging of all +%% netconf traffic into one single text file. +%% +%% ``` +%% {ct_conn_log,[{ct_netconfc,[{log_type,raw}]}]}. +%% ''' +%% +%% The `ct_hooks' statement must look like this: +%% +%% ``` +%% suite() -> +%% [{ct_hooks, [{cth_conn_log, []}]}]. +%% ''' +%% +%% The same `ct_hooks' statement without the configuration file would +%% cause HTML logging of all netconf connections into the test case +%% HTML log. +%% +%% == Notifications == +%% +%% The netconf client is also compliant with RFC5277 NETCONF Event +%% Notifications, which defines a mechanism for an asynchronous +%% message notification delivery service for the netconf protocol. +%% +%% Specific functions to support this are {@link +%% create_subscription/6} and {@link get_event_streams/3}. (The +%% functions also exist with other arities.) +%% +%% @end +%%---------------------------------------------------------------------- +-module(ct_netconfc). + +-include("ct_netconfc.hrl"). +-include("ct_util.hrl"). +-include_lib("xmerl/include/xmerl.hrl"). + +%%---------------------------------------------------------------------- +%% External exports +%%---------------------------------------------------------------------- +-export([open/1, + open/2, + only_open/1, + only_open/2, + hello/1, + hello/2, + close_session/1, + close_session/2, + kill_session/2, + kill_session/3, + send/2, + send/3, + send_rpc/2, + send_rpc/3, + lock/2, + lock/3, + unlock/2, + unlock/3, + get/2, + get/3, + get_config/3, + get_config/4, + edit_config/3, + edit_config/4, + delete_config/2, + delete_config/3, + copy_config/3, + copy_config/4, + action/2, + action/3, + create_subscription/1, + create_subscription/2, + create_subscription/3, + create_subscription/4, + create_subscription/5, + create_subscription/6, + get_event_streams/2, + get_event_streams/3, + get_capabilities/1, + get_capabilities/2, + get_session_id/1, + get_session_id/2]). + +%%---------------------------------------------------------------------- +%% Exported types +%%---------------------------------------------------------------------- +-export_type([hook_options/0, + conn_mod/0, + log_type/0, + key_or_name/0, + notification/0]). + +%%---------------------------------------------------------------------- +%% Internal exports +%%---------------------------------------------------------------------- +%% ct_gen_conn callbacks +-export([init/3, + handle_msg/3, + handle_msg/2, + terminate/2, + close/1]). + +%% ct_conn_log callback +-export([format_data/2]). + +%%---------------------------------------------------------------------- +%% Internal defines +%%---------------------------------------------------------------------- +-define(APPLICATION,?MODULE). +-define(VALID_SSH_OPTS,[user, password, user_dir]). +-define(DEFAULT_STREAM,"NETCONF"). + +-define(error(ConnName,Report), + error_logger:error_report([{ct_connection,ConnName}, + {client,self()}, + {module,?MODULE}, + {line,?LINE} | + Report])). + +-define(is_timeout(T), (is_integer(T) orelse T==infinity)). +-define(is_filter(F), + (is_atom(F) orelse (is_tuple(F) andalso is_atom(element(1,F))))). +-define(is_string(S), (is_list(S) andalso is_integer(hd(S)))). + +%%---------------------------------------------------------------------- +%% Records +%%---------------------------------------------------------------------- +%% Client state +-record(state, {host, + port, + connection, % #connection + capabilities, + session_id, + msg_id = 1, + hello_status, + buff = <<>>, + pending = [], % [#pending] + event_receiver}).% pid + +%% Run-time client options. +-record(options, {ssh = [], % Options for the ssh application + host, + port = ?DEFAULT_PORT, + timeout = ?DEFAULT_TIMEOUT, + name}). + +%% Connection reference +-record(connection, {reference, % {CM,Ch} + host, + port, + name}). + +%% Pending replies from server +-record(pending, {tref, % timer ref (returned from timer:xxx) + ref, % pending ref + msg_id, + op, + caller}).% pid which sent the request + +%%---------------------------------------------------------------------- +%% Type declarations +%%---------------------------------------------------------------------- +-type client() :: handle() | server_id() | target_name(). +-type handle() :: term(). +%% An opaque reference for a connection (netconf session). See {@link +%% ct} for more information. + +-type server_id() :: atom(). +%% A `ServerId' which exists in a configuration file. +-type target_name() :: atom(). +%% A name which is associated to a `server_id()' via a +%% `require' statement or a call to {@link ct:require/2} in the +%% test suite. +-type key_or_name() :: server_id() | target_name(). + +-type options() :: [option()]. +%% Options used for setting up ssh connection to a netconf server. + +-type option() :: {ssh,host()} | {port,inet:port_number()} | {user,string()} | + {password,string()} | {user_dir,string()} | + {timeout,timeout()}. +-type host() :: inet:host_name() | inet:ip_address(). + +-type notification() :: {notification, xml_attributes(), notification_content()}. +-type notification_content() :: [event_time()|simple_xml()]. +-type event_time() :: {eventTime,xml_attributes(),[xs_datetime()]}. + +-type stream_name() :: string(). +-type streams() :: [{stream_name(),[stream_data()]}]. +-type stream_data() :: {description,string()} | + {replaySupport,string()} | + {replayLogCreationTime,string()} | + {replayLogAgedTime,string()}. +%% See XML Schema for Event Notifications found in RFC5277 for further +%% detail about the data format for the string values. + +-type hook_options() :: [hook_option()]. +%% Options that can be given to `cth_conn_log' in the `ct_hook' statement. +-type hook_option() :: {log_type,log_type()} | + {hosts,[key_or_name()]}. +-type log_type() :: raw | pretty | html | silent. +%-type error_handler() :: module(). +-type conn_mod() :: ct_netconfc. + +-type error_reason() :: term(). + +-type simple_xml() :: {xml_tag(), xml_attributes(), xml_content()} | + {xml_tag(), xml_content()} | + xml_tag(). +%% <p>This type is further described in the documentation for the +%% <tt>Xmerl</tt> application.</p> +-type xml_tag() :: atom(). +-type xml_attributes() :: [{xml_attribute_tag(),xml_attribute_value()}]. +-type xml_attribute_tag() :: atom(). +-type xml_attribute_value() :: string(). +-type xml_content() :: [simple_xml() | iolist()]. +-type xpath() :: {xpath,string()}. + +-type netconf_db() :: running | startup | candidate. +-type xs_datetime() :: string(). +%% This date and time identifyer has the same format as the XML type +%% dateTime and compliant to RFC3339. The format is +%% ```[-]CCYY-MM-DDThh:mm:ss[.s][Z|(+|-)hh:mm]''' + +%%---------------------------------------------------------------------- +%% External interface functions +%%---------------------------------------------------------------------- + +%%---------------------------------------------------------------------- +-spec open(Options) -> Result when + Options :: options(), + Result :: {ok,handle()} | {error,error_reason()}. +%% @doc Open a netconf session and exchange `hello' messages. +%% +%% If the server options are specified in a configuration file, or if +%% a named client is needed for logging purposes (see {@section +%% Logging}) use {@link open/2} instead. +%% +%% The opaque `handler()' reference which is returned from this +%% function is required as client identifier when calling any other +%% function in this module. +%% +%% The `timeout' option (milli seconds) is used when setting up +%% the ssh connection and when waiting for the hello message from the +%% server. It is not used for any other purposes during the lifetime +%% of the connection. +%% +%% @end +%%---------------------------------------------------------------------- +open(Options) -> + open(Options,#options{},[],true). + +%%---------------------------------------------------------------------- +-spec open(KeyOrName, ExtraOptions) -> Result when + KeyOrName :: key_or_name(), + ExtraOptions :: options(), + Result :: {ok,handle()} | {error,error_reason()}. +%% @doc Open a named netconf session and exchange `hello' messages. +%% +%% If `KeyOrName' is a configured `server_id()' or a +%% `target_name()' associated with such an ID, then the options +%% for this server will be fetched from the configuration file. +% +%% The `ExtraOptions' argument will be added to the options found in +%% the configuration file. If the same options are given, the values +%% from the configuration file will overwrite `ExtraOptions'. +%% +%% If the server is not specified in a configuration file, use {@link +%% open/1} instead. +%% +%% The opaque `handle()' reference which is returned from this +%% function can be used as client identifier when calling any other +%% function in this module. However, if `KeyOrName' is a +%% `target_name()', i.e. if the server is named via a call to +%% `ct:require/2' or a `require' statement in the test +%% suite, then this name may be used instead of the `handle()'. +%% +%% The `timeout' option (milli seconds) is used when setting up +%% the ssh connection and when waiting for the hello message from the +%% server. It is not used for any other purposes during the lifetime +%% of the connection. +%% +%% @end +%%---------------------------------------------------------------------- +open(KeyOrName, ExtraOpts) -> + open(KeyOrName, ExtraOpts, true). + +open(KeyOrName, ExtraOpts, Hello) -> + SortedExtra = lists:keysort(1,ExtraOpts), + SortedConfig = lists:keysort(1,ct:get_config(KeyOrName,[])), + AllOpts = lists:ukeymerge(1,SortedConfig,SortedExtra), + open(AllOpts,#options{name=KeyOrName},[{name,KeyOrName}],Hello). + +open(OptList,InitOptRec,NameOpt,Hello) -> + case check_options(OptList,undefined,undefined,InitOptRec) of + {Host,Port,Options} -> + case ct_gen_conn:start({Host,Port},Options,?MODULE, + NameOpt ++ [{reconnect,false}, + {use_existing_connection,false}, + {forward_messages,true}]) of + {ok,Client} when Hello==true -> + case hello(Client,Options#options.timeout) of + ok -> + {ok,Client}; + Error -> + Error + end; + Other -> + Other + end; + Error -> + Error + end. + + +%%---------------------------------------------------------------------- +-spec only_open(Options) -> Result when + Options :: options(), + Result :: {ok,handle()} | {error,error_reason()}. +%% @doc Open a netconf session, but don't send `hello'. +%% +%% As {@link open/1} but does not send a `hello' message. +%% +%% @end +%%---------------------------------------------------------------------- +only_open(Options) -> + open(Options,#options{},[],false). + +%%---------------------------------------------------------------------- +-spec only_open(KeyOrName,ExtraOptions) -> Result when + KeyOrName :: key_or_name(), + ExtraOptions :: options(), + Result :: {ok,handle()} | {error,error_reason()}. +%% @doc Open a name netconf session, but don't send `hello'. +%% +%% As {@link open/2} but does not send a `hello' message. +%% +%% @end +%%---------------------------------------------------------------------- +only_open(KeyOrName, ExtraOpts) -> + open(KeyOrName, ExtraOpts, false). + +%%---------------------------------------------------------------------- +%% @spec hello(Client) -> Result +%% @equiv hello(Client, infinity) +hello(Client) -> + hello(Client,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec hello(Client,Timeout) -> Result when + Client :: handle(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Exchange `hello' messages with the server. +%% +%% Sends a `hello' message to the server and waits for the return. +%% +%% @end +%%---------------------------------------------------------------------- +hello(Client,Timeout) -> + call(Client, {hello, Timeout}). + +%%---------------------------------------------------------------------- +%% @spec get_session_id(Client) -> Result +%% @equiv get_session_id(Client, infinity) +get_session_id(Client) -> + get_session_id(Client, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get_session_id(Client, Timeout) -> Result when + Client :: client(), + Timeout :: timeout(), + Result :: pos_integer() | {error,error_reason()}. +%% @doc Returns the session id associated with the given client. +%% +%% @end +%%---------------------------------------------------------------------- +get_session_id(Client, Timeout) -> + call(Client, get_session_id, Timeout). + +%%---------------------------------------------------------------------- +%% @spec get_capabilities(Client) -> Result +%% @equiv get_capabilities(Client, infinity) +get_capabilities(Client) -> + get_capabilities(Client, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get_capabilities(Client, Timeout) -> Result when + Client :: client(), + Timeout :: timeout(), + Result :: [string()] | {error,error_reason()}. +%% @doc Returns the server side capabilities +%% +%% The following capability identifiers, defined in RFC 4741, can be returned: +%% +%% <ul> +%% <li>`"urn:ietf:params:netconf:base:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:writable-running:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:candidate:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:confirmed-commit:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:rollback-on-error:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:startup:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:url:1.0"'</li> +%% <li>`"urn:ietf:params:netconf:capability:xpath:1.0"'</li> +%% </ul> +%% +%% Note, additional identifiers may exist, e.g. server side namespace. +%% +%% @end +%%---------------------------------------------------------------------- +get_capabilities(Client, Timeout) -> + call(Client, get_capabilities, Timeout). + +%% @private +send(Client, SimpleXml) -> + send(Client, SimpleXml, ?DEFAULT_TIMEOUT). +%% @private +send(Client, SimpleXml, Timeout) -> + call(Client,{send, Timeout, SimpleXml}). + +%% @private +send_rpc(Client, SimpleXml) -> + send_rpc(Client, SimpleXml, ?DEFAULT_TIMEOUT). +%% @private +send_rpc(Client, SimpleXml, Timeout) -> + call(Client,{send_rpc, SimpleXml, Timeout}). + + + +%%---------------------------------------------------------------------- +%% @spec lock(Client, Target) -> Result +%% @equiv lock(Client, Target, infinity) +lock(Client, Target) -> + lock(Client, Target,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec lock(Client, Target, Timeout) -> Result when + Client :: client(), + Target :: netconf_db(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Unlock configuration target. +%% +%% Which target parameters that can be used depends on if +%% `:candidate' and/or `:startup' are supported by the +%% server. If successfull, the configuration system of the device is +%% not available to other clients (Netconf, CORBA, SNMP etc). Locks +%% are intended to be short-lived. +%% +%% The operations {@link kill_session/2} or {@link kill_session/3} can +%% be used to force the release of a lock owned by another Netconf +%% session. How this is achieved by the server side is implementation +%% specific. +%% +%% @end +%%---------------------------------------------------------------------- +lock(Client, Target, Timeout) -> + call(Client,{send_rpc_op,lock,[Target],Timeout}). + +%%---------------------------------------------------------------------- +%% @spec unlock(Client, Target) -> Result +%% @equiv unlock(Client, Target, infinity) +unlock(Client, Target) -> + unlock(Client, Target,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec unlock(Client, Target, Timeout) -> Result when + Client :: client(), + Target :: netconf_db(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Unlock configuration target. +%% +%% If the client earlier has aquired a lock, via {@link lock/2} or +%% {@link lock/3}, this operation release the associated lock. To be +%% able to access another target than `running', the server must +%% support `:candidate' and/or `:startup'. +%% +%% @end +%%---------------------------------------------------------------------- +unlock(Client, Target, Timeout) -> + call(Client, {send_rpc_op, unlock, [Target], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec get(Client, Filter) -> Result +%% @equiv get(Client, Filter, infinity) +get(Client, Filter) -> + get(Client, Filter, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get(Client, Filter, Timeout) -> Result when + Client :: client(), + Filter :: simple_xml() | xpath(), + Timeout :: timeout(), + Result :: {ok,simple_xml()} | {error,error_reason()}. +%% @doc Get data. +%% +%% This operation returns both configuration and state data from the +%% server. +%% +%% Filter type `xpath' can only be used if the server supports +%% `:xpath'. +%% +%% @end +%%---------------------------------------------------------------------- +get(Client, Filter, Timeout) -> + call(Client,{send_rpc_op, get, [Filter], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec get_config(Client, Source, Filter) -> Result +%% @equiv get_config(Client, Source, Filter, infinity) +get_config(Client, Source, Filter) -> + get_config(Client, Source, Filter, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get_config(Client, Source, Filter, Timeout) -> Result when + Client :: client(), + Source :: netconf_db(), + Filter :: simple_xml() | xpath(), + Timeout :: timeout(), + Result :: {ok,simple_xml()} | {error,error_reason()}. +%% @doc Get configuration data. +%% +%% To be able to access another source than `running', the server +%% must advertise `:candidate' and/or `:startup'. +%% +%% Filter type `xpath' can only be used if the server supports +%% `:xpath'. +%% +%% +%% @end +%%---------------------------------------------------------------------- +get_config(Client, Source, Filter, Timeout) -> + call(Client, {send_rpc_op, get_config, [Source, Filter], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec edit_config(Client, Target, Config) -> Result +%% @equiv edit_config(Client, Target, Config, infinity) +edit_config(Client, Target, Config) -> + edit_config(Client, Target, Config, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec edit_config(Client, Target, Config, Timeout) -> Result when + Client :: client(), + Target :: netconf_db(), + Config :: simple_xml(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Edit configuration data. +%% +%% Per default only the running target is available, unless the server +%% include `:candidate' or `:startup' in its list of +%% capabilities. +%% +%% @end +%%---------------------------------------------------------------------- +edit_config(Client, Target, Config, Timeout) -> + call(Client, {send_rpc_op, edit_config, [Target,Config], Timeout}). + + +%%---------------------------------------------------------------------- +%% @spec delete_config(Client, Target) -> Result +%% @equiv delete_config(Client, Target, infinity) +delete_config(Client, Target) -> + delete_config(Client, Target, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec delete_config(Client, Target, Timeout) -> Result when + Client :: client(), + Target :: startup | candidate, + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Delete configuration data. +%% +%% The running configuration cannot be deleted and `:candidate' +%% or `:startup' must be advertised by the server. +%% +%% @end +%%---------------------------------------------------------------------- +delete_config(Client, Target, Timeout) when Target == startup; + Target == candidate -> + call(Client,{send_rpc_op, delete_config, [Target], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec copy_config(Client, Source, Target) -> Result +%% @equiv copy_config(Client, Source, Target, infinity) +copy_config(Client, Source, Target) -> + copy_config(Client, Source, Target, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec copy_config(Client, Target, Source, Timeout) -> Result when + Client :: client(), + Target :: netconf_db(), + Source :: netconf_db(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Copy configuration data. +%% +%% Which source and target options that can be issued depends on the +%% capabilities supported by the server. I.e. `:candidate' and/or +%% `:startup' are required. +%% +%% @end +%%---------------------------------------------------------------------- +copy_config(Client, Target, Source, Timeout) -> + call(Client,{send_rpc_op, copy_config, [Target, Source], Timeout}). + +%%---------------------------------------------------------------------- +%% @spec action(Client, Action) -> Result +%% @equiv action(Client, Action, infinity) +action(Client,Action) -> + action(Client,Action,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec action(Client, Action, Timeout) -> Result when + Client :: client(), + Action :: simple_xml(), + Timeout :: timeout(), + Result :: {ok,simple_xml()} | {error,error_reason()}. +%% @doc Execute an action. +%% +%% @end +%%---------------------------------------------------------------------- +action(Client,Action,Timeout) -> + call(Client,{send_rpc_op, action, [Action], Timeout}). + +%%---------------------------------------------------------------------- +create_subscription(Client) -> + create_subscription(Client,?DEFAULT_STREAM,?DEFAULT_TIMEOUT). + +create_subscription(Client,Timeout) + when ?is_timeout(Timeout) -> + create_subscription(Client,?DEFAULT_STREAM,Timeout); +create_subscription(Client,Stream) + when is_list(Stream) -> + create_subscription(Client,Stream,?DEFAULT_TIMEOUT); +create_subscription(Client,Filter) + when ?is_filter(Filter) -> + create_subscription(Client,?DEFAULT_STREAM,Filter, + ?DEFAULT_TIMEOUT). + +create_subscription(Client,Stream,Timeout) + when is_list(Stream) andalso + ?is_timeout(Timeout) -> + call(Client,{send_rpc_op,{create_subscription,self()}, + [Stream,undefined,undefined,undefined], + Timeout}); +create_subscription(Client,StartTime,StopTime) + when is_list(StartTime) andalso + is_list(StopTime) -> + create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime, + ?DEFAULT_TIMEOUT); +create_subscription(Client,Filter,Timeout) + when ?is_filter(Filter) andalso + ?is_timeout(Timeout) -> + create_subscription(Client,?DEFAULT_STREAM,Filter,Timeout); +create_subscription(Client,Stream,Filter) + when is_list(Stream) andalso + ?is_filter(Filter) -> + create_subscription(Client,Stream,Filter,?DEFAULT_TIMEOUT). + +create_subscription(Client,StartTime,StopTime,Timeout) + when is_list(StartTime) andalso + is_list(StopTime) andalso + ?is_timeout(Timeout) -> + create_subscription(Client,?DEFAULT_STREAM,StartTime,StopTime,Timeout); +create_subscription(Client,Stream,StartTime,StopTime) + when is_list(Stream) andalso + is_list(StartTime) andalso + is_list(StopTime) -> + create_subscription(Client,Stream,StartTime,StopTime,?DEFAULT_TIMEOUT); +create_subscription(Client,Filter,StartTime,StopTime) + when ?is_filter(Filter) andalso + is_list(StartTime) andalso + is_list(StopTime) -> + create_subscription(Client,?DEFAULT_STREAM,Filter, + StartTime,StopTime,?DEFAULT_TIMEOUT); +create_subscription(Client,Stream,Filter,Timeout) + when is_list(Stream) andalso + ?is_filter(Filter) andalso + ?is_timeout(Timeout) -> + call(Client,{send_rpc_op,{create_subscription,self()}, + [Stream,Filter,undefined,undefined], + Timeout}). + +create_subscription(Client,Stream,StartTime,StopTime,Timeout) + when is_list(Stream) andalso + is_list(StartTime) andalso + is_list(StopTime) andalso + ?is_timeout(Timeout) -> + call(Client,{send_rpc_op,{create_subscription,self()}, + [Stream,undefined,StartTime,StopTime], + Timeout}); +create_subscription(Client,Stream,Filter,StartTime,StopTime) + when is_list(Stream) andalso + ?is_filter(Filter) andalso + is_list(StartTime) andalso + is_list(StopTime) -> + create_subscription(Client,Stream,Filter,StartTime,StopTime,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec create_subscription(Client, Stream, Filter,StartTime, StopTime, Timeout) -> + Result when + Client :: client(), + Stream :: stream_name(), + Filter :: simple_xml(), + StartTime :: xs_datetime(), + StopTime :: xs_datetime(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Create a subscription for event notifications. +%% +%% This function sets up a subscription for netconf event +%% notifications of the given stream type, matching the given +%% filter. The calling process will receive notifications as messages +%% of type `notification()'. +%% +%% <dl> +%% <dt>Stream:</dt> +%% <dd> An optional parameter that indicates which stream of events +%% is of interest. If not present, events in the default NETCONF +%% stream will be sent.</dd> +%% +%% <dt>Filter:</dt> +%% <dd>An optional parameter that indicates which subset of all +%% possible events is of interest. The format of this parameter is +%% the same as that of the filter parameter in the NETCONF protocol +%% operations. If not present, all events not precluded by other +%% parameters will be sent. See section 3.6 for more information on +%% filters.</dd> +%% +%% <dt>StartTime:</dt> +%% <dd>An optional parameter used to trigger the replay feature and +%% indicate that the replay should start at the time specified. If +%% `StartTime' is not present, this is not a replay subscription. +%% It is not valid to specify start times that are later than the +%% current time. If the `StartTime' specified is earlier than the +%% log can support, the replay will begin with the earliest +%% available notification. This parameter is of type dateTime and +%% compliant to [RFC3339]. Implementations must support time +%% zones.</dd> +%% +%% <dt>StopTime:</dt> +%% <dd>An optional parameter used with the optional replay feature +%% to indicate the newest notifications of interest. If `StopTime' +%% is not present, the notifications will continue until the +%% subscription is terminated. Must be used with and be later than +%% `StartTime'. Values of `StopTime' in the future are valid. This +%% parameter is of type dateTime and compliant to [RFC3339]. +%% Implementations must support time zones.</dd> +%% </dl> +%% +%% See RFC5277 for further details about the event notification +%% mechanism. +%% +%% @end +%%---------------------------------------------------------------------- +create_subscription(Client,Stream,Filter,StartTime,StopTime,Timeout) -> + call(Client,{send_rpc_op,{create_subscription, self()}, + [Stream,Filter,StartTime,StopTime], + Timeout}). + +%%---------------------------------------------------------------------- +%% @spec get_event_streams(Client, Timeout) -> Result +%% @equiv get_event_streams(Client, [], Timeout) +get_event_streams(Client,Timeout) when is_integer(Timeout); Timeout==infinity -> + get_event_streams(Client,[],Timeout); + +%%---------------------------------------------------------------------- +%% @spec get_event_streams(Client, Streams) -> Result +%% @equiv get_event_streams(Client, Streams, infinity) +get_event_streams(Client,Streams) when is_list(Streams) -> + get_event_streams(Client,Streams,?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec get_event_streams(Client, Streams, Timeout) + -> Result when + Client :: client(), + Streams :: [stream_name()], + Timeout :: timeout(), + Result :: {ok,streams()} | {error,error_reason()}. +%% @doc Send a request to get the given event streams. +%% +%% `Streams' is a list of stream names. The following filter will +%% be sent to the netconf server in a `get' request: +%% +%% ``` +%% <netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"> +%% <streams> +%% <stream> +%% <name>StreamName1</name> +%% </stream> +%% <stream> +%% <name>StreamName2</name> +%% </stream> +%% ... +%% </streams> +%% </netconf> +%% ''' +%% +%% If `Streams' is an empty list, ALL streams will be requested +%% by sending the following filter: +%% +%% ``` +%% <netconf xmlns="urn:ietf:params:xml:ns:netmod:notification"> +%% <streams/> +%% </netconf> +%% ''' +%% +%% If more complex filtering is needed, a use {@link get/2} or {@link +%% get/3} and specify the exact filter according to XML Schema for +%% Event Notifications found in RFC5277. +%% +%% @end +%%---------------------------------------------------------------------- +get_event_streams(Client,Streams,Timeout) -> + call(Client,{get_event_streams,Streams,Timeout}). + + +%%---------------------------------------------------------------------- +%% @spec close_session(Client) -> Result +%% @equiv close_session(Client, infinity) +close_session(Client) -> + close_session(Client, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec close_session(Client, Timeout) -> Result when + Client :: client(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Request graceful termination of the session associated with the client. +%% +%% When a netconf server receives a `close-session' request, it +%% will gracefully close the session. The server will release any +%% locks and resources associated with the session and gracefully +%% close any associated connections. Any NETCONF requests received +%% after a `close-session' request will be ignored. +%% +%% @end +%%---------------------------------------------------------------------- +close_session(Client, Timeout) -> + call(Client,{send_rpc_op, close_session, [], Timeout}). + + +%%---------------------------------------------------------------------- +%% @spec kill_session(Client, SessionId) -> Result +%% @equiv kill_session(Client, SessionId, infinity) +kill_session(Client, SessionId) -> + kill_session(Client, SessionId, ?DEFAULT_TIMEOUT). + +%%---------------------------------------------------------------------- +-spec kill_session(Client, SessionId, Timeout) -> Result when + Client :: client(), + SessionId :: pos_integer(), + Timeout :: timeout(), + Result :: ok | {error,error_reason()}. +%% @doc Force termination of the session associated with the supplied +%% session id. +%% +%% The server side shall abort any operations currently in process, +%% release any locks and resources associated with the session, and +%% close any associated connections. +%% +%% Only if the server is in the confirmed commit phase, the +%% configuration will be restored to its state before entering the +%% confirmed commit phase. Otherwise, no configuration roll back will +%% be performed. +%% +%% If the given `SessionId' is equal to the current session id, +%% an error will be returned. +%% +%% @end +%% ---------------------------------------------------------------------- +kill_session(Client, SessionId, Timeout) -> + call(Client,{send_rpc_op, kill_session, [SessionId], Timeout}). + + +%%---------------------------------------------------------------------- +%% Callback functions +%%---------------------------------------------------------------------- + +%% @private +init(_KeyOrName,{_Host,_Port},Options) -> + case ssh_open(Options) of + {ok, Connection} -> + log(Connection,open), + {ConnPid,_} = Connection#connection.reference, + {ok, ConnPid, #state{connection = Connection}}; + {error,Reason}-> + {error,Reason} + end. + +%% @private +terminate(_, #state{connection=Connection}) -> + ssh_close(Connection), + log(Connection,close), + ok. + +%% @private +handle_msg({hello,Timeout}, From, + #state{connection=Connection,hello_status=HelloStatus} = State) -> + case do_send(Connection, client_hello()) of + ok -> + case HelloStatus of + undefined -> + {Ref,TRef} = set_request_timer(Timeout), + {noreply, State#state{hello_status=#pending{tref=TRef, + ref=Ref, + caller=From}}}; + received -> + {reply, ok, State#state{hello_status=done}}; + {error,Reason} -> + {stop, {error,Reason}, State} + end; + Error -> + {stop, Error, State} + end; +handle_msg(_, _From, #state{session_id=undefined} = State) -> + %% Hello is not yet excanged - this shall never happen + {reply,{error,waiting_for_hello},State}; +handle_msg(get_capabilities, _From, #state{capabilities = Caps} = State) -> + {reply, Caps, State}; +handle_msg(get_session_id, _From, #state{session_id = Id} = State) -> + {reply, Id, State}; +handle_msg({send, Timeout, SimpleXml}, From, + #state{connection=Connection,pending=Pending} = State) -> + case do_send(Connection, SimpleXml) of + ok -> + {Ref,TRef} = set_request_timer(Timeout), + {noreply, State#state{pending=[#pending{tref=TRef, + ref=Ref, + caller=From} | Pending]}}; + Error -> + {reply, Error, State} + end; +handle_msg({send_rpc, SimpleXml, Timeout}, From, State) -> + do_send_rpc(undefined, SimpleXml, Timeout, From, State); +handle_msg({send_rpc_op, Op, Data, Timeout}, From, State) -> + SimpleXml = encode_rpc_operation(Op,Data), + do_send_rpc(Op, SimpleXml, Timeout, From, State); +handle_msg({get_event_streams=Op,Streams,Timeout}, From, State) -> + Filter = {netconf,?NETMOD_NOTIF_NAMESPACE_ATTR, + [{streams,[{stream,[{name,[Name]}]} || Name <- Streams]}]}, + SimpleXml = encode_rpc_operation(get,[Filter]), + do_send_rpc(Op, SimpleXml, Timeout, From, State). + +handle_msg({ssh_cm, _CM, {data, _Ch, _Type, Data}}, State) -> + handle_data(Data, State); +handle_msg({ssh_cm, _CM, {closed,_Ch}}, State) -> + %% This will happen if the server terminates the connection, as in + %% kill-session (or if ssh:close is called from somewhere + %% unexpected). + + %%! Log this?? - i.e. as server closing the connection + %%! Currently the log will say that the client closed the + %%! connection - due to terminate/2 + + {stop, State}; +handle_msg({Ref,timeout}, + #state{hello_status=#pending{ref=Ref,caller=Caller}} = State) -> + ct_gen_conn:return(Caller,{error,{hello_session_failed,timeout}}), + {stop,State#state{hello_status={error,timeout}}}; +handle_msg({Ref,timeout},#state{pending=Pending} = State) -> + {value,#pending{caller=Caller},Pending1} = + lists:keytake(Ref,#pending.ref,Pending), + ct_gen_conn:return(Caller,{error,timeout}), + {noreply,State#state{pending=Pending1}}. + +%% @private +%% Called by ct_util_server to close registered connections before terminate. +close(Client) -> + case get_handle(Client) of + {ok,Pid} -> + case ct_gen_conn:stop(Pid) of + {error,{process_down,Pid,noproc}} -> + {error,already_closed}; + Result -> + Result + end; + Error -> + Error + end. + + +%%---------------------------------------------------------------------- +%% Internal functions +%%---------------------------------------------------------------------- +call(Client, Msg) -> + call(Client, Msg, infinity). +call(Client, Msg, Timeout) -> + case get_handle(Client) of + {ok,Pid} -> + case ct_gen_conn:call(Pid,Msg,Timeout) of + {error,{process_down,Client,noproc}} -> + {error,no_such_client}; + {error,{process_down,Client,normal}} -> + {error,closed}; + {error,{process_down,Client,Reason}} -> + {error,{closed,Reason}}; + Other -> + Other + end; + Error -> + Error + end. + +get_handle(Client) when is_pid(Client) -> + {ok,Client}; +get_handle(Client) -> + case ct_util:get_connections(Client, ?MODULE) of + {ok,[{Pid,_}]} -> + {ok,Pid}; + {ok,[]} -> + {error,{no_connection_found,Client}}; + {ok,Conns} -> + {error,{multiple_connections_found,Client,Conns}}; + Error -> + Error + end. + +check_options([], undefined, _Port, _Options) -> + {error, no_host_address}; +check_options([], _Host, undefined, _Options) -> + {error, no_port}; +check_options([], Host, Port, Options) -> + {Host,Port,Options}; +check_options([{ssh, Host}|T], _, Port, #options{} = Options) -> + check_options(T, Host, Port, Options#options{host=Host}); +check_options([{port,Port}|T], Host, _, #options{} = Options) -> + check_options(T, Host, Port, Options#options{port=Port}); +check_options([{timeout, Timeout}|T], Host, Port, Options) + when is_integer(Timeout); Timeout==infinity -> + check_options(T, Host, Port, Options#options{timeout = Timeout}); +check_options([{X,_}=Opt|T], Host, Port, #options{ssh=SshOpts}=Options) -> + case lists:member(X,?VALID_SSH_OPTS) of + true -> + check_options(T, Host, Port, Options#options{ssh=[Opt|SshOpts]}); + false -> + {error, {invalid_option, Opt}} + end. + +%%%----------------------------------------------------------------- +set_request_timer(infinity) -> + {undefined,undefined}; +set_request_timer(T) -> + Ref = make_ref(), + {ok,TRef} = timer:send_after(T,{Ref,timeout}), + {Ref,TRef}. + + +%%%----------------------------------------------------------------- +client_hello() -> + {hello, ?NETCONF_NAMESPACE_ATTR, + [{capabilities, + [{capability,[?NETCONF_BASE_CAP++?NETCONF_BASE_CAP_VSN]}]}]}. + +%%%----------------------------------------------------------------- + +encode_rpc_operation(Lock,[Target]) when Lock==lock; Lock==unlock -> + {Lock,[{target,[Target]}]}; +encode_rpc_operation(get,[Filter]) -> + {get,filter(Filter)}; +encode_rpc_operation(get_config,[Source,Filter]) -> + {'get-config',[{source,[Source]}] ++ filter(Filter)}; +encode_rpc_operation(edit_config,[Target,Config]) -> + {'edit-config',[{target,[Target]},{config,[Config]}]}; +encode_rpc_operation(delete_config,[Target]) -> + {'delete-config',[{target,[Target]}]}; +encode_rpc_operation(copy_config,[Target,Source]) -> + {'copy-config',[{target,[Target]},{source,[Source]}]}; +encode_rpc_operation(action,[Action]) -> + {action,?ACTION_NAMESPACE_ATTR,[{data,[Action]}]}; +encode_rpc_operation(kill_session,[SessionId]) -> + {'kill-session',[{'session-id',[integer_to_list(SessionId)]}]}; +encode_rpc_operation(close_session,[]) -> + 'close-session'; +encode_rpc_operation({create_subscription,_}, + [Stream,Filter,StartTime,StopTime]) -> + {'create-subscription',?NETCONF_NOTIF_NAMESPACE_ATTR, + [{stream,[Stream]}] ++ + filter(Filter) ++ + maybe_element(startTime,StartTime) ++ + maybe_element(stopTime,StopTime)}. + +filter(undefined) -> + []; +filter({xpath,Filter}) when ?is_string(Filter) -> + [{filter,[{type,"xpath"},{select, Filter}],[]}]; +filter(Filter) -> + [{filter,[{type,"subtree"}],[Filter]}]. + +maybe_element(_,undefined) -> + []; +maybe_element(Tag,Value) -> + [{Tag,[Value]}]. + +%%%----------------------------------------------------------------- +%%% Send XML data to server +do_send_rpc(PendingOp,SimpleXml,Timeout,Caller, + #state{connection=Connection,msg_id=MsgId,pending=Pending} = State) -> + case do_send_rpc(Connection, MsgId, SimpleXml) of + ok -> + {Ref,TRef} = set_request_timer(Timeout), + {noreply, State#state{msg_id=MsgId+1, + pending=[#pending{tref=TRef, + ref=Ref, + msg_id=MsgId, + op=PendingOp, + caller=Caller} | Pending]}}; + Error -> + {reply, Error, State#state{msg_id=MsgId+1}} + end. + +do_send_rpc(Connection, MsgId, SimpleXml) -> + do_send(Connection, + {rpc, + [{'message-id',MsgId} | ?NETCONF_NAMESPACE_ATTR], + [SimpleXml]}). + +do_send(Connection, SimpleXml) -> + Xml=to_xml_doc(SimpleXml), + log(Connection,send,Xml), + ssh_send(Connection, Xml). + +to_xml_doc(Simple) -> + Prolog = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>", + Xml = list_to_binary(xmerl:export_simple([Simple], + xmerl_xml, + [#xmlAttribute{name=prolog, + value=Prolog}])), + <<Xml/binary,?END_TAG/binary>>. + +%%%----------------------------------------------------------------- +%%% Parse and handle received XML data +handle_data(NewData,#state{connection=Connection,buff=Buff} = State) -> + log(Connection,recv,NewData), + Data = <<Buff/binary,NewData/binary>>, + case xmerl_sax_parser:stream(<<>>, + [{continuation_fun,fun sax_cont/1}, + {continuation_state,{Data,Connection,false}}, + {event_fun,fun sax_event/3}, + {event_state,[]}]) of + {ok, Simple, Rest} -> + decode(Simple,State#state{buff=Rest}); + {fatal_error,_Loc,Reason,_EndTags,_EventState} -> + ?error(Connection#connection.name,[{parse_error,Reason}, + {data,Data}]), + case Reason of + {could_not_fetch_data,Msg} -> + handle_msg(Msg,State#state{buff = <<>>}); + _Other -> + Pending1 = + case State#state.pending of + [] -> + []; + Pending -> + %% Assuming the first request gets the + %% first answer + P=#pending{tref=TRef,caller=Caller} = + lists:last(Pending), + timer:cancel(TRef), + Reason1 = {failed_to_parse_received_data,Reason}, + ct_gen_conn:return(Caller,{error,Reason1}), + lists:delete(P,Pending) + end, + {noreply,State#state{pending=Pending1,buff = <<>>}} + end + end. + +%%%----------------------------------------------------------------- +%%% Parsing of XML data +%% Contiuation function for the sax parser +sax_cont(done) -> + {<<>>,done}; +sax_cont({Data,Connection,false}) -> + case binary:split(Data,[?END_TAG],[]) of + [All] -> + %% No end tag found. Remove what could be a part + %% of an end tag from the data and save for next + %% iteration + SafeSize = size(All)-5, + <<New:SafeSize/binary,Save:5/binary>> = All, + {New,{Save,Connection,true}}; + [_Msg,_Rest]=Msgs -> + %% We have at least one full message. Any excess data will + %% be returned from xmerl_sax_parser:stream/2 in the Rest + %% parameter. + {list_to_binary(Msgs),done} + end; +sax_cont({Data,Connection,true}) -> + case ssh_receive_data() of + {ok,Bin} -> + log(Connection,recv,Bin), + sax_cont({<<Data/binary,Bin/binary>>,Connection,false}); + {error,Reason} -> + throw({could_not_fetch_data,Reason}) + end. + + + +%% Event function for the sax parser. It builds a simple XML structure. +%% Care is taken to keep namespace attributes and prefixes as in the original XML. +sax_event(Event,_Loc,State) -> + sax_event(Event,State). + +sax_event({startPrefixMapping, Prefix, Uri},Acc) -> + %% startPrefixMapping will always come immediately before the + %% startElement where the namespace is defined. + [{xmlns,{Prefix,Uri}}|Acc]; +sax_event({startElement,_Uri,_Name,QN,Attrs},Acc) -> + %% Pick out any namespace attributes inserted due to a + %% startPrefixMapping event.The rest of Acc will then be only + %% elements. + {NsAttrs,NewAcc} = split_attrs_and_elements(Acc,[]), + Tag = qn_to_tag(QN), + [{Tag,NsAttrs ++ parse_attrs(Attrs),[]}|NewAcc]; +sax_event({endElement,_Uri,_Name,_QN},[{Name,Attrs,Cont},{Parent,PA,PC}|Acc]) -> + [{Parent,PA,[{Name,Attrs,lists:reverse(Cont)}|PC]}|Acc]; +sax_event(endDocument,[{Tag,Attrs,Cont}]) -> + {Tag,Attrs,lists:reverse(Cont)}; +sax_event({characters,String},[{Name,Attrs,Cont}|Acc]) -> + [{Name,Attrs,[String|Cont]}|Acc]; +sax_event(_Event,State) -> + State. + +split_attrs_and_elements([{xmlns,{Prefix,Uri}}|Rest],Attrs) -> + split_attrs_and_elements(Rest,[{xmlnstag(Prefix),Uri}|Attrs]); +split_attrs_and_elements(Elements,Attrs) -> + {Attrs,Elements}. + +xmlnstag([]) -> + xmlns; +xmlnstag(Prefix) -> + list_to_atom("xmlns:"++Prefix). + +qn_to_tag({[],Name}) -> + list_to_atom(Name); +qn_to_tag({Prefix,Name}) -> + list_to_atom(Prefix ++ ":" ++ Name). + +parse_attrs([{_Uri, [], Name, Value}|Attrs]) -> + [{list_to_atom(Name),Value}|parse_attrs(Attrs)]; +parse_attrs([{_Uri, Prefix, Name, Value}|Attrs]) -> + [{list_to_atom(Prefix ++ ":" ++ Name),Value}|parse_attrs(Attrs)]; +parse_attrs([]) -> + []. + + +%%%----------------------------------------------------------------- +%%% Decoding of parsed XML data +decode({Tag,Attrs,_}=E, #state{connection=Connection,pending=Pending}=State) -> + ConnName = Connection#connection.name, + case get_local_name_atom(Tag) of + 'rpc-reply' -> + case get_msg_id(Attrs) of + undefined -> + case Pending of + [#pending{msg_id=MsgId}] -> + ?error(ConnName,[{warning,rpc_reply_missing_msg_id}, + {assuming,MsgId}]), + decode_rpc_reply(MsgId,E,State); + _ -> + ?error(ConnName,[{error,rpc_reply_missing_msg_id}]), + {noreply,State} + end; + MsgId -> + decode_rpc_reply(MsgId,E,State) + end; + hello -> + case State#state.hello_status of + undefined -> + case decode_hello(E) of + {ok,SessionId,Capabilities} -> + {noreply,State#state{session_id = SessionId, + capabilities = Capabilities, + hello_status = received}}; + {error,Reason} -> + {noreply,State#state{hello_status = {error,Reason}}} + end; + #pending{tref=TRef,caller=Caller} -> + timer:cancel(TRef), + case decode_hello(E) of + {ok,SessionId,Capabilities} -> + ct_gen_conn:return(Caller,ok), + {noreply,State#state{session_id = SessionId, + capabilities = Capabilities, + hello_status = done}}; + {error,Reason} -> + ct_gen_conn:return(Caller,{error,Reason}), + {stop,State#state{hello_status={error,Reason}}} + end; + Other -> + ?error(ConnName,[{got_unexpected_hello,E}, + {hello_status,Other}]), + {noreply,State} + end; + notification -> + EventReceiver = State#state.event_receiver, + EventReceiver ! E, + {noreply,State}; + Other -> + %% Result of send/2, when not sending an rpc request - or + %% if netconf server sends noise. Can handle this only if + %% there is just one pending that matches (i.e. has + %% undefined msg_id and op) + case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of + [#pending{tref=TRef, + caller=Caller}] -> + timer:cancel(TRef), + ct_gen_conn:return(Caller,E), + {noreply,State#state{pending=[]}}; + _ -> + ?error(ConnName,[{got_unexpected_msg,Other}, + {expecting,Pending}]), + {noreply,State} + end + + end. + +get_msg_id(Attrs) -> + case lists:keyfind('message-id',1,Attrs) of + {_,Str} -> + list_to_integer(Str); + false -> + undefined + end. + +decode_rpc_reply(MsgId,{_,Attrs,Content0}=E,#state{pending=Pending} = State) -> + case lists:keytake(MsgId,#pending.msg_id,Pending) of + {value, #pending{tref=TRef,op=Op,caller=Caller}, Pending1} -> + timer:cancel(TRef), + Content = forward_xmlns_attr(Attrs,Content0), + {CallerReply,{ServerReply,State2}} = + do_decode_rpc_reply(Op,Content,State#state{pending=Pending1}), + ct_gen_conn:return(Caller,CallerReply), + {ServerReply,State2}; + false -> + %% Result of send/2, when receiving a correct + %% rpc-reply. Can handle this only if there is just one + %% pending that matches (i.e. has undefined msg_id and op) + case [P || P = #pending{msg_id=undefined,op=undefined} <- Pending] of + [#pending{tref=TRef, + msg_id=undefined, + op=undefined, + caller=Caller}] -> + timer:cancel(TRef), + ct_gen_conn:return(Caller,E), + {noreply,State#state{pending=[]}}; + _ -> + ConnName = (State#state.connection)#connection.name, + ?error(ConnName,[{got_unexpected_msg_id,MsgId}, + {expecting,Pending}]), + {noreply,State} + end + end. + +do_decode_rpc_reply(Op,Result,State) + when Op==lock; Op==unlock; Op==edit_config; Op==delete_config; + Op==copy_config; Op==kill_session -> + {decode_ok(Result),{noreply,State}}; +do_decode_rpc_reply(Op,Result,State) + when Op==get; Op==get_config; Op==action -> + {decode_data(Result),{noreply,State}}; +do_decode_rpc_reply(close_session,Result,State) -> + case decode_ok(Result) of + ok -> {ok,{stop,State}}; + Other -> {Other,{noreply,State}} + end; +do_decode_rpc_reply({create_subscription,Caller},Result,State) -> + case decode_ok(Result) of + ok -> + {ok,{noreply,State#state{event_receiver=Caller}}}; + Other -> + {Other,{noreply,State}} + end; +do_decode_rpc_reply(get_event_streams,Result,State) -> + {decode_streams(decode_data(Result)),{noreply,State}}; +do_decode_rpc_reply(undefined,Result,State) -> + {Result,{noreply,State}}. + + + +decode_ok([{Tag,Attrs,Content}]) -> + case get_local_name_atom(Tag) of + ok -> + ok; + 'rpc-error' -> + {error,forward_xmlns_attr(Attrs,Content)}; + _Other -> + {error,{unexpected_rpc_reply,[{Tag,Attrs,Content}]}} + end; +decode_ok(Other) -> + {error,{unexpected_rpc_reply,Other}}. + +decode_data([{Tag,Attrs,Content}]) -> + case get_local_name_atom(Tag) of + data -> + %% Since content of data has nothing from the netconf + %% namespace, we remove the parent's xmlns attribute here + %% - just to make the result cleaner + {ok,forward_xmlns_attr(remove_xmlnsattr_for_tag(Tag,Attrs),Content)}; + 'rpc-error' -> + {error,forward_xmlns_attr(Attrs,Content)}; + _Other -> + {error,{unexpected_rpc_reply,[{Tag,Attrs,Content}]}} + end; +decode_data(Other) -> + {error,{unexpected_rpc_reply,Other}}. + +get_qualified_name(Tag) -> + case string:tokens(atom_to_list(Tag),":") of + [TagStr] -> {[],TagStr}; + [PrefixStr,TagStr] -> {PrefixStr,TagStr} + end. + +get_local_name_atom(Tag) -> + {_,TagStr} = get_qualified_name(Tag), + list_to_atom(TagStr). + + +%% Remove the xmlns attr that points to the tag. I.e. if the tag has a +%% prefix, remove {'xmlns:prefix',_}, else remove default {xmlns,_}. +remove_xmlnsattr_for_tag(Tag,Attrs) -> + {Prefix,_TagStr} = get_qualified_name(Tag), + XmlnsTag = xmlnstag(Prefix), + case lists:keytake(XmlnsTag,1,Attrs) of + {value,_,NoNsAttrs} -> + NoNsAttrs; + false -> + Attrs + end. + +%% Take all xmlns attributes from the parent's attribute list and +%% forward into all childrens' attribute lists. But do not overwrite +%% any. +forward_xmlns_attr(ParentAttrs,Children) -> + do_forward_xmlns_attr(get_all_xmlns_attrs(ParentAttrs,[]),Children). + +do_forward_xmlns_attr(XmlnsAttrs,[{ChT,ChA,ChC}|Children]) -> + ChA1 = add_xmlns_attrs(XmlnsAttrs,ChA), + [{ChT,ChA1,ChC} | do_forward_xmlns_attr(XmlnsAttrs,Children)]; +do_forward_xmlns_attr(_XmlnsAttrs,[]) -> + []. + +add_xmlns_attrs([{Key,_}=A|XmlnsAttrs],ChA) -> + case lists:keymember(Key,1,ChA) of + true -> + add_xmlns_attrs(XmlnsAttrs,ChA); + false -> + add_xmlns_attrs(XmlnsAttrs,[A|ChA]) + end; +add_xmlns_attrs([],ChA) -> + ChA. + +get_all_xmlns_attrs([{xmlns,_}=Default|Attrs],XmlnsAttrs) -> + get_all_xmlns_attrs(Attrs,[Default|XmlnsAttrs]); +get_all_xmlns_attrs([{Key,_}=Attr|Attrs],XmlnsAttrs) -> + case atom_to_list(Key) of + "xmlns:"++_Prefix -> + get_all_xmlns_attrs(Attrs,[Attr|XmlnsAttrs]); + _ -> + get_all_xmlns_attrs(Attrs,XmlnsAttrs) + end; +get_all_xmlns_attrs([],XmlnsAttrs) -> + XmlnsAttrs. + + +%% Decode server hello to pick out session id and capabilities +decode_hello({hello,_Attrs,Hello}) -> + case lists:keyfind('session-id',1,Hello) of + {'session-id',_,[SessionId]} -> + case lists:keyfind(capabilities,1,Hello) of + {capabilities,_,Capabilities} -> + case decode_caps(Capabilities,[],false) of + {ok,Caps} -> + {ok,list_to_integer(SessionId),Caps}; + Error -> + Error + end; + false -> + {error,{incorrect_hello,capabilities_not_found}} + end; + false -> + {error,{incorrect_hello,no_session_id_found}} + end. + +decode_caps([{capability,[],[?NETCONF_BASE_CAP++Vsn=Cap]} |Caps], Acc, _) -> + case Vsn of + ?NETCONF_BASE_CAP_VSN -> + decode_caps(Caps, [Cap|Acc], true); + _ -> + {error,{incompatible_base_capability_vsn,Vsn}} + end; +decode_caps([{capability,[],[Cap]}|Caps],Acc,Base) -> + decode_caps(Caps,[Cap|Acc],Base); +decode_caps([H|_T],_,_) -> + {error,{unexpected_capability_element,H}}; +decode_caps([],_,false) -> + {error,{incorrect_hello,no_base_capability_found}}; +decode_caps([],Acc,true) -> + {ok,lists:reverse(Acc)}. + + +%% Return a list of {Name,Data}, where data is a {Tag,Value} list for each stream +decode_streams({error,Reason}) -> + {error,Reason}; +decode_streams({ok,[{netconf,_,Streams}]}) -> + {ok,decode_streams(Streams)}; +decode_streams([{streams,_,Streams}]) -> + decode_streams(Streams); +decode_streams([{stream,_,Stream} | Streams]) -> + {name,_,[Name]} = lists:keyfind(name,1,Stream), + [{Name,[{Tag,Value} || {Tag,_,[Value]} <- Stream, Tag /= name]} + | decode_streams(Streams)]; +decode_streams([]) -> + []. + + +%%%----------------------------------------------------------------- +%%% Logging + +log(Connection,Action) -> + log(Connection,Action,<<>>). +log(#connection{host=Host,port=Port,name=Name},Action,Data) -> + error_logger:info_report(#conn_log{client=self(), + address={Host,Port}, + name=Name, + action=Action, + module=?MODULE}, + Data). + + +%% Log callback - called from the error handler process +format_data(raw,Data) -> + io_lib:format("~n~s~n",[hide_password(Data)]); +format_data(pretty,Data) -> + io_lib:format("~n~s~n",[indent(Data)]); +format_data(html,Data) -> + io_lib:format("~n~s~n",[html_format(Data)]). + +%%%----------------------------------------------------------------- +%%% Hide password elements from XML data +hide_password(Bin) -> + re:replace(Bin,<<"(<password[^>]*>)[^<]*(</password>)">>,<<"\\1*****\\2">>, + [global,{return,binary}]). + +%%%----------------------------------------------------------------- +%%% HTML formatting +html_format(Bin) -> + binary:replace(indent(Bin),<<"<">>,<<"<">>,[global]). + +%%%----------------------------------------------------------------- +%%% Indentation of XML code +indent(Bin) -> + String = normalize(hide_password(Bin)), + IndentedString = + case erase(part_of_line) of + undefined -> + indent1(String,[]); + Part -> + indent1(lists:reverse(Part)++String,erase(indent)) + end, + list_to_binary(IndentedString). + +%% Normalizes the XML document by removing all space and newline +%% between two XML tags. +%% Returns a list, no matter if the input was a list or a binary. +normalize(Str) -> + re:replace(Str,<<">[ \r\n\t]+<">>,<<"><">>,[global,{return,list}]). + + +indent1("<?"++Rest1,Indent1) -> + %% Prolog + {Line,Rest2,Indent2} = indent_line(Rest1,Indent1,[$?,$<]), + Line++indent1(Rest2,Indent2); +indent1("</"++Rest1,Indent1) -> + %% Stop tag + {Line,Rest2,Indent2} = indent_line1(Rest1,Indent1,[$/,$<]), + "\n"++Line++indent1(Rest2,Indent2); +indent1("<"++Rest1,Indent1) -> + %% Start- or empty tag + put(tag,get_tag(Rest1)), + {Line,Rest2,Indent2} = indent_line(Rest1,Indent1,[$<]), + "\n"++Line++indent1(Rest2,Indent2); +indent1([H|T],Indent) -> + [H|indent1(T,Indent)]; +indent1([],_Indent) -> + []. + +indent_line("?>"++Rest,Indent,Line) -> + %% Prolog + {lists:reverse(Line)++"?>",Rest,Indent}; +indent_line("/></"++Rest,Indent,Line) -> + %% Empty tag, and stop of parent tag -> one step out in indentation + {Indent++lists:reverse(Line)++"/>","</"++Rest,Indent--" "}; +indent_line("/>"++Rest,Indent,Line) -> + %% Empty tag, then probably next tag -> keep indentation + {Indent++lists:reverse(Line)++"/>",Rest,Indent}; +indent_line("></"++Rest,Indent,Line) -> + LastTag = erase(tag), + case get_tag(Rest) of + LastTag -> + %% Start and stop tag, but no content + indent_line1(Rest,Indent,[$/,$<,$>|Line]); + _ -> + %% Stop tag completed, and then stop tag of parent -> one step out + {Indent++lists:reverse(Line)++">","</"++Rest,Indent--" "} + end; +indent_line("><"++Rest,Indent,Line) -> + %% Stop tag completed, and new tag comming -> keep indentation + {Indent++lists:reverse(Line)++">","<"++Rest," "++Indent}; +indent_line("</"++Rest,Indent,Line) -> + %% Stop tag starting -> search for end of this tag + indent_line1(Rest,Indent,[$/,$<|Line]); +indent_line([H|T],Indent,Line) -> + indent_line(T,Indent,[H|Line]); +indent_line([],Indent,Line) -> + %% The line is not complete - will be continued later + put(part_of_line,Line), + put(indent,Indent), + {[],[],Indent}. + +indent_line1("></"++Rest,Indent,Line) -> + %% Stop tag completed, and then stop tag of parent -> one step out + {Indent++lists:reverse(Line)++">","</"++Rest,Indent--" "}; +indent_line1(">"++Rest,Indent,Line) -> + %% Stop tag completed -> keep indentation + {Indent++lists:reverse(Line)++">",Rest,Indent}; +indent_line1([H|T],Indent,Line) -> + indent_line1(T,Indent,[H|Line]); +indent_line1([],Indent,Line) -> + %% The line is not complete - will be continued later + put(part_of_line,Line), + put(indent,Indent), + {[],[],Indent}. + +get_tag("/>"++_) -> + []; +get_tag(">"++_) -> + []; +get_tag([H|T]) -> + [H|get_tag(T)]; +get_tag([]) -> + %% The line is not complete - will be continued later. + []. + + +%%%----------------------------------------------------------------- +%%% SSH stuff +ssh_receive_data() -> + receive + {ssh_cm, _CM, {data, _Ch, _Type, Data}} -> + {ok, Data}; + {ssh_cm, _CM, {Closed, _Ch}} = X when Closed == closed; Closed == eof -> + {error,X}; + {_Ref,timeout} = X -> + {error,X} + end. + +ssh_open(#options{host=Host,timeout=Timeout,port=Port,ssh=SshOpts,name=Name}) -> + case ssh:connect(Host, Port, + [{user_interaction,false}, + {silently_accept_hosts, true}|SshOpts]) of + {ok,CM} -> + case ssh_connection:session_channel(CM, Timeout) of + {ok,Ch} -> + case ssh_connection:subsystem(CM, Ch, "netconf", Timeout) of + success -> + {ok, #connection{reference = {CM,Ch}, + host = Host, + port = Port, + name = Name}}; + failure -> + ssh:close(CM), + {error,{ssh,could_not_execute_netconf_subsystem}} + end; + {error, Reason} -> + ssh:close(CM), + {error,{ssh,could_not_open_channel,Reason}}; + Other -> + %% Bug in ssh?? got {closed,0} here once... + {error,{ssh,unexpected_from_session_channel,Other}} + end; + {error,Reason} -> + {error,{ssh,could_not_connect_to_server,Reason}} + end. + +ssh_send(#connection{reference = {CM,Ch}}, Data) -> + case ssh_connection:send(CM, Ch, Data) of + ok -> ok; + {error,Reason} -> {error,{ssh,failed_to_send_data,Reason}} + end. + +ssh_close(#connection{reference = {CM,_Ch}}) -> + ssh:close(CM). + + +%%---------------------------------------------------------------------- +%% END OF MODULE +%%---------------------------------------------------------------------- diff --git a/lib/common_test/src/ct_netconfc.hrl b/lib/common_test/src/ct_netconfc.hrl new file mode 100644 index 0000000000..295a61a98b --- /dev/null +++ b/lib/common_test/src/ct_netconfc.hrl @@ -0,0 +1,58 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%%---------------------------------------------------------------------- +%% File: ct_netconfc.hrl +%% +%% Description: +%% This file defines constant values and records used by the +%% netconf client ct_netconfc. +%% +%% @author Support +%% @doc Netconf Client Interface. +%% @end +%%---------------------------------------------------------------------- +%%---------------------------------------------------------------------- + + +%% Default port number (RFC 4742/IANA). +-define(DEFAULT_PORT, 830). + +%% Default timeout to wait for netconf server to reply to a request +-define(DEFAULT_TIMEOUT, infinity). %% msec + +%% Namespaces +-define(NETCONF_NAMESPACE_ATTR,[{xmlns,?NETCONF_NAMESPACE}]). +-define(ACTION_NAMESPACE_ATTR,[{xmlns,?ACTION_NAMESPACE}]). +-define(NETCONF_NOTIF_NAMESPACE_ATTR,[{xmlns,?NETCONF_NOTIF_NAMESPACE}]). +-define(NETMOD_NOTIF_NAMESPACE_ATTR,[{xmlns,?NETMOD_NOTIF_NAMESPACE}]). + +-define(NETCONF_NAMESPACE,"urn:ietf:params:xml:ns:netconf:base:1.0"). +-define(ACTION_NAMESPACE,"urn:com:ericsson:ecim:1.0"). +-define(NETCONF_NOTIF_NAMESPACE, + "urn:ietf:params:xml:ns:netconf:notification:1.0"). +-define(NETMOD_NOTIF_NAMESPACE,"urn:ietf:params:xml:ns:netmod:notification"). + +%% Capabilities +-define(NETCONF_BASE_CAP,"urn:ietf:params:netconf:base:"). +-define(NETCONF_BASE_CAP_VSN,"1.0"). + +%% Misc +-define(END_TAG,<<"]]>]]>">>). + +-define(FORMAT(_F, _A), lists:flatten(io_lib:format(_F, _A))). diff --git a/lib/common_test/src/ct_repeat.erl b/lib/common_test/src/ct_repeat.erl index e6eb135ae8..a47309c6ee 100644 --- a/lib/common_test/src/ct_repeat.erl +++ b/lib/common_test/src/ct_repeat.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -41,72 +41,86 @@ loop_test(If,Args) when is_list(Args) -> case get_loop_info(Args) of no_loop -> false; - {error,E} -> + E = {error,_} -> io:format("Common Test error: ~p\n\n",[E]), file:set_cwd(Cwd), E; {repeat,N} -> io:format("\nCommon Test: Will repeat tests ~w times.\n\n",[N]), Args1 = [{loop_info,[{repeat,1,N}]} | Args], - loop(If,repeat,0,N,undefined,Args1,undefined), - file:set_cwd(Cwd); + Result = loop(If,repeat,0,N,undefined,Args1,undefined,[]), + file:set_cwd(Cwd), + Result; {stop_time,StopTime} -> - case remaining_time(StopTime) of - 0 -> - io:format("\nCommon Test: No time left to run tests.\n\n",[]), - ok; - Secs -> - io:format("\nCommon Test: Will repeat tests for ~s.\n\n", - [ts(Secs)]), - TPid = - case lists:keymember(force_stop,1,Args) of - true -> - CtrlPid = self(), - spawn(fun() -> stop_after(CtrlPid,Secs) end); - false -> - undefined - end, - Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args], - loop(If,stop_time,0,Secs,StopTime,Args1,TPid) - end, - file:set_cwd(Cwd) + Result = + case remaining_time(StopTime) of + 0 -> + io:format("\nCommon Test: " + "No time left to run tests.\n\n",[]), + {error,not_enough_time}; + Secs -> + io:format("\nCommon Test: " + "Will repeat tests for ~s.\n\n",[ts(Secs)]), + TPid = + case lists:keymember(force_stop,1,Args) of + true -> + CtrlPid = self(), + spawn(fun() -> stop_after(CtrlPid,Secs) end); + false -> + undefined + end, + Args1 = [{loop_info,[{stop_time,Secs,StopTime,1}]} | Args], + loop(If,stop_time,0,Secs,StopTime,Args1,TPid,[]) + end, + file:set_cwd(Cwd), + Result end. -loop(_,repeat,N,N,_,_Args,_) -> - ok; +loop(_,repeat,N,N,_,_Args,_,AccResult) -> + lists:reverse(AccResult); -loop(If,Type,N,Data0,Data1,Args,TPid) -> +loop(If,Type,N,Data0,Data1,Args,TPid,AccResult) -> Pid = spawn_tester(If,self(),Args), receive {'EXIT',Pid,Reason} -> - io:format("Test run crashed! This could be an internal error " - "- please report!\n\n" - "~p\n\n",[Reason]), - cancel(TPid), - {error,Reason}; + case Reason of + {user_error,What} -> + io:format("\nTest run failed!\nReason: ~p\n\n\n", [What]), + cancel(TPid), + {error,What}; + _ -> + io:format("Test run crashed! This could be an internal error " + "- please report!\n\n" + "~p\n\n\n",[Reason]), + cancel(TPid), + {error,Reason} + end; {Pid,{error,Reason}} -> - io:format("\nTest run failed!\nReason: ~p\n\n",[Reason]), + io:format("\nTest run failed!\nReason: ~p\n\n\n",[Reason]), cancel(TPid), {error,Reason}; {Pid,Result} -> if Type == repeat -> - io:format("\nTest run ~w(~w) complete.\n\n",[N+1,Data0]), + io:format("\nTest run ~w(~w) complete.\n\n\n",[N+1,Data0]), lists:keydelete(loop_info,1,Args), Args1 = [{loop_info,[{repeat,N+2,Data0}]} | Args], - loop(If,repeat,N+1,Data0,Data1,Args1,TPid); + loop(If,repeat,N+1,Data0,Data1,Args1,TPid,[Result|AccResult]); Type == stop_time -> case remaining_time(Data1) of 0 -> - io:format("\nTest time (~s) has run out.\n\n",[ts(Data0)]), + io:format("\nTest time (~s) has run out.\n\n\n", + [ts(Data0)]), cancel(TPid), - Result; + lists:reverse([Result|AccResult]); Secs -> io:format("\n~s of test time remaining, " - "starting run #~w...\n\n",[ts(Secs),N+2]), + "starting run #~w...\n\n\n", + [ts(Secs),N+2]), lists:keydelete(loop_info,1,Args), ST = {stop_time,Data0,Data1,N+2}, Args1 = [{loop_info,[ST]} | Args], - loop(If,stop_time,N+1,Data0,Data1,Args1,TPid) + loop(If,stop_time,N+1,Data0,Data1,Args1,TPid, + [Result|AccResult]) end end end. diff --git a/lib/common_test/src/ct_run.erl b/lib/common_test/src/ct_run.erl index 666eb3c988..a202ca15e2 100644 --- a/lib/common_test/src/ct_run.erl +++ b/lib/common_test/src/ct_run.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -39,12 +39,20 @@ %% Misc internal functions -export([variables_file_name/1,script_start1/2,run_test2/1]). +-include("ct.hrl"). -include("ct_event.hrl"). -include("ct_util.hrl"). -define(abs(Name), filename:absname(Name)). -define(testdir(Name, Suite), ct_util:get_testdir(Name, Suite)). +-define(EXIT_STATUS_TEST_SUCCESSFUL, 0). +-define(EXIT_STATUS_TEST_CASE_FAILED, 1). +-define(EXIT_STATUS_TEST_RUN_FAILED, 2). + +-define(default_verbosity, [{default,?MAX_VERBOSITY}, + {'$unspecified',?MAX_VERBOSITY}]). + -record(opts, {label, profile, vts, @@ -54,11 +62,14 @@ step, logdir, logopts = [], + basic_html, + verbosity = [], config = [], event_handlers = [], ct_hooks = [], - enable_builtin_hooks = true, + enable_builtin_hooks, include = [], + auto_compile, silent_connections, stylesheet, multiply_timetraps = 1, @@ -102,7 +113,8 @@ script_start() -> end, Flags) end, %% used for purpose of testing the run_test interface - io:format(user, "~n-------------------- START ARGS --------------------~n", []), + io:format(user, "~n-------------------- START ARGS " + "--------------------~n", []), io:format(user, "--- Init args:~n~p~n", [FlagFilter(Init)]), io:format(user, "--- CT args:~n~p~n", [FlagFilter(CtArgs)]), EnvArgs = opts2args(EnvStartOpts), @@ -110,7 +122,8 @@ script_start() -> [EnvStartOpts,EnvArgs]), Merged = merge_arguments(CtArgs ++ EnvArgs), io:format(user, "--- Merged args:~n~p~n", [FlagFilter(Merged)]), - io:format(user, "----------------------------------------------------~n~n", []), + io:format(user, "-----------------------------------" + "-----------------~n~n", []), Merged; _ -> merge_arguments(CtArgs) @@ -122,46 +135,100 @@ script_start() -> script_start(Args) -> Tracing = start_trace(Args), - Res = - case ct_repeat:loop_test(script, Args) of - false -> - {ok,Cwd} = file:get_cwd(), - CTVsn = - case filename:basename(code:lib_dir(common_test)) of - CTBase when is_list(CTBase) -> - case string:tokens(CTBase, "-") of - ["common_test",Vsn] -> " v"++Vsn; - _ -> "" - end - end, - io:format("~nCommon Test~s starting (cwd is ~s)~n~n", [CTVsn,Cwd]), - Self = self(), - Pid = spawn_link(fun() -> script_start1(Self, Args) end), - receive - {'EXIT',Pid,Reason} -> - case Reason of - {user_error,What} -> - io:format("\nTest run failed!\nReason: ~p\n\n", [What]), - {error,What}; - _ -> - io:format("Test run crashed! This could be an internal error " - "- please report!\n\n" - "~p\n\n", [Reason]), - {error,Reason} - end; - {Pid,{error,Reason}} -> - io:format("\nTest run failed! Reason:\n~p\n\n",[Reason]), - {error,Reason}; - {Pid,Result} -> - Result - end; - Result -> - Result - end, + case ct_repeat:loop_test(script, Args) of + false -> + {ok,Cwd} = file:get_cwd(), + CTVsn = + case filename:basename(code:lib_dir(common_test)) of + CTBase when is_list(CTBase) -> + case string:tokens(CTBase, "-") of + ["common_test",Vsn] -> " v"++Vsn; + _ -> "" + end + end, + io:format("~nCommon Test~s starting (cwd is ~s)~n~n", + [CTVsn,Cwd]), + Self = self(), + Pid = spawn_link(fun() -> script_start1(Self, Args) end), + receive + {'EXIT',Pid,Reason} -> + case Reason of + {user_error,What} -> + io:format("\nTest run failed!\nReason: ~p\n\n\n", + [What]), + finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args); + _ -> + io:format("Test run crashed! " + "This could be an internal error " + "- please report!\n\n" + "~p\n\n\n", [Reason]), + finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args) + end; + {Pid,{error,Reason}} -> + io:format("\nTest run failed! Reason:\n~p\n\n\n",[Reason]), + finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args); + {Pid,Result} -> + io:nl(), + finish(Tracing, analyze_test_result(Result, Args), Args) + end; + {error,_LoopReason} -> + finish(Tracing, ?EXIT_STATUS_TEST_RUN_FAILED, Args); + Result -> + io:nl(), + finish(Tracing, analyze_test_result(Result, Args), Args) + end. + +%% analyze the result of one test run, or many (in case of looped test) +analyze_test_result(ok, _) -> + ?EXIT_STATUS_TEST_SUCCESSFUL; +analyze_test_result({error,_Reason}, _) -> + ?EXIT_STATUS_TEST_RUN_FAILED; +analyze_test_result({_Ok,Failed,{_UserSkipped,AutoSkipped}}, Args) -> + if Failed > 0 -> + ?EXIT_STATUS_TEST_CASE_FAILED; + true -> + case AutoSkipped of + 0 -> + ?EXIT_STATUS_TEST_SUCCESSFUL; + _ -> + case get_start_opt(exit_status, + fun([ExitOpt]) -> ExitOpt end, + Args) of + undefined -> + ?EXIT_STATUS_TEST_CASE_FAILED; + "ignore_config" -> + ?EXIT_STATUS_TEST_SUCCESSFUL + end + end + end; +analyze_test_result([Result|Rs], Args) -> + case analyze_test_result(Result, Args) of + ?EXIT_STATUS_TEST_SUCCESSFUL -> + analyze_test_result(Rs, Args); + Other -> + Other + end; +analyze_test_result([], _) -> + ?EXIT_STATUS_TEST_SUCCESSFUL; +analyze_test_result(Unknown, _) -> + io:format("\nTest run failed! Reason:\n~p\n\n\n",[Unknown]), + ?EXIT_STATUS_TEST_RUN_FAILED. + +finish(Tracing, ExitStatus, Args) -> stop_trace(Tracing), timer:sleep(1000), - io:nl(), - Res. + %% it's possible to tell CT to finish execution with a call + %% to a different function than the normal halt/1 BIF + %% (meant to be used mainly for reading the CT exit status) + case get_start_opt(halt_with, + fun([HaltMod,HaltFunc]) -> {list_to_atom(HaltMod), + list_to_atom(HaltFunc)} end, + Args) of + undefined -> + halt(ExitStatus); + {M,F} -> + apply(M, F, [ExitStatus]) + end. script_start1(Parent, Args) -> %% read general start flags @@ -173,6 +240,7 @@ script_start1(Parent, Args) -> LogDir = get_start_opt(logdir, fun([LogD]) -> LogD end, Args), LogOpts = get_start_opt(logopts, fun(Os) -> [list_to_atom(O) || O <- Os] end, [], Args), + Verbosity = verbosity_args2opts(Args), MultTT = get_start_opt(multiply_timetraps, fun([MT]) -> list_to_integer(MT) end, 1, Args), ScaleTT = get_start_opt(scale_timetraps, @@ -187,8 +255,8 @@ script_start1(Parent, Args) -> CTHooks = ct_hooks_args2opts(Args), EnableBuiltinHooks = get_start_opt(enable_builtin_hooks, fun([CT]) -> list_to_atom(CT); - ([]) -> true - end, true, Args), + ([]) -> undefined + end, undefined, Args), %% check flags and set corresponding application env variables @@ -206,7 +274,7 @@ script_start1(Parent, Args) -> end end, %% no_auto_compile + include - IncludeDirs = + {AutoCompile,IncludeDirs} = case proplists:get_value(no_auto_compile, Args) of undefined -> application:set_env(common_test, auto_compile, true), @@ -222,16 +290,16 @@ script_start1(Parent, Args) -> case os:getenv("CT_INCLUDE_PATH") of false -> application:set_env(common_test, include, InclDirs), - InclDirs; + {undefined,InclDirs}; CtInclPath -> AllInclDirs = string:tokens(CtInclPath,[$:,$ ,$,]) ++ InclDirs, application:set_env(common_test, include, AllInclDirs), - AllInclDirs + {undefined,AllInclDirs} end; _ -> application:set_env(common_test, auto_compile, false), - [] + {false,[]} end, %% silent connections SilentConns = @@ -243,19 +311,24 @@ script_start1(Parent, Args) -> Stylesheet = get_start_opt(stylesheet, fun([SS]) -> ?abs(SS) end, Args), %% basic_html - used by ct_logs - case proplists:get_value(basic_html, Args) of - undefined -> - application:set_env(common_test, basic_html, false); - _ -> - application:set_env(common_test, basic_html, true) - end, + BasicHtml = case proplists:get_value(basic_html, Args) of + undefined -> + application:set_env(common_test, basic_html, false), + undefined; + _ -> + application:set_env(common_test, basic_html, true), + true + end, StartOpts = #opts{label = Label, profile = Profile, vts = Vts, shell = Shell, cover = Cover, logdir = LogDir, logopts = LogOpts, + basic_html = BasicHtml, + verbosity = Verbosity, event_handlers = EvHandlers, ct_hooks = CTHooks, enable_builtin_hooks = EnableBuiltinHooks, + auto_compile = AutoCompile, include = IncludeDirs, silent_connections = SilentConns, stylesheet = Stylesheet, @@ -325,9 +398,12 @@ script_start2(StartOpts = #opts{vts = undefined, AllLogOpts = merge_vals([StartOpts#opts.logopts, SpecStartOpts#opts.logopts]), - - Cover = choose_val(StartOpts#opts.cover, - SpecStartOpts#opts.cover), + AllVerbosity = + merge_keyvals([StartOpts#opts.verbosity, + SpecStartOpts#opts.verbosity]), + Cover = + choose_val(StartOpts#opts.cover, + SpecStartOpts#opts.cover), MultTT = choose_val(StartOpts#opts.multiply_timetraps, SpecStartOpts#opts.multiply_timetraps), @@ -352,9 +428,36 @@ script_start2(StartOpts = #opts{vts = undefined, StartOpts#opts.enable_builtin_hooks, SpecStartOpts#opts.enable_builtin_hooks), + Stylesheet = + choose_val(StartOpts#opts.stylesheet, + SpecStartOpts#opts.stylesheet), + AllInclude = merge_vals([StartOpts#opts.include, SpecStartOpts#opts.include]), application:set_env(common_test, include, AllInclude), + + AutoCompile = + case choose_val(StartOpts#opts.auto_compile, + SpecStartOpts#opts.auto_compile) of + undefined -> + true; + ACBool -> + application:set_env(common_test, + auto_compile, + ACBool), + ACBool + end, + + BasicHtml = + case choose_val(StartOpts#opts.basic_html, + SpecStartOpts#opts.basic_html) of + undefined -> + false; + BHBool -> + application:set_env(common_test, basic_html, + BHBool), + BHBool + end, {TS,StartOpts#opts{label = Label, profile = Profile, @@ -362,11 +465,15 @@ script_start2(StartOpts = #opts{vts = undefined, cover = Cover, logdir = LogDir, logopts = AllLogOpts, + basic_html = BasicHtml, + verbosity = AllVerbosity, config = SpecStartOpts#opts.config, event_handlers = AllEvHs, ct_hooks = AllCTHooks, enable_builtin_hooks = EnableBuiltinHooks, + stylesheet = Stylesheet, + auto_compile = AutoCompile, include = AllInclude, multiply_timetraps = MultTT, scale_timetraps = ScaleTT, @@ -519,6 +626,7 @@ script_start4(#opts{label = Label, profile = Profile, event_handlers = EvHandlers, ct_hooks = CTHooks, logopts = LogOpts, + verbosity = Verbosity, enable_builtin_hooks = EnableBuiltinHooks, logdir = LogDir, testspecs = Specs}, _Args) -> %% label - used by ct_logs @@ -536,7 +644,8 @@ script_start4(#opts{label = Label, profile = Profile, {ct_hooks, CTHooks}, {enable_builtin_hooks,EnableBuiltinHooks}]) of ok -> - ct_util:start(interactive, LogDir), + ct_util:start(interactive, LogDir, + add_verbosity_defaults(Verbosity)), ct_util:set_testdata({logopts, LogOpts}), log_ts_names(Specs), io:nl(), @@ -553,7 +662,7 @@ script_start4(#opts{vts = true, cover = Cover}, _) -> %% Add support later (maybe). io:format("\nCan't run cover in vts mode.\n\n", []) end, - erlang:halt(); + {error,no_cover_in_vts_mode}; script_start4(#opts{shell = true, cover = Cover}, _) -> case Cover of @@ -562,7 +671,8 @@ script_start4(#opts{shell = true, cover = Cover}, _) -> _ -> %% Add support later (maybe). io:format("\nCan't run cover in interactive mode.\n\n", []) - end; + end, + {error,no_cover_in_interactive_mode}; script_start4(Opts = #opts{tests = Tests}, Args) -> do_run(Tests, [], Opts, Args). @@ -579,6 +689,7 @@ script_usage() -> "\n\t[-dir TestDir1 TestDir2 .. TestDirN] |" "\n\t[-suite Suite [-case Case]]" "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" + "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]" "\n\t[-include InclDir1 InclDir2 .. InclDirN]" "\n\t[-no_auto_compile]" "\n\t[-multiply_timetraps N]" @@ -593,11 +704,12 @@ script_usage() -> "\n\t[-userconfig CallbackModule ConfigFile1 .. ConfigFileN]" "\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]" "\n\t[-logdir LogDir]" + "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" + "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]" "\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]" - "\n\t[-stylesheet CSSFile]" + "\n\t[-stylesheet CSSFile]" "\n\t[-cover CoverCfgFile]" "\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]" - "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]" "\n\t[-include InclDir1 InclDir2 .. InclDirN]" "\n\t[-no_auto_compile]" @@ -613,12 +725,13 @@ script_usage() -> "\n\t[-config ConfigFile1 ConfigFile2 .. ConfigFileN]" "\n\t[-decrypt_key Key] | [-decrypt_file KeyFile]" "\n\t[-logdir LogDir]" + "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" + "\n\t[-verbosity GenVLvl | [CategoryVLvl1 .. CategoryVLvlN]]" "\n\t[-allow_user_terms]" "\n\t[-silent_connections [ConnType1 ConnType2 .. ConnTypeN]]" "\n\t[-stylesheet CSSFile]" "\n\t[-cover CoverCfgFile]" "\n\t[-event_handler EvHandler1 EvHandler2 .. EvHandlerN]" - "\n\t[-logopts LogOpt1 LogOpt2 .. LogOptN]" "\n\t[-ct_hooks CTHook1 CTHook2 .. CTHookN]" "\n\t[-include InclDir1 InclDir2 .. InclDirN]" "\n\t[-no_auto_compile]" @@ -702,7 +815,7 @@ run_test(StartOpts) when is_list(StartOpts) -> Ref = monitor(process, CTPid), receive {'DOWN',Ref,process,CTPid,{user_error,Error}} -> - Error; + {error,Error}; {'DOWN',Ref,process,CTPid,Other} -> Other end. @@ -739,8 +852,10 @@ run_test2(StartOpts) -> (Lbl) when is_atom(Lbl) -> atom_to_list(Lbl) end, StartOpts), %% profile - Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) -> Prof; - (Prof) when is_atom(Prof) -> atom_to_list(Prof) + Profile = get_start_opt(profile, fun(Prof) when is_list(Prof) -> + Prof; + (Prof) when is_atom(Prof) -> + atom_to_list(Prof) end, StartOpts), %% logdir LogDir = get_start_opt(logdir, fun(LD) when is_list(LD) -> LD end, @@ -748,6 +863,19 @@ run_test2(StartOpts) -> %% logopts LogOpts = get_start_opt(logopts, value, [], StartOpts), + %% verbosity + Verbosity = + get_start_opt(verbosity, + fun(VLvls) when is_list(VLvls) -> + lists:map(fun(VLvl = {_Cat,_Lvl}) -> + VLvl; + (Lvl) -> + {'$unspecified',Lvl} + end, VLvls); + (VLvl) when is_integer(VLvl) -> + [{'$unspecified',VLvl}] + end, [], StartOpts), + %% config & userconfig CfgFiles = ct_config:get_config_file_list(StartOpts), @@ -782,7 +910,7 @@ run_test2(StartOpts) -> fun(EBH) when EBH == true; EBH == false -> EBH - end, true, StartOpts), + end, undefined, StartOpts), %% silent connections SilentConns = get_start_opt(silent_connections, @@ -805,7 +933,7 @@ run_test2(StartOpts) -> CreatePrivDir = get_start_opt(create_priv_dir, value, StartOpts), %% auto compile & include files - Include = + {AutoCompile,Include} = case proplists:get_value(auto_compile, StartOpts) of undefined -> application:set_env(common_test, auto_compile, true), @@ -821,16 +949,16 @@ run_test2(StartOpts) -> case os:getenv("CT_INCLUDE_PATH") of false -> application:set_env(common_test, include, InclDirs), - InclDirs; + {undefined,InclDirs}; CtInclPath -> InclDirs1 = string:tokens(CtInclPath, [$:,$ ,$,]), AllInclDirs = InclDirs1++InclDirs, application:set_env(common_test, include, AllInclDirs), - AllInclDirs + {undefined,AllInclDirs} end; ACBool -> application:set_env(common_test, auto_compile, ACBool), - [] + {ACBool,[]} end, %% decrypt config file @@ -844,11 +972,14 @@ run_test2(StartOpts) -> end, %% basic html - used by ct_logs - case proplists:get_value(basic_html, StartOpts) of - undefined -> - application:set_env(common_test, basic_html, false); - BasicHtmlBool -> - application:set_env(common_test, basic_html, BasicHtmlBool) + BasicHtml = + case proplists:get_value(basic_html, StartOpts) of + undefined -> + application:set_env(common_test, basic_html, false), + undefined; + BasicHtmlBool -> + application:set_env(common_test, basic_html, BasicHtmlBool), + BasicHtmlBool end, %% stepped execution @@ -856,10 +987,13 @@ run_test2(StartOpts) -> Opts = #opts{label = Label, profile = Profile, cover = Cover, step = Step, logdir = LogDir, - logopts = LogOpts, config = CfgFiles, + logopts = LogOpts, basic_html = BasicHtml, + config = CfgFiles, + verbosity = Verbosity, event_handlers = EvHandlers, ct_hooks = CTHooks, enable_builtin_hooks = EnableBuiltinHooks, + auto_compile = AutoCompile, include = Include, silent_connections = SilentConns, stylesheet = Stylesheet, @@ -894,7 +1028,7 @@ run_spec_file(Relaxed, log_ts_names(AbsSpecs), case catch ct_testspec:collect_tests_from_file(AbsSpecs, Relaxed) of {Error,CTReason} when Error == error ; Error == 'EXIT' -> - exit(CTReason); + exit({error,CTReason}); TS -> SpecOpts = get_data_for_node(TS, node()), Label = choose_val(Opts#opts.label, @@ -905,6 +1039,10 @@ run_spec_file(Relaxed, SpecOpts#opts.logdir), AllLogOpts = merge_vals([Opts#opts.logopts, SpecOpts#opts.logopts]), + Stylesheet = choose_val(Opts#opts.stylesheet, + SpecOpts#opts.stylesheet), + AllVerbosity = merge_keyvals([Opts#opts.verbosity, + SpecOpts#opts.verbosity]), AllConfig = merge_vals([CfgFiles, SpecOpts#opts.config]), Cover = choose_val(Opts#opts.cover, SpecOpts#opts.cover), @@ -918,21 +1056,44 @@ run_spec_file(Relaxed, SpecOpts#opts.event_handlers]), AllInclude = merge_vals([Opts#opts.include, SpecOpts#opts.include]), - AllCTHooks = merge_vals([Opts#opts.ct_hooks, - SpecOpts#opts.ct_hooks]), + SpecOpts#opts.ct_hooks]), EnableBuiltinHooks = choose_val(Opts#opts.enable_builtin_hooks, SpecOpts#opts.enable_builtin_hooks), application:set_env(common_test, include, AllInclude), + AutoCompile = case choose_val(Opts#opts.auto_compile, + SpecOpts#opts.auto_compile) of + undefined -> + true; + ACBool -> + application:set_env(common_test, auto_compile, + ACBool), + ACBool + end, + + BasicHtml = case choose_val(Opts#opts.basic_html, + SpecOpts#opts.basic_html) of + undefined -> + false; + BHBool -> + application:set_env(common_test, basic_html, + BHBool), + BHBool + end, + Opts1 = Opts#opts{label = Label, profile = Profile, cover = Cover, logdir = which(logdir, LogDir), logopts = AllLogOpts, + stylesheet = Stylesheet, + basic_html = BasicHtml, + verbosity = AllVerbosity, config = AllConfig, event_handlers = AllEvHs, + auto_compile = AutoCompile, include = AllInclude, testspecs = AbsSpecs, multiply_timetraps = MultTT, @@ -948,20 +1109,20 @@ run_spec_file(Relaxed, {Run,Skip} = ct_testspec:prepare_tests(TS, node()), reformat_result(catch do_run(Run, Skip, Opts1, StartOpts)); {error,GCFReason} -> - exit(GCFReason) + exit({error,GCFReason}) end end. run_prepared(Run, Skip, Opts = #opts{logdir = LogDir, - config = CfgFiles }, + config = CfgFiles}, StartOpts) -> LogDir1 = which(logdir, LogDir), case check_and_install_configfiles(CfgFiles, LogDir1, Opts) of ok -> reformat_result(catch do_run(Run, Skip, Opts#opts{logdir = LogDir1}, StartOpts)); - {error,Reason} -> - exit(Reason) + {error,_Reason} = Error -> + exit(Error) end. check_config_file(Callback, File)-> @@ -969,7 +1130,7 @@ check_config_file(Callback, File)-> false -> case code:load_file(Callback) of {module,_} -> ok; - {error,Why} -> exit({cant_load_callback_module,Why}) + {error,Why} -> exit({error,{cant_load_callback_module,Why}}) end; _ -> ok @@ -980,16 +1141,17 @@ check_config_file(Callback, File)-> {ok,{config,_}}-> File; {error,{wrong_config,Message}}-> - exit({wrong_config,{Callback,Message}}); + exit({error,{wrong_config,{Callback,Message}}}); {error,{nofile,File}}-> - exit({no_such_file,?abs(File)}) + exit({error,{no_such_file,?abs(File)}}) end. run_dir(Opts = #opts{logdir = LogDir, config = CfgFiles, event_handlers = EvHandlers, ct_hooks = CTHook, - enable_builtin_hooks = EnableBuiltinHooks }, StartOpts) -> + enable_builtin_hooks = EnableBuiltinHooks}, + StartOpts) -> LogDir1 = which(logdir, LogDir), Opts1 = Opts#opts{logdir = LogDir1}, AbsCfgFiles = @@ -1002,7 +1164,8 @@ run_dir(Opts = #opts{logdir = LogDir, {module,Callback}-> ok; {error,_}-> - exit({no_such_module,Callback}) + exit({error,{no_such_module, + Callback}}) end end, {Callback, @@ -1015,7 +1178,7 @@ run_dir(Opts = #opts{logdir = LogDir, {ct_hooks, CTHook}, {enable_builtin_hooks,EnableBuiltinHooks}], LogDir1) of ok -> ok; - {error,IReason} -> exit(IReason) + {error,_IReason} = IError -> exit(IError) end, case {proplists:get_value(dir, StartOpts), proplists:get_value(suite, StartOpts), @@ -1057,7 +1220,7 @@ run_dir(Opts = #opts{logdir = LogDir, [], Opts1, StartOpts)); {undefined,[Hd,_|_],_GsAndCs} when not is_integer(Hd) -> - exit(multiple_suites_and_cases); + exit({error,multiple_suites_and_cases}); {undefined,Suite=[Hd|Tl],GsAndCs} when is_integer(Hd) ; (is_list(Hd) and (Tl == [])) ; @@ -1067,10 +1230,10 @@ run_dir(Opts = #opts{logdir = LogDir, [], Opts1, StartOpts)); {[Hd,_|_],_Suites,[]} when is_list(Hd) ; not is_integer(Hd) -> - exit(multiple_dirs_and_suites); + exit({error,multiple_dirs_and_suites}); {undefined,undefined,GsAndCs} when GsAndCs /= [] -> - exit(incorrect_start_options); + exit({error,incorrect_start_options}); {Dir,Suite,GsAndCs} when is_integer(hd(Dir)) ; (is_atom(Dir) and (Dir /= undefined)) ; @@ -1079,7 +1242,7 @@ run_dir(Opts = #opts{logdir = LogDir, Dir1 = if is_atom(Dir) -> atom_to_list(Dir); true -> Dir end, if Suite == undefined -> - exit(incorrect_start_options); + exit({error,incorrect_start_options}); is_integer(hd(Suite)) ; (is_atom(Suite) and (Suite /= undefined)) ; @@ -1098,7 +1261,7 @@ run_dir(Opts = #opts{logdir = LogDir, is_list(Suite) -> % multiple suites case [suite_to_test(Dir1, S) || S <- Suite] of [_,_|_] when GsAndCs /= [] -> - exit(multiple_suites_and_cases); + exit({error,multiple_suites_and_cases}); [{Dir2,Mod}] when GsAndCs /= [] -> reformat_result(catch do_run(tests(Dir2, Mod, GsAndCs), [], Opts1, StartOpts)); @@ -1109,10 +1272,10 @@ run_dir(Opts = #opts{logdir = LogDir, end; {undefined,undefined,[]} -> - exit(no_test_specified); + exit({error,no_test_specified}); {Dir,Suite,GsAndCs} -> - exit({incorrect_start_options,{Dir,Suite,GsAndCs}}) + exit({error,{incorrect_start_options,{Dir,Suite,GsAndCs}}}) end. %%%----------------------------------------------------------------- @@ -1157,7 +1320,7 @@ run_testspec2(File) when is_list(File), is_integer(hd(File)) -> run_testspec2(TestSpec) -> case catch ct_testspec:collect_tests_from_list(TestSpec, false) of {E,CTReason} when E == error ; E == 'EXIT' -> - exit(CTReason); + exit({error,CTReason}); TS -> Opts = get_data_for_node(TS, node()), @@ -1179,8 +1342,8 @@ run_testspec2(TestSpec) -> include = AllInclude}, {Run,Skip} = ct_testspec:prepare_tests(TS, node()), reformat_result(catch do_run(Run, Skip, Opts1, [])); - {error,GCFReason} -> - exit(GCFReason) + {error,_GCFReason} = GCFError -> + exit(GCFError) end end. @@ -1188,12 +1351,16 @@ get_data_for_node(#testspec{label = Labels, profile = Profiles, logdir = LogDirs, logopts = LogOptsList, + basic_html = BHs, + stylesheet = SSs, + verbosity = VLvls, cover = CoverFs, config = Cfgs, userconfig = UsrCfgs, event_handler = EvHs, ct_hooks = CTHooks, enable_builtin_hooks = EnableBuiltinHooks, + auto_compile = ACs, include = Incl, multiply_timetraps = MTs, scale_timetraps = STs, @@ -1208,6 +1375,12 @@ get_data_for_node(#testspec{label = Labels, undefined -> []; LOs -> LOs end, + BasicHtml = proplists:get_value(Node, BHs), + Stylesheet = proplists:get_value(Node, SSs), + Verbosity = case proplists:get_value(Node, VLvls) of + undefined -> []; + Lvls -> Lvls + end, Cover = proplists:get_value(Node, CoverFs), MT = proplists:get_value(Node, MTs), ST = proplists:get_value(Node, STs), @@ -1216,16 +1389,21 @@ get_data_for_node(#testspec{label = Labels, [CBF || {N,CBF} <- UsrCfgs, N==Node], EvHandlers = [{H,A} || {N,H,A} <- EvHs, N==Node], FiltCTHooks = [Hook || {N,Hook} <- CTHooks, N==Node], + AutoCompile = proplists:get_value(Node, ACs), Include = [I || {N,I} <- Incl, N==Node], #opts{label = Label, profile = Profile, logdir = LogDir, logopts = LogOpts, + basic_html = BasicHtml, + stylesheet = Stylesheet, + verbosity = Verbosity, cover = Cover, config = ConfigFiles, event_handlers = EvHandlers, ct_hooks = FiltCTHooks, enable_builtin_hooks = EnableBuiltinHooks, + auto_compile = AutoCompile, include = Include, multiply_timetraps = MT, scale_timetraps = ST, @@ -1267,6 +1445,14 @@ choose_val(V0, _V1) -> merge_vals(Vs) -> lists:append(Vs). +merge_keyvals(Vs) -> + make_unique(lists:append(Vs)). + +make_unique([Elem={Key,_} | Elems]) -> + [Elem | make_unique(proplists:delete(Key, Elems))]; +make_unique([]) -> + []. + listify([C|_]=Str) when is_integer(C) -> [Str]; listify(L) when is_list(L) -> L; listify(E) -> [E]. @@ -1376,7 +1562,8 @@ do_run(Tests, Misc, LogDir, LogOpts) when is_list(Misc), do_run(Tests, [], Opts1#opts{logdir = LogDir}, []); do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> - #opts{label = Label, profile = Profile, cover = Cover} = Opts, + #opts{label = Label, profile = Profile, cover = Cover, + verbosity = VLvls} = Opts, %% label - used by ct_logs TestLabel = if Label == undefined -> undefined; @@ -1397,7 +1584,7 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> case code:which(test_server) of non_existing -> - exit({error,no_path_to_test_server}); + {error,no_path_to_test_server}; _ -> Opts1 = if Cover == undefined -> Opts; @@ -1413,81 +1600,131 @@ do_run(Tests, Skip, Opts, Args) when is_record(Opts, opts) -> %% which framework it runs under. case os:getenv("TEST_SERVER_FRAMEWORK") of false -> - os:putenv("TEST_SERVER_FRAMEWORK", "ct_framework"); + os:putenv("TEST_SERVER_FRAMEWORK", "ct_framework"), + os:putenv("TEST_SERVER_FRAMEWORK_NAME", "common_test"); "ct_framework" -> ok; Other -> - erlang:display(list_to_atom("Note: TEST_SERVER_FRAMEWORK = " ++ Other)) + erlang:display( + list_to_atom( + "Note: TEST_SERVER_FRAMEWORK = " ++ Other)) end, - case ct_util:start(Opts#opts.logdir) of + Verbosity = add_verbosity_defaults(VLvls), + case ct_util:start(Opts#opts.logdir, Verbosity) of {error,interactive_mode} -> io:format("CT is started in interactive mode. " - "To exit this mode, run ct:stop_interactive().\n" + "To exit this mode, " + "run ct:stop_interactive().\n" "To enter the interactive mode again, " "run ct:start_interactive()\n\n",[]), {error,interactive_mode}; _Pid -> - %% save stylesheet info - ct_util:set_testdata({stylesheet,Opts#opts.stylesheet}), - %% save logopts - ct_util:set_testdata({logopts,Opts#opts.logopts}), - %% enable silent connections - case Opts#opts.silent_connections of - [] -> - Conns = ct_util:override_silence_all_connections(), - ct_logs:log("Silent connections", "~p", [Conns]); - Conns when is_list(Conns) -> - ct_util:override_silence_connections(Conns), - ct_logs:log("Silent connections", "~p", [Conns]); - _ -> - ok - end, - log_ts_names(Opts1#opts.testspecs), - TestSuites = suite_tuples(Tests), - - {_TestSuites1,SuiteMakeErrors,AllMakeErrors} = - case application:get_env(common_test, auto_compile) of - {ok,false} -> - {TestSuites1,SuitesNotFound} = - verify_suites(TestSuites), - {TestSuites1,SuitesNotFound,SuitesNotFound}; - _ -> - {SuiteErrs,HelpErrs} = auto_compile(TestSuites), - {TestSuites,SuiteErrs,SuiteErrs++HelpErrs} - end, + compile_and_run(Tests, Skip, + Opts1#opts{verbosity=Verbosity}, Args) + end + end. - case continue(AllMakeErrors) of - true -> - SavedErrors = save_make_errors(SuiteMakeErrors), - ct_repeat:log_loop_info(Args), +compile_and_run(Tests, Skip, Opts, Args) -> + %% save stylesheet info + ct_util:set_testdata({stylesheet,Opts#opts.stylesheet}), + %% save logopts + ct_util:set_testdata({logopts,Opts#opts.logopts}), + %% enable silent connections + case Opts#opts.silent_connections of + [] -> + Conns = ct_util:override_silence_all_connections(), + ct_logs:log("Silent connections", "~p", [Conns]); + Conns when is_list(Conns) -> + ct_util:override_silence_connections(Conns), + ct_logs:log("Silent connections", "~p", [Conns]); + _ -> + ok + end, + log_ts_names(Opts#opts.testspecs), + TestSuites = suite_tuples(Tests), + + {_TestSuites1,SuiteMakeErrors,AllMakeErrors} = + case application:get_env(common_test, auto_compile) of + {ok,false} -> + {TestSuites1,SuitesNotFound} = + verify_suites(TestSuites), + {TestSuites1,SuitesNotFound,SuitesNotFound}; + _ -> + {SuiteErrs,HelpErrs} = auto_compile(TestSuites), + {TestSuites,SuiteErrs,SuiteErrs++HelpErrs} + end, + + case continue(AllMakeErrors) of + true -> + SavedErrors = save_make_errors(SuiteMakeErrors), + ct_repeat:log_loop_info(Args), + + {Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors), + + possibly_spawn(true == proplists:get_value(noinput, Args), + Tests1, Skip1, Opts); + false -> + io:nl(), + ct_util:stop(clean), + BadMods = + lists:foldr( + fun({{_,_},Ms}, Acc) -> + Ms ++ lists:foldl( + fun(M, Acc1) -> + lists:delete(M, Acc1) + end, Acc, Ms) + end, [], AllMakeErrors), + {error,{make_failed,BadMods}} + end. - {Tests1,Skip1} = final_tests(Tests,Skip,SavedErrors), +%% keep the shell as the top controlling process +possibly_spawn(false, Tests, Skip, Opts) -> + TestResult = (catch do_run_test(Tests, Skip, Opts)), + case TestResult of + {EType,_} = Error when EType == user_error; + EType == error -> + ct_util:stop(clean), + exit(Error); + _ -> + ct_util:stop(normal), + TestResult + end; - R = (catch do_run_test(Tests1, Skip1, Opts1)), - case R of - {EType,_} = Error when EType == user_error ; +%% we must return control to the shell now, so we spawn +%% a test supervisor process to keep an eye on the test run +possibly_spawn(true, Tests, Skip, Opts) -> + CTUtilSrv = whereis(ct_util_server), + Supervisor = + fun() -> + process_flag(trap_exit, true), + link(CTUtilSrv), + TestRun = + fun() -> + TestResult = (catch do_run_test(Tests, Skip, Opts)), + case TestResult of + {EType,_} = Error when EType == user_error; EType == error -> ct_util:stop(clean), exit(Error); _ -> ct_util:stop(normal), - R - end; - false -> - io:nl(), - ct_util:stop(clean), - BadMods = - lists:foldr( - fun({{_,_},Ms}, Acc) -> - Ms ++ lists:foldl( - fun(M, Acc1) -> - lists:delete(M, Acc1) - end, Acc, Ms) - end, [], AllMakeErrors), - {error,{make_failed,BadMods}} - end - end - end. + exit({ok,TestResult}) + end + end, + TestRunPid = spawn_link(TestRun), + receive + {'EXIT',TestRunPid,{ok,TestResult}} -> + io:format(user, "~nCommon Test returned ~p~n~n", + [TestResult]); + {'EXIT',TestRunPid,Error} -> + exit(Error) + end + end, + unlink(CTUtilSrv), + SupPid = spawn(Supervisor), + io:format(user, "~nTest control handed over to process ~p~n~n", + [SupPid]), + SupPid. %% attempt to compile the modules specified in TestSuites auto_compile(TestSuites) -> @@ -1503,11 +1740,13 @@ auto_compile(TestSuites) -> end, SuiteMakeErrors = lists:flatmap(fun({TestDir,Suite} = TS) -> - case run_make(suites, TestDir, Suite, UserInclude) of + case run_make(suites, TestDir, + Suite, UserInclude) of {error,{make_failed,Bad}} -> [{TS,Bad}]; {error,_} -> - [{TS,[filename:join(TestDir,"*_SUITE")]}]; + [{TS,[filename:join(TestDir, + "*_SUITE")]}]; _ -> [] end @@ -1546,23 +1785,29 @@ verify_suites(TestSuites) -> {[DS|Found],NotFound}; true -> Beam = filename:join(TestDir, - atom_to_list(Suite)++".beam"), + atom_to_list(Suite)++ + ".beam"), case filelib:is_regular(Beam) of true -> {[DS|Found],NotFound}; false -> case code:is_loaded(Suite) of {file,SuiteFile} -> - %% test suite is already loaded and - %% since auto_compile == false, + %% test suite is already + %% loaded and since + %% auto_compile == false, %% let's assume the user has - %% loaded the beam file explicitly - ActualDir = filename:dirname(SuiteFile), - {[{ActualDir,Suite}|Found],NotFound}; + %% loaded the beam file + %% explicitly + ActualDir = + filename:dirname(SuiteFile), + {[{ActualDir,Suite}|Found], + NotFound}; false -> Name = filename:join(TestDir, - atom_to_list(Suite)), + atom_to_list( + Suite)), io:format(user, "Suite ~w not found" "in directory ~s~n", @@ -1580,7 +1825,8 @@ verify_suites(TestSuites) -> ActualDir = filename:dirname(SuiteFile), {[{ActualDir,Suite}|Found],NotFound}; false -> - io:format(user, "Directory ~s is invalid~n", [Dir]), + io:format(user, "Directory ~s is " + "invalid~n", [Dir]), Name = filename:join(Dir, atom_to_list(Suite)), {Found,[{DS,[Name]}|NotFound]} end @@ -1594,7 +1840,8 @@ save_make_errors([]) -> save_make_errors(Errors) -> Suites = get_bad_suites(Errors,[]), ct_logs:log("MAKE RESULTS", - "Error compiling or locating the following suites: ~n~p",[Suites]), + "Error compiling or locating the " + "following suites: ~n~p",[Suites]), %% save the info for logger file:write_file(?missing_suites_info,term_to_binary(Errors)), Errors. @@ -1615,8 +1862,9 @@ step(TestDir, Suite, Case) -> %%%----------------------------------------------------------------- %%% @hidden %%% @equiv ct:step/4 -step(TestDir, Suite, Case, Opts) when is_list(TestDir), is_atom(Suite), is_atom(Case), - Suite =/= all, Case =/= all -> +step(TestDir, Suite, Case, Opts) when is_list(TestDir), + is_atom(Suite), is_atom(Case), + Suite =/= all, Case =/= all -> do_run([{TestDir,Suite,Case}], [{step,Opts}]). @@ -1734,9 +1982,11 @@ continue(_MakeErrors) -> case set_group_leader_same_as_shell() of true -> S = self(), - io:format("Failed to compile or locate one or more test suites\n" + io:format("Failed to compile or locate one " + "or more test suites\n" "Press \'c\' to continue or \'a\' to abort.\n" - "Will continue in 15 seconds if no answer is given!\n"), + "Will continue in 15 seconds if no " + "answer is given!\n"), Pid = spawn(fun() -> case io:get_line('(c/a) ') of "c\n" -> @@ -1768,7 +2018,8 @@ set_group_leader_same_as_shell() -> end end, case [P || P <- processes(), GS2or3(P), - true == lists:keymember(shell,1,element(2,process_info(P,dictionary)))] of + true == lists:keymember(shell,1, + element(2,process_info(P,dictionary)))] of [GL|_] -> group_leader(GL, self()); [] -> @@ -1814,12 +2065,14 @@ do_run_test(Tests, Skip, Opts) -> incl_mods = CovIncl, cross = CovCross, src = _CovSrc}} -> - ct_logs:log("COVER INFO","Using cover specification file: ~s~n" + ct_logs:log("COVER INFO", + "Using cover specification file: ~s~n" "App: ~w~n" "Cross cover: ~w~n" "Including ~w modules~n" "Excluding ~w modules", - [CovFile,CovApp,CovCross,length(CovIncl),length(CovExcl)]), + [CovFile,CovApp,CovCross, + length(CovIncl),length(CovExcl)]), %% cover export file will be used for export and import %% between tests so make sure it doesn't exist initially @@ -1827,7 +2080,8 @@ do_run_test(Tests, Skip, Opts) -> true -> DelResult = file:delete(CovExport), ct_logs:log("COVER INFO", - "Warning! Export file ~s already exists. " + "Warning! " + "Export file ~s already exists. " "Deleting with result: ~p", [CovExport,DelResult]); false -> @@ -1843,7 +2097,8 @@ do_run_test(Tests, Skip, Opts) -> %% start cover on specified nodes if (CovNodes /= []) and (CovNodes /= undefined) -> ct_logs:log("COVER INFO", - "Nodes included in cover session: ~w", + "Nodes included in cover " + "session: ~w", [CovNodes]), cover:start(CovNodes); true -> @@ -1868,17 +2123,27 @@ do_run_test(Tests, Skip, Opts) -> ct_logs:log("TEST INFO","~w test(s), ~w suite(s)", [NoOfTests,NoOfSuites]); true -> - io:format("~nTEST INFO: ~w test(s), ~w case(s) in ~w suite(s)~n~n", + io:format("~nTEST INFO: ~w test(s), ~w case(s) " + "in ~w suite(s)~n~n", [NoOfTests,NoOfCases,NoOfSuites]), - ct_logs:log("TEST INFO","~w test(s), ~w case(s) in ~w suite(s)", + ct_logs:log("TEST INFO","~w test(s), ~w case(s) " + "in ~w suite(s)", [NoOfTests,NoOfCases,NoOfSuites]) end, - + %% if the verbosity level is set lower than ?STD_IMPORTANCE, tell + %% test_server to ignore stdout printouts to the test case log file + case proplists:get_value(default, Opts#opts.verbosity) of + VLvl when is_integer(VLvl), (?STD_IMPORTANCE < (100-VLvl)) -> + test_server_ctrl:reject_io_reqs(true); + _Lower -> + ok + end, test_server_ctrl:multiply_timetraps(Opts#opts.multiply_timetraps), test_server_ctrl:scale_timetraps(Opts#opts.scale_timetraps), - test_server_ctrl:create_priv_dir(choose_val(Opts#opts.create_priv_dir, - auto_per_run)), + test_server_ctrl:create_priv_dir(choose_val( + Opts#opts.create_priv_dir, + auto_per_run)), ct_event:notify(#event{name=start_info, node=node(), data={NoOfTests,NoOfSuites,NoOfCases}}), @@ -1897,9 +2162,15 @@ do_run_test(Tests, Skip, Opts) -> maybe_cleanup_interpret(Suite, Opts#opts.step) end, CleanUp), [code:del_path(Dir) || Dir <- AddedToPath], - ok; + + case ct_util:get_testdata(stats) of + Stats = {_Ok,_Failed,{_UserSkipped,_AutoSkipped}} -> + Stats; + _ -> + {error,test_result_unknown} + end; Error -> - Error + exit(Error) end. delete_dups([S | Suites]) -> @@ -2328,7 +2599,7 @@ ct_hooks_args2opts(Args) -> Acc end,[],Args). -ct_hooks_args2opts([CTH,Arg,Prio,"and"| Rest],Acc) -> +ct_hooks_args2opts([CTH,Arg,Prio,"and"| Rest],Acc) when Arg /= "and" -> ct_hooks_args2opts(Rest,[{list_to_atom(CTH), parse_cth_args(Arg), parse_cth_args(Prio)}|Acc]); @@ -2356,7 +2627,6 @@ parse_cth_args(String) -> String end. - event_handler_args2opts(Args) -> case proplists:get_value(event_handler, Args) of undefined -> @@ -2379,6 +2649,42 @@ event_handler_init_args2opts([EH, Arg]) -> event_handler_init_args2opts([]) -> []. +verbosity_args2opts(Args) -> + case proplists:get_value(verbosity, Args) of + undefined -> + []; + VArgs -> + GetVLvls = + fun("and", {new,SoFar}) when is_list(SoFar) -> + {new,SoFar}; + ("and", {Lvl,SoFar}) when is_list(SoFar) -> + {new,[{'$unspecified',list_to_integer(Lvl)} | SoFar]}; + (CatOrLvl, {new,SoFar}) when is_list(SoFar) -> + {CatOrLvl,SoFar}; + (Lvl, {Cat,SoFar}) -> + {new,[{list_to_atom(Cat),list_to_integer(Lvl)} | SoFar]} + end, + case lists:foldl(GetVLvls, {new,[]}, VArgs) of + {new,Parsed} -> + Parsed; + {Lvl,Parsed} -> + [{'$unspecified',list_to_integer(Lvl)} | Parsed] + end + end. + +add_verbosity_defaults(VLvls) -> + case {proplists:get_value('$unspecified', VLvls), + proplists:get_value(default, VLvls)} of + {undefined,undefined} -> + ?default_verbosity ++ VLvls; + {Lvl,undefined} -> + [{default,Lvl} | VLvls]; + {undefined,_Lvl} -> + [{'$unspecified',?MAX_VERBOSITY} | VLvls]; + _ -> + VLvls + end. + %% This function reads pa and pz arguments, converts dirs from relative %% to absolute, and re-inserts them in the code path. The order of the %% dirs in the code path remain the same. Note however that since this @@ -2445,7 +2751,11 @@ make_abs1([], Path) -> %% to ct_run start arguments (on the init arguments format) - %% this is useful mainly for testing the ct_run start functions. opts2args(EnvStartOpts) -> - lists:flatmap(fun({config,CfgFiles}) -> + lists:flatmap(fun({exit_status,ExitStatusOpt}) when is_atom(ExitStatusOpt) -> + [{exit_status,[atom_to_list(ExitStatusOpt)]}]; + ({halt_with,{HaltM,HaltF}}) -> + [{halt_with,[atom_to_list(HaltM),atom_to_list(HaltF)]}]; + ({config,CfgFiles}) -> [{ct_config,[CfgFiles]}]; ({userconfig,{CBM,CfgStr=[X|_]}}) when is_integer(X) -> [{userconfig,[atom_to_list(CBM),CfgStr]}]; @@ -2453,10 +2763,14 @@ opts2args(EnvStartOpts) -> [{userconfig,[atom_to_list(CBM) | CfgStrs]}]; ({userconfig,UserCfg}) when is_list(UserCfg) -> Strs = - lists:map(fun({CBM,CfgStr=[X|_]}) when is_integer(X) -> - [atom_to_list(CBM),CfgStr,"and"]; - ({CBM,CfgStrs}) when is_list(CfgStrs) -> - [atom_to_list(CBM) | CfgStrs] ++ ["and"] + lists:map(fun({CBM,CfgStr=[X|_]}) + when is_integer(X) -> + [atom_to_list(CBM), + CfgStr,"and"]; + ({CBM,CfgStrs}) + when is_list(CfgStrs) -> + [atom_to_list(CBM) | CfgStrs] ++ + ["and"] end, UserCfg), [_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)), [{userconfig,lists:reverse(StrsR)}]; @@ -2491,7 +2805,7 @@ opts2args(EnvStartOpts) -> ({decrypt,{file,File}}) -> [{ct_decrypt_file,[File]}]; ({basic_html,true}) -> - ({basic_html,[]}); + [{basic_html,[]}]; ({basic_html,false}) -> []; ({event_handler,EH}) when is_atom(EH) -> @@ -2504,12 +2818,32 @@ opts2args(EnvStartOpts) -> ({event_handler,{EHs,Arg}}) when is_list(EHs) -> ArgStr = lists:flatten(io_lib:format("~p", [Arg])), Strs = lists:map(fun(EH) -> - [atom_to_list(EH),ArgStr,"and"] + [atom_to_list(EH), + ArgStr,"and"] end, EHs), [_LastAnd|StrsR] = lists:reverse(lists:flatten(Strs)), [{event_handler_init,lists:reverse(StrsR)}]; ({logopts,LOs}) when is_list(LOs) -> [{logopts,[atom_to_list(LO) || LO <- LOs]}]; + ({verbosity,?default_verbosity}) -> + []; + ({verbosity,VLvl}) when is_integer(VLvl) -> + [{verbosity,[integer_to_list(VLvl)]}]; + ({verbosity,VLvls}) when is_list(VLvls) -> + VLvlArgs = + lists:flatmap(fun({'$unspecified',Lvl}) -> + [integer_to_list(Lvl), + "and"]; + ({Cat,Lvl}) -> + [atom_to_list(Cat), + integer_to_list(Lvl), + "and"]; + (Lvl) -> + [integer_to_list(Lvl), + "and"] + end, VLvls), + [_LastAnd|VLvlArgsR] = lists:reverse(VLvlArgs), + [{verbosity,lists:reverse(VLvlArgsR)}]; ({ct_hooks,[]}) -> []; ({ct_hooks,CTHs}) when is_list(CTHs) -> diff --git a/lib/common_test/src/ct_ssh.erl b/lib/common_test/src/ct_ssh.erl index aebb28bc42..09cd4ef02a 100644 --- a/lib/common_test/src/ct_ssh.erl +++ b/lib/common_test/src/ct_ssh.erl @@ -182,19 +182,22 @@ connect(KeyOrName, ConnType, ExtraOpts) -> undefined -> {ssh,undefined,AllOpts}; SFTPAddr -> - log(heading(connect,KeyOrName), - "Note: Opening ssh connection to sftp host.\n", + try_log(heading(connect,KeyOrName), + "Note: Opening ssh connection " + "to sftp host.\n", []), {ssh,SFTPAddr, - [{ssh,SFTPAddr}|proplists:delete(sftp, AllOpts)]} + [{ssh,SFTPAddr} | + proplists:delete(sftp, AllOpts)]} end; undefined when ConnType == sftp -> case proplists:get_value(ssh, AllOpts) of undefined -> {sftp,undefined,AllOpts}; SSHAddr -> - log(heading(connect,KeyOrName), - "Note: Opening sftp connection to ssh host.\n", + try_log(heading(connect,KeyOrName), + "Note: Opening sftp connection " + "to ssh host.\n", []), {sftp,SSHAddr, [{sftp,SSHAddr}|proplists:delete(ssh, AllOpts)]} @@ -209,15 +212,15 @@ connect(KeyOrName, ConnType, ExtraOpts) -> [{not_available,{KeyOrName,ConnType1}}]), {error,{not_available,{KeyOrName,ConnType1}}}; {_,undefined} -> - log(heading(connect,KeyOrName), - "Opening ~w connection to ~p:22\n", - [ConnType1,Addr]), + try_log(heading(connect,KeyOrName), + "Opening ~w connection to ~p:22\n", + [ConnType1,Addr]), ct_gen_conn:start(KeyOrName, {ConnType1,Addr,22}, AllOpts1, ?MODULE); {_,Port} -> - log(heading(connect,KeyOrName), - "Opening ~w connection to ~p:~w\n", - [ConnType1,Addr,Port]), + try_log(heading(connect,KeyOrName), + "Opening ~w connection to ~p:~w\n", + [ConnType1,Addr,Port]), ct_gen_conn:start(KeyOrName, {ConnType1,Addr,Port}, AllOpts1, ?MODULE) end @@ -232,7 +235,7 @@ connect(KeyOrName, ConnType, ExtraOpts) -> disconnect(SSH) -> case get_handle(SSH) of {ok,Pid} -> - log(heading(disconnect,SSH), "Handle: ~p", [Pid]), + try_log(heading(disconnect,SSH), "Handle: ~p", [Pid], 5000), case ct_gen_conn:stop(Pid) of {error,{process_down,Pid,noproc}} -> {error,already_closed}; @@ -968,8 +971,9 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) -> Error; Ok -> SSHRef = element(2, Ok), - log(heading(init,KeyOrName), - "Opened ~w connection:\nHost: ~p (~p)\nUser: ~p\nPassword: ~p\n", + try_log(heading(init,KeyOrName), + "Opened ~w connection:\n" + "Host: ~p (~p)\nUser: ~p\nPassword: ~p\n", [ConnType,Addr,Port,User,lists:duplicate(length(Password),$*)]), {ok,SSHRef,#state{ssh_ref=SSHRef, conn_type=ConnType, target=KeyOrName}} @@ -978,25 +982,26 @@ init(KeyOrName, {ConnType,Addr,Port}, AllOpts) -> %% @hidden handle_msg(sftp_connect, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(sftp_connect,Target), "SSH Ref: ~p", [SSHRef]), + try_log(heading(sftp_connect,Target), "SSH Ref: ~p", [SSHRef]), {ssh_sftp:start_channel(SSHRef),State}; handle_msg({session_open,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(session_open,Target), "SSH Ref: ~p, Timeout: ~p", [SSHRef,TO]), + try_log(heading(session_open,Target), "SSH Ref: ~p, Timeout: ~p", + [SSHRef,TO]), {ssh_connection:session_channel(SSHRef, TO),State}; handle_msg({session_close,Chn}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(session_close,Target), "SSH Ref: ~p, Chn: ~p", [SSHRef,Chn]), + try_log(heading(session_close,Target), "SSH Ref: ~p, Chn: ~p", [SSHRef,Chn]), {ssh_connection:close(SSHRef, Chn),State}; handle_msg({exec,Chn,Command,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, Chn1 = if Chn == undefined -> - log(heading(exec,Target), - "Opening channel for exec, SSH Ref: ~p", [SSHRef]), + try_log(heading(exec,Target), + "Opening channel for exec, SSH Ref: ~p", [SSHRef]), case ssh_connection:session_channel(SSHRef, TO) of {ok,C} -> C; CErr -> CErr @@ -1009,9 +1014,9 @@ handle_msg({exec,Chn,Command,TO}, State) -> log(heading(exec,Target), "Opening channel failed: ~p", [ChnError]), {ChnError,State}; _ -> - log(heading(exec,Target), - "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p", - [SSHRef,Chn1,Command,TO]), + try_log(heading(exec,Target), + "SSH Ref: ~p, Chn: ~p, Command: ~p, Timeout: ~p", + [SSHRef,Chn1,Command,TO]), case ssh_connection:exec(SSHRef, Chn1, Command, TO) of success -> Result = do_recv_response(SSHRef, Chn1, [], close, TO), @@ -1024,24 +1029,24 @@ handle_msg({exec,Chn,Command,TO}, State) -> handle_msg({receive_response,Chn,End,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(receive_response,Target), - "SSH Ref: ~p, Chn: ~p, Timeout: ~p", [SSHRef,Chn,TO]), + try_log(heading(receive_response,Target), + "SSH Ref: ~p, Chn: ~p, Timeout: ~p", [SSHRef,Chn,TO]), Result = do_recv_response(SSHRef, Chn, [], End, TO), {Result,State}; handle_msg({send,Chn,Type,Data,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(send,Target), - "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" - "Data: ~p", [SSHRef,Chn,Type,TO,Data]), + try_log(heading(send,Target), + "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" + "Data: ~p", [SSHRef,Chn,Type,TO,Data]), Result = ssh_connection:send(SSHRef, Chn, Type, Data, TO), {Result,State}; handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(send_and_receive,Target), - "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" - "Data: ~p", [SSHRef,Chn,Type,TO,Data]), + try_log(heading(send_and_receive,Target), + "SSH Ref: ~p, Chn: ~p, Type: ~p, Timeout: ~p~n" + "Data: ~p", [SSHRef,Chn,Type,TO,Data]), case ssh_connection:send(SSHRef, Chn, Type, Data, TO) of ok -> Result = do_recv_response(SSHRef, Chn, [], End, TO), @@ -1052,137 +1057,162 @@ handle_msg({send_and_receive,Chn,Type,Data,End,TO}, State) -> handle_msg({subsystem,Chn,Subsystem,TO}, State) -> #state{ssh_ref=SSHRef, target=Target} = State, - log(heading(subsystem,Target), - "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p", - [SSHRef,Chn,Subsystem,TO]), + try_log(heading(subsystem,Target), + "SSH Ref: ~p, Chn: ~p, Subsys: ~p, Timeout: ~p", + [SSHRef,Chn,Subsystem,TO]), Result = ssh_connection:subsystem(SSHRef, Chn, Subsystem, TO), {Result,State}; %% --- SFTP Commands --- handle_msg({read_file,Srv,File}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read_file(ref(Srv,SSHRef), File),S}; handle_msg({write_file,Srv,File,Iolist}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:write_file(ref(Srv,SSHRef), File, Iolist),S}; handle_msg({list_dir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:list_dir(ref(Srv,SSHRef), Path),S}; handle_msg({open,Srv,File,Mode}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:open(ref(Srv,SSHRef), File, Mode),S}; handle_msg({opendir,Srv,Path}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:opendir(ref(Srv,SSHRef), Path),S}; handle_msg({close,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:close(ref(Srv,SSHRef), Handle),S}; handle_msg({read,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read(ref(Srv,SSHRef), Handle, Len),S}; handle_msg({pread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:pread(ref(Srv,SSHRef),Handle,Position,Length),S}; handle_msg({aread,Srv,Handle,Len}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:aread(ref(Srv,SSHRef), Handle, Len),S}; handle_msg({apread,Srv,Handle,Position,Length}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:apread(ref(Srv,SSHRef), Handle, Position, Length),S}; handle_msg({write,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:write(ref(Srv,SSHRef), Handle, Data),S}; handle_msg({pwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:pwrite(ref(Srv,SSHRef), Handle, Position, Data),S}; handle_msg({awrite,Srv,Handle,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:awrite(ref(Srv,SSHRef), Handle, Data),S}; handle_msg({apwrite,Srv,Handle,Position,Data}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:apwrite(ref(Srv,SSHRef), Handle, Position, Data),S}; handle_msg({position,Srv,Handle,Location}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:position(ref(Srv,SSHRef), Handle, Location),S}; handle_msg({read_file_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read_file_info(ref(Srv,SSHRef), Name),S}; handle_msg({get_file_info,Srv,Handle}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:get_file_info(ref(Srv,SSHRef), Handle),S}; handle_msg({read_link_info,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read_link_info(ref(Srv,SSHRef), Name),S}; handle_msg({write_file_info,Srv,Name,Info}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:write_file_info(ref(Srv,SSHRef), Name, Info),S}; handle_msg({read_link,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:read_link(ref(Srv,SSHRef), Name),S}; handle_msg({make_symlink,Srv,Name,Target}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:make_symlink(ref(Srv,SSHRef), Name, Target),S}; handle_msg({rename,Srv,OldName,NewName}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:rename(ref(Srv,SSHRef), OldName, NewName),S}; handle_msg({delete,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:delete(ref(Srv,SSHRef), Name),S}; handle_msg({make_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:make_dir(ref(Srv,SSHRef), Name),S}; handle_msg({del_dir,Srv,Name}=Cmd, S=#state{ssh_ref=SSHRef}) -> - log(heading(sftp,S#state.target), - "SSH Ref: ~p, Server: ~p~nCmd: ~p", [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), + try_log(heading(sftp,S#state.target), + "SSH Ref: ~p, Server: ~p~nCmd: ~p", + [SSHRef,ref(Srv,SSHRef),mod(Cmd)]), {ssh_sftp:del_dir(ref(Srv,SSHRef), Name),S}. %% @hidden @@ -1197,12 +1227,12 @@ close(SSHRef) -> terminate(SSHRef, State) -> case State#state.conn_type of ssh -> - log(heading(disconnect_ssh,State#state.target), - "SSH Ref: ~p",[SSHRef]), + try_log(heading(disconnect_ssh,State#state.target), + "SSH Ref: ~p",[SSHRef], 5000), ssh:close(SSHRef); sftp -> - log(heading(disconnect_sftp,State#state.target), - "SFTP Ref: ~p",[SSHRef]), + try_log(heading(disconnect_sftp,State#state.target), + "SFTP Ref: ~p",[SSHRef], 5000), ssh_sftp:stop_channel(SSHRef) end. @@ -1217,7 +1247,7 @@ do_recv_response(SSH, Chn, Data, End, Timeout) -> {ssh_cm, SSH, {open,Chn,RemoteChn,{session}}} -> debug("RECVD open"), {ok,{open,Chn,RemoteChn,{session}}}; - + {ssh_cm, SSH, {closed,Chn}} -> ssh_connection:close(SSH, Chn), debug("CLSD~n~p ~p", [SSH,Chn]), @@ -1245,38 +1275,38 @@ do_recv_response(SSH, Chn, Data, End, Timeout) -> {ssh_cm, SSH, {exit_signal,Chn,Signal,Err,_Lang}} -> debug("RECVD exit_signal~n~p ~p~n~p ~p", [SSH,Chn,Signal,Err]), do_recv_response(SSH, Chn, Data, End, Timeout); -%% {ok,{exit_signal,Chn,Signal,Err,_Lang}}; + %% {ok,{exit_signal,Chn,Signal,Err,_Lang}}; {ssh_cm, SSH, {exit_status,Chn,Status}} -> debug("RECVD exit_status~n~p ~p~n~p", [SSH,Chn,Status]), do_recv_response(SSH, Chn, Data, End, Timeout); -%% {ok,{exit_status,Chn,_Status}}; + %% {ok,{exit_status,Chn,_Status}}; -%% --- INTERACTIVE MESSAGES - NOT HANDLED --- -%% -%% {ssh_cm, SSH, {subsystem,Chn,WantReply,Name}} -> -%% debug("RECVD SUBS WNTRPLY~n~p ~p~n~p~n~p", -%% [SSH,Chn,WantReply]), -%% ssh_connection:reply_request(SSH, WantReply, success, Chn), -%% do_recv_response(SSH, Chn, Data, End, Timeout); - -%% {ssh_cm, SSH, {shell,WantReply}} -> -%% debug("RECVD SHELL WNTRPLY~n~p ~p~n~p~n~p", -%% [SSH,Chn,WantReply]), -%% ssh_connection:reply_request(SSH, WantReply, success, Chn), -%% do_recv_response(SSH,Chn,Data,End,Timeout); - -%% {ssh_cm, SSH, {pty,Chn,WantReply,Pty}} -> -%% debug("RECVD PTY WNTRPLY~n~p ~p~n~p~n~p", -%% [SSH,Chn,WantReply,Pty]), -%% ssh_connection:reply_request(SSH, WantReply, success, Chn), -%% do_recv_response(SSH, Chn, Data, End, Timeout); - -%% {ssh_cm, SSH, WCh={window_change,_Chn,_Width,_Height,_PixWidth,_PixHeight}} -> -%% debug("RECVD WINCH"), -%% {ok,WCh}; - + %% --- INTERACTIVE MESSAGES - NOT HANDLED --- + %% + %% {ssh_cm, SSH, {subsystem,Chn,WantReply,Name}} -> + %% debug("RECVD SUBS WNTRPLY~n~p ~p~n~p~n~p", + %% [SSH,Chn,WantReply]), + %% ssh_connection:reply_request(SSH, WantReply, success, Chn), + %% do_recv_response(SSH, Chn, Data, End, Timeout); + + %% {ssh_cm, SSH, {shell,WantReply}} -> + %% debug("RECVD SHELL WNTRPLY~n~p ~p~n~p~n~p", + %% [SSH,Chn,WantReply]), + %% ssh_connection:reply_request(SSH, WantReply, success, Chn), + %% do_recv_response(SSH,Chn,Data,End,Timeout); + + %% {ssh_cm, SSH, {pty,Chn,WantReply,Pty}} -> + %% debug("RECVD PTY WNTRPLY~n~p ~p~n~p~n~p", + %% [SSH,Chn,WantReply,Pty]), + %% ssh_connection:reply_request(SSH, WantReply, success, Chn), + %% do_recv_response(SSH, Chn, Data, End, Timeout); + + %% {ssh_cm, SSH, WCh={window_change,_Chn,_Width,_Height,_PixWidth,_PixHeight}} -> + %% debug("RECVD WINCH"), + %% {ok,WCh}; + Other -> debug("UNEXPECTED MESSAGE~n~p ~p~n~p", [SSH,Chn,Other]), do_recv_response(SSH, Chn, Data, End, Timeout) @@ -1307,9 +1337,12 @@ get_handle(SSH) -> %%%----------------------------------------------------------------- %%% call(SSH, Msg) -> + call(SSH, Msg, infinity). + +call(SSH, Msg, Timeout) -> case get_handle(SSH) of {ok,Pid} -> - ct_gen_conn:call(Pid, Msg); + ct_gen_conn:call(Pid, Msg, Timeout); Error -> Error end. @@ -1318,13 +1351,13 @@ call(SSH, Msg) -> %%% ref(sftp, SSHRef) -> SSHRef; ref(Server, _) -> Server. - + %%%----------------------------------------------------------------- %%% mod(Cmd) -> [Op,_Server|Args] = tuple_to_list(Cmd), list_to_tuple([Op|Args]). - + %%%----------------------------------------------------------------- %%% heading(Function, Ref) -> @@ -1335,6 +1368,20 @@ heading(Function, Ref) -> log(Heading, Str, Args) -> ct_gen_conn:log(Heading, Str, Args). +%%%----------------------------------------------------------------- +%%% +try_log(Heading, Str, Args) -> + try_log(Heading, Str, Args, infinity). + +try_log(Heading, Str, Args, Timeout) -> + case ct_util:is_silenced(ssh, Timeout) of + true -> + ok; + false -> + ct_gen_conn:log(Heading, Str, Args); + _Error -> + ok + end. %%%----------------------------------------------------------------- %%% @@ -1342,5 +1389,5 @@ debug(Str) -> debug(Str, []). debug(_Str, _Args) -> -%% io:format("~n--- ct_ssh debug ---~n" ++ _Str ++ "~n", _Args), + %% io:format("~n--- ct_ssh debug ---~n" ++ _Str ++ "~n", _Args), ok. diff --git a/lib/common_test/src/ct_testspec.erl b/lib/common_test/src/ct_testspec.erl index 5b197c0c81..7759aca16b 100644 --- a/lib/common_test/src/ct_testspec.erl +++ b/lib/common_test/src/ct_testspec.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,6 +29,8 @@ -include("ct_util.hrl"). +-define(testspec_fields, record_info(fields, testspec)). + %%%------------------------------------------------------------------ %%% NOTE: %%% Multiple testspecs may be used as input with the result that @@ -46,7 +48,8 @@ %%% Version 1 - extract and return all tests and skips for Node %%% (incl all_nodes) %%%------------------------------------------------------------------- -prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec), is_atom(Node) -> +prepare_tests(TestSpec,Node) when is_record(TestSpec,testspec), + is_atom(Node) -> case lists:keysearch(Node,1,prepare_tests(TestSpec)) of {value,{Node,Run,Skip}} -> {Run,Skip}; @@ -249,22 +252,23 @@ collect_tests_from_file1([Spec|Specs],TestSpec,Relaxed) -> SpecDir = filename:dirname(filename:absname(Spec)), case file:consult(Spec) of {ok,Terms} -> - TestSpec1 = collect_tests(Terms, - TestSpec#testspec{spec_dir=SpecDir}, - Relaxed), - collect_tests_from_file1(Specs,TestSpec1,Relaxed); + case collect_tests(Terms, + TestSpec#testspec{spec_dir=SpecDir}, + Relaxed) of + TS = #testspec{tests=Tests, logdir=LogDirs} when Specs == [] -> + LogDirs1 = lists:delete(".",LogDirs) ++ ["."], + TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1}; + TS = #testspec{alias = As, nodes = Ns} -> + TS1 = TS#testspec{alias = lists:reverse(As), + nodes = lists:reverse(Ns)}, + collect_tests_from_file1(Specs,TS1,Relaxed) + end; {error,Reason} -> ReasonStr = lists:flatten(io_lib:format("~s", [file:format_error(Reason)])), throw({error,{Spec,ReasonStr}}) - end; -collect_tests_from_file1([],TS=#testspec{config=Cfgs,event_handler=EvHs, - include=Incl,tests=Tests},_) -> - TS#testspec{config=lists:reverse(Cfgs), - event_handler=lists:reverse(EvHs), - include=lists:reverse(Incl), - tests=lists:flatten(Tests)}. + end. collect_tests_from_list(Terms,Relaxed) -> collect_tests_from_list(Terms,[node()],Relaxed). @@ -278,30 +282,163 @@ collect_tests_from_list(Terms,Nodes,Relaxed) when is_list(Nodes) -> E = {error,_} -> E; TS -> - #testspec{config=Cfgs,event_handler=EvHs,include=Incl,tests=Tests} = TS, - TS#testspec{config=lists:reverse(Cfgs), - event_handler=lists:reverse(EvHs), - include=lists:reverse(Incl), - tests=lists:flatten(Tests)} + #testspec{tests=Tests, logdir=LogDirs} = TS, + LogDirs1 = lists:delete(".",LogDirs) ++ ["."], + TS#testspec{tests=lists:flatten(Tests), logdir=LogDirs1} end. collect_tests(Terms,TestSpec,Relaxed) -> put(relaxed,Relaxed), - TestSpec1 = get_global(Terms,TestSpec), - TestSpec2 = get_all_nodes(Terms,TestSpec1), - {Terms2, TestSpec3} = filter_init_terms(Terms, [], TestSpec2), + Terms1 = replace_names(Terms), + TestSpec1 = get_global(Terms1,TestSpec), + TestSpec2 = get_all_nodes(Terms1,TestSpec1), + {Terms2, TestSpec3} = filter_init_terms(Terms1, [], TestSpec2), add_tests(Terms2,TestSpec3). -get_global([{merge_tests, Bool} | Ts], Spec) -> - get_global(Ts,Spec#testspec{ merge_tests = Bool }); +%% replace names (atoms) in the testspec matching those in 'define' terms by +%% searching recursively through tuples and lists +replace_names(Terms) -> + Defs = + lists:flatmap(fun(Def={define,Name,_Replacement}) -> + %% check that name follows convention + if not is_atom(Name) -> + throw({illegal_name_in_testspec,Name}); + true -> + [First|_] = atom_to_list(Name), + if ((First == $?) or (First == $$) + or (First == $_) + or ((First >= $A) + and (First =< $Z))) -> + [Def]; + true -> + throw({illegal_name_in_testspec, + Name}) + end + end; + (_) -> [] + end, Terms), + DefProps = replace_names_in_defs(Defs,[]), + replace_names(Terms,[],DefProps). + +replace_names_in_defs([Def|Left],ModDefs) -> + [{define,Name,Replacement}] = replace_names([Def],[],ModDefs), + replace_names_in_defs(Left,[{Name,Replacement}|ModDefs]); +replace_names_in_defs([],ModDefs) -> + ModDefs. + +replace_names([Term|Ts],Modified,Defs) when is_tuple(Term) -> + [TypeTag|Data] = tuple_to_list(Term), + Term1 = list_to_tuple([TypeTag|replace_names_in_elems(Data,[],Defs)]), + replace_names(Ts,[Term1|Modified],Defs); +replace_names([Term|Ts],Modified,Defs) when is_atom(Term) -> + case proplists:get_value(Term,Defs) of + undefined -> + replace_names(Ts,[Term|Modified],Defs); + Replacement -> + replace_names(Ts,[Replacement|Modified],Defs) + end; +replace_names([Term=[Ch|_]|Ts],Modified,Defs) when is_integer(Ch) -> + %% Term *could* be a string, attempt to search through it + Term1 = replace_names_in_string(Term,Defs), + replace_names(Ts,[Term1|Modified],Defs); +replace_names([Term|Ts],Modified,Defs) -> + replace_names(Ts,[Term|Modified],Defs); +replace_names([],Modified,_Defs) -> + lists:reverse(Modified). + +replace_names_in_elems([Elem|Es],Modified,Defs) when is_tuple(Elem) -> + Elem1 = list_to_tuple(replace_names_in_elems(tuple_to_list(Elem),[],Defs)), + replace_names_in_elems(Es,[Elem1|Modified],Defs); +replace_names_in_elems([Elem|Es],Modified,Defs) when is_atom(Elem) -> + case proplists:get_value(Elem,Defs) of + undefined -> + %% if Term is a node name, check it for replacements as well + Elem1 = replace_names_in_node(Elem,Defs), + replace_names_in_elems(Es,[Elem1|Modified],Defs); + Replacement -> + replace_names_in_elems(Es,[Replacement|Modified],Defs) + end; +replace_names_in_elems([Elem=[Ch|_]|Es],Modified,Defs) when is_integer(Ch) -> + %% Term *could* be a string, attempt to search through it + case replace_names_in_string(Elem,Defs) of + Elem -> + List = replace_names_in_elems(Elem,[],Defs), + replace_names_in_elems(Es,[List|Modified],Defs); + Elem1 -> + replace_names_in_elems(Es,[Elem1|Modified],Defs) + end; +replace_names_in_elems([Elem|Es],Modified,Defs) when is_list(Elem) -> + List = replace_names_in_elems(Elem,[],Defs), + replace_names_in_elems(Es,[List|Modified],Defs); +replace_names_in_elems([Elem|Es],Modified,Defs) -> + replace_names_in_elems(Es,[Elem|Modified],Defs); +replace_names_in_elems([],Modified,_Defs) -> + lists:reverse(Modified). + +replace_names_in_string(Term,Defs=[{Name,Replacement=[Ch|_]}|Ds]) + when is_integer(Ch) -> + try re:replace(Term,[$'|atom_to_list(Name)]++"'", + Replacement,[{return,list}]) of + Term -> % no match, proceed + replace_names_in_string(Term,Ds); + Term1 -> + replace_names_in_string(Term1,Defs) + catch + _:_ -> Term % Term is not a string + end; +replace_names_in_string(Term,[_|Ds]) -> + replace_names_in_string(Term,Ds); +replace_names_in_string(Term,[]) -> + Term. + +replace_names_in_node(Node,Defs) -> + String = atom_to_list(Node), + case lists:member($@,String) of + true -> + list_to_atom(replace_names_in_node1(String,Defs)); + false -> + Node + end. + +replace_names_in_node1(NodeStr,Defs=[{Name,Replacement}|Ds]) -> + ReplStr = case Replacement of + [Ch|_] when is_integer(Ch) -> Replacement; + _ when is_atom(Replacement) -> atom_to_list(Replacement); + _ -> false + end, + if ReplStr == false -> + replace_names_in_node1(NodeStr,Ds); + true -> + case re:replace(NodeStr,atom_to_list(Name), + ReplStr,[{return,list}]) of + NodeStr -> % no match, proceed + replace_names_in_node1(NodeStr,Ds); + NodeStr1 -> + replace_names_in_node1(NodeStr1,Defs) + end + end; +replace_names_in_node1(NodeStr,[]) -> + NodeStr. + + +%% global terms that will be used for analysing all other terms in the spec +get_global([{merge_tests,Bool} | Ts], Spec) -> + get_global(Ts,Spec#testspec{merge_tests=Bool}); + +%% the 'define' term replaces the 'alias' and 'node' terms, but we need to keep +%% the latter two for backwards compatibility... get_global([{alias,Ref,Dir}|Ts],Spec=#testspec{alias=Refs}) -> get_global(Ts,Spec#testspec{alias=[{Ref,get_absdir(Dir,Spec)}|Refs]}); get_global([{node,Ref,Node}|Ts],Spec=#testspec{nodes=Refs}) -> - get_global(Ts,Spec#testspec{nodes=[{Ref,Node}|lists:keydelete(Node,2,Refs)]}); -get_global([_|Ts],Spec) -> get_global(Ts,Spec); -get_global([],Spec) -> Spec. + get_global(Ts,Spec#testspec{nodes=[{Ref,Node} | + lists:keydelete(Node,2,Refs)]}); -get_absfile(Callback, FullName,#testspec{spec_dir=SpecDir}) -> +get_global([_|Ts],Spec) -> + get_global(Ts,Spec); +get_global([],Spec=#testspec{nodes=Ns, alias=As}) -> + Spec#testspec{nodes=lists:reverse(Ns), alias=lists:reverse(As)}. + +get_absfile(Callback,FullName,#testspec{spec_dir=SpecDir}) -> % we need to temporary switch to new cwd here, because % otherwise config files cannot be found {ok, OldWd} = file:get_cwd(), @@ -329,29 +466,45 @@ get_absfile(FullName,#testspec{spec_dir=SpecDir}) -> get_absdir(Dir,#testspec{spec_dir=SpecDir}) -> get_absname(Dir,SpecDir). -get_absname(TestDir,SpecDir) -> - AbsName = filename:absname(TestDir,SpecDir), - TestDirName = filename:basename(AbsName), - Path = filename:dirname(AbsName), - TopDir = filename:basename(Path), - Path1 = - case TopDir of - "." -> - [_|Rev] = lists:reverse(filename:split(Path)), - filename:join(lists:reverse(Rev)); - ".." -> - [_,_|Rev] = lists:reverse(filename:split(Path)), - filename:join(lists:reverse(Rev)); - _ -> - Path - end, - filename:join(Path1,TestDirName). +get_absname(Dir,SpecDir) -> + AbsName = filename:absname(Dir,SpecDir), + shorten_path(AbsName,SpecDir). + +shorten_path(Path,SpecDir) -> + case shorten_split_path(filename:split(Path),[]) of + [] -> + [Root|_] = filename:split(SpecDir), + Root; + Short -> + filename:join(Short) + end. + +shorten_split_path([".."|Path],SoFar) -> + shorten_split_path(Path,tl(SoFar)); +shorten_split_path(["."|Path],SoFar) -> + shorten_split_path(Path,SoFar); +shorten_split_path([Dir|Path],SoFar) -> + shorten_split_path(Path,[Dir|SoFar]); +shorten_split_path([],SoFar) -> + lists:reverse(SoFar). %% go through all tests and register all nodes found get_all_nodes([{suites,Nodes,_,_}|Ts],Spec) when is_list(Nodes) -> get_all_nodes(Ts,save_nodes(Nodes,Spec)); get_all_nodes([{suites,Node,_,_}|Ts],Spec) -> get_all_nodes(Ts,save_nodes([Node],Spec)); +get_all_nodes([{groups,[Char|_],_,_,_}|Ts],Spec) when is_integer(Char) -> + get_all_nodes(Ts,Spec); +get_all_nodes([{groups,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) -> + get_all_nodes(Ts,save_nodes(Nodes,Spec)); +get_all_nodes([{groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) -> + get_all_nodes(Ts,save_nodes(Nodes,Spec)); +get_all_nodes([{groups,_,_,_,{cases,_}}|Ts],Spec) -> + get_all_nodes(Ts,Spec); +get_all_nodes([{groups,Node,_,_,_}|Ts],Spec) -> + get_all_nodes(Ts,save_nodes([Node],Spec)); +get_all_nodes([{groups,Node,_,_,_,_}|Ts],Spec) -> + get_all_nodes(Ts,save_nodes([Node],Spec)); get_all_nodes([{cases,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) -> get_all_nodes(Ts,save_nodes(Nodes,Spec)); get_all_nodes([{cases,Node,_,_,_}|Ts],Spec) -> @@ -360,74 +513,93 @@ get_all_nodes([{skip_suites,Nodes,_,_,_}|Ts],Spec) when is_list(Nodes) -> get_all_nodes(Ts,save_nodes(Nodes,Spec)); get_all_nodes([{skip_suites,Node,_,_,_}|Ts],Spec) -> get_all_nodes(Ts,save_nodes([Node],Spec)); +get_all_nodes([{skip_groups,[Char|_],_,_,_,_}|Ts],Spec) when is_integer(Char) -> + get_all_nodes(Ts,Spec); +get_all_nodes([{skip_groups,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) -> + get_all_nodes(Ts,save_nodes(Nodes,Spec)); +get_all_nodes([{skip_groups,Node,_,_,_,_}|Ts],Spec) -> + get_all_nodes(Ts,save_nodes([Node],Spec)); +get_all_nodes([{skip_groups,Nodes,_,_,_,_,_}|Ts],Spec) when is_list(Nodes) -> + get_all_nodes(Ts,save_nodes(Nodes,Spec)); +get_all_nodes([{skip_groups,Node,_,_,_,_,_}|Ts],Spec) -> + get_all_nodes(Ts,save_nodes([Node],Spec)); get_all_nodes([{skip_cases,Nodes,_,_,_,_}|Ts],Spec) when is_list(Nodes) -> get_all_nodes(Ts,save_nodes(Nodes,Spec)); get_all_nodes([{skip_cases,Node,_,_,_,_}|Ts],Spec) -> get_all_nodes(Ts,save_nodes([Node],Spec)); -get_all_nodes([_|Ts],Spec) -> +get_all_nodes([_Other|Ts],Spec) -> get_all_nodes(Ts,Spec); get_all_nodes([],Spec) -> Spec. -filter_init_terms([{init, InitOptions}|Ts], NewTerms, Spec)-> - filter_init_terms([{init, list_nodes(Spec), InitOptions}|Ts], NewTerms, Spec); -filter_init_terms([{init, NodeRef, InitOptions}|Ts], NewTerms, Spec) - when is_atom(NodeRef)-> - filter_init_terms([{init, [NodeRef], InitOptions}|Ts], NewTerms, Spec); -filter_init_terms([{init, NodeRefs, InitOption}|Ts], NewTerms, Spec) when is_tuple(InitOption) -> - filter_init_terms([{init, NodeRefs, [InitOption]}|Ts], NewTerms, Spec); -filter_init_terms([{init, [NodeRef|NodeRefs], InitOptions}|Ts], NewTerms, Spec=#testspec{init=InitData})-> - NodeStartOptions = case lists:keyfind(node_start, 1, InitOptions) of - {node_start, NSOptions}-> - case lists:keyfind(callback_module, 1, NSOptions) of - {callback_module, _Callback}-> - NSOptions; - false-> - [{callback_module, ct_slave}|NSOptions] - end; - false-> - [] - end, - EvalTerms = case lists:keyfind(eval, 1, InitOptions) of - {eval, MFA} when is_tuple(MFA)-> - [MFA]; - {eval, MFAs} when is_list(MFAs)-> - MFAs; - false-> - [] - end, +filter_init_terms([{init,InitOptions}|Ts],NewTerms,Spec) -> + filter_init_terms([{init,list_nodes(Spec),InitOptions}|Ts], + NewTerms,Spec); +filter_init_terms([{init,all_nodes,InitOptions}|Ts],NewTerms,Spec) -> + filter_init_terms([{init,list_nodes(Spec),InitOptions}|Ts], + NewTerms,Spec); +filter_init_terms([{init,NodeRef,InitOptions}|Ts], + NewTerms,Spec) when is_atom(NodeRef) -> + filter_init_terms([{init,[NodeRef],InitOptions}|Ts],NewTerms,Spec); +filter_init_terms([{init,NodeRefs,InitOption}|Ts], + NewTerms,Spec) when is_tuple(InitOption) -> + filter_init_terms([{init,NodeRefs,[InitOption]}|Ts],NewTerms,Spec); +filter_init_terms([{init,[NodeRef|NodeRefs],InitOptions}|Ts], + NewTerms,Spec=#testspec{init=InitData}) -> + NodeStartOptions = + case lists:keyfind(node_start,1,InitOptions) of + {node_start,NSOptions}-> + case lists:keyfind(callback_module,1,NSOptions) of + {callback_module,_Callback}-> + NSOptions; + false-> + [{callback_module,ct_slave}|NSOptions] + end; + false-> + [] + end, + EvalTerms = case lists:keyfind(eval,1,InitOptions) of + {eval,MFA} when is_tuple(MFA) -> + [MFA]; + {eval,MFAs} when is_list(MFAs) -> + MFAs; + false-> + [] + end, Node = ref2node(NodeRef,Spec#testspec.nodes), - InitData2 = add_option({node_start, NodeStartOptions}, Node, InitData, true), - InitData3 = add_option({eval, EvalTerms}, Node, InitData2, false), - filter_init_terms([{init, NodeRefs, InitOptions}|Ts], NewTerms, Spec#testspec{init=InitData3}); -filter_init_terms([{init, [], _}|Ts], NewTerms, Spec)-> - filter_init_terms(Ts, NewTerms, Spec); -filter_init_terms([Term|Ts], NewTerms, Spec)-> - filter_init_terms(Ts, [Term|NewTerms], Spec); -filter_init_terms([], NewTerms, Spec)-> - {lists:reverse(NewTerms), Spec}. - -add_option({Key, Value}, Node, List, WarnIfExists) when is_list(Value)-> - OldOptions = case lists:keyfind(Node, 1, List) of - {Node, Options}-> + InitData2 = add_option({node_start,NodeStartOptions},Node,InitData,true), + InitData3 = add_option({eval,EvalTerms},Node,InitData2,false), + filter_init_terms([{init,NodeRefs,InitOptions}|Ts], + NewTerms,Spec#testspec{init=InitData3}); +filter_init_terms([{init,[],_}|Ts],NewTerms,Spec) -> + filter_init_terms(Ts,NewTerms,Spec); +filter_init_terms([Term|Ts],NewTerms,Spec) -> + filter_init_terms(Ts,[Term|NewTerms],Spec); +filter_init_terms([],NewTerms,Spec) -> + {lists:reverse(NewTerms),Spec}. + +add_option({Key,Value},Node,List,WarnIfExists) when is_list(Value) -> + OldOptions = case lists:keyfind(Node,1,List) of + {Node,Options}-> Options; false-> [] end, - NewOption = case lists:keyfind(Key, 1, OldOptions) of - {Key, OldOption} when WarnIfExists, OldOption/=[]-> - io:format("There is an option ~w=~w already defined for node ~p, skipping new ~w~n", - [Key, OldOption, Node, Value]), + NewOption = case lists:keyfind(Key,1,OldOptions) of + {Key,OldOption} when WarnIfExists,OldOption/=[]-> + io:format("There is an option ~w=~w already " + "defined for node ~p, skipping new ~w~n", + [Key,OldOption,Node,Value]), OldOption; - {Key, OldOption}-> + {Key,OldOption}-> OldOption ++ Value; false-> Value end, - lists:keystore(Node, 1, List, - {Node, lists:keystore(Key, 1, OldOptions, {Key, NewOption})}); -add_option({Key, Value}, Node, List, WarnIfExists)-> - add_option({Key, [Value]}, Node, List, WarnIfExists). + lists:keystore(Node,1,List, + {Node,lists:keystore(Key,1,OldOptions,{Key,NewOption})}); +add_option({Key,Value},Node,List,WarnIfExists) -> + add_option({Key,[Value]},Node,List,WarnIfExists). save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) -> NodeRefs1 = @@ -446,267 +618,18 @@ save_nodes(Nodes,Spec=#testspec{nodes=NodeRefs}) -> end end end,NodeRefs,Nodes), - Spec#testspec{nodes=NodeRefs1}. + Spec#testspec{nodes=NodeRefs1}. list_nodes(#testspec{nodes=NodeRefs}) -> lists:map(fun({_Ref,Node}) -> Node end, NodeRefs). - -%% --------------------------------------------------------- -%% / \ -%% | When adding tests, remember to update valid_terms/0 also! | -%% \ / -%% --------------------------------------------------------- - - -%% Associate a "global" logdir with all nodes -%% except those with specific logdir, e.g: -%% ["/tmp/logdir",{ct1@finwe,"/tmp/logdir2"}] -%% means all nodes should write to /tmp/logdir -%% except ct1@finwe that should use /tmp/logdir2. - -%% --- logdir --- -add_tests([{logdir,all_nodes,Dir}|Ts],Spec) -> - Dirs = Spec#testspec.logdir, - Tests = [{logdir,N,get_absdir(Dir,Spec)} || - N <- list_nodes(Spec), - lists:keymember(ref2node(N,Spec#testspec.nodes), - 1,Dirs) == false], - add_tests(Tests++Ts,Spec); -add_tests([{logdir,Nodes,Dir}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,logdir,[Dir],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{logdir,Node,Dir}|Ts],Spec) -> - Dirs = Spec#testspec.logdir, - Dirs1 = [{ref2node(Node,Spec#testspec.nodes),get_absdir(Dir,Spec)} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Dirs)], - add_tests(Ts,Spec#testspec{logdir=Dirs1}); -add_tests([{logdir,Dir}|Ts],Spec) -> - add_tests([{logdir,all_nodes,Dir}|Ts],Spec); - -%% --- logopts --- -add_tests([{logopts,all_nodes,Opts}|Ts],Spec) -> - LogOpts = Spec#testspec.logopts, - Tests = [{logopts,N,Opts} || - N <- list_nodes(Spec), - lists:keymember(ref2node(N,Spec#testspec.nodes),1, - LogOpts) == false], - add_tests(Tests++Ts,Spec); -add_tests([{logopts,Nodes,Opts}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,logopts,[Opts],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{logopts,Node,Opts}|Ts],Spec) -> - LogOpts = Spec#testspec.logopts, - LogOpts1 = [{ref2node(Node,Spec#testspec.nodes),Opts} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes), - 1,LogOpts)], - add_tests(Ts,Spec#testspec{logopts=LogOpts1}); -add_tests([{logopts,Opts}|Ts],Spec) -> - add_tests([{logopts,all_nodes,Opts}|Ts],Spec); - -%% --- label --- -add_tests([{label,all_nodes,Lbl}|Ts],Spec) -> - Labels = Spec#testspec.label, - Tests = [{label,N,Lbl} || N <- list_nodes(Spec), - lists:keymember(ref2node(N,Spec#testspec.nodes), - 1,Labels) == false], - add_tests(Tests++Ts,Spec); -add_tests([{label,Nodes,Lbl}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,label,[Lbl],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{label,Node,Lbl}|Ts],Spec) -> - Labels = Spec#testspec.label, - Labels1 = [{ref2node(Node,Spec#testspec.nodes),Lbl} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,Labels)], - add_tests(Ts,Spec#testspec{label=Labels1}); -add_tests([{label,Lbl}|Ts],Spec) -> - add_tests([{label,all_nodes,Lbl}|Ts],Spec); - -%% --- cover --- -add_tests([{cover,all_nodes,File}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {cover,N,File} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{cover,Nodes,File}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,cover,[File],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{cover,Node,File}|Ts],Spec) -> - CoverFs = Spec#testspec.cover, - CoverFs1 = [{ref2node(Node,Spec#testspec.nodes),get_absfile(File,Spec)} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,CoverFs)], - add_tests(Ts,Spec#testspec{cover=CoverFs1}); -add_tests([{cover,File}|Ts],Spec) -> - add_tests([{cover,all_nodes,File}|Ts],Spec); - -%% --- multiply_timetraps --- -add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {multiply_timetraps,N,MT} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{multiply_timetraps,Nodes,MT}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,multiply_timetraps,[MT],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{multiply_timetraps,Node,MT}|Ts],Spec) -> - MTs = Spec#testspec.multiply_timetraps, - MTs1 = [{ref2node(Node,Spec#testspec.nodes),MT} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,MTs)], - add_tests(Ts,Spec#testspec{multiply_timetraps=MTs1}); -add_tests([{multiply_timetraps,MT}|Ts],Spec) -> - add_tests([{multiply_timetraps,all_nodes,MT}|Ts],Spec); - -%% --- scale_timetraps --- -add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {scale_timetraps,N,ST} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{scale_timetraps,Nodes,ST}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,scale_timetraps,[ST],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{scale_timetraps,Node,ST}|Ts],Spec) -> - STs = Spec#testspec.scale_timetraps, - STs1 = [{ref2node(Node,Spec#testspec.nodes),ST} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,STs)], - add_tests(Ts,Spec#testspec{scale_timetraps=STs1}); -add_tests([{scale_timetraps,ST}|Ts],Spec) -> - add_tests([{scale_timetraps,all_nodes,ST}|Ts],Spec); - -%% --- create_priv_dir --- -add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {create_priv_dir,N,PD} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{create_priv_dir,Nodes,PD}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,create_priv_dir,[PD],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{create_priv_dir,Node,PD}|Ts],Spec) -> - PDs = Spec#testspec.create_priv_dir, - PDs1 = [{ref2node(Node,Spec#testspec.nodes),PD} | - lists:keydelete(ref2node(Node,Spec#testspec.nodes),1,PDs)], - add_tests(Ts,Spec#testspec{create_priv_dir=PDs1}); -add_tests([{create_priv_dir,PD}|Ts],Spec) -> - add_tests([{create_priv_dir,all_nodes,PD}|Ts],Spec); - -%% --- config --- -add_tests([{config,all_nodes,Files}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {config,N,Files} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{config,Nodes,Files}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,config,[Files],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{config,Node,[F|Fs]}|Ts],Spec) when is_list(F) -> - Cfgs = Spec#testspec.config, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{config,Node,Fs}|Ts], - Spec#testspec{config=[{Node1,get_absfile(F,Spec)}|Cfgs]}); -add_tests([{config,_Node,[]}|Ts],Spec) -> - add_tests(Ts,Spec); -add_tests([{config,Node,F}|Ts],Spec) -> - add_tests([{config,Node,[F]}|Ts],Spec); -add_tests([{config,Files}|Ts],Spec) -> - add_tests([{config,all_nodes,Files}|Ts],Spec); - - -%% --- userconfig --- -add_tests([{userconfig,all_nodes,CBF}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {userconfig,N,CBF} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{userconfig,Nodes,CBF}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,userconfig,[CBF],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{userconfig,Node,[{Callback, Config}|CBF]}|Ts],Spec) -> - Cfgs = Spec#testspec.userconfig, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{userconfig,Node,CBF}|Ts], - Spec#testspec{userconfig=[{Node1,{Callback, - get_absfile(Callback, Config ,Spec)}}|Cfgs]}); -add_tests([{userconfig,_Node,[]}|Ts],Spec) -> - add_tests(Ts,Spec); -add_tests([{userconfig,Node,CBF}|Ts],Spec) -> - add_tests([{userconfig,Node,[CBF]}|Ts],Spec); -add_tests([{userconfig,CBF}|Ts],Spec) -> - add_tests([{userconfig,all_nodes,CBF}|Ts],Spec); - -%% --- event_handler --- -add_tests([{event_handler,all_nodes,Hs}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {event_handler,N,Hs,[]} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{event_handler,all_nodes,Hs,Args}|Ts],Spec) when is_list(Args) -> - Tests = lists:map(fun(N) -> {event_handler,N,Hs,Args} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{event_handler,Hs}|Ts],Spec) -> - add_tests([{event_handler,all_nodes,Hs,[]}|Ts],Spec); -add_tests([{event_handler,HsOrNodes,HsOrArgs}|Ts],Spec) -> - case is_noderef(HsOrNodes,Spec#testspec.nodes) of - true -> % HsOrNodes == Nodes, HsOrArgs == Hs - case {HsOrNodes,HsOrArgs} of - {Nodes,Hs} when is_list(Nodes) -> - Ts1 = separate(Nodes,event_handler,[Hs,[]],Ts, - Spec#testspec.nodes), - add_tests(Ts1,Spec); - {_Node,[]} -> - add_tests(Ts,Spec); - {Node,HOrHs} -> - EvHs = Spec#testspec.event_handler, - Node1 = ref2node(Node,Spec#testspec.nodes), - case HOrHs of - [H|Hs] when is_atom(H) -> - add_tests([{event_handler,Node,Hs}|Ts], - Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]}); - H when is_atom(H) -> - add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,[]}|EvHs]}) - end - end; - false -> % HsOrNodes == Hs, HsOrArgs == Args - add_tests([{event_handler,all_nodes,HsOrNodes,HsOrArgs}|Ts],Spec) - end; -add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,event_handler,[Hs,Args],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{event_handler,Node,[H|Hs],Args}|Ts],Spec) when is_atom(H) -> - EvHs = Spec#testspec.event_handler, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{event_handler,Node,Hs,Args}|Ts], - Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]}); -add_tests([{event_handler,_Node,[],_Args}|Ts],Spec) -> - add_tests(Ts,Spec); -add_tests([{event_handler,Node,H,Args}|Ts],Spec) when is_atom(H) -> - EvHs = Spec#testspec.event_handler, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests(Ts,Spec#testspec{event_handler=[{Node1,H,Args}|EvHs]}); - -%% --- ct_hooks -- -add_tests([{ct_hooks, all_nodes, Hooks} | Ts], Spec) -> - Tests = [{ct_hooks,N,Hooks} || N <- list_nodes(Spec)], - add_tests(Tests ++ Ts, Spec); -add_tests([{ct_hooks, Node, [Hook|Hooks]}|Ts], Spec) -> - SuiteCbs = Spec#testspec.ct_hooks, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{ct_hooks, Node, Hooks} | Ts], - Spec#testspec{ct_hooks = [{Node1,Hook} | SuiteCbs]}); -add_tests([{ct_hooks, _Node, []}|Ts], Spec) -> - add_tests(Ts, Spec); -add_tests([{ct_hooks, Hooks}|Ts], Spec) -> - add_tests([{ct_hooks, all_nodes, Hooks}|Ts], Spec); - -%% -- enable_builtin_hooks -- -add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) -> - add_tests(Ts, Spec#testspec{ enable_builtin_hooks = Bool }); - -%% --- include --- -add_tests([{include,all_nodes,InclDirs}|Ts],Spec) -> - Tests = lists:map(fun(N) -> {include,N,InclDirs} end, list_nodes(Spec)), - add_tests(Tests++Ts,Spec); -add_tests([{include,Nodes,InclDirs}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,include,[InclDirs],Ts,Spec#testspec.nodes), - add_tests(Ts1,Spec); -add_tests([{include,Node,[D|Ds]}|Ts],Spec) when is_list(D) -> - Dirs = Spec#testspec.include, - Node1 = ref2node(Node,Spec#testspec.nodes), - add_tests([{include,Node,Ds}|Ts], - Spec#testspec{include=[{Node1,get_absdir(D,Spec)}|Dirs]}); -add_tests([{include,_Node,[]}|Ts],Spec) -> - add_tests(Ts,Spec); -add_tests([{include,Node,D}|Ts],Spec) -> - add_tests([{include,Node,[D]}|Ts],Spec); -add_tests([{include,InclDirs}|Ts],Spec) -> - add_tests([{include,all_nodes,InclDirs}|Ts],Spec); +%% ----------------------------------------------------- +%% / \ +%% | When adding test/config terms, remember to update | +%% | valid_terms/0 also! | +%% \ / +%% ----------------------------------------------------- %% --- suites --- add_tests([{suites,all_nodes,Dir,Ss}|Ts],Spec) -> @@ -719,7 +642,7 @@ add_tests([{suites,Nodes,Dir,Ss}|Ts],Spec) when is_list(Nodes) -> add_tests([{suites,Node,Dir,Ss}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = insert_suites(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Ss,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -739,20 +662,22 @@ add_tests([{groups,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) -> add_tests([{groups,Nodes,Dir,Suite,Gs}|Ts],Spec) when is_list(Nodes) -> Ts1 = separate(Nodes,groups,[Dir,Suite,Gs],Ts,Spec#testspec.nodes), add_tests(Ts1,Spec); -add_tests([{groups,Nodes,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts,Spec#testspec.nodes), +add_tests([{groups,Nodes,Dir,Suite,Gs,{cases,TCs}}|Ts], + Spec) when is_list(Nodes) -> + Ts1 = separate(Nodes,groups,[Dir,Suite,Gs,{cases,TCs}],Ts, + Spec#testspec.nodes), add_tests(Ts1,Spec); add_tests([{groups,Node,Dir,Suite,Gs}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Gs,all,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); add_tests([{groups,Node,Dir,Suite,Gs,{cases,TCs}}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = insert_groups(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Gs,TCs,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -768,7 +693,7 @@ add_tests([{cases,Nodes,Dir,Suite,Cs}|Ts],Spec) when is_list(Nodes) -> add_tests([{cases,Node,Dir,Suite,Cs}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = insert_cases(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Cs,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -783,7 +708,7 @@ add_tests([{skip_suites,Nodes,Dir,Ss,Cmt}|Ts],Spec) when is_list(Nodes) -> add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = skip_suites(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Ss,Cmt,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -792,7 +717,8 @@ add_tests([{skip_suites,Node,Dir,Ss,Cmt}|Ts],Spec) -> add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) -> add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,Cmt}|Ts],Spec); add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) -> - add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec); + add_tests([{skip_groups,list_nodes(Spec),Dir,Suite,Gs,{cases,TCs},Cmt}|Ts], + Spec); add_tests([{skip_groups,Dir,Suite,Gs,Cmt}|Ts],Spec) -> add_tests([{skip_groups,all_nodes,Dir,Suite,Gs,Cmt}|Ts],Spec); add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) -> @@ -800,20 +726,22 @@ add_tests([{skip_groups,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) -> add_tests([{skip_groups,Nodes,Dir,Suite,Gs,Cmt}|Ts],Spec) when is_list(Nodes) -> Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,Cmt],Ts,Spec#testspec.nodes), add_tests(Ts1,Spec); -add_tests([{skip_groups,Nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) when is_list(Nodes) -> - Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,{cases,TCs},Cmt],Ts,Spec#testspec.nodes), +add_tests([{skip_groups,Nodes,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts], + Spec) when is_list(Nodes) -> + Ts1 = separate(Nodes,skip_groups,[Dir,Suite,Gs,{cases,TCs},Cmt],Ts, + Spec#testspec.nodes), add_tests(Ts1,Spec); add_tests([{skip_groups,Node,Dir,Suite,Gs,Cmt}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Gs,all,Cmt,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); add_tests([{skip_groups,Node,Dir,Suite,Gs,{cases,TCs},Cmt}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = skip_groups(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Gs,TCs,Cmt,Tests, Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); @@ -829,45 +757,101 @@ add_tests([{skip_cases,Nodes,Dir,Suite,Cs,Cmt}|Ts],Spec) when is_list(Nodes) -> add_tests([{skip_cases,Node,Dir,Suite,Cs,Cmt}|Ts],Spec) -> Tests = Spec#testspec.tests, Tests1 = skip_cases(ref2node(Node,Spec#testspec.nodes), - ref2dir(Dir,Spec#testspec.alias), + ref2dir(Dir,Spec), Suite,Cs,Cmt,Tests,Spec#testspec.merge_tests), add_tests(Ts,Spec#testspec{tests=Tests1}); +%% --- various configuration terms --- +add_tests([{config,Nodes,CfgDir,Files}|Ts],Spec) when is_list(Nodes); + Nodes == all_nodes -> + add_tests([{config,Nodes,{CfgDir,Files}}|Ts],Spec); +add_tests([{config,Node,CfgDir,FileOrFiles}|Ts],Spec) -> + add_tests([{config,Node,{CfgDir,FileOrFiles}}|Ts],Spec); +add_tests([{config,CfgDir=[Ch|_],Files}|Ts],Spec) when is_integer(Ch) -> + add_tests([{config,all_nodes,{CfgDir,Files}}|Ts],Spec); + +add_tests([{event_handler,Nodes,Hs,Args}|Ts],Spec) when is_list(Nodes); + Nodes == all_nodes -> + add_tests([{event_handler,Nodes,{Hs,Args}}|Ts],Spec); +add_tests([{event_handler,Node,HOrHs,Args}|Ts],Spec) -> + add_tests([{event_handler,Node,{HOrHs,Args}}|Ts],Spec); + +add_tests([{enable_builtin_hooks,Bool}|Ts],Spec) -> + add_tests(Ts, Spec#testspec{enable_builtin_hooks = Bool}); + +add_tests([{noinput,Bool}|Ts],Spec) -> + add_tests(Ts, Spec#testspec{noinput = Bool}); + %% --- handled/errors --- +add_tests([{define,_,_}|Ts],Spec) -> % handled + add_tests(Ts,Spec); + add_tests([{alias,_,_}|Ts],Spec) -> % handled add_tests(Ts,Spec); add_tests([{node,_,_}|Ts],Spec) -> % handled add_tests(Ts,Spec); -add_tests([{merge_tests, _} | Ts], Spec) -> % handled +add_tests([{merge_tests, _} | Ts], Spec) -> % handled add_tests(Ts,Spec); -%% check if it's a CT term that has bad format or if the user seems to -%% have added something of his/her own, which we'll let pass if relaxed -%% mode is enabled. -add_tests([Other|Ts],Spec) when is_tuple(Other) -> - [Name|_] = tuple_to_list(Other), - case lists:keymember(Name,1,valid_terms()) of - true -> % halt - throw({error,{bad_term_in_spec,Other}}); - false -> % ignore - case get(relaxed) of - true -> - %% warn if name resembles a CT term - case resembles_ct_term(Name,size(Other)) of - true -> - io:format("~nSuspicious term, please check:~n" - "~p~n", [Other]); - false -> - ok - end, - add_tests(Ts,Spec); - false -> - throw({error,{undefined_term_in_spec,Other}}) - end +%% -------------------------------------------------- +%% / \ +%% | General add_tests/2 clauses below will work for | +%% | most test spec configuration terms | +%% \ / +%% -------------------------------------------------- + +%% create one test entry per known node and reinsert +add_tests([Term={Tag,all_nodes,Data}|Ts],Spec) -> + case check_term(Term) of + valid -> + Tests = [{Tag,Node,Data} || Node <- list_nodes(Spec), + should_be_added(Tag,Node,Data,Spec)], + add_tests(Tests++Ts,Spec); + invalid -> % ignore term + add_tests(Ts,Spec) end; - +%% create one test entry per node in Nodes and reinsert +add_tests([{Tag,[],Data}|Ts],Spec) -> + add_tests([{Tag,all_nodes,Data}|Ts],Spec); +add_tests([{Tag,String=[Ch|_],Data}|Ts],Spec) when is_integer(Ch) -> + add_tests([{Tag,all_nodes,{String,Data}}|Ts],Spec); +add_tests([{Tag,NodesOrOther,Data}|Ts],Spec) when is_list(NodesOrOther) -> + case lists:all(fun(Test) -> is_node(Test,Spec#testspec.nodes) + end, NodesOrOther) of + true -> + Ts1 = separate(NodesOrOther,Tag,[Data],Ts,Spec#testspec.nodes), + add_tests(Ts1,Spec); + false -> + add_tests([{Tag,all_nodes,{NodesOrOther,Data}}|Ts],Spec) + end; +%% update data for testspec term of type Tag +add_tests([Term={Tag,NodeOrOther,Data}|Ts],Spec) -> + case is_node(NodeOrOther,Spec#testspec.nodes) of + true -> + case check_term(Term) of + valid -> + Node = ref2node(NodeOrOther,Spec#testspec.nodes), + NodeIxData = + update_recorded(Tag,Node,Spec) ++ + handle_data(Tag,Node,Data,Spec), + add_tests(Ts,mod_field(Spec,Tag,NodeIxData)); + invalid -> % ignore term + add_tests(Ts,Spec) + end; + false -> + add_tests([{Tag,all_nodes,{NodeOrOther,Data}}|Ts],Spec) + end; +%% this test should be added for all known nodes +add_tests([Term={Tag,Data}|Ts],Spec) -> + case check_term(Term) of + valid -> + add_tests([{Tag,all_nodes,Data}|Ts],Spec); + invalid -> + add_tests(Ts,Spec) + end; +%% some other data than a tuple add_tests([Other|Ts],Spec) -> case get(relaxed) of true -> @@ -879,6 +863,112 @@ add_tests([Other|Ts],Spec) -> add_tests([],Spec) -> % done Spec. +%% check if it's a CT term that has bad format or if the user seems to +%% have added something of his/her own, which we'll let pass if relaxed +%% mode is enabled. +check_term(Term) -> + Size = size(Term), + [Name|_] = tuple_to_list(Term), + Valid = valid_terms(), + case lists:member({Name,Size},Valid) of + true -> + valid; + false -> + case lists:keymember(Name,1,Valid) of + true -> % halt + throw({error,{bad_term_in_spec,Term}}); + false -> % ignore + case get(relaxed) of + true -> + %% warn if name resembles a CT term + case resembles_ct_term(Name,size(Term)) of + true -> + io:format("~nSuspicious term, " + "please check:~n" + "~p~n", [Term]), + invalid; + false -> + invalid + end; + false -> + throw({error,{undefined_term_in_spec,Term}}) + end + end + end. + +%% specific data handling before saving in testspec record, e.g. +%% converting relative paths to absolute for directories and files +%% (introduce a clause *only* if the data value needs processing) +handle_data(logdir,Node,Dir,Spec) -> + [{Node,ref2dir(Dir,Spec)}]; +handle_data(cover,Node,File,Spec) -> + [{Node,get_absfile(File,Spec)}]; +handle_data(include,Node,Dirs=[D|_],Spec) when is_list(D) -> + [{Node,ref2dir(Dir,Spec)} || Dir <- Dirs]; +handle_data(include,Node,Dir=[Ch|_],Spec) when is_integer(Ch) -> + handle_data(include,Node,[Dir],Spec); +handle_data(config,Node,File=[Ch|_],Spec) when is_integer(Ch) -> + handle_data(config,Node,[File],Spec); +handle_data(config,Node,{CfgDir,File=[Ch|_]},Spec) when is_integer(Ch) -> + handle_data(config,Node,{CfgDir,[File]},Spec); +handle_data(config,Node,Files=[F|_],Spec) when is_list(F) -> + [{Node,get_absfile(File,Spec)} || File <- Files]; +handle_data(config,Node,{CfgDir,Files=[F|_]},Spec) when is_list(F) -> + [{Node,filename:join(ref2dir(CfgDir,Spec),File)} || File <- Files]; +handle_data(userconfig,Node,CBs,Spec) when is_list(CBs) -> + [{Node,{Callback,get_absfile(Callback,Config,Spec)}} || + {Callback,Config} <- CBs]; +handle_data(userconfig,Node,CB,Spec) when is_tuple(CB) -> + handle_data(userconfig,Node,[CB],Spec); +handle_data(event_handler,Node,H,Spec) when is_atom(H) -> + handle_data(event_handler,Node,{[H],[]},Spec); +handle_data(event_handler,Node,{H,Args},Spec) when is_atom(H) -> + handle_data(event_handler,Node,{[H],Args},Spec); +handle_data(event_handler,Node,Hs,_Spec) when is_list(Hs) -> + [{Node,EvH,[]} || EvH <- Hs]; +handle_data(event_handler,Node,{Hs,Args},_Spec) when is_list(Hs) -> + [{Node,EvH,Args} || EvH <- Hs]; +handle_data(ct_hooks,Node,Hooks,_Spec) when is_list(Hooks) -> + [{Node,Hook} || Hook <- Hooks ]; +handle_data(ct_hooks,Node,Hook,_Spec) -> + [{Node,Hook}]; +handle_data(stylesheet,Node,CSSFile,Spec) -> + [{Node,get_absfile(CSSFile,Spec)}]; +handle_data(verbosity,Node,VLvls,_Spec) when is_integer(VLvls) -> + [{Node,[{'$unspecified',VLvls}]}]; +handle_data(verbosity,Node,VLvls,_Spec) when is_list(VLvls) -> + VLvls1 = lists:map(fun(VLvl = {_Cat,_Lvl}) -> VLvl; + (Lvl) -> {'$unspecified',Lvl} end, VLvls), + [{Node,VLvls1}]; +handle_data(_Tag,Node,Data,_Spec) -> + [{Node,Data}]. + +%% check if duplicates should be saved or not +should_be_added(Tag,Node,_Data,Spec) -> + if + %% list terms *without* possible duplicates here + Tag == logdir; Tag == logopts; + Tag == basic_html; Tag == label; + Tag == auto_compile; Tag == stylesheet; + Tag == verbosity -> + lists:keymember(ref2node(Node,Spec#testspec.nodes),1, + read_field(Spec,Tag)) == false; + %% for terms *with* possible duplicates + true -> + true + end. + +%% check if previous elements for Node should be deleted +update_recorded(Tag,Node,Spec) -> + if Tag == config; Tag == userconfig; Tag == event_handler; + Tag == ct_hooks; Tag == include -> + read_field(Spec,Tag); + true -> + %% delete previous value for Tag + lists:keydelete(Node,1,read_field(Spec,Tag)) + end. + +%% create one test term per node separate(Nodes,Tag,Data,Tests,Refs) -> Separated = separate(Nodes,Tag,Data,Refs), Separated ++ Tests. @@ -886,7 +976,25 @@ separate([N|Ns],Tag,Data,Refs) -> [list_to_tuple([Tag,ref2node(N,Refs)|Data])|separate(Ns,Tag,Data,Refs)]; separate([],_,_,_) -> []. - + +%% read the value for FieldName in record Rec#testspec +read_field(Rec, FieldName) -> + catch lists:foldl(fun(F, Pos) when F == FieldName -> + throw(element(Pos, Rec)); + (_,Pos) -> + Pos+1 + end,2,?testspec_fields). + +%% modify the value for FieldName in record Rec#testspec +mod_field(Rec, FieldName, NewVal) -> + [_testspec|RecList] = tuple_to_list(Rec), + RecList1 = + (catch lists:foldl(fun(F, {Prev,[_OldVal|Rest]}) when F == FieldName -> + throw(lists:reverse(Prev) ++ [NewVal|Rest]); + (_,{Prev,[Field|Rest]}) -> + {[Field|Prev],Rest} + end,{[],RecList},?testspec_fields)), + list_to_tuple([testspec|RecList1]). %% Representation: %% {{Node,Dir},[{Suite1,[GrOrCase11,GrOrCase12,...]}, @@ -1094,33 +1202,40 @@ ref2node(all_nodes,_Refs) -> ref2node(master,_Refs) -> master; ref2node(RefOrNode,Refs) -> - case string:chr(atom_to_list(RefOrNode),$@) of - 0 -> % a ref + case lists:member($@,atom_to_list(RefOrNode)) of + false -> % a ref case lists:keysearch(RefOrNode,1,Refs) of {value,{RefOrNode,Node}} -> Node; false -> throw({error,{noderef_missing,RefOrNode}}) end; - _ -> % a node + true -> % a node RefOrNode end. -ref2dir(Ref,Refs) when is_atom(Ref) -> +ref2dir(Ref,Spec) -> + ref2dir(Ref,Spec#testspec.alias,Spec). + +ref2dir(Ref,Refs,Spec) when is_atom(Ref) -> case lists:keysearch(Ref,1,Refs) of {value,{Ref,Dir}} -> - Dir; + get_absdir(Dir,Spec); false -> throw({error,{alias_missing,Ref}}) end; -ref2dir(Dir,_) when is_list(Dir) -> - Dir. - -is_noderef(What,Nodes) when is_atom(What) -> - is_noderef([What],Nodes); -is_noderef([master|_],_Nodes) -> +ref2dir(Dir,_,Spec) when is_list(Dir) -> + get_absdir(Dir,Spec); +ref2dir(What,_,_) -> + throw({error,{invalid_directory_name,What}}). + +is_node(What,Nodes) when is_atom(What) -> + is_node([What],Nodes); +is_node([master|_],_Nodes) -> true; -is_noderef([What|_],Nodes) -> +is_node(What={N,H},Nodes) when is_atom(N), is_atom(H) -> + is_node([What],Nodes); +is_node([What|_],Nodes) -> case lists:keymember(What,1,Nodes) or lists:keymember(What,2,Nodes) of true -> @@ -1128,24 +1243,30 @@ is_noderef([What|_],Nodes) -> false -> false end; -is_noderef([],_) -> +is_node([],_) -> false. valid_terms() -> [ + {define,3}, {node,3}, {cover,2}, {cover,3}, {config,2}, {config,3}, + {config,4}, {userconfig,2}, {userconfig,3}, {alias,3}, - {merge_tests,1}, + {merge_tests,2}, {logdir,2}, {logdir,3}, {logopts,2}, {logopts,3}, + {basic_html,2}, + {basic_html,3}, + {verbosity,2}, + {verbosity,3}, {label,2}, {label,3}, {event_handler,2}, @@ -1153,13 +1274,18 @@ valid_terms() -> {event_handler,4}, {ct_hooks,2}, {ct_hooks,3}, - {enable_builtin_hooks,1}, + {enable_builtin_hooks,2}, + {noinput,2}, {multiply_timetraps,2}, {multiply_timetraps,3}, {scale_timetraps,2}, {scale_timetraps,3}, {include,2}, {include,3}, + {auto_compile,2}, + {auto_compile,3}, + {stylesheet,2}, + {stylesheet,3}, {suites,3}, {suites,4}, {groups,4}, @@ -1174,7 +1300,8 @@ valid_terms() -> {skip_groups,7}, {skip_cases,5}, {skip_cases,6}, - {create_priv_dir,2} + {create_priv_dir,2}, + {create_priv_dir,3} ]. %% this function "guesses" if the user has misspelled a term name diff --git a/lib/common_test/src/ct_util.erl b/lib/common_test/src/ct_util.erl index e9bfb2590b..efc85543ac 100644 --- a/lib/common_test/src/ct_util.erl +++ b/lib/common_test/src/ct_util.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,7 +25,8 @@ %%% -module(ct_util). --export([start/0,start/1,start/2,stop/1,update_last_run_index/0]). +-export([start/0,start/1,start/2,start/3, + stop/1,update_last_run_index/0]). -export([register_connection/4,unregister_connection/1, does_connection_exist/3,get_key_from_name/1]). @@ -36,14 +37,15 @@ save_suite_data_async/3, save_suite_data_async/2, read_suite_data/1, delete_suite_data/0, delete_suite_data/1, match_delete_suite_data/1, - delete_testdata/0, delete_testdata/1, set_testdata/1, get_testdata/1, + delete_testdata/0, delete_testdata/1, + set_testdata/1, get_testdata/1, get_testdata/2, set_testdata_async/1, update_testdata/2]). -export([override_silence_all_connections/0, override_silence_connections/1, get_overridden_silenced_connections/0, delete_overridden_silenced_connections/0, - silence_all_connections/0, silence_connections/1, is_silenced/1, - reset_silent_connections/0]). + silence_all_connections/0, silence_connections/1, + is_silenced/1, is_silenced/2, reset_silent_connections/0]). -export([get_mode/0, create_table/3, read_opts/0]). @@ -64,9 +66,13 @@ -export([get_profile_data/0, get_profile_data/1, get_profile_data/2, open_url/3]). +-include("ct.hrl"). -include("ct_event.hrl"). -include("ct_util.hrl"). +-define(default_verbosity, [{default,?MAX_VERBOSITY}, + {'$unspecified',?MAX_VERBOSITY}]). + -record(suite_data, {key,name,value}). %%%----------------------------------------------------------------- @@ -85,18 +91,21 @@ %%% %%% @see ct start() -> - start(normal,"."). + start(normal, ".", ?default_verbosity). start(LogDir) when is_list(LogDir) -> - start(normal,LogDir); + start(normal, LogDir, ?default_verbosity); start(Mode) -> - start(Mode,"."). + start(Mode, ".", ?default_verbosity). + +start(LogDir, Verbosity) when is_list(LogDir) -> + start(normal, LogDir, Verbosity). -start(Mode,LogDir) -> +start(Mode, LogDir, Verbosity) -> case whereis(ct_util_server) of undefined -> S = self(), - Pid = spawn_link(fun() -> do_start(S,Mode,LogDir) end), + Pid = spawn_link(fun() -> do_start(S, Mode, LogDir, Verbosity) end), receive {Pid,started} -> Pid; {Pid,Error} -> exit(Error); @@ -113,7 +122,7 @@ start(Mode,LogDir) -> end end. -do_start(Parent,Mode,LogDir) -> +do_start(Parent, Mode, LogDir, Verbosity) -> process_flag(trap_exit,true), register(ct_util_server,self()), create_table(?conn_table,#conn.handle), @@ -173,7 +182,7 @@ do_start(Parent,Mode,LogDir) -> false -> ok end, - {StartTime,TestLogDir} = ct_logs:init(Mode), + {StartTime,TestLogDir} = ct_logs:init(Mode, Verbosity), ct_event:notify(#event{name=test_start, node=node(), @@ -193,7 +202,7 @@ do_start(Parent,Mode,LogDir) -> self() ! {{stop,{self(),{user_error,CTHReason}}}, {Parent,make_ref()}} end, - loop(Mode,[],StartDir). + loop(Mode, [{{verbosity,Cat},Lvl} || {Cat,Lvl} <- Verbosity], StartDir). create_table(TableName,KeyPos) -> create_table(TableName,set,KeyPos). @@ -254,6 +263,9 @@ set_testdata_async(TestData) -> get_testdata(Key) -> call({get_testdata, Key}). +get_testdata(Key, Timeout) -> + call({get_testdata, Key}, Timeout). + set_cwd(Dir) -> call({set_cwd,Dir}). @@ -369,14 +381,25 @@ loop(Mode,TestData,StartDir) -> {'EXIT',_Pid,normal} -> loop(Mode,TestData,StartDir); {'EXIT',Pid,Reason} -> - %% Let process crash in case of error, this shouldn't happen! - io:format("\n\nct_util_server got EXIT from ~p: ~p\n\n", - [Pid,Reason]), - file:set_cwd(StartDir), - exit(Reason) + case ets:lookup(?conn_table,Pid) of + [#conn{address=A,callback=CB}] -> + %% A connection crashed - remove the connection but don't die + ct_logs:tc_log_async(ct_error_notify, + "Connection process died: " + "Pid: ~p, Address: ~p, Callback: ~p\n" + "Reason: ~p\n\n", + [Pid,A,CB,Reason]), + catch CB:close(Pid), + loop(Mode,TestData,StartDir); + _ -> + %% Let process crash in case of error, this shouldn't happen! + io:format("\n\nct_util_server got EXIT from ~p: ~p\n\n", + [Pid,Reason]), + file:set_cwd(StartDir), + exit(Reason) + end end. - close_connections([#conn{handle=Handle,callback=CB}|Conns]) -> CB:close(Handle), close_connections(Conns); @@ -508,7 +531,7 @@ close_connections() -> %%% %%% @doc override_silence_all_connections() -> - Protocols = [telnet,ftp,rpc,snmp], + Protocols = [telnet,ftp,rpc,snmp,ssh], override_silence_connections(Protocols), Protocols. @@ -545,7 +568,10 @@ silence_connections(Conns) when is_list(Conns) -> set_testdata({silent_connections,Conns1}). is_silenced(Conn) -> - case get_testdata(silent_connections) of + is_silenced(Conn, infinity). + +is_silenced(Conn, Timeout) -> + case get_testdata(silent_connections, Timeout) of Conns when is_list(Conns) -> case lists:keysearch(Conn,1,Conns) of {value,{Conn,true}} -> @@ -553,6 +579,8 @@ is_silenced(Conn) -> _ -> false end; + Error = {error,_} -> + Error; _ -> false end. @@ -827,19 +855,28 @@ get_profile_data(Profile, Key, StartDir) -> %%%----------------------------------------------------------------- %%% Internal functions call(Msg) -> - case whereis(ct_util_server) of - undefined -> + call(Msg, infinity). + +call(Msg, Timeout) -> + case {self(),whereis(ct_util_server)} of + {_,undefined} -> {error,ct_util_server_not_running}; - Pid -> + {Pid,Pid} -> + %% the caller is ct_util_server, which must + %% be a mistake + {error,bad_invocation}; + {Self,Pid} -> MRef = erlang:monitor(process, Pid), Ref = make_ref(), - ct_util_server ! {Msg,{self(),Ref}}, + ct_util_server ! {Msg,{Self,Ref}}, receive {Ref, Result} -> erlang:demonitor(MRef, [flush]), Result; {'DOWN',MRef,process,_,Reason} -> {error,{ct_util_server_down,Reason}} + after + Timeout -> {error,timeout} end end. diff --git a/lib/common_test/src/ct_util.hrl b/lib/common_test/src/ct_util.hrl index 082599a9c6..7015014441 100644 --- a/lib/common_test/src/ct_util.hrl +++ b/lib/common_test/src/ct_util.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -34,13 +34,18 @@ profile=[], logdir=["."], logopts=[], + basic_html=[], + verbosity=[], cover=[], config=[], userconfig=[], event_handler=[], ct_hooks=[], enable_builtin_hooks=true, + noinput=false, include=[], + auto_compile=[], + stylesheet=[], multiply_timetraps=[], scale_timetraps=[], create_priv_dir=[], @@ -64,3 +69,11 @@ -define(ct_config_txt, ct_config_plain). -define(ct_profile_file, ".common_test"). + +-define(css_default, "ct_default.css"). +-define(sortable_table_name, "SortableTable"). +-define(jquery_script, "jquery-latest.js"). +-define(tablesorter_script, "jquery.tablesorter.min.js"). + +%% Logging information for error handler +-record(conn_log, {client, name, address, action, module}). diff --git a/lib/common_test/src/cth_conn_log.erl b/lib/common_test/src/cth_conn_log.erl new file mode 100644 index 0000000000..3af89db3a5 --- /dev/null +++ b/lib/common_test/src/cth_conn_log.erl @@ -0,0 +1,124 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%%---------------------------------------------------------------------- +%% CT hook for logging of connections. +%% +%% HookOptions can be hardcoded in the test suite: +%% +%% suite() -> +%% [{ct_hooks, [{cth_conn_log, +%% [{ct_netconfc:conn_mod(),ct_netconfc:hook_options()}]}]}]. +%% +%% or specified in a configuration file: +%% +%% {ct_conn_log,[{ct_netconfc:conn_mod(),ct_netconfc:hook_options()}]}. +%% +%% The conn_mod() is the common test module implementing the protocol, +%% e.g. ct_netconfc, ct_telnet, etc. This module must log by calling +%% +%% error_logger:info_report(ConnLogInfo,Data). +%% ConnLogInfo = #conn_log{} | {ct_connection,Action,ConnName} +%% Action = open | close | send | recv | term() +%% ConnName = atom() - The 'KeyOrName' argument used when opening the connection +%% +%% ct_conn_log_h will print to html log or separate file (depending on +%% log_type() option). conn_mod() must implement and export +%% +%% format_data(log_type(), Data). +%% +%% If logging to separate file, ct_conn_log_h will also log error +%% reports which are witten like this: +%% +%% error_logger:error_report([{ct_connection,ConnName} | Report]). +%% +%%---------------------------------------------------------------------- +-module(cth_conn_log). + +-include_lib("common_test/include/ct.hrl"). + +-export([init/2, + pre_init_per_testcase/3, + post_end_per_testcase/4]). + +-spec init(Id, HookOpts) -> Result when + Id :: term(), + HookOpts :: ct:hook_options(), + Result :: {ok,[{ct_netconfc:conn_mod(), + {ct_netconfc:log_type(),[ct_netconfc:key_or_name()]}}]}. +init(_Id, HookOpts) -> + ConfOpts = ct:get_config(ct_conn_log,[]), + {ok,merge_log_info(ConfOpts,HookOpts)}. + +merge_log_info([{Mod,ConfOpts}|ConfList],HookList) -> + {Opts,HookList1} = + case lists:keytake(Mod,1,HookList) of + false -> + {ConfOpts,HookList}; + {value,{_,HookOpts},HL1} -> + {ConfOpts ++ HookOpts, HL1} % ConfOpts overwrites HookOpts! + end, + [{Mod,get_log_opts(Opts)} | merge_log_info(ConfList,HookList1)]; +merge_log_info([],HookList) -> + [{Mod,get_log_opts(Opts)} || {Mod,Opts} <- HookList]. + +get_log_opts(Opts) -> + LogType = proplists:get_value(log_type,Opts,html), + Hosts = proplists:get_value(hosts,Opts,[]), + {LogType,Hosts}. + + +pre_init_per_testcase(TestCase,Config,CthState) -> + Logs = + lists:map( + fun({ConnMod,{LogType,Hosts}}) -> + case LogType of + LogType when LogType==raw; LogType==pretty -> + Dir = ?config(priv_dir,Config), + TCStr = atom_to_list(TestCase), + ConnModStr = atom_to_list(ConnMod), + DefLogName = TCStr ++ "-" ++ ConnModStr ++ ".txt", + DefLog = filename:join(Dir,DefLogName), + Ls = [{Host, + filename:join(Dir,TCStr ++ "-"++ + atom_to_list(Host) ++ "-" ++ + ConnModStr ++ + ".txt")} + || Host <- Hosts] + ++[{default,DefLog}], + Str = + "<table borders=1>" + "<b>" ++ ConnModStr ++ " logs:</b>\n" ++ + [io_lib:format( + "<tr><td>~p</td><td><a href=~p>~s</a></td></tr>", + [S,L,filename:basename(L)]) + || {S,L} <- Ls] ++ + "</table>", + io:format(Str,[]), + {ConnMod,{LogType,Ls}}; + _ -> + {ConnMod,{LogType,[]}} + end + end, + CthState), + error_logger:add_report_handler(ct_conn_log_h,{group_leader(),Logs}), + {Config,CthState}. + +post_end_per_testcase(_TestCase,_Config,Return,CthState) -> + error_logger:delete_report_handler(ct_conn_log_h), + {Return,CthState}. diff --git a/lib/common_test/src/cth_log_redirect.erl b/lib/common_test/src/cth_log_redirect.erl index ea79251cfc..77f57c6195 100644 --- a/lib/common_test/src/cth_log_redirect.erl +++ b/lib/common_test/src/cth_log_redirect.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 diff --git a/lib/common_test/src/cth_surefire.erl b/lib/common_test/src/cth_surefire.erl new file mode 100644 index 0000000000..e7bd84e51b --- /dev/null +++ b/lib/common_test/src/cth_surefire.erl @@ -0,0 +1,219 @@ +%%% @doc Common Test Framework functions handling test specifications. +%%% +%%% <p>This module creates a junit report of the test run if plugged in +%%% as a suite_callback.</p> + +-module(cth_surefire). + +%% Suite Callbacks +-export([id/1, init/2]). + +-export([pre_init_per_suite/3]). +-export([post_init_per_suite/4]). +-export([pre_end_per_suite/3]). +-export([post_end_per_suite/4]). + +-export([pre_init_per_group/3]). +-export([post_init_per_group/4]). +-export([pre_end_per_group/3]). +-export([post_end_per_group/4]). + +-export([pre_init_per_testcase/3]). +-export([post_end_per_testcase/4]). + +-export([on_tc_fail/3]). +-export([on_tc_skip/3]). + +-export([terminate/1]). + +-record(state, { filepath, axis, properties, package, hostname, + curr_suite, curr_suite_ts, curr_group = [], curr_tc, + curr_log_dir, timer, tc_log, + test_cases = [], + test_suites = [] }). + +-record(testcase, { log, group, classname, name, time, failure, timestamp }). +-record(testsuite, { errors, failures, hostname, name, tests, + time, timestamp, id, package, + properties, testcases }). + +id(Opts) -> + filename:absname(proplists:get_value(path, Opts, "junit_report.xml")). + +init(Path, Opts) -> + {ok, Host} = inet:gethostname(), + #state{ filepath = Path, + hostname = proplists:get_value(hostname,Opts,Host), + package = proplists:get_value(package,Opts), + axis = proplists:get_value(axis,Opts,[]), + properties = proplists:get_value(properties,Opts,[]), + timer = now() }. + +pre_init_per_suite(Suite,Config,#state{ test_cases = [] } = State) -> + {Config, init_tc(State#state{ curr_suite = Suite, curr_suite_ts = now() }, + Config) }; +pre_init_per_suite(Suite,Config,State) -> + %% Have to close the previous suite + pre_init_per_suite(Suite,Config,close_suite(State)). + +post_init_per_suite(_Suite,Config, Result, State) -> + {Result, end_tc(init_per_suite,Config,Result,State)}. + +pre_end_per_suite(_Suite,Config,State) -> {Config, init_tc(State, Config)}. + +post_end_per_suite(_Suite,Config,Result,State) -> + {Result, end_tc(end_per_suite,Config,Result,State)}. + +pre_init_per_group(Group,Config,State) -> + {Config, init_tc(State#state{ curr_group = [Group|State#state.curr_group]}, + Config)}. + +post_init_per_group(_Group,Config,Result,State) -> + {Result, end_tc(init_per_group,Config,Result,State)}. + +pre_end_per_group(_Group,Config,State) -> {Config, init_tc(State, Config)}. + +post_end_per_group(_Group,Config,Result,State) -> + NewState = end_tc(end_per_group, Config, Result, State), + {Result, NewState#state{ curr_group = tl(NewState#state.curr_group)}}. + +pre_init_per_testcase(_TC,Config,State) -> {Config, init_tc(State, Config)}. + +post_end_per_testcase(TC,Config,Result,State) -> + {Result, end_tc(TC,Config, Result,State)}. + +on_tc_fail(_TC, _Res, State = #state{test_cases = []}) -> + State; +on_tc_fail(_TC, Res, State) -> + TCs = State#state.test_cases, + TC = hd(TCs), + NewTC = TC#testcase{ + failure = + {fail,lists:flatten(io_lib:format("~p",[Res]))} }, + State#state{ test_cases = [NewTC | tl(TCs)]}. + +on_tc_skip(Tc,{Type,Reason} = Res, State) when Type == tc_auto_skip -> + do_tc_skip(Res, end_tc(Tc,[],Res,init_tc(State,[]))); +on_tc_skip(_Tc, _Res, State = #state{test_cases = []}) -> + State; +on_tc_skip(_Tc, Res, State) -> + do_tc_skip(Res, State). + +do_tc_skip(Res, State) -> + TCs = State#state.test_cases, + TC = hd(TCs), + NewTC = TC#testcase{ + failure = + {skipped,lists:flatten(io_lib:format("~p",[Res]))} }, + State#state{ test_cases = [NewTC | tl(TCs)]}. + +init_tc(State, Config) when is_list(Config) == false -> + State#state{ timer = now(), tc_log = "" }; +init_tc(State, Config) -> + State#state{ timer = now(), + tc_log = proplists:get_value(tc_logfile, Config, [])}. + +end_tc(Func, Config, Res, State) when is_atom(Func) -> + end_tc(atom_to_list(Func), Config, Res, State); +end_tc(Name, _Config, _Res, State = #state{ curr_suite = Suite, + curr_group = Groups, + timer = TS, tc_log = Log } ) -> + ClassName = atom_to_list(Suite), + PGroup = string:join([ atom_to_list(Group)|| + Group <- lists:reverse(Groups)],"."), + TimeTakes = io_lib:format("~f",[timer:now_diff(now(),TS) / 1000000]), + State#state{ test_cases = [#testcase{ log = Log, + timestamp = now_to_string(TS), + classname = ClassName, + group = PGroup, + name = Name, + time = TimeTakes, + failure = passed }| State#state.test_cases]}. +close_suite(#state{ test_cases = [] } = State) -> + State; +close_suite(#state{ test_cases = TCs } = State) -> + Total = length(TCs), + Succ = length(lists:filter(fun(#testcase{ failure = F }) -> + F == passed + end,TCs)), + Fail = Total - Succ, + TimeTaken = timer:now_diff(now(),State#state.curr_suite_ts) / 1000000, + Suite = #testsuite{ name = atom_to_list(State#state.curr_suite), + package = State#state.package, + time = io_lib:format("~f",[TimeTaken]), + timestamp = now_to_string(State#state.curr_suite_ts), + errors = Fail, tests = Total, + testcases = lists:reverse(TCs) }, + State#state{ test_cases = [], + test_suites = [Suite | State#state.test_suites]}. + +terminate(State = #state{ test_cases = [] }) -> + {ok,D} = file:open(State#state.filepath,[write,{encoding,utf8}]), + io:format(D, "<?xml version=\"1.0\" encoding= \"UTF-8\" ?>", []), + io:format(D, to_xml(State), []), + catch file:sync(D), + catch file:close(D); +terminate(State) -> + %% Have to close the last suite + terminate(close_suite(State)). + + + +to_xml(#testcase{ group = Group, classname = CL, log = L, name = N, time = T, timestamp = TS, failure = F}) -> + ["<testcase ", + [["group=\"",Group,"\""]||Group /= ""]," " + "name=\"",N,"\" " + "time=\"",T,"\" " + "timestamp=\"",TS,"\" " + "log=\"",L,"\">", + case F of + passed -> + []; + {skipped,Reason} -> + ["<skipped type=\"skip\" message=\"Test ",N," in ",CL, + " skipped!\">", sanitize(Reason),"</skipped>"]; + {fail,Reason} -> + ["<failure message=\"Test ",N," in ",CL," failed!\" type=\"crash\">", + sanitize(Reason),"</failure>"] + end,"</testcase>"]; +to_xml(#testsuite{ package = P, hostname = H, errors = E, time = Time, + timestamp = TS, tests = T, name = N, testcases = Cases }) -> + ["<testsuite ", + [["package=\"",P,"\" "]||P /= undefined], + [["hostname=\"",P,"\" "]||H /= undefined], + [["name=\"",N,"\" "]||N /= undefined], + [["time=\"",Time,"\" "]||Time /= undefined], + [["timestamp=\"",TS,"\" "]||TS /= undefined], + "errors=\"",integer_to_list(E),"\" " + "tests=\"",integer_to_list(T),"\">", + [to_xml(Case) || Case <- Cases], + "</testsuite>"]; +to_xml(#state{ test_suites = TestSuites, axis = Axis, properties = Props }) -> + ["<testsuites>",properties_to_xml(Axis,Props), + [to_xml(TestSuite) || TestSuite <- TestSuites],"</testsuites>"]. + +properties_to_xml(Axis,Props) -> + ["<properties>", + [["<property name=\"",Name,"\" axis=\"yes\" value=\"",Value,"\" />"] || {Name,Value} <- Axis], + [["<property name=\"",Name,"\" value=\"",Value,"\" />"] || {Name,Value} <- Props], + "</properties>" + ]. + +sanitize([$>|T]) -> + ">" ++ sanitize(T); +sanitize([$<|T]) -> + "<" ++ sanitize(T); +sanitize([$"|T]) -> + """ ++ sanitize(T); +sanitize([$'|T]) -> + "'" ++ sanitize(T); +sanitize([$&|T]) -> + "&" ++ sanitize(T); +sanitize([H|T]) -> + [H|sanitize(T)]; +sanitize([]) -> + []. + +now_to_string(Now) -> + {{YY,MM,DD},{HH,Mi,SS}} = calendar:now_to_local_time(Now), + io_lib:format("~p-~2..0B-~2..0BT~2..0B:~2..0B:~2..0B",[YY,MM,DD,HH,Mi,SS]). diff --git a/lib/common_test/src/vts.erl b/lib/common_test/src/vts.erl index cc8a932887..b340c6fdd1 100644 --- a/lib/common_test/src/vts.erl +++ b/lib/common_test/src/vts.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -766,10 +766,6 @@ report1(tc_done,{_Suite,init_per_group,_},State) -> State; report1(tc_done,{_Suite,end_per_group,_},State) -> State; -report1(tc_done,{_Suite,ct_init_per_group,_},State) -> - State; -report1(tc_done,{_Suite,ct_end_per_group,_},State) -> - State; report1(tc_done,{_Suite,_Case,ok},State) -> State#state{ok=State#state.ok+1}; report1(tc_done,{_Suite,_Case,{failed,_Reason}},State) -> diff --git a/lib/common_test/test/Makefile b/lib/common_test/test/Makefile index 332c444145..7628ada61a 100644 --- a/lib/common_test/test/Makefile +++ b/lib/common_test/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2008-2011. All Rights Reserved. +# Copyright Ericsson AB 2008-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -39,13 +39,18 @@ MODULES= \ ct_sequence_1_SUITE \ ct_repeat_1_SUITE \ ct_testspec_1_SUITE \ + ct_testspec_2_SUITE \ ct_skip_SUITE \ ct_error_SUITE \ ct_test_server_if_1_SUITE \ ct_config_SUITE \ ct_master_SUITE \ ct_misc_1_SUITE \ - ct_hooks_SUITE + ct_hooks_SUITE \ + ct_netconfc_SUITE \ + ct_basic_html_SUITE \ + ct_auto_compile_SUITE \ + ct_verbosity_SUITE ERL_FILES= $(MODULES:%=%.erl) @@ -97,10 +102,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(COVERFILE) $(RELSYSDIR) - $(INSTALL_DATA) common_test.spec $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)" + $(INSTALL_DATA) common_test.spec "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/common_test/test/common_test.spec b/lib/common_test/test/common_test.spec index 8755b08117..8bec66d6f2 100644 --- a/lib/common_test/test/common_test.spec +++ b/lib/common_test/test/common_test.spec @@ -1 +1 @@ -{suites,"../common_test_test",all}.
\ No newline at end of file +{suites,"../common_test_test",all}. diff --git a/lib/common_test/test/ct_auto_compile_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE.erl new file mode 100644 index 0000000000..cc546ed30d --- /dev/null +++ b/lib/common_test/test/ct_auto_compile_SUITE.erl @@ -0,0 +1,187 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_auto_compile_SUITE +%%% +%%% Description: +%%% +%%% +%%% The suites used for the test are located in the data directory. +%%%------------------------------------------------------------------- +-module(ct_auto_compile_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ac_flag, ac_spec]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +ac_flag(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + file:copy(filename:join(DataDir, "bad_SUITE.erl"), + filename:join(PrivDir, "bad_SUITE.erl")), + Suite = filename:join(DataDir, "dummy_SUITE"), + compile:file(Suite, [{outdir,PrivDir}]), + {Opts,ERPid} = setup([{dir,PrivDir}, + {auto_compile,false}, + {label,"ac_flag"}], + Config), + + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(ac_flag, + reformat(Events, ?eh), + PrivDir, + Opts), + + TestEvents = events_to_check(ac_flag), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + +%%%----------------------------------------------------------------- +%%% +ac_spec(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + file:copy(filename:join(DataDir, "bad_SUITE.erl"), + filename:join(PrivDir, "bad_SUITE.erl")), + TestSpec = [{label,ac_spec}, + {auto_compile,false}, + {suites,PrivDir,all}], + FileName = filename:join(?config(priv_dir, Config),"ac_spec.spec"), + {ok,Dev} = file:open(FileName, [write]), + [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec], + file:close(Dev), + + {Opts,ERPid} = setup([{spec,FileName}], Config), + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(ac_spec, + reformat(Events, ?eh), + PrivDir, + Opts), + + TestEvents = events_to_check(ac_spec), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + %reformat(Events, _EH) -> + % Events. + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test) -> + %% 2 tests (ct:run_test + script_start) is default + events_to_check(Test, 2). + +events_to_check(_, 0) -> + []; +events_to_check(Test, N) -> + test_events(Test) ++ events_to_check(Test, N-1). + +test_events(ac_flag) -> + [ + {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}}, + {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {ct_test_support_eh,start_info,{1,1,3}}, + {ct_test_support_eh,tc_start,{dummy_SUITE,init_per_suite}}, + {ct_test_support_eh,tc_done,{dummy_SUITE,init_per_suite,ok}}, + {ct_test_support_eh,test_stats,{1,1,{1,0}}}, + {ct_test_support_eh,tc_start,{dummy_SUITE,end_per_suite}}, + {ct_test_support_eh,tc_done,{dummy_SUITE,end_per_suite,ok}}, + {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}}, + {ct_test_support_eh,stop_logging,[]} + ]; + +test_events(ac_spec) -> + [ + {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}}, + {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {ct_test_support_eh,start_info,{1,1,3}}, + {ct_test_support_eh,tc_start,{dummy_SUITE,init_per_suite}}, + {ct_test_support_eh,tc_done,{dummy_SUITE,init_per_suite,ok}}, + {ct_test_support_eh,test_stats,{1,1,{1,0}}}, + {ct_test_support_eh,tc_start,{dummy_SUITE,end_per_suite}}, + {ct_test_support_eh,tc_done,{dummy_SUITE,end_per_suite,ok}}, + {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}}, + {ct_test_support_eh,stop_logging,[]} + ]. diff --git a/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl new file mode 100644 index 0000000000..6ebcb3570e --- /dev/null +++ b/lib/common_test/test/ct_auto_compile_SUITE_data/bad_SUITE.erl @@ -0,0 +1,23 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% + +-module(bad_SUITE). + +-compile(export_all). + +bad_bad_suite diff --git a/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl b/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl new file mode 100644 index 0000000000..0b1eafc31d --- /dev/null +++ b/lib/common_test/test/ct_auto_compile_SUITE_data/dummy_SUITE.erl @@ -0,0 +1,130 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% + +-module(dummy_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% @spec suite() -> Info +%% Info = [tuple()] +%% @end +%%-------------------------------------------------------------------- +suite() -> + [{timetrap,{seconds,30}}]. + +%%-------------------------------------------------------------------- +%% @spec init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_suite(Config0) -> void() | {save_config,Config1} +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_group(GroupName, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_group(GroupName, Config0) -> +%% void() | {save_config,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_group(_GroupName, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} | {fail,Reason} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec groups() -> [Group] +%% Group = {GroupName,Properties,GroupsAndTestCases} +%% GroupName = atom() +%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}] +%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase] +%% TestCase = atom() +%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}} +%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | +%% repeat_until_any_ok | repeat_until_any_fail +%% N = integer() | forever +%% @end +%%-------------------------------------------------------------------- +groups() -> + []. + +%%-------------------------------------------------------------------- +%% @spec all() -> GroupsAndTestCases | {skip,Reason} +%% GroupsAndTestCases = [{group,GroupName} | TestCase] +%% GroupName = atom() +%% TestCase = atom() +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +all() -> + [ok,fail,skip]. + + +ok(_Config) -> + ok. + +fail(Config) -> + tuple_to_list(Config), + ok. + +skip(_Config) -> + {skip,"should be skipped"}. diff --git a/lib/common_test/test/ct_basic_html_SUITE.erl b/lib/common_test/test/ct_basic_html_SUITE.erl new file mode 100644 index 0000000000..a5f2e6197e --- /dev/null +++ b/lib/common_test/test/ct_basic_html_SUITE.erl @@ -0,0 +1,180 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_basic_html_SUITE +%%% +%%% Description: +%%% +%%% +%%% The suites used for the test are located in the data directory. +%%%------------------------------------------------------------------- +-module(ct_basic_html_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [basic_flag, basic_spec]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +basic_flag(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Suites = [filename:join(DataDir, "babbling_SUITE")], + {Opts,ERPid} = setup([{suite,Suites}, + {basic_html,true}, + {label,"basic_flag"}], + Config), + + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(basic_flag, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(basic_flag), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + +%%%----------------------------------------------------------------- +%%% +basic_spec(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + TestSpec = [{label,basic_spec}, + {basic_html,true}, + {suites,DataDir,babbling_SUITE}], + FileName = filename:join(?config(priv_dir, Config),"basic_spec.spec"), + {ok,Dev} = file:open(FileName, [write]), + [io:format(Dev, "~p.~n", [Term]) || Term <- TestSpec], + file:close(Dev), + + {Opts,ERPid} = setup([{spec,FileName}], Config), + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(basic_spec, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(basic_spec), + ok = ct_test_support:verify_events(TestEvents, Events, Config). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + %reformat(Events, _EH) -> + % Events. + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test) -> + %% 2 tests (ct:run_test + script_start) is default + events_to_check(Test, 2). + +events_to_check(_, 0) -> + []; +events_to_check(Test, N) -> + test_events(Test) ++ events_to_check(Test, N-1). + +test_events(basic_flag) -> + [ + {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}}, + {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {ct_test_support_eh,start_info,{1,1,3}}, + {ct_test_support_eh,tc_start,{babbling_SUITE,init_per_suite}}, + {ct_test_support_eh,tc_done,{babbling_SUITE,init_per_suite,ok}}, + {ct_test_support_eh,test_stats,{1,1,{1,0}}}, + {ct_test_support_eh,tc_start,{babbling_SUITE,end_per_suite}}, + {ct_test_support_eh,tc_done,{babbling_SUITE,end_per_suite,ok}}, + {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}}, + {ct_test_support_eh,stop_logging,[]} + ]; + +test_events(basic_spec) -> + [ + {ct_test_support_eh,start_logging,{'DEF','RUNDIR'}}, + {ct_test_support_eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {ct_test_support_eh,start_info,{1,1,3}}, + {ct_test_support_eh,tc_start,{babbling_SUITE,init_per_suite}}, + {ct_test_support_eh,tc_done,{babbling_SUITE,init_per_suite,ok}}, + {ct_test_support_eh,test_stats,{1,1,{1,0}}}, + {ct_test_support_eh,tc_start,{babbling_SUITE,end_per_suite}}, + {ct_test_support_eh,tc_done,{babbling_SUITE,end_per_suite,ok}}, + {ct_test_support_eh,test_done,{'DEF','STOP_TIME'}}, + {ct_test_support_eh,stop_logging,[]} + ]. diff --git a/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl b/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl new file mode 100644 index 0000000000..d67383c606 --- /dev/null +++ b/lib/common_test/test/ct_basic_html_SUITE_data/babbling_SUITE.erl @@ -0,0 +1,130 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% + +-module(babbling_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% @spec suite() -> Info +%% Info = [tuple()] +%% @end +%%-------------------------------------------------------------------- +suite() -> + [{timetrap,{seconds,30}}]. + +%%-------------------------------------------------------------------- +%% @spec init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_suite(Config0) -> void() | {save_config,Config1} +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_group(GroupName, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_group(GroupName, Config0) -> +%% void() | {save_config,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_group(_GroupName, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} | {fail,Reason} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec groups() -> [Group] +%% Group = {GroupName,Properties,GroupsAndTestCases} +%% GroupName = atom() +%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}] +%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase] +%% TestCase = atom() +%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}} +%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | +%% repeat_until_any_ok | repeat_until_any_fail +%% N = integer() | forever +%% @end +%%-------------------------------------------------------------------- +groups() -> + []. + +%%-------------------------------------------------------------------- +%% @spec all() -> GroupsAndTestCases | {skip,Reason} +%% GroupsAndTestCases = [{group,GroupName} | TestCase] +%% GroupName = atom() +%% TestCase = atom() +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +all() -> + [ok,fail,skip]. + + +ok(_Config) -> + ok. + +fail(Config) -> + tuple_to_list(Config), + ok. + +skip(_Config) -> + {skip,"should be skipped"}. diff --git a/lib/common_test/test/ct_error_SUITE.erl b/lib/common_test/test/ct_error_SUITE.erl index 79ed51bc28..338e76264e 100644 --- a/lib/common_test/test/ct_error_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -848,77 +848,124 @@ test_events(timetrap_parallel) -> test_events(timetrap_fun) -> [ {?eh,start_logging,{'DEF','RUNDIR'}}, - {?eh,start_info,{4,4,17}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,start_info,{4,4,24}}, + {?eh,tc_start,{timetrap_4_SUITE,init_per_suite}}, {?eh,tc_done,{timetrap_4_SUITE,init_per_suite,ok}}, {?eh,tc_start,{timetrap_4_SUITE,tc0}}, - {?eh,tc_done, - {timetrap_4_SUITE,tc0,{failed,{timetrap_timeout,1000}}}}, + {?eh,tc_done,{timetrap_4_SUITE,tc0, + {failed,{timetrap_timeout,{'$approx',1000}}}}}, + {?eh,test_stats,{0,1,{0,0}}}, {?eh,tc_start,{timetrap_4_SUITE,tc1}}, - {?eh,tc_done, - {timetrap_4_SUITE,tc1,{failed,{timetrap_timeout,2000}}}}, + {?eh,tc_done,{timetrap_4_SUITE,tc1, + {failed,{timetrap_timeout,{'$approx',2000}}}}}, + {?eh,test_stats,{0,2,{0,0}}}, {?eh,tc_start,{timetrap_4_SUITE,tc2}}, - {?eh,tc_done, - {timetrap_4_SUITE,tc2,{failed,{timetrap_timeout,500}}}}, + {?eh,tc_done,{timetrap_4_SUITE,tc2, + {failed,{timetrap_timeout,{'$approx',500}}}}}, + {?eh,test_stats,{0,3,{0,0}}}, {?eh,tc_start,{timetrap_4_SUITE,tc3}}, - {?eh,tc_done, - {timetrap_4_SUITE,tc3,{failed,{timetrap_timeout,1000}}}}, + {?eh,tc_done,{timetrap_4_SUITE,tc3, + {failed,{timetrap_timeout,{'$approx',1000}}}}}, {?eh,test_stats,{0,4,{0,0}}}, + {?eh,tc_start,{timetrap_4_SUITE,end_per_suite}}, {?eh,tc_done,{timetrap_4_SUITE,end_per_suite,ok}}, + {?eh,tc_start,{timetrap_5_SUITE,init_per_suite}}, {?eh,tc_done,{timetrap_5_SUITE,init_per_suite,ok}}, {?eh,tc_start,{timetrap_5_SUITE,tc0}}, - {?eh,tc_done, - {timetrap_5_SUITE,tc0,{failed,{timetrap_timeout,1000}}}}, + {?eh,tc_done,{timetrap_5_SUITE,tc0, + {failed,{timetrap_timeout,{'$approx',1000}}}}}, {?eh,test_stats,{0,5,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc1}}, - {?eh,tc_done, - {timetrap_5_SUITE,tc1,{skipped,{timetrap_error,kaboom}}}}, + {?eh,tc_done,{timetrap_5_SUITE,tc1,{user_timetrap_error, + {kaboom,'_'}}}}, + {?eh,test_stats,{0,6,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc2}}, - {?eh,tc_done, - {timetrap_5_SUITE,tc2,{skipped,{timetrap_error,kaboom}}}}, + {?eh,tc_done,{timetrap_5_SUITE,tc2,{user_timetrap_error, + {kaboom,'_'}}}}, + {?eh,test_stats,{0,7,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc3}}, - {?eh,tc_done, - {timetrap_5_SUITE,tc3, - {skipped,{invalid_time_format,{timetrap_utils,timetrap_val,[5000]}}}}}, + {?eh,tc_done,{timetrap_5_SUITE,tc3, + {failed,{timetrap_timeout,{'$approx',2000}}}}}, + {?eh,test_stats,{0,8,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc4}}, - {?eh,tc_done, - {timetrap_5_SUITE,tc4,{skipped,{invalid_time_format,'_'}}}}, - {?eh,test_stats,{0,5,{0,4}}}, + {?eh,tc_done,{timetrap_5_SUITE,tc4, + {failed,{timetrap_timeout,{'$approx',500}}}}}, + {?eh,test_stats,{0,9,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc5}}, - {?eh,tc_done, - {timetrap_5_SUITE,tc5,{failed,{timetrap_timeout,1000}}}}, + {?eh,tc_done,{timetrap_5_SUITE,tc5, + {failed,{timetrap_timeout,{'$approx',1000}}}}}, + {?eh,test_stats,{0,10,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc6}}, - {?eh,tc_done, - {timetrap_5_SUITE,tc6,{failed,{timetrap_timeout,1000}}}}, + {?eh,tc_done,{timetrap_5_SUITE,tc6, + {failed,{timetrap_timeout,{'$approx',41000}}}}}, + {?eh,test_stats,{0,11,{0,0}}}, {?eh,tc_start,{timetrap_5_SUITE,tc7}}, - {?eh,tc_done, - {timetrap_5_SUITE,tc7,{failed,{timetrap_timeout,1000}}}}, - {?eh,test_stats,{0,8,{0,4}}}, + {?eh,tc_done,{timetrap_5_SUITE,tc7, + {failed,{timetrap_timeout,{'$approx',3000}}}}}, + {?eh,test_stats,{0,12,{0,0}}}, + {?eh,tc_start,{timetrap_5_SUITE,tc8}}, + {?eh,tc_done,{timetrap_5_SUITE,tc8, + {failed,{timetrap_timeout,{'$approx',7000}}}}}, + {?eh,test_stats,{0,13,{0,0}}}, + {?eh,tc_start,{timetrap_5_SUITE,tc9}}, + {?eh,tc_done,{timetrap_5_SUITE,tc9, + {failed,{timetrap_timeout,{'$approx',2000}}}}}, + {?eh,test_stats,{0,14,{0,0}}}, + {?eh,tc_start,{timetrap_5_SUITE,tc10}}, + {?eh,tc_done,{timetrap_5_SUITE,tc10, + {failed,{timetrap_timeout,{'$approx',1500}}}}}, + {?eh,test_stats,{0,15,{0,0}}}, + {?eh,tc_start,{timetrap_5_SUITE,tc11}}, + {?eh,tc_done,{timetrap_5_SUITE,tc11, + {failed,{timetrap_timeout,{'$approx',1500}}}}}, + {?eh,test_stats,{0,16,{0,0}}}, + {?eh,tc_start,{timetrap_5_SUITE,tc12}}, + {?eh,tc_done,{timetrap_5_SUITE,tc12, + {failed,{timetrap_timeout,{'$approx',1000}}}}}, + {?eh,test_stats,{0,17,{0,0}}}, + {?eh,tc_start,{timetrap_5_SUITE,tc13}}, + {?eh,tc_done,{timetrap_5_SUITE,tc13, + {failed,{timetrap_timeout,{'$approx',500}}}}}, + {?eh,test_stats,{0,18,{0,0}}}, + {?eh,tc_start,{timetrap_5_SUITE,tc14}}, + {?eh,tc_done,{timetrap_5_SUITE,tc14, + {failed,{timetrap_timeout,{'$approx',1000}}}}}, + {?eh,test_stats,{0,19,{0,0}}}, + {?eh,tc_start,{timetrap_5_SUITE,end_per_suite}}, {?eh,tc_done,{timetrap_5_SUITE,end_per_suite,ok}}, {?eh,tc_start,{timetrap_6_SUITE,init_per_suite}}, - {?eh,tc_done, - {timetrap_6_SUITE,init_per_suite,{skipped,{timetrap_error,kaboom}}}}, - {?eh,tc_auto_skip, - {timetrap_6_SUITE,tc0,{timetrap_error,kaboom}}}, - {?eh,test_stats,{0,8,{0,5}}}, - {?eh,tc_auto_skip, - {timetrap_6_SUITE,end_per_suite,{timetrap_error,kaboom}}}, - + {?eh,tc_done,{timetrap_6_SUITE,init_per_suite,{user_timetrap_error, + {kaboom,'_'}}}}, + {?eh,tc_auto_skip,{timetrap_6_SUITE,tc0, + {failed,{timetrap_6_SUITE,init_per_suite, + {user_timetrap_error,{kaboom,'_'}}}}}}, + {?eh,test_stats,{0,19,{0,1}}}, + {?eh,tc_auto_skip,{timetrap_6_SUITE,end_per_suite, + {failed,{timetrap_6_SUITE,init_per_suite, + {user_timetrap_error,{kaboom,'_'}}}}}}, + + {?eh,tc_start,{timetrap_7_SUITE,init_per_suite}}, {?eh,tc_done,{timetrap_7_SUITE,init_per_suite,ok}}, {?eh,tc_start,{timetrap_7_SUITE,tc0}}, - {?eh,tc_done, - {timetrap_7_SUITE,tc0,{failed,{timetrap_timeout,1000}}}}, + {?eh,tc_done,{timetrap_7_SUITE,tc0, + {failed,{timetrap_timeout,{'$approx',7000}}}}}, + {?eh,test_stats,{0,20,{0,1}}}, {?eh,tc_start,{timetrap_7_SUITE,tc1}}, - {?eh,tc_done, - {timetrap_7_SUITE,tc1,{failed,{timetrap_timeout,2000}}}}, + {?eh,tc_done,{timetrap_7_SUITE,tc1, + {failed,{timetrap_timeout,{'$approx',2000}}}}}, + {?eh,test_stats,{0,21,{0,1}}}, {?eh,tc_start,{timetrap_7_SUITE,tc2}}, - {?eh,tc_done, - {timetrap_7_SUITE,tc2,{failed,{timetrap_timeout,500}}}}, + {?eh,tc_done,{timetrap_7_SUITE,tc2, + {failed,{timetrap_timeout,{'$approx',500}}}}}, + {?eh,test_stats,{0,22,{0,1}}}, {?eh,tc_start,{timetrap_7_SUITE,tc3}}, - {?eh,tc_done, - {timetrap_7_SUITE,tc3,{failed,{timetrap_timeout,1000}}}}, - {?eh,test_stats,{0,12,{0,5}}}, + {?eh,tc_done,{timetrap_7_SUITE,tc3, + {failed,{timetrap_timeout,{'$approx',7000}}}}}, + {?eh,test_stats,{0,23,{0,1}}}, + {?eh,tc_start,{timetrap_7_SUITE,end_per_suite}}, {?eh,tc_done,{timetrap_7_SUITE,end_per_suite,ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl index 4e20875505..99c3ed05ec 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/misc_error_1_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl index a44ff6d0bc..a98382965f 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_1_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_5_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_5_SUITE.erl index c5d4b5062e..5b931c351f 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_5_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_5_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -108,7 +108,8 @@ groups() -> %% Reason = term() %%-------------------------------------------------------------------- all() -> - [tc0,tc1,tc2,tc3,tc4,tc5,tc6,tc7]. + [tc0,tc1,tc2,tc3,tc4,tc5,tc6,tc7,tc8,tc9, + tc10,tc11,tc12,tc13,tc14]. tc0(_) -> ct:comment(io_lib:format("TO after ~w sec", [?TO])), @@ -126,30 +127,89 @@ tc2(_) -> exit(this_should_not_execute). tc3() -> - [{timetrap,{timetrap_utils,timetrap_err_mfa,[]}}]. -tc3(_) -> - exit(this_should_not_execute). + [{timetrap,{timetrap_utils,timetrap_val,[{seconds,2}]}}]. +tc3(_) -> + ct:comment("TO after ~2 sec"), + ct:sleep({seconds,10}), + ok. tc4() -> - [{timetrap,fun() -> timetrap_utils:timetrap_err_fun() end}]. -tc4(_) -> - exit(this_should_not_execute). + [{timetrap,fun() -> 500 end}]. +tc4(_) -> + ct:comment("TO after 500 ms"), + ct:sleep({seconds,10}), + ok. tc5() -> + [{timetrap,{timetrap_utils,timetrap_timeout,[1000,ok]}}]. +tc5(_) -> + ct:comment("TO after ~1 sec"), + ct:sleep({seconds,10}), + ok. + +tc6() -> [{timetrap,{timetrap_utils,timetrap_timeout,[{seconds,40}, {seconds,1}]}}]. -tc5(_) -> +tc6(_) -> ct:comment("TO after 40+1 sec"), ct:sleep({seconds,42}), ok. -tc6() -> +tc7() -> + [{timetrap,{timetrap_utils,timetrap_timeout,[1000,2000]}}]. +tc7(_) -> + ct:comment("TO after ~3 sec"), + ct:sleep({seconds,10}), + ok. + +tc8() -> [{timetrap,fun() -> ct:sleep(6000), 1000 end}]. -tc6(_) -> +tc8(_) -> ct:comment("TO after 6+1 sec"), - ct:sleep({seconds,10}). + ct:sleep({seconds,10}), + ok. -tc7(_) -> +tc9() -> + [{timetrap,{timetrap_utils,timetrap_timeout, + [500,fun() -> {seconds,2} end]}}]. +tc9(_) -> + ct:comment("TO after ~2 sec (2.5 sec in reality)"), + ct:sleep({seconds,10}), + ok. + +tc10() -> + [{timetrap,500}]. +tc10(_) -> + ct:timetrap({timetrap_utils,timetrap_val,[1500]}), + ct:comment("TO after ~1.5 sec"), + ct:sleep({seconds,10}), + ok. + +tc11() -> + [{timetrap,2000}]. +tc11(_) -> + ct:timetrap(fun() -> 1500 end), + ct:comment("TO after ~1.5 sec"), + ct:sleep({seconds,10}), + ok. + +tc12() -> + [{timetrap,500}]. +tc12(_) -> + ct:timetrap({timetrap_utils,timetrap_timeout,[1000,ok]}), + ct:comment("TO after ~1 sec"), + ct:sleep({seconds,10}), + ok. + +tc13() -> + [{timetrap,2000}]. +tc13(_) -> + ct:timetrap(fun() -> ct:sleep(500), ok end), + ct:comment("TO after ~500 ms"), + ct:sleep({seconds,10}), + ok. + +tc14(_) -> ct:comment(io_lib:format("TO after ~w sec", [?TO])), ct:sleep({seconds,5}), ok. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_7_SUITE.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_7_SUITE.erl index b25b7770a7..922d49c086 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_7_SUITE.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_7_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -114,7 +114,7 @@ all() -> tc0(_) -> ct:comment(io_lib:format("TO after ~w+~w sec", [?HANG,?TO])), - ct:sleep({seconds,5}), + ct:sleep({seconds,10}), ok. tc1() -> @@ -133,5 +133,5 @@ tc2(_) -> tc3(_) -> ct:comment(io_lib:format("TO after ~w+~w sec", [?HANG,?TO])), - ct:sleep({seconds,5}), + ct:sleep({seconds,10}), ok. diff --git a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_utils.erl b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_utils.erl index fcde6cd701..413ea342a8 100644 --- a/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_utils.erl +++ b/lib/common_test/test/ct_error_SUITE_data/error/test/timetrap_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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,24 +20,15 @@ -module(timetrap_utils). -export([timetrap_val/1, - timetrap_err_fun/0, - timetrap_err_mfa/0, timetrap_exit/1, timetrap_timeout/2]). timetrap_val(Val) -> Val. -timetrap_err_fun() -> - fun() -> 5000 end. - -timetrap_err_mfa() -> - {?MODULE,timetrap_val,[5000]}. - timetrap_exit(Reason) -> exit(Reason). timetrap_timeout(Sleep, Val) -> ct:sleep(Sleep), Val. - diff --git a/lib/common_test/test/ct_group_info_SUITE.erl b/lib/common_test/test/ct_group_info_SUITE.erl index 2da8219196..c56fa952e8 100644 --- a/lib/common_test/test/ct_group_info_SUITE.erl +++ b/lib/common_test/test/ct_group_info_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -440,72 +440,72 @@ test_events(timetrap_all_no_ipg) -> {?eh,tc_done,{group_timetrap_3_SUITE,t1,{failed,{timetrap_timeout,1000}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t11,{failed,{timetrap_timeout,500}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g1,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g1,[{suite,group_timetrap_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t21,{failed,{timetrap_timeout,1500}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g2,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g2,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,tc_done,{group_timetrap_3_SUITE,t2,{failed,{timetrap_timeout,1000}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t41,{failed,{timetrap_timeout,250}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g4,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g4,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,tc_done,{group_timetrap_3_SUITE,t31,{failed,{timetrap_timeout,500}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t51,{failed,{timetrap_timeout,1500}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}], - {?eh,tc_start,{ct_framework,{ct_end_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g5,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g5,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g3,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g3,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,tc_done,{group_timetrap_3_SUITE,t3,{failed,{timetrap_timeout,250}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t61,{failed,{timetrap_timeout,500}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g6,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g6,[{suite,group_timetrap_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t81,{failed,{timetrap_timeout,750}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g8,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g8,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,tc_done,{group_timetrap_3_SUITE,t71,{failed,{timetrap_timeout,500}}}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t91,{failed,{timetrap_timeout,250}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}], - {?eh,tc_start,{ct_framework,{ct_end_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g9,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g9,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g7,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g7,[{suite,group_timetrap_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t101,{failed,{timetrap_timeout,1000}}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g10,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g10,[{suite,group_timetrap_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}, {?eh,tc_done,{group_timetrap_3_SUITE,t111,{failed,{timetrap_timeout,1000}}}}, {?eh,test_stats,{0,14,{0,0}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g11,[{suite,group_timetrap_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g11,[{suite,group_timetrap_3_SUITE}]},ok}}], {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} @@ -779,78 +779,78 @@ test_events(require_no_ipg) -> {?eh,start_info,{1,1,13}}, {?eh,tc_done,{group_require_3_SUITE,t1,ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g1,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g1,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t11,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g1,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g1,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g1,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g2,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g2,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t21,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g2,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g2,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g2,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g3,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g3,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t31,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g3,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g3,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g3,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g4,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g4,[{suite,group_require_3_SUITE}]}, + [{?eh,tc_start,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g4,[{suite,group_require_3_SUITE}]}, {skipped,{require_failed,{name_in_use,common2_alias,common2}}}}}, {?eh,tc_auto_skip,{group_require_3_SUITE,t41, {require_failed,{name_in_use,common2_alias,common2}}}}, {?eh,test_stats,{4,0,{0,1}}}, - {?eh,tc_auto_skip,{ct_framework,ct_end_per_group, + {?eh,tc_auto_skip,{ct_framework,end_per_group, {require_failed,{name_in_use,common2_alias,common2}}}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g5,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g6,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g5,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g6,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t61,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g6,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g6,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g6,[{suite,group_require_3_SUITE}]},ok}}], {?eh,tc_done,{group_require_3_SUITE,t51,ok}}, - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g7,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g7,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t71,ok}}, {?eh,tc_done,{group_require_3_SUITE,t72,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g7,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}], - {?eh,tc_start,{ct_framework,{ct_end_per_group,g5,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g7,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g7,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g5,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g5,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g8,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g8,[{suite,group_require_3_SUITE}]}, + [{?eh,tc_start,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g8,[{suite,group_require_3_SUITE}]}, {skipped,{require_failed,{not_available,non_existing}}}}}, {?eh,tc_auto_skip,{group_require_3_SUITE,t81, {require_failed,{not_available,non_existing}}}}, {?eh,test_stats,{8,0,{0,2}}}, - {?eh,tc_auto_skip,{ct_framework,ct_end_per_group, + {?eh,tc_auto_skip,{ct_framework,end_per_group, {require_failed,{not_available,non_existing}}}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g9,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g9,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t91, {skipped,{require_failed,{not_available,non_existing}}}}}, {?eh,test_stats,{8,0,{0,3}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g9,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g9,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g9,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g10,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g10,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t101,ok}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g10,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g10,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g10,[{suite,group_require_3_SUITE}]},ok}}], - [{?eh,tc_start,{ct_framework,{ct_init_per_group,g11,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}, + [{?eh,tc_start,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}, {?eh,tc_done,{group_require_3_SUITE,t111,ok}}, {?eh,test_stats,{10,0,{0,3}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,g11,[{suite,group_require_3_SUITE}]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}], + {?eh,tc_start,{ct_framework,{end_per_group,g11,[{suite,group_require_3_SUITE}]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,g11,[{suite,group_require_3_SUITE}]},ok}}], {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} diff --git a/lib/common_test/test/ct_groups_test_2_SUITE.erl b/lib/common_test/test/ct_groups_test_2_SUITE.erl index 2392b0b850..1b2ad12e2f 100644 --- a/lib/common_test/test/ct_groups_test_2_SUITE.erl +++ b/lib/common_test/test/ct_groups_test_2_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -171,16 +171,16 @@ test_events(missing_conf) -> {?eh,start_logging,{'DEF','RUNDIR'}}, {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, {?eh,start_info,{1,1,2}}, - {?eh,tc_start,{ct_framework,{ct_init_per_group,group1,[]}}}, - {?eh,tc_done,{ct_framework,{ct_init_per_group,group1,[]},ok}}, + {?eh,tc_start,{ct_framework,{init_per_group,group1,[]}}}, + {?eh,tc_done,{ct_framework,{init_per_group,group1,[]},ok}}, {?eh,tc_start,{missing_conf_SUITE,tc1}}, {?eh,tc_done,{missing_conf_SUITE,tc1,ok}}, {?eh,test_stats,{1,0,{0,0}}}, {?eh,tc_start,{missing_conf_SUITE,tc2}}, {?eh,tc_done,{missing_conf_SUITE,tc2,ok}}, {?eh,test_stats,{2,0,{0,0}}}, - {?eh,tc_start,{ct_framework,{ct_end_per_group,group1,[]}}}, - {?eh,tc_done,{ct_framework,{ct_end_per_group,group1,[]},ok}}, + {?eh,tc_start,{ct_framework,{end_per_group,group1,[]}}}, + {?eh,tc_done,{ct_framework,{end_per_group,group1,[]},ok}}, {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]} ]; diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl index 2c519f08b5..405df1e978 100644 --- a/lib/common_test/test/ct_hooks_SUITE.erl +++ b/lib/common_test/test/ct_hooks_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -83,10 +83,10 @@ all(suite) -> fail_post_suite_cth, skip_pre_suite_cth, skip_post_suite_cth, recover_post_suite_cth, update_config_cth, state_update_cth, options_cth, same_id_cth, - fail_n_skip_with_minimal_cth, prio_cth + fail_n_skip_with_minimal_cth, prio_cth, no_config, + data_dir ] - ) - . + ). %%-------------------------------------------------------------------- @@ -214,6 +214,16 @@ prio_cth(Config) when is_list(Config) -> [{empty_cth,[1000],1000},{empty_cth,[900],900}, {prio_cth,[1100,100],100},{prio_cth,[1100]}],Config). +no_config(Config) when is_list(Config) -> + do_test(no_config, "ct_no_config_SUITE.erl", + [verify_config_cth],Config). + +data_dir(Config) when is_list(Config) -> + do_test(data_dir, "ct_data_dir_SUITE.erl", + [verify_data_dir_cth],Config). + + + %%%----------------------------------------------------------------- %%% HELP FUNCTIONS %%%----------------------------------------------------------------- @@ -1078,6 +1088,105 @@ test_events(prio_cth) -> {?eh,test_done,{'DEF','STOP_TIME'}}, {?eh,stop_logging,[]}]; +test_events(no_config) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,cth,{empty_cth,init,[verify_config_cth,[]]}}, + {?eh,start_info,{1,1,2}}, + {?eh,tc_start,{ct_framework,init_per_suite}}, + {?eh,cth,{empty_cth,pre_init_per_suite, + [ct_no_config_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_init_per_suite, + [ct_no_config_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_framework,init_per_suite,ok}}, + {?eh,tc_start,{ct_no_config_SUITE,test_case_1}}, + {?eh,cth,{empty_cth,pre_init_per_testcase, + [test_case_1,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_testcase, + [test_case_1,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_no_config_SUITE,test_case_1,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + [{?eh,tc_start,{ct_framework,{init_per_group,test_group,'$proplist'}}}, + {?eh,cth,{empty_cth,pre_init_per_group, + [test_group,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_init_per_group, + [test_group,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_framework, + {init_per_group,test_group,'$proplist'},ok}}, + {?eh,tc_start,{ct_no_config_SUITE,test_case_2}}, + {?eh,cth,{empty_cth,pre_init_per_testcase, + [test_case_2,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_testcase, + [test_case_2,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_no_config_SUITE,test_case_2,ok}}, + {?eh,test_stats,{2,0,{0,0}}}, + {?eh,tc_start,{ct_framework,{end_per_group,test_group,'$proplist'}}}, + {?eh,cth,{empty_cth,pre_end_per_group, + [test_group,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_group, + [test_group,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_framework,{end_per_group,test_group,'$proplist'},ok}}], + {?eh,tc_start,{ct_framework,end_per_suite}}, + {?eh,cth,{empty_cth,pre_end_per_suite, + [ct_no_config_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_suite, + [ct_no_config_SUITE,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_framework,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{empty_cth,terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + +test_events(data_dir) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,cth,{empty_cth,init,[verify_data_dir_cth,[]]}}, + {?eh,start_info,{1,1,2}}, + {?eh,tc_start,{ct_framework,init_per_suite}}, + {?eh,cth,{empty_cth,pre_init_per_suite, + [ct_data_dir_SUITE,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,cth,{empty_cth,post_init_per_suite, + [ct_data_dir_SUITE,'$proplist','$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,tc_done,{ct_framework,init_per_suite,ok}}, + {?eh,tc_start,{ct_data_dir_SUITE,test_case_1}}, + {?eh,cth,{empty_cth,pre_init_per_testcase, + [test_case_1,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,cth,{empty_cth,post_end_per_testcase, + [test_case_1,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,tc_done,{ct_data_dir_SUITE,test_case_1,ok}}, + {?eh,test_stats,{1,0,{0,0}}}, + [{?eh,tc_start,{ct_framework,{init_per_group,test_group,'$proplist'}}}, + {?eh,cth,{empty_cth,pre_init_per_group, + [test_group,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,cth,{empty_cth,post_init_per_group, + [test_group,'$proplist','$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,tc_done,{ct_framework, + {init_per_group,test_group,'$proplist'},ok}}, + {?eh,tc_start,{ct_data_dir_SUITE,test_case_2}}, + {?eh,cth,{empty_cth,pre_init_per_testcase, + [test_case_2,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,cth,{empty_cth,post_end_per_testcase, + [test_case_2,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,tc_done,{ct_data_dir_SUITE,test_case_2,ok}}, + {?eh,test_stats,{2,0,{0,0}}}, + {?eh,tc_start,{ct_framework,{end_per_group,test_group,'$proplist'}}}, + {?eh,cth,{empty_cth,pre_end_per_group, + [test_group,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,cth,{empty_cth,post_end_per_group, + [test_group,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,tc_done,{ct_framework,{end_per_group,test_group,'$proplist'},ok}}], + {?eh,tc_start,{ct_framework,end_per_suite}}, + {?eh,cth,{empty_cth,pre_end_per_suite, + [ct_data_dir_SUITE,'$proplist',[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,cth,{empty_cth,post_end_per_suite, + [ct_data_dir_SUITE,'$proplist',ok,[{data_dir_name,"ct_data_dir_SUITE_data"}]]}}, + {?eh,tc_done,{ct_framework,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]; + test_events(ok) -> ok. diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_data_dir_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_data_dir_SUITE.erl new file mode 100644 index 0000000000..66074c20c0 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_data_dir_SUITE.erl @@ -0,0 +1,67 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_data_dir_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+-define(data_dir_name, atom_to_list(?MODULE)++"_data").
+
+suite() ->
+ [{timetrap, {seconds,1}},
+ {ct_hooks, [verify_data_dir_cth]}].
+
+all() ->
+ [test_case_1, {group,test_group}].
+
+groups() ->
+ [{test_group,[],[test_case_2]}].
+
+init_per_testcase(_, Config) ->
+ check_dirs(Config),
+ Config.
+
+end_per_testcase(_, Config) ->
+ check_dirs(Config),
+ ok.
+
+test_case_1(Config) ->
+ check_dirs(Config),
+ ok.
+
+test_case_2(Config) ->
+ check_dirs(Config),
+ ok.
+
+check_dirs(Config) ->
+ %% check priv_dir
+ PrivDir = proplists:get_value(priv_dir, Config),
+ "log_private" = filename:basename(PrivDir),
+ {ok,_} = file:list_dir(PrivDir),
+
+ %% check data_dir
+ DataDir = proplists:get_value(data_dir, Config),
+ DataDirName = ?data_dir_name,
+ DataDirName = filename:basename(DataDir),
+ ok.
+
+
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl new file mode 100644 index 0000000000..fb8c420b8e --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_no_config_SUITE.erl @@ -0,0 +1,64 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(ct_no_config_SUITE).
+
+%% Note: This directive should only be used in test suites.
+-compile(export_all).
+
+-include("ct.hrl").
+
+%%% This suite is used to verify 2 things:
+%%%
+%%% 1) All hook pre/post functions get called, even if no init/end
+%%% config functions exist in the suite (new from ver 1.6.1, R15B01).
+%%%
+%%% 2) The hook functions can read Config list elements, as well as
+%%% required config variables, even if no init/end config
+%%% functions exist.
+
+suite() ->
+ [{timetrap, {seconds,1}},
+ {ct_hooks, [verify_config_cth]},
+ {require,suite_cfg},
+ {default_config,suite_cfg,?MODULE}].
+
+group(test_group) ->
+ [{require,group_cfg},
+ {default_config,group_cfg,test_group}].
+
+test_case_1() ->
+ [{require,test_case_1_cfg},
+ {default_config,test_case_1_cfg,test_case_1}].
+
+test_case_2() ->
+ [{require,test_case_2_cfg},
+ {default_config,test_case_2_cfg,test_case_2}].
+
+all() ->
+ [test_case_1, {group,test_group}].
+
+groups() ->
+ [{test_group,[],[test_case_2]}].
+
+test_case_1(Config) ->
+ ok.
+
+test_case_2(Config) ->
+ ok.
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 7befcfa57c..9ee2a90896 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 @@ -1,277 +1,277 @@ -%%
-%% %CopyrightBegin%
-%%
-%% Copyright Ericsson AB 2010-2011. All Rights Reserved.
-%%
-%% The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved online at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% %CopyrightEnd%
-%%
-
-%%% @doc Common Test Example Suite Callback module.
-%%%
-%%% <p>This module gives an example of a common test CTH (Common Test Hook).
-%%% There are many ways to add a CTH to a test run, you can do it either in
-%%% the command line using -ct_hook, in a test spec using
-%%% {ct_hook,M} or in the suite it self by returning ct_hook
-%%% from either suite/0, init_per_suite/1, init_per_group/2 and
-%%% init_per_testcase/2. The scope of the CTH is determined by where is it
-%%% started. If it is started in the command line or test spec then it will
-%%% be stopped at the end of all tests. If it is started in init_per_suite,
-%%% it will be stopped after end_per_suite and so on. See terminate
-%%% documentation for a table describing the scoping machanics.
-%%%
-%%% All of callbacks except init/1 in a CTH are optional.</p>
-
--module(empty_cth).
-
-%% CT Hooks
--export([id/1]).
--export([init/2]).
-
--export([pre_init_per_suite/3]).
--export([post_init_per_suite/4]).
--export([pre_end_per_suite/3]).
--export([post_end_per_suite/4]).
-
--export([pre_init_per_group/3]).
--export([post_init_per_group/4]).
--export([pre_end_per_group/3]).
--export([post_end_per_group/4]).
-
--export([pre_init_per_testcase/3]).
--export([post_end_per_testcase/4]).
-
--export([on_tc_fail/3]).
--export([on_tc_skip/3]).
-
--export([terminate/1]).
-
--include_lib("common_test/src/ct_util.hrl").
--include_lib("common_test/include/ct_event.hrl").
-
+%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%% @doc Common Test Example Suite Callback module. +%%% +%%% <p>This module gives an example of a common test CTH (Common Test Hook). +%%% There are many ways to add a CTH to a test run, you can do it either in +%%% the command line using -ct_hook, in a test spec using +%%% {ct_hook,M} or in the suite it self by returning ct_hook +%%% from either suite/0, init_per_suite/1, init_per_group/2 and +%%% init_per_testcase/2. The scope of the CTH is determined by where is it +%%% started. If it is started in the command line or test spec then it will +%%% be stopped at the end of all tests. If it is started in init_per_suite, +%%% it will be stopped after end_per_suite and so on. See terminate +%%% documentation for a table describing the scoping machanics. +%%% +%%% All of callbacks except init/1 in a CTH are optional.</p> + +-module(empty_cth). + +%% CT Hooks +-export([id/1]). +-export([init/2]). + +-export([pre_init_per_suite/3]). +-export([post_init_per_suite/4]). +-export([pre_end_per_suite/3]). +-export([post_end_per_suite/4]). + +-export([pre_init_per_group/3]). +-export([post_init_per_group/4]). +-export([pre_end_per_group/3]). +-export([post_end_per_group/4]). + +-export([pre_init_per_testcase/3]). +-export([post_end_per_testcase/4]). + +-export([on_tc_fail/3]). +-export([on_tc_skip/3]). + +-export([terminate/1]). + +-include_lib("common_test/src/ct_util.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + -type config() :: proplists:proplist(). --type reason() :: term().
--type skip_or_fail() :: {skip, reason()} |
- {auto_skip, reason()} |
- {fail, reason()} |
- {'EXIT',reason()}.
-
--record(state, { id = ?MODULE :: term()}).
-
-%% @doc Always called before any other callback function. Use this to initiate
-%% any common state. It should return an state for this CTH.
+-type reason() :: term(). +-type skip_or_fail() :: {skip, reason()} | + {auto_skip, reason()} | + {fail, reason()} | + {'EXIT',reason()}. + +-record(state, { id = ?MODULE :: term()}). + +%% @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 :: proplists:proplist()) -> - {ok, State :: #state{}}.
-init(Id, Opts) ->
- gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, init, [Id, Opts]}}),
- {ok,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.
+ {ok, State :: #state{}}. +init(Id, Opts) -> + gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, init, [Id, Opts]}}), + {ok,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 :: proplists:proplist()) -> - Id :: term().
-id(Opts) ->
- gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, id, [Opts]}}),
- now().
-
-%% @doc Called before init_per_suite is called. Note that this callback is
-%% only called if the CTH is added before init_per_suite is run (eg. in a test
-%% specification, suite/0 function etc).
-%% You can change the config in the this function.
--spec pre_init_per_suite(Suite :: atom(),
- Config :: config(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_suite(Suite,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_init_per_suite,
- [Suite,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after init_per_suite.
-%% you can change the return value in this function.
--spec post_init_per_suite(Suite :: atom(),
- Config :: config(),
- Return :: config() | skip_or_fail(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-post_init_per_suite(Suite,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_init_per_suite,
- [Suite,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called before end_per_suite. The config/state can be changed here,
-%% though it will only affect the *end_per_suite function.
--spec pre_end_per_suite(Suite :: atom(),
- Config :: config() | skip_or_fail(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-pre_end_per_suite(Suite,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_end_per_suite,
- [Suite,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after end_per_suite. Note that the config cannot be
-%% changed here, only the status of the suite.
--spec post_end_per_suite(Suite :: atom(),
- Config :: config(),
- Return :: term(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_suite(Suite,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_end_per_suite,
- [Suite,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called before each init_per_group.
-%% You can change the config in this function.
--spec pre_init_per_group(Group :: atom(),
- Config :: config(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_group(Group,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_init_per_group,
- [Group,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after each init_per_group.
-%% You can change the return value in this function.
--spec post_init_per_group(Group :: atom(),
- Config :: config(),
- Return :: config() | skip_or_fail(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-post_init_per_group(Group,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_init_per_group,
- [Group,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called after each end_per_group. The config/state can be changed here,
-%% though it will only affect the *end_per_group functions.
--spec pre_end_per_group(Group :: atom(),
- Config :: config() | skip_or_fail(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-pre_end_per_group(Group,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_end_per_group,
- [Group,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after each end_per_group. Note that the config cannot be
-%% changed here, only the status of the group.
--spec post_end_per_group(Group :: atom(),
- Config :: config(),
- Return :: term(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_group(Group,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_end_per_group,
- [Group,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called before each test case.
-%% You can change the config in this function.
--spec pre_init_per_testcase(TC :: atom(),
- Config :: config(),
- State :: #state{}) ->
- {config() | skip_or_fail(), NewState :: #state{}}.
-pre_init_per_testcase(TC,Config,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, pre_init_per_testcase,
- [TC,Config,State]}}),
- {Config, State}.
-
-%% @doc Called after each test case. Note that the config cannot be
-%% changed here, only the status of the test case.
--spec post_end_per_testcase(TC :: atom(),
- Config :: config(),
- Return :: term(),
- State :: #state{}) ->
- {ok | skip_or_fail(), NewState :: #state{}}.
-post_end_per_testcase(TC,Config,Return,State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, post_end_per_testcase,
- [TC,Config,Return,State]}}),
- {Return, State}.
-
-%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group,
-%% post_end_per_group and post_end_per_tc if the suite, group or test case failed.
-%% This function should be used for extra cleanup which might be needed.
-%% It is not possible to modify the config or the status of the test run.
--spec on_tc_fail(TC :: init_per_suite | end_per_suite |
- init_per_group | end_per_group | atom(),
- Reason :: term(), State :: #state{}) ->
- NewState :: #state{}.
-on_tc_fail(TC, Reason, State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, on_tc_fail,
- [TC,Reason,State]}}),
- State.
-
-%% @doc Called when a test case is skipped by either user action
-%% or due to an init function failing. Test case can be
-%% end_per_suite, init_per_group, end_per_group and the actual test cases.
--spec on_tc_skip(TC :: end_per_suite |
- init_per_group | end_per_group | atom(),
- {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} |
- {tc_user_skip, {skipped, Reason :: term()}},
- State :: #state{}) ->
- NewState :: #state{}.
-on_tc_skip(TC, Reason, State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, on_tc_skip,
- [TC,Reason,State]}}),
- State.
-
-%% @doc Called when the scope of the CTH is done, this depends on
-%% when the CTH was specified. This translation table describes when this
-%% function is called.
-%%
-%% | Started in | terminate called |
-%% |---------------------|-------------------------|
-%% | command_line | after all tests are run |
-%% | test spec | after all tests are run |
-%% | suite/0 | after SUITE is done |
-%% | init_per_suite/1 | after SUITE is done |
-%% | init_per_group/2 | after group is done |
-%% |-----------------------------------------------|
-%%
--spec terminate(State :: #state{}) ->
- term().
-terminate(State) ->
- gen_event:notify(
- ?CT_EVMGR_REF, #event{ name = cth, node = node(),
- data = {?MODULE, terminate, [State]}}),
- ok.
+ Id :: term(). +id(Opts) -> + gen_event:notify(?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, id, [Opts]}}), + now(). + +%% @doc Called before init_per_suite is called. Note that this callback is +%% only called if the CTH is added before init_per_suite is run (eg. in a test +%% specification, suite/0 function etc). +%% You can change the config in the this function. +-spec pre_init_per_suite(Suite :: atom(), + Config :: config(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +pre_init_per_suite(Suite,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_init_per_suite, + [Suite,Config,State]}}), + {Config, State}. + +%% @doc Called after init_per_suite. +%% you can change the return value in this function. +-spec post_init_per_suite(Suite :: atom(), + Config :: config(), + Return :: config() | skip_or_fail(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +post_init_per_suite(Suite,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_init_per_suite, + [Suite,Config,Return,State]}}), + {Return, State}. + +%% @doc Called before end_per_suite. The config/state can be changed here, +%% though it will only affect the *end_per_suite function. +-spec pre_end_per_suite(Suite :: atom(), + Config :: config() | skip_or_fail(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +pre_end_per_suite(Suite,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_end_per_suite, + [Suite,Config,State]}}), + {Config, State}. + +%% @doc Called after end_per_suite. Note that the config cannot be +%% changed here, only the status of the suite. +-spec post_end_per_suite(Suite :: atom(), + Config :: config(), + Return :: term(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +post_end_per_suite(Suite,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_end_per_suite, + [Suite,Config,Return,State]}}), + {Return, State}. + +%% @doc Called before each init_per_group. +%% You can change the config in this function. +-spec pre_init_per_group(Group :: atom(), + Config :: config(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +pre_init_per_group(Group,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_init_per_group, + [Group,Config,State]}}), + {Config, State}. + +%% @doc Called after each init_per_group. +%% You can change the return value in this function. +-spec post_init_per_group(Group :: atom(), + Config :: config(), + Return :: config() | skip_or_fail(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +post_init_per_group(Group,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_init_per_group, + [Group,Config,Return,State]}}), + {Return, State}. + +%% @doc Called after each end_per_group. The config/state can be changed here, +%% though it will only affect the *end_per_group functions. +-spec pre_end_per_group(Group :: atom(), + Config :: config() | skip_or_fail(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +pre_end_per_group(Group,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_end_per_group, + [Group,Config,State]}}), + {Config, State}. + +%% @doc Called after each end_per_group. Note that the config cannot be +%% changed here, only the status of the group. +-spec post_end_per_group(Group :: atom(), + Config :: config(), + Return :: term(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +post_end_per_group(Group,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_end_per_group, + [Group,Config,Return,State]}}), + {Return, State}. + +%% @doc Called before each test case. +%% You can change the config in this function. +-spec pre_init_per_testcase(TC :: atom(), + Config :: config(), + State :: #state{}) -> + {config() | skip_or_fail(), NewState :: #state{}}. +pre_init_per_testcase(TC,Config,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, pre_init_per_testcase, + [TC,Config,State]}}), + {Config, State}. + +%% @doc Called after each test case. Note that the config cannot be +%% changed here, only the status of the test case. +-spec post_end_per_testcase(TC :: atom(), + Config :: config(), + Return :: term(), + State :: #state{}) -> + {ok | skip_or_fail(), NewState :: #state{}}. +post_end_per_testcase(TC,Config,Return,State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, post_end_per_testcase, + [TC,Config,Return,State]}}), + {Return, State}. + +%% @doc Called after post_init_per_suite, post_end_per_suite, post_init_per_group, +%% post_end_per_group and post_end_per_tc if the suite, group or test case failed. +%% This function should be used for extra cleanup which might be needed. +%% It is not possible to modify the config or the status of the test run. +-spec on_tc_fail(TC :: init_per_suite | end_per_suite | + init_per_group | end_per_group | atom(), + Reason :: term(), State :: #state{}) -> + NewState :: #state{}. +on_tc_fail(TC, Reason, State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, on_tc_fail, + [TC,Reason,State]}}), + State. + +%% @doc Called when a test case is skipped by either user action +%% or due to an init function failing. Test case can be +%% end_per_suite, init_per_group, end_per_group and the actual test cases. +-spec on_tc_skip(TC :: end_per_suite | + init_per_group | end_per_group | atom(), + {tc_auto_skip, {failed, {Mod :: atom(), Function :: atom(), Reason :: term()}}} | + {tc_user_skip, {skipped, Reason :: term()}}, + State :: #state{}) -> + NewState :: #state{}. +on_tc_skip(TC, Reason, State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, on_tc_skip, + [TC,Reason,State]}}), + State. + +%% @doc Called when the scope of the CTH is done, this depends on +%% when the CTH was specified. This translation table describes when this +%% function is called. +%% +%% | Started in | terminate called | +%% |---------------------|-------------------------| +%% | command_line | after all tests are run | +%% | test spec | after all tests are run | +%% | suite/0 | after SUITE is done | +%% | init_per_suite/1 | after SUITE is done | +%% | init_per_group/2 | after group is done | +%% |-----------------------------------------------| +%% +-spec terminate(State :: #state{}) -> + term(). +terminate(State) -> + gen_event:notify( + ?CT_EVMGR_REF, #event{ name = cth, node = node(), + data = {?MODULE, terminate, [State]}}), + ok. diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl new file mode 100644 index 0000000000..f6de69f321 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_config_cth.erl @@ -0,0 +1,130 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(verify_config_cth).
+
+-include_lib("common_test/src/ct_util.hrl").
+
+%% CT Hooks
+-compile(export_all).
+
+-define(val(K, L), proplists:get_value(K, L)).
+
+id(Opts) ->
+ ?MODULE.
+
+init(Id, Opts) ->
+ {ok, State} = empty_cth:init(Id, Opts),
+ {ok, State}.
+
+pre_init_per_suite(Suite, Config, State) ->
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:pre_init_per_suite(Suite,
+ [{pre_init_per_suite,true} | Config],
+ State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ true = ?val(pre_init_per_suite, Return),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:post_init_per_suite(Suite,
+ Config,
+ [{post_init_per_suite,true} | Return],
+ State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ true = ?val(post_init_per_suite, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:pre_end_per_suite(Suite,
+ [{pre_end_per_suite,true} | Config],
+ State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ true = ?val(pre_end_per_suite, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ true = ?val(post_init_per_suite, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:pre_init_per_group(Group,
+ [{pre_init_per_group,true} | Config],
+ State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ true = ?val(pre_init_per_group, Return),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:post_init_per_group(Group,
+ Config,
+ [{post_init_per_group,true} | Return],
+ State).
+
+pre_end_per_group(Group,Config,State) ->
+ true = ?val(post_init_per_group, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:pre_end_per_group(Group,
+ [{pre_end_per_group,true} | Config],
+ State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ true = ?val(pre_end_per_group, Config),
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ test_group = ct:get_config(group_cfg),
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ true = ?val(post_init_per_suite, Config),
+ case ?val(name, ?val(tc_group_properties, Config)) of
+ undefined ->
+ ok;
+ _ ->
+ true = ?val(post_init_per_group, Config),
+ test_group = ct:get_config(group_cfg)
+ end,
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ CfgKey = list_to_atom(atom_to_list(TC) ++ "_cfg"),
+ TC = ct:get_config(CfgKey),
+ empty_cth:pre_init_per_testcase(TC,
+ [{pre_init_per_testcase,true} | Config],
+ State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ true = ?val(post_init_per_suite, Config),
+ true = ?val(pre_init_per_testcase, Config),
+ case ?val(name, ?val(tc_group_properties, Config)) of
+ undefined ->
+ ok;
+ _ ->
+ true = ?val(post_init_per_group, Config),
+ test_group = ct:get_config(group_cfg)
+ end,
+ ct_no_config_SUITE = ct:get_config(suite_cfg),
+ CfgKey = list_to_atom(atom_to_list(TC) ++ "_cfg"),
+ TC = ct:get_config(CfgKey),
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl new file mode 100644 index 0000000000..279a04b9a9 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/verify_data_dir_cth.erl @@ -0,0 +1,95 @@ +%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2010-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(verify_data_dir_cth).
+
+-include_lib("common_test/src/ct_util.hrl").
+
+%% CT Hooks
+-compile(export_all).
+
+-define(val(K, L), proplists:get_value(K, L)).
+
+check_dirs(State,Config) ->
+ DataDirName = ?val(data_dir_name, State),
+ %% check priv_dir
+ PrivDir = proplists:get_value(priv_dir, Config),
+ "log_private" = filename:basename(PrivDir),
+ {ok,_} = file:list_dir(PrivDir),
+
+ %% check data_dir
+ DataDir = proplists:get_value(data_dir, Config),
+ DataDirName = filename:basename(DataDir),
+ ok.
+
+id(_Opts) ->
+ ?MODULE.
+
+init(Id, _Opts) ->
+ {ok, _State} = empty_cth:init(Id, []),
+ {ok, [{data_dir_name,"ct_data_dir_SUITE_data"}]}.
+
+pre_init_per_suite(Suite,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_init_per_suite(Suite,Config,State).
+
+post_init_per_suite(Suite,Config,Return,State) ->
+ check_dirs(State,Return),
+ empty_cth:post_init_per_suite(Suite,Config,Return,State).
+
+pre_end_per_suite(Suite,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_end_per_suite(Suite,Config,State).
+
+post_end_per_suite(Suite,Config,Return,State) ->
+ check_dirs(State,Config),
+ empty_cth:post_end_per_suite(Suite,Config,Return,State).
+
+pre_init_per_group(Group,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_init_per_group(Group,Config,State).
+
+post_init_per_group(Group,Config,Return,State) ->
+ check_dirs(State,Return),
+ empty_cth:post_init_per_group(Group,Config,Return,State).
+
+pre_end_per_group(Group,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_end_per_group(Group,Config,State).
+
+post_end_per_group(Group,Config,Return,State) ->
+ check_dirs(State,Config),
+ empty_cth:post_end_per_group(Group,Config,Return,State).
+
+pre_init_per_testcase(TC,Config,State) ->
+ check_dirs(State,Config),
+ empty_cth:pre_init_per_testcase(TC,Config,State).
+
+post_end_per_testcase(TC,Config,Return,State) ->
+ check_dirs(State,Config),
+ empty_cth:post_end_per_testcase(TC,Config,Return,State).
+
+on_tc_fail(TC, Reason, State) ->
+ empty_cth:on_tc_fail(TC,Reason,State).
+
+on_tc_skip(TC, Reason, State) ->
+ empty_cth:on_tc_skip(TC,Reason,State).
+
+terminate(State) ->
+ empty_cth:terminate(State).
diff --git a/lib/common_test/test/ct_master_SUITE.erl b/lib/common_test/test/ct_master_SUITE.erl index 1471cc1e0c..27243a0067 100644 --- a/lib/common_test/test/ct_master_SUITE.erl +++ b/lib/common_test/test/ct_master_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -98,7 +98,7 @@ end_per_group(_GroupName, Config) -> %%-------------------------------------------------------------------- %% TEST CASES %%-------------------------------------------------------------------- -ct_master_test(Config) when is_list(Config)-> +ct_master_test(Config) when is_list(Config) -> NodeNames = proplists:get_value(node_names, Config), DataDir = ?config(data_dir, Config), PrivDir = ?config(priv_dir, Config), @@ -106,19 +106,14 @@ ct_master_test(Config) when is_list(Config)-> FileName = filename:join(PrivDir, "ct_master_spec.spec"), Suites = [master_SUITE], TSFile = make_spec(DataDir, FileName, NodeNames, Suites, Config), + ERPid = ct_test_support:start_event_receiver(Config), - spawn(ct@ancalagon, - fun() -> - dbg:tracer(),dbg:p(all,c), - dbg:tpl(erlang, spawn_link, 4,x), - receive ok -> ok end - end), - [{TSFile, ok}] = run_test(ct_master_test, FileName, Config), + [{TSFile,ok}] = run_test(ct_master_test, FileName, Config), Events = ct_test_support:get_events(ERPid, Config), - ct_test_support:log_events(groups_suite_1, + ct_test_support:log_events(ct_master_test, reformat(Events, ?eh), PrivDir, []), @@ -134,48 +129,59 @@ ct_master_test(Config) when is_list(Config)-> %%%----------------------------------------------------------------- %%% HELP FUNCTIONS %%%----------------------------------------------------------------- -make_spec(DataDir, FileName, NodeNames, Suites, Config)-> - {ok, HostName} = inet:gethostname(), +make_spec(DataDir, FileName, NodeNames, Suites, Config) -> + {ok,HostName} = inet:gethostname(), - N = lists:map(fun(NodeName)-> + N = lists:map(fun(NodeName) -> {node, NodeName, list_to_atom(atom_to_list(NodeName)++"@"++HostName)} end, NodeNames), - C = lists:map(fun(NodeName)-> - Rnd = random:uniform(2), - if Rnd == 1-> - {config, NodeName, filename:join(DataDir, "master/config.txt")}; - true-> - {userconfig, NodeName, {ct_config_xml, filename:join(DataDir, "master/config.xml")}} - end - end, - NodeNames), - - NS = lists:map(fun(NodeName)-> - {init, NodeName, [ - {node_start, [{startup_functions, []}, {monitor_master, true}]}, - {eval, {erlang, nodes, []}} - ] - } - end, - NodeNames), - + C = lists:map( + fun(NodeName) -> + Rnd = random:uniform(2), + if Rnd == 1-> + {config,NodeName,filename:join(DataDir, + "master/config.txt")}; + true -> + {userconfig,NodeName, + {ct_config_xml,filename:join(DataDir, + "master/config.xml")}} + end + end, + NodeNames), + + CM = [{config,master,filename:join(DataDir,"master/config.txt")}], + + NS = lists:map( + fun(NodeName) -> + {init,NodeName,[ + {node_start,[{startup_functions,[]}, + {monitor_master,true}]}, + {eval,{erlang,nodes,[]}} + ] + } + end, + NodeNames), + S = [{suites, NodeNames, filename:join(DataDir, "master"), Suites}], - + PrivDir = ?config(priv_dir, Config), - LD = lists:map(fun(NodeName)-> - {logdir, NodeName, get_log_dir(os:type(),PrivDir, NodeName)} - end, - NodeNames) ++ [{logdir, master, PrivDir}], + + LD = lists:map( + fun(NodeName) -> + {logdir,NodeName,get_log_dir(os:type(),PrivDir, NodeName)} + end, + NodeNames) ++ [{logdir,master,PrivDir}], + EvHArgs = [{cbm,ct_test_support},{trace_level,?config(trace_level,Config)}], EH = [{event_handler,master,[?eh],EvHArgs}], - + Include = [{include,filename:join([DataDir,"master/include"])}], + + ct_test_support:write_testspec(N++Include++EH++C++CM++S++LD++NS, FileName). - ct_test_support:write_testspec(N++Include++EH++C++S++LD++NS, FileName). - -get_log_dir({win32,_}, _PrivDir, NodeName)-> +get_log_dir({win32,_}, _PrivDir, NodeName) -> case filelib:is_dir(?TEMP_DIR) of false -> file:make_dir(?TEMP_DIR); @@ -188,8 +194,15 @@ get_log_dir(_,PrivDir,NodeName) -> file:make_dir(LogDir), LogDir. -run_test(_Name, FileName, Config)-> - [{FileName, ok}] = ct_test_support:run(ct_master, run, [FileName], Config). +run_test(_Name, FileName, Config) -> + %% run the test twice, using different html versions + [{FileName,ok}] = ct_test_support:run({ct_master,run,[FileName]}, + [{ct_master,basic_html,[true]}], + Config), + timer:sleep(5000), + [{FileName,ok}] = ct_test_support:run({ct_master,run,[FileName]}, + [{ct_master,basic_html,[false]}], + Config). reformat(Events, EH) -> ct_test_support:reformat(Events, EH). @@ -220,5 +233,5 @@ add_host(NodeName) -> {ok, HostName} = inet:gethostname(), list_to_atom(atom_to_list(NodeName)++"@"++HostName). -expected_events(_)-> +expected_events(_) -> []. diff --git a/lib/common_test/test/ct_misc_1_SUITE.erl b/lib/common_test/test/ct_misc_1_SUITE.erl index cb17af9ab5..d2318de445 100644 --- a/lib/common_test/test/ct_misc_1_SUITE.erl +++ b/lib/common_test/test/ct_misc_1_SUITE.erl @@ -106,7 +106,7 @@ beam_me_up(Config) when is_list(Config) -> {Opts,ERPid} = setup([{suite,Suites},{auto_compile,false}], Config), - ok = ct_test_support:run(ct, run_test, [Opts], Config), + {_Ok,_Fail,_Skip} = ct_test_support:run(ct, run_test, [Opts], Config), Events = ct_test_support:get_events(ERPid, Config), ct_test_support:log_events(beam_me_up, diff --git a/lib/common_test/test/ct_netconfc_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE.erl new file mode 100644 index 0000000000..e6e8d5b09c --- /dev/null +++ b/lib/common_test/test/ct_netconfc_SUITE.erl @@ -0,0 +1,124 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_netconfc_SUITE +%%% +%%% Description: +%%% Test ct_netconfc module +%%% +%%%------------------------------------------------------------------- +-module(ct_netconfc_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + default + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +default(Config) when is_list(Config) -> + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "netconfc1_SUITE"), + CfgFile = filename:join(DataDir, "netconfc1.cfg"), + {Opts,ERPid} = setup([{suite,Suite},{config,CfgFile}, + {label,default}], Config), + + ok = execute(default, Opts, ERPid, Config). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +execute(Name, Opts, ERPid, Config) -> + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Name, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(Name,Config), + ct_test_support:verify_events(TestEvents, Events, Config). + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test,Config) -> + {module,_} = code:load_abs(filename:join(?config(data_dir,Config), + netconfc1_SUITE)), + TCs = netconfc1_SUITE:all(), + code:purge(netconfc1_SUITE), + code:delete(netconfc1_SUITE), + + OneTest = + [{?eh,start_logging,{'DEF','RUNDIR'}}] ++ + [{?eh,tc_done,{netconfc1_SUITE,TC,ok}} || TC <- TCs] ++ + [{?eh,stop_logging,[]}], + + %% 2 tests (ct:run_test + script_start) is default + OneTest ++ OneTest. diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg new file mode 100644 index 0000000000..6466571623 --- /dev/null +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1.cfg @@ -0,0 +1,6 @@ +%% -*- erlang -*- +{netconf1,[{ssh,"localhost"}, + {port,2060}, + {user,"xxx"}, + {password,"xxx"}]}. +{ct_conn_log,[{ct_netconfc,[{log_type,pretty}]}]}. %overrides args to cth_conn_log diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl new file mode 100644 index 0000000000..79768a9a6a --- /dev/null +++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl @@ -0,0 +1,1130 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%%---------------------------------------------------------------------- +%% File: ct_netconfc_SUITE.erl +%% +%% Description: +%% This file contains the test cases for the ct_netconfc API. +%% +%% @author Support +%% @doc Netconf Client Interface. +%% @end +%%---------------------------------------------------------------------- +%%---------------------------------------------------------------------- +-module(netconfc1_SUITE). +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/src/ct_netconfc.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +-compile(export_all). + +%% Default timetrap timeout (set in init_per_testcase). +-define(default_timeout, ?t:minutes(1)). + +-define(NS,ns). +-define(LOCALHOST, "127.0.0.1"). +-define(SSH_PORT, 2060). + +-define(DEFAULT_SSH_OPTS,[{ssh,?LOCALHOST}, + {port,?SSH_PORT}, + {user,"xxx"}, + {password,"xxx"}]). +-define(DEFAULT_SSH_OPTS(Dir), ?DEFAULT_SSH_OPTS++[{user_dir,Dir}]). + +-define(ok,ok). + +suite() -> + [{ct_hooks, [{cth_conn_log, + [{ct_netconfc,[{log_type,html}, %will be overwritten by config + {hosts,[my_named_connection,netconf1]}] + }] + }] + }]. + +all() -> + case os:find_executable("ssh") of + false -> + {skip, "SSH not installed on host"}; + _ -> + [hello, + hello_from_server_first, + hello_named, + hello_configured, + hello_configured_extraopts, + hello_required, + hello_required_exists, + hello_global_pwd, + hello_no_session_id, + hello_incomp_base_vsn, + hello_no_base_cap, + hello_no_caps, + no_server_hello, + no_client_hello, + get_session_id, + get_capabilities, + faulty_user, + faulty_passwd, + faulty_port, + no_host, + no_port, + invalid_opt, + get, + get_xpath, + get_config, + get_config_xpath, + edit_config, + copy_config, + delete_config, + lock, + unlock, + kill_session, + get_no_such_client, + action, + send_any_rpc, + send_any, + hide_password, + not_proper_xml, + prefixed_namespace, + receive_chunked_data, + timeout_receive_chunked_data, + close_while_waiting_for_chunked_data, + connection_crash, + get_event_streams, + create_subscription, + receive_event] + end. + + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +init_per_testcase(_Case, Config) -> + ets:delete_all_objects(ns_tab), + Dog = test_server:timetrap(?default_timeout), + [{watchdog, Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog=?config(watchdog, Config), + test_server:timetrap_cancel(Dog), + ok. + +init_per_suite(Config) -> + case catch {crypto:start(), ssh:start()} of + {ok, ok} -> + {ok, _} = get_id_keys(Config), + make_dsa_files(Config), + Server = ?NS:start(?config(data_dir,Config)), + [{server,Server}|Config]; + _ -> + {skip, "Crypto and/or SSH could not be started!"} + end. + +end_per_suite(Config) -> + PrivDir = ?config(priv_dir, Config), + ?NS:stop(?config(server,Config)), + ssh:stop(), + crypto:stop(), + remove_id_keys(PrivDir), + Config. + +hello(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_from_server_first(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1), + {ok,Client} = ct_netconfc:only_open(?DEFAULT_SSH_OPTS(DataDir)), + ct:sleep(500), + ?NS:expect(hello), + ?ok = ct_netconfc:hello(Client), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_named(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(any_name,DataDir), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_configured() -> + [{require, netconf1}]. +hello_configured(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_configured_success(netconf1,DataDir), + ?NS:expect_do_reply('close-session',close,ok), + {error, {no_such_name,netconf1}} = ct_netconfc:close_session(netconf1), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_configured_extraopts() -> + [{require, netconf1}]. +hello_configured_extraopts(Config) -> + DataDir = ?config(data_dir,Config), + %% Test that the cofiguration overwrites the ExtraOpts parameter + %% to ct_netconfc:open/2. + {ok,Client} = open_configured_success(netconf1,DataDir,[{password,"faulty"}]), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_required() -> + [{require, my_named_connection, netconf1}]. +hello_required(Config) -> + DataDir = ?config(data_dir,Config), + {ok,_Client} = open_configured_success(my_named_connection,DataDir), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(my_named_connection), + ok. + +hello_required_exists() -> + [{require, my_named_connection, netconf1}]. +hello_required_exists(Config) -> + DataDir = ?config(data_dir,Config), + {ok,_Client1} = open_configured_success(my_named_connection,DataDir), + + %% Check that same name can not be used twice + {error,{connection_exists,_Client1}} = + ct_netconfc:open(my_named_connection,[{user_dir,DataDir}]), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(my_named_connection), + + %% Then check that it can be used again after the first is closed + {ok,_Client2} = open_configured_success(my_named_connection,DataDir), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(my_named_connection), + ok. + +hello_global_pwd(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir,[{user,"any-user"}, + {password,"global-xxx"}]), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hello_no_session_id(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(no_session_id), + ?NS:expect(hello), + {error,{incorrect_hello,no_session_id_found}} = open(DataDir), + ok. + +hello_incomp_base_vsn(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1,{base,"1.1"}), + ?NS:expect(hello), + {error,{incompatible_base_capability_vsn,"1.1"}} = open(DataDir), + ok. + +hello_no_base_cap(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1,no_base), + ?NS:expect(hello), + {error,{incorrect_hello,no_base_capability_found}} = open(DataDir), + ok. + +hello_no_caps(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1,no_caps), + ?NS:expect(hello), + {error,{incorrect_hello,capabilities_not_found}} = open(DataDir), + ok. + +no_server_hello(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:expect(hello), + {error,{hello_session_failed,timeout}} = open(DataDir,[{timeout,2000}]), + ok. + +no_client_hello(Config) -> + DataDir = ?config(data_dir,Config), + ?NS:hello(1), + {ok,Client} = ct_netconfc:only_open(?DEFAULT_SSH_OPTS(DataDir)), + + %% Allow server hello to arrive + ct:sleep(500), + + %% Tell server to receive a get request and then die without + %% replying since no hello has been received. (is this correct + %% behavoiur??) + ?NS:expect_do(get,close), + {error,closed} = ct_netconfc:get(Client,whatever), + ok. + +get_session_id(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + 1 = ct_netconfc:get_session_id(Client), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +get_capabilities(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + Caps = ct_netconfc:get_capabilities(Client), + BaseCap = ?NETCONF_BASE_CAP ++ ?NETCONF_BASE_CAP_VSN, + [BaseCap,"urn:ietf:params:netconf:capability:writable-running:1.0" |_] = Caps, + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +faulty_user(Config) -> + DataDir = ?config(data_dir,Config), + {error,{ssh,could_not_connect_to_server, + "Unable to connect using the available authentication methods"}} = + open(DataDir,[{user,"yyy"}]), + ok. + +faulty_passwd(Config) -> + DataDir = ?config(data_dir,Config), + {error,{ssh,could_not_connect_to_server, + "Unable to connect using the available authentication methods"}} = + open(DataDir,[{password,"yyy"}]), + ok. + +faulty_port(Config) -> + DataDir = ?config(data_dir,Config), + {error,{ssh,could_not_connect_to_server,econnrefused}} = + open(DataDir,[{port,2062}]), + ok. + +no_host(Config) -> + DataDir = ?config(data_dir,Config), + Opts = lists:keydelete(ssh,1,?DEFAULT_SSH_OPTS(DataDir)), + {error,no_host_address} = ct_netconfc:open(Opts), + ok. + +no_port(Config) -> + DataDir = ?config(data_dir,Config), + Opts = lists:keydelete(port,1,?DEFAULT_SSH_OPTS(DataDir)), + {error,no_port} = ct_netconfc:open(Opts), + ok. + +invalid_opt(Config) -> + DataDir = ?config(data_dir,Config), + Opts1 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{timeout,invalidvalue}], + {error,{invalid_option,{timeout,invalidvalue}}} = ct_netconfc:open(Opts1), + Opts2 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{some_other_opt,true}], + {error,{invalid_option,{some_other_opt,true}}} = ct_netconfc:open(Opts2), + ok. + +get(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + ?NS:expect_reply('get',{data,Data}), + {ok,Data} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +get_xpath(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + ?NS:expect_reply({'get',xpath},{data,Data}), + {ok,Data} = ct_netconfc:get(Client,{xpath,"/server"}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +get_config(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + ?NS:expect_reply('get-config',{data,Data}), + {ok,Data} = ct_netconfc:get_config(Client,running, + {server,[{xmlns,"myns"}],[]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +get_config_xpath(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + ?NS:expect_reply({'get-config',xpath},{data,Data}), + {ok,Data} = ct_netconfc:get_config(Client,running,{xpath,"/server"}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +edit_config(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('edit-config',ok), + ?ok = ct_netconfc:edit_config(Client,running, + {server,[{xmlns,"myns"}], + [{name,["myserver"]}]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +copy_config(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('copy-config',ok), + ?ok = ct_netconfc:copy_config(Client,startup,running), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +delete_config(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('delete-config',ok), + ?ok = ct_netconfc:delete_config(Client,startup), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +lock(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('lock',ok), + ?ok = ct_netconfc:lock(Client,running), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +unlock(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply('unlock',ok), + ?ok = ct_netconfc:unlock(Client,running), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +kill_session(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + ?NS:hello(2), + ?NS:expect(hello), + {ok,_OtherClient} = open(DataDir), + + ?NS:expect_do_reply('kill-session',{kill,2},ok), + ?ok = ct_netconfc:kill_session(Client,2), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + + ok. + +get_no_such_client(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + case ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}) of + {error,no_such_client} -> + ok; + {error,closed} -> + %% Means that the Client process was not terminated before the call. + %% Give it one more go. + {error,no_such_client} = + ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}) + end, + ok. + +action(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{myactionreturn,[{xmlns,"myns"}],["value"]}], + ?NS:expect_reply(action,{data,Data}), + {ok,Data} = ct_netconfc:action(Client,{myaction,[{xmlns,"myns"}],[]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +send_any_rpc(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + GetConf = {'get-config', + [{source,["running"]}, + {filter,[{type,"subtree"}], + [{server,[{xmlns,"myns"}],[]}]}]}, + ?NS:expect_reply('get-config',{data,Data}), + [{data,?NETCONF_NAMESPACE_ATTR,Data}] = ct_netconfc:send_rpc(Client,GetConf), + + EditConf = {'edit-config', + [{target,["running"]}, + {config,[{server,[{xmlns,"myns"}], + [{name,["myserver"]}]}]}]}, + ?NS:expect_reply('edit-config',ok), + [{ok,?NETCONF_NAMESPACE_ATTR,[]}] = ct_netconfc:send_rpc(Client,EditConf), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +send_any(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Correct get-config rpc + Data = [{server,[{xmlns,"myns"}],[{name,[],["myserver"]}]}], + RpcAttr1 = ?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}], + RpcGetConf = {rpc,RpcAttr1, + [{'get-config', + [{source,["running"]}, + {filter,[{type,"subtree"}], + [{server,[{xmlns,"myns"}],[]}]}]}]}, + ?NS:expect_reply('get-config',{data,Data}), + {'rpc-reply',RpcAttr1,[{data,_,Data}]} = ct_netconfc:send(Client,RpcGetConf), + + %% Correct edit-config rpc + RpcAttr2 = ?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"2"}], + RpcEditConf = {rpc,RpcAttr2, + [{'edit-config', + [{target,["running"]}, + {config,[{server,[{xmlns,"myns"}], + [{name,["myserver"]}]}]}]}]}, + ?NS:expect_reply('edit-config',ok), + {'rpc-reply',RpcAttr2,[{ok,_,[]}]} = ct_netconfc:send(Client,RpcEditConf), + + %% Send any data + ?NS:expect_reply(any,{ok,[],[]}), + {ok,_,[]} = ct_netconfc:send(Client,{any,[],[]}), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +hide_password(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + Password = "my_very_secret_password", + Data = [{passwords,[{xmlns,"myns"}], + [{password,[{xmlns,"pwdns"}],[Password]}, + {password,[],[Password]}]}], + ?NS:expect_reply('get',{data,Data}), + ct:capture_start(), % in case of html logging + {ok,Data} = ct_netconfc:get(Client,{passwords,[{xmlns,"myns"}],[]}), + ct:capture_stop(), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + + Log = filename:join(?config(priv_dir,Config),"hide_password-netconf.txt"), + + Text = + case file:read_file(Log) of + {ok,Bin} -> + Bin; + _NoLog -> + %% Assume html logging + list_to_binary(ct:capture_get()) + end, + + nomatch = binary:match(Text,list_to_binary(Password)), + + ok. + +not_proper_xml(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + NS = list_to_binary(?NETCONF_NAMESPACE), + NotProper = <<"<rpc-reply message-id=\"1\" xmlns=\"", + NS/binary,"\"><data></rpc-reply>">>, + ?NS:expect_reply('get',NotProper), + {error,{failed_to_parse_received_data,_}} = + ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +prefixed_namespace(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + NS = list_to_binary(?NETCONF_NAMESPACE), + + %% Test that data element can be properly decoded and that + %% prefixed namespace attributes (exepct the netconf namespace) + %% are forwarded to the content of the data element - i.e. that + %% the xmlns:my is forwarded from the rpc-reply element to the + %% server element below. + Data = <<"<nc:rpc-reply message-id=\"1\" xmlns:nc=\"", + NS/binary,"\" xmlns:my=\"myns\"><nc:data><my:server>", + "<my:name my:lang=\"en\">myserver</my:name></my:server>" + "</nc:data></nc:rpc-reply>">>, + ?NS:expect_reply('get',Data), + {ok,[{'my:server',[{'xmlns:my',"myns"}], + [{'my:name',[{'my:lang',"en"}],["myserver"]}]}]} = + ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + + Ok = <<"<nc:rpc-reply message-id=\"2\" xmlns:nc=\"", + NS/binary,"\"><nc:ok/></nc:rpc-reply>">>, + ?NS:expect_reply('edit-config',Ok), + ?ok = ct_netconfc:edit_config(Client,running, + {server,[{xmlns,"myns"}], + [{name,["myserver"]}]}), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +%% Test that the client can parse data which is received in chunks, +%% i.e. when the complete rpc-reply is not contained in one single ssh +%% data message. +receive_chunked_data(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Construct the data to return from netconf server + Data = [{servers,[{xmlns,"myns"}], + [{server,[],[{name,[],["server0"]}]}, + {server,[],[{name,[],["server1"]}]}, + {server,[],[{name,[],["server2"]}]}, + {server,[],[{name,[],["server3"]}]}, + {server,[],[{name,[],["server4"]}]}, + {server,[],[{name,[],["server5"]}]}, + {server,[],[{name,[],["server6"]}]}, + {server,[],[{name,[],["server7"]}]}, + {server,[],[{name,[],["server8"]}]}, + {server,[],[{name,[],["server9"]}]}] + }], + Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}], + [{data,Data}]}, + Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)), + Netconf = + <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", + Xml/binary,"\n",?END_TAG/binary>>, + + %% Split the data in some chunks + PartLength = size(Netconf) div 3, + <<Part1:PartLength/binary,Part2:PartLength/binary,Part3:PartLength/binary, + Part4/binary>> = Netconf, + + %% Spawn a process which will wait a bit for the client to send + %% the request (below), then order the server to the chunks of the + %% rpc-reply one by one. + spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1), + timer:sleep(100),?NS:hupp(send,Part2), + timer:sleep(100),?NS:hupp(send,Part3), + timer:sleep(100),?NS:hupp(send,Part4) + end), + + %% Order server to expect a get - then the process above will make + %% sure the rpc-reply is sent. + ?NS:expect('get'), + {ok,Data} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +%% Same as receive_chunked_data, but timeout waiting for last part. +timeout_receive_chunked_data(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Construct the data to return from netconf server + Data = [{servers,[{xmlns,"myns"}], + [{server,[],[{name,[],["server0"]}]}, + {server,[],[{name,[],["server1"]}]}, + {server,[],[{name,[],["server2"]}]}, + {server,[],[{name,[],["server3"]}]}, + {server,[],[{name,[],["server4"]}]}, + {server,[],[{name,[],["server5"]}]}, + {server,[],[{name,[],["server6"]}]}, + {server,[],[{name,[],["server7"]}]}, + {server,[],[{name,[],["server8"]}]}, + {server,[],[{name,[],["server9"]}]}] + }], + Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}], + [{data,Data}]}, + Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)), + Netconf = + <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", + Xml/binary,"\n",?END_TAG/binary>>, + + %% Split the data in some chunks + PartLength = size(Netconf) div 3, + <<Part1:PartLength/binary,Part2:PartLength/binary,_Part3:PartLength/binary, + _Part4/binary>> = Netconf, + + %% Spawn a process which will wait a bit for the client to send + %% the request (below), then order the server to the chunks of the + %% rpc-reply one by one. + spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1), + timer:sleep(100),?NS:hupp(send,Part2) + end), + + %% Order server to expect a get - then the process above will make + %% sure the rpc-reply is sent - but only a part of it - then timeout. + ?NS:expect('get'), + {error,timeout} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},2000), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +%% Same as receive_chunked_data, but timeout waiting for last part. +close_while_waiting_for_chunked_data(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Construct the data to return from netconf server + Data = [{servers,[{xmlns,"myns"}], + [{server,[],[{name,[],["server0"]}]}, + {server,[],[{name,[],["server1"]}]}, + {server,[],[{name,[],["server2"]}]}, + {server,[],[{name,[],["server3"]}]}, + {server,[],[{name,[],["server4"]}]}, + {server,[],[{name,[],["server5"]}]}, + {server,[],[{name,[],["server6"]}]}, + {server,[],[{name,[],["server7"]}]}, + {server,[],[{name,[],["server8"]}]}, + {server,[],[{name,[],["server9"]}]}] + }], + Rpc = {'rpc-reply',?NETCONF_NAMESPACE_ATTR ++ [{'message-id',"1"}], + [{data,Data}]}, + Xml = list_to_binary(xmerl:export_simple_element(Rpc,xmerl_xml)), + Netconf = + <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", + Xml/binary,"\n",?END_TAG/binary>>, + + %% Split the data in some chunks + PartLength = size(Netconf) div 3, + <<Part1:PartLength/binary,Part2:PartLength/binary,_Part3:PartLength/binary, + _Part4/binary>> = Netconf, + + %% Spawn a process which will wait a bit for the client to send + %% the request (below), then order the server to the chunks of the + %% rpc-reply one by one. + spawn(fun() -> timer:sleep(500),?NS:hupp(send,Part1), + timer:sleep(100),?NS:hupp(send,Part2), + timer:sleep(100),?NS:hupp(kill) + end), + + %% Order server to expect a get - then the process above will make + %% sure the rpc-reply is sent - but only a part of it - then close. + ?NS:expect('get'), + {error,closed} = ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]},2000), + ok. + +connection_crash(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + + %% Test that if the test survives killing the connection + %% process. Earlier this caused ct_util_server to terminate, and + %% this aborting the complete test run. + spawn(fun() -> timer:sleep(500),exit(Client,kill) end), + ?NS:expect(get), + {error,{closed,killed}}=ct_netconfc:get(Client,{server,[{xmlns,"myns"}],[]}), + ok. + +get_event_streams(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + StreamNames = ["NETCONF","stream1","stream2"], + Streams = [{N,[{description,"descr of " ++ N}]} || N <- StreamNames], + StreamsXml = [{stream,[{name,[N]}|[{Tag,[Value]} || {Tag,Value} <- Data]]} + || {N,Data} <- Streams], + ReplyData = [{netconf,?NETMOD_NOTIF_NAMESPACE_ATTR,[{streams,StreamsXml}]}], + ?NS:expect_reply('get',{data,ReplyData}), + {ok,Streams} = ct_netconfc:get_event_streams(Client,StreamNames), + + ?NS:expect_reply('get',{data,ReplyData}), + {ok,Streams} = ct_netconfc:get_event_streams(Client,StreamNames,5000), + + ?NS:expect('get'), + {error,timeout} = ct_netconfc:get_event_streams(Client,100), + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + ok. + +create_subscription(Config) -> + DataDir = ?config(data_dir,Config), + + %% All defaults + {ok,Client1} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream]},ok), + ?ok = ct_netconfc:create_subscription(Client1), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client1), + + %% All defaults with timeout + {ok,Client1a} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream]},ok), + ?ok = ct_netconfc:create_subscription(Client1a,5000), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client1a), + + %% All defaults timing out + {ok,Client1b} = open_success(DataDir), + ?NS:expect({'create-subscription',[stream]}), + {error,timeout} = ct_netconfc:create_subscription(Client1b,100), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client1b), + + %% Stream + {ok,Client2} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream]},ok), + Stream = "some_stream", + ?ok = ct_netconfc:create_subscription(Client2,Stream), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client2), + + %% Filter + {ok,Client3} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter]},ok), + Filter = {notification,?NETMOD_NOTIF_NAMESPACE_ATTR, + [eventTime]}, + ?ok = ct_netconfc:create_subscription(Client3,Filter), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client3), + + %% Filter with timeout + {ok,Client3a} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter]},ok), + ?ok = ct_netconfc:create_subscription(Client3a,Filter,5000), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client3a), + + %% Filter timing out + {ok,Client3b} = open_success(DataDir), + ?NS:expect({'create-subscription',[stream,filter]}), + {error,timeout}=ct_netconfc:create_subscription(Client3b,Filter,100), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client3b), + + %% Stream and filter + {ok,Client4} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter]},ok), + ?ok = ct_netconfc:create_subscription(Client4,Stream,Filter), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client4), + + %% Start/stop time + {ok,Client5} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok), + StartTime = xs_datetime({D,{H,M,S}}= calendar:local_time()), + StopTime = xs_datetime({D,{H+2,M,S}}), + ?ok = ct_netconfc:create_subscription(Client5,StartTime,StopTime), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client5), + + %% Start/stop time with timeout + {ok,Client5a} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok), + ?ok = ct_netconfc:create_subscription(Client5a,StartTime,StopTime,5000), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client5a), + + %% Start/stop time timing out + {ok,Client5b} = open_success(DataDir), + ?NS:expect({'create-subscription',[stream,startTime,stopTime]}), + {error,timeout} = + ct_netconfc:create_subscription(Client5b,StartTime,StopTime,100), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client5b), + + %% Stream and start/stop time + {ok,Client6} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,startTime,stopTime]},ok), + ?ok = ct_netconfc:create_subscription(Client6,Stream,StartTime,StopTime), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client6), + + %% Filter and start/stop time + {ok,Client7} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter,startTime,stopTime]}, + ok), + ?ok = ct_netconfc:create_subscription(Client7,Filter, + StartTime,StopTime), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client7), + + %% Stream, filter and start/stop time + {ok,Client8} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream,filter,startTime,stopTime]}, + ok), + ?ok = ct_netconfc:create_subscription(Client8,Stream,Filter, + StartTime,StopTime), + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client8), + + ok. + +receive_event(Config) -> + DataDir = ?config(data_dir,Config), + {ok,Client} = open_success(DataDir), + ?NS:expect_reply({'create-subscription',[stream]},ok), + ?ok = ct_netconfc:create_subscription(Client), + + ?NS:hupp(send_event), + + receive + %% Matching ?NS:make_msg(event) + {notification,?NETCONF_NOTIF_NAMESPACE_ATTR, + [{eventTime,[],[_Time]}, + {event,[{xmlns,"http://my.namespaces.com/event"}], + [{severity,_,_}, + {description,_,_}]}]} -> + ok; + Other -> + ct:fail({got_unexpected_while_waiting_for_event, Other}) + after 3000 -> + ct:fail(timeout_waiting_for_event) + end, + + ?NS:expect_do_reply('close-session',close,ok), + ?ok = ct_netconfc:close_session(Client), + + ok. + +%%%----------------------------------------------------------------- + +break(_Config) -> + test_server:break("break test case"). + +br() -> + test_server:break(""). + +%%%----------------------------------------------------------------- +%% Open a netconf session which is not specified in a config file +open_success(Dir) -> + open_success(Dir,[]). + +%% Open a netconf session which is not specified in a config file, and +%% give som extra options in addition to the test defaults. +open_success(Dir,ExtraOpts) when is_list(Dir), is_list(ExtraOpts) -> + ?NS:hello(1), % tell server to send hello with session id 1 + ?NS:expect(hello), % tell server to expect a hello message from client + open(Dir,ExtraOpts); + +%% Open a named netconf session which is not specified in a config file +open_success(KeyOrName,Dir) when is_atom(KeyOrName), is_list(Dir) -> + ?NS:hello(1), + ?NS:expect(hello), + ct_netconfc:open(KeyOrName,?DEFAULT_SSH_OPTS(Dir)). + +open(Dir) -> + open(Dir,[]). +open(Dir,ExtraOpts) -> + Opts = lists:ukeymerge(1,lists:keysort(1,ExtraOpts), + lists:keysort(1,?DEFAULT_SSH_OPTS(Dir))), + ct_netconfc:open(Opts). + +%%%----------------------------------------------------------------- +%%% Open a netconf session which is specified in a config file +%%% KeyOrName is the config key (server_id()) or name given in a +%%% require statement (target_name()). +open_configured_success(KeyOrName,Dir) when is_atom(KeyOrName) -> + open_configured_success(KeyOrName,Dir,[]). +open_configured_success(KeyOrName,Dir,ExtraOpts) when is_atom(KeyOrName) -> + ?NS:hello(1), + ?NS:expect(hello), + ct_netconfc:open(KeyOrName,[{user_dir,Dir}|ExtraOpts]). + +%%%----------------------------------------------------------------- +%%% Convert erlang datetime to the simplest variant of XML dateTime +xs_datetime({{Y,M,D},{H,Mi,S}}) -> + lists:flatten( + io_lib:format("~p-~s-~sT~s:~s:~s",[Y,pad(M),pad(D),pad(H),pad(Mi),pad(S)])). + +pad(I) when I<10 -> + "0"++integer_to_list(I); +pad(I) -> + integer_to_list(I). + + +%%%----------------------------------------------------------------- +%%% BEGIN SSH key management +%% copy private keys to given dir from ~/.ssh +get_id_keys(Config) -> + DstDir = ?config(priv_dir, Config), + SrcDir = filename:join(os:getenv("HOME"), ".ssh"), + RsaOk = copyfile(SrcDir, DstDir, "id_rsa"), + DsaOk = copyfile(SrcDir, DstDir, "id_dsa"), + case {RsaOk, DsaOk} of + {{ok, _}, {ok, _}} -> {ok, both}; + {{ok, _}, _} -> {ok, rsa}; + {_, {ok, _}} -> {ok, dsa}; + {Error, _} -> Error + end. + +%% Remove later on. Use make_dsa_files instead. +remove_id_keys(Config) -> + Dir = ?config(priv_dir, Config), + file:delete(filename:join(Dir, "id_rsa")), + file:delete(filename:join(Dir, "id_dsa")). + + +make_dsa_files(Config) -> + make_dsa_files(Config, rfc4716_public_key). +make_dsa_files(Config, Type) -> + {DSA, EncodedKey} = gen_dsa(128, 20), + PKey = DSA#'DSAPrivateKey'.y, + P = DSA#'DSAPrivateKey'.p, + Q = DSA#'DSAPrivateKey'.q, + G = DSA#'DSAPrivateKey'.g, + Dss = #'Dss-Parms'{p=P, q=Q, g=G}, + {ok, Hostname} = inet:gethostname(), + {ok, {A, B, C, D}} = inet:getaddr(Hostname, inet), + IP = lists:concat([A, ".", B, ".", C, ".", D]), + Attributes = [], % Could be [{comment,"user@" ++ Hostname}], + HostNames = [{hostnames,[IP, IP]}], + PublicKey = [{{PKey, Dss}, Attributes}], + KnownHosts = [{{PKey, Dss}, HostNames}], + + KnownHostsEnc = public_key:ssh_encode(KnownHosts, known_hosts), + KnownHosts = public_key:ssh_decode(KnownHostsEnc, known_hosts), + + PublicKeyEnc = public_key:ssh_encode(PublicKey, Type), + + SystemTmpDir = ?config(data_dir, Config), + filelib:ensure_dir(SystemTmpDir), + file:make_dir(SystemTmpDir), + + DSAFile = filename:join(SystemTmpDir, "ssh_host_dsa_key.pub"), + file:delete(DSAFile), + + DSAPrivateFile = filename:join(SystemTmpDir, "ssh_host_dsa_key"), + file:delete(DSAPrivateFile), + + KHFile = filename:join(SystemTmpDir, "known_hosts"), + file:delete(KHFile), + + PemBin = public_key:pem_encode([EncodedKey]), + + file:write_file(DSAFile, PublicKeyEnc), + file:write_file(KHFile, KnownHostsEnc), + file:write_file(DSAPrivateFile, PemBin), + ok. + +%%-------------------------------------------------------------------- +%% Creates a dsa key (OBS: for testing only) +%% the sizes are in bytes +%% gen_dsa(::integer()) -> {::atom(), ::binary(), ::opaque()} +%%-------------------------------------------------------------------- +gen_dsa(LSize,NSize) when is_integer(LSize), is_integer(NSize) -> + Key = gen_dsa2(LSize, NSize), + {Key, encode_key(Key)}. + +encode_key(Key = #'RSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('RSAPrivateKey', Key), + {'RSAPrivateKey', list_to_binary(Der), not_encrypted}; +encode_key(Key = #'DSAPrivateKey'{}) -> + {ok, Der} = 'OTP-PUB-KEY':encode('DSAPrivateKey', Key), + {'DSAPrivateKey', list_to_binary(Der), not_encrypted}. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% DSA key generation (OBS: for testing only) +%% See http://en.wikipedia.org/wiki/Digital_Signature_Algorithm +%% and the fips_186-3.pdf +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +gen_dsa2(LSize, NSize) -> + Q = prime(NSize), %% Choose N-bit prime Q + X0 = prime(LSize), + P0 = prime((LSize div 2) +1), + + %% Choose L-bit prime modulus P such that p-1 is a multiple of q. + case dsa_search(X0 div (2*Q*P0), P0, Q, 1000) of + error -> + gen_dsa2(LSize, NSize); + P -> + G = crypto:mod_exp(2, (P-1) div Q, P), % Choose G a number whose multiplicative order modulo p is q. + %% such that This may be done by setting g = h^(p-1)/q mod p, commonly h=2 is used. + + X = prime(20), %% Choose x by some random method, where 0 < x < q. + Y = crypto:mod_exp(G, X, P), %% Calculate y = g^x mod p. + + #'DSAPrivateKey'{version=0, p=P, q=Q, g=G, y=Y, x=X} + end. + +%% See fips_186-3.pdf +dsa_search(T, P0, Q, Iter) when Iter > 0 -> + P = 2*T*Q*P0 + 1, + case is_prime(crypto:mpint(P), 50) of + true -> P; + false -> dsa_search(T+1, P0, Q, Iter-1) + end; +dsa_search(_,_,_,_) -> + error. + + +%%%%%%% Crypto Math %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +prime(ByteSize) -> + Rand = odd_rand(ByteSize), + crypto:erlint(prime_odd(Rand, 0)). + +prime_odd(Rand, N) -> + case is_prime(Rand, 50) of + true -> + Rand; + false -> + NotPrime = crypto:erlint(Rand), + prime_odd(crypto:mpint(NotPrime+2), N+1) + end. + +%% see http://en.wikipedia.org/wiki/Fermat_primality_test +is_prime(_, 0) -> true; +is_prime(Candidate, Test) -> + CoPrime = odd_rand(<<0,0,0,4, 10000:32>>, Candidate), + case crypto:mod_exp(CoPrime, Candidate, Candidate) of + CoPrime -> is_prime(Candidate, Test-1); + _ -> false + end. + +odd_rand(Size) -> + Min = 1 bsl (Size*8-1), + Max = (1 bsl (Size*8))-1, + odd_rand(crypto:mpint(Min), crypto:mpint(Max)). + +odd_rand(Min,Max) -> + Rand = <<Sz:32, _/binary>> = crypto:rand_uniform(Min,Max), + BitSkip = (Sz+4)*8-1, + case Rand of + Odd = <<_:BitSkip, 1:1>> -> Odd; + Even = <<_:BitSkip, 0:1>> -> + crypto:mpint(crypto:erlint(Even)+1) + end. + +copyfile(SrcDir, DstDir, Fn) -> + file:copy(filename:join(SrcDir, Fn), + filename:join(DstDir, Fn)). + +%%% END SSH key management +%%%----------------------------------------------------------------- diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl new file mode 100644 index 0000000000..665b0e556c --- /dev/null +++ b/lib/common_test/test/ct_netconfc_SUITE_data/ns.erl @@ -0,0 +1,506 @@ +%%-------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%%---------------------------------------------------------------------- +%% A netconf server used for testing of netconfc +-module(ns). + +%-compile(export_all). +-include_lib("common_test/src/ct_netconfc.hrl"). + + +%%%----------------------------------------------------------------- +%%% API +-export([start/1, + stop/1, + hello/1, + hello/2, + expect/1, + expect_reply/2, + expect_do/2, + expect_do_reply/3, + hupp/1, + hupp/2]). + +%%%----------------------------------------------------------------- +%%% ssh_channel callbacks +-export([init/1, + terminate/2, + handle_ssh_msg/2, + handle_msg/2]). + +%%%----------------------------------------------------------------- +%% Server specifications +-define(SERVER_DATA_NAMESPACE, "ClientTest"). +-define(CAPABILITIES,?CAPABILITIES_VSN("1.0")). +-define(CAPABILITIES_VSN(Vsn), + [ + ?NETCONF_BASE_CAP ++ Vsn, + "urn:ietf:params:netconf:capability:writable-running:1.0", + "urn:ietf:params:netconf:capability:candidate:1.0", + "urn:ietf:params:netconf:capability:confirmed-commit:1.0", + "urn:ietf:params:netconf:capability:rollback-on-error:1.0", + "urn:ietf:params:netconf:capability:startup:1.0", + "urn:ietf:params:netconf:capability:url:1.0", + "urn:ietf:params:netconf:capability:xpath:1.0", + "urn:ietf:params:netconf:capability:notification:1.0", + "urn:ietf:params:netconf:capability:interleave:1.0", + ?ACTION_NAMESPACE, + ?SERVER_DATA_NAMESPACE + ]). +-define(SSH_PORT, 2060). +-define(ssh_config(Dir),[{port, ?SSH_PORT}, + {interface, {127,0,0,1}}, + {system_dir, Dir}, + {user_dir, Dir}, + {user_passwords, [{"xxx","xxx"}]}, + {password, "global-xxx"}]). + +%% Some help for debugging +%-define(dbg(F,A),io:format(F,A)). +-define(dbg(F,A),ok). +-define(dbg_event(Event,Expect), + ?dbg("Event: ~p~nExpected: ~p~n",[Event,Expect])). + +%% State +-record(session, {cb, + connection, + buffer = <<>>, + session_id}). + + +%%%----------------------------------------------------------------- +%%% API + +%% Start the netconf server and use the given directory as system_dir +%% and user_dir +start(Dir) -> + spawn(fun() -> init_server(Dir) end). + +%% Stop the netconf server +stop(Pid) -> + Pid ! {stop,self()}, + receive stopped -> ok end. + +%% Set the session id for the hello message. +%% If this is not called prior to starting the session, no hello +%% message will be sent. +%% 'Stuff' indicates some special handling to e.g. provoke error cases +hello(SessionId) -> + hello(SessionId,undefined). +hello(SessionId,Stuff) -> + insert(hello,{SessionId,Stuff}). + +%% Tell server to expect the given message without doing any further +%% actions. To be called directly before sending a request. +expect(Expect) -> + expect_do_reply(Expect,undefined,undefined). + +%% Tell server to expect the given message and reply with the give +%% reply. To be called directly before sending a request. +expect_reply(Expect,Reply) -> + expect_do_reply(Expect,undefined,Reply). + +%% Tell server to expect the given message and perform an action. To +%% be called directly before sending a request. +expect_do(Expect,Do) -> + expect_do_reply(Expect,Do,undefined). + +%% Tell server to expect the given message, perform an action and +%% reply with the given reply. To be called directly before sending a +%% request. +expect_do_reply(Expect,Do,Reply) -> + add_expect({Expect,Do,Reply}). + +%% Hupp the server - i.e. tell it to do something - +%% e.g. hupp(send_event) will cause send_event(State) to be called on +%% the session channel process. +hupp(send_event) -> + hupp(send,[make_msg(event)]); +hupp(kill) -> + hupp(fun hupp_kill/1,[]). + +hupp(send,Data) -> + hupp(fun hupp_send/2,[Data]); +hupp(Fun,Args) when is_function(Fun) -> + [{_,Pid}] = lookup(channel_process), + Pid ! {hupp,Fun,Args}. + +%%%----------------------------------------------------------------- +%%% Main loop of the netconf server +init_server(Dir) -> + ets:new(ns_tab,[set,named_table,public]), + Config = ?ssh_config(Dir), + {_,Host} = lists:keyfind(interface, 1, Config), + {_,Port} = lists:keyfind(port, 1, Config), + Opts = lists:filter(fun({Key,_}) -> + lists:member(Key,[system_dir, + password, + user_passwords, + pwdfun]) + end, + Config), + {ok, Daemon} = + ssh:daemon(Host, Port, + [{subsystems,[{"netconf",{?MODULE,[]}}]} + |Opts]), + loop(Daemon). + +loop(Daemon) -> + receive + {stop,From} -> + ssh:stop_daemon(Daemon), + From ! stopped + end. + +%%---------------------------------------------------------------------- +%% Behaviour callback functions (ssh_channel) +%%---------------------------------------------------------------------- +init([]) -> + {ok, undefined}. + +terminate(_Reason, _State) -> + ok. + +handle_ssh_msg({ssh_cm,CM,{data, Ch, _Type = 0, Data}}, State) -> + %% erlang:display({self(),data,CM,Ch,State}), + data_for_channel(CM, Ch, Data, State); +handle_ssh_msg({ssh_cm,CM,{closed, Ch}}, State) -> + %% erlang:display({self(),closed,CM,Ch,State}), + stop_channel(CM, Ch, State); +handle_ssh_msg({ssh_cm,CM,{eof, Ch}}, State) -> + %% erlang:display({self(),eof,CM,Ch,State}), + data_for_channel(CM,Ch, <<>>, State). + + +handle_msg({'EXIT', _Pid, _Reason}, State) -> + {ok, State}; +handle_msg({ssh_channel_up,Ch,CM},undefined) -> + %% erlang:display({self(),up,CM,Ch}), + ConnRef = {CM,Ch}, + SessionId = maybe_hello(ConnRef), + insert(channel_process,self()), % used to hupp the server + {ok, #session{connection = ConnRef, + session_id = SessionId}}; +handle_msg({hupp,Fun,Args},State) -> + {ok,apply(Fun,Args ++ [State])}. + +data_for_channel(CM, Ch, Data, State) -> + try data(Data, State) of + {ok, NewState} -> + case erase(stop) of + true -> + stop_channel(CM, Ch, NewState); + _ -> + {ok, NewState} + end + catch + Class:Reason -> + Stacktrace = erlang:get_stacktrace(), + error_logger:error_report([{?MODULE, data_for_channel}, + {request, Data}, + {reason, {Class, Reason}}, + {stacktrace, Stacktrace}]), + stop_channel(CM, Ch, State) + end. + +data(Data, State = #session{connection = ConnRef, + buffer = Buffer}) -> + AllData = <<Buffer/binary,Data/binary>>, + case find_endtag(AllData) of + {ok,Msgs,Rest} -> + [check_expected(ConnRef,Msg) || Msg <- Msgs], + {ok,State#session{buffer=Rest}}; + need_more -> + {ok,State#session{buffer=AllData}} + end. + +stop_channel(CM, Ch, State) -> + ssh:close(CM), + {stop, Ch, State}. + + +%%%----------------------------------------------------------------- +%%% Functions to trigg via hupp/1: + +%% Send data spontaneously - e.g. an event +hupp_send(Data,State = #session{connection = ConnRef}) -> + send(ConnRef,Data), + State. +hupp_kill(State = #session{connection = ConnRef}) -> + kill(ConnRef), + State. + +%%%----------------------------------------------------------------- +%%% Internal functions + + +%%% Send ssh data to the client +send({CM,Ch},Data) -> + ssh_connection:send(CM, Ch, Data). + +%%% Kill ssh connection +kill({CM,_Ch}) -> + ssh:close(CM). + +add_expect(Add) -> + case lookup(expect) of + [] -> + insert(expect,[Add]); + [{expect,First}] -> + insert(expect,First ++ [Add]) + end, + ok. + +insert(Key,Value) -> + ets:insert(ns_tab,{Key,Value}). +lookup(Key) -> + ets:lookup(ns_tab,Key). + +maybe_hello(ConnRef) -> + case lookup(hello) of + [{hello,{SessionId,Stuff}}] -> + %% erlang:display({SessionId,Stuff}), + ets:delete(ns_tab,hello), + insert({session,SessionId},ConnRef), + reply(ConnRef,{hello,SessionId,Stuff}), + SessionId; + [] -> + undefined + end. + +find_endtag(Data) -> + case binary:split(Data,[?END_TAG],[global]) of + [Data] -> + need_more; + Msgs -> + {ok,lists:sublist(Msgs,length(Msgs)-1),lists:last(Msgs)} + end. + +check_expected(ConnRef,Msg) -> + case lookup(expect) of + [{expect,[{Expect,Do,Reply}|Rest]}] -> + insert(expect,Rest), + %% erlang:display({got,io_lib:format("~s",[Msg])}), + %% erlang:display({expected,Expect}), + match(Msg,Expect), + do(ConnRef, Do), + reply(ConnRef,Reply); + Expected -> + exit({error,{got_unexpected,Msg,Expected}}) + end. + +match(Msg,Expect) -> + ?dbg("Match: ~p~n",[Msg]), + {ok,ok,<<>>} = xmerl_sax_parser:stream(Msg,[{event_fun,fun event/3}, + {event_state,Expect}]). + +event(Event,_Loc,Expect) -> + ?dbg_event(Event,Expect), + event(Event,Expect). + +event(startDocument,Expect) -> match(Expect); +event({startElement,_,Name,_,Attrs},[{se,Name}|Match]) -> + msg_id(Name,Attrs), + Match; +event({startElement,_,Name,_,Attrs},[ignore,{se,Name}|Match]) -> + msg_id(Name,Attrs), + Match; +event({startElement,_,Name,_,Attrs},[{se,Name,As}|Match]) -> + msg_id(Name,Attrs), + match_attrs(Name,As,Attrs), + Match; +event({startElement,_,Name,_,Attrs},[ignore,{se,Name,As}|Match]) -> + msg_id(Name,Attrs), + match_attrs(Name,As,Attrs), + Match; +event({startPrefixMapping,_,Ns},[{ns,Ns}|Match]) -> Match; +event({startPrefixMapping,_,Ns},[ignore,{ns,Ns}|Match]) -> Match; +event({endPrefixMapping,_},Match) -> Match; +event({endElement,_,Name,_},[{ee,Name}|Match]) -> Match; +event({endElement,_,Name,_},[ignore,{ee,Name}|Match]) -> Match; +event(endDocument,Match) when Match==[]; Match==[ignore] -> ok; +event(_,[ignore|_]=Match) -> Match; +event(Event,Match) -> throw({nomatch,{Event,Match}}). + +msg_id("rpc",Attrs) -> + case lists:keyfind("message-id",3,Attrs) of + {_,_,_,Str} -> put(msg_id,Str); + false -> erase(msg_id) + end; +msg_id(_,_) -> + ok. + +match_attrs(Name,[{Key,Value}|As],Attrs) -> + case lists:keyfind(atom_to_list(Key),3,Attrs) of + {_,_,_,Value} -> match_attrs(Name,As,Attrs); + false -> throw({missing_attr,Key,Name,Attrs}); + _ -> throw({faulty_attr_value,Key,Name,Attrs}) + end; +match_attrs(_,[],_) -> + ok. + +do(ConnRef, close) -> + ets:match_delete(ns_tab,{{session,'_'},ConnRef}), + put(stop,true); +do(_ConnRef, {kill,SessionId}) -> + case lookup({session,SessionId}) of + [{_,Owner}] -> + ets:delete(ns_tab,{session,SessionId}), + kill(Owner); + _ -> + exit({no_session_to_kill,SessionId}) + end; +do(_, undefined) -> + ok. + +reply(_,undefined) -> + ?dbg("no reply~n",[]), + ok; +reply(ConnRef,Reply) -> + ?dbg("Reply: ~p~n",[Reply]), + send(ConnRef, make_msg(Reply)). + +from_simple(Simple) -> + list_to_binary(xmerl:export_simple_element(Simple,xmerl_xml)). + +xml(Content) -> + <<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", + Content/binary,"\n",?END_TAG/binary>>. + +rpc_reply(Content) when is_binary(Content) -> + MsgId = case erase(msg_id) of + undefined -> <<>>; + Id -> list_to_binary([" message-id=\"",Id,"\""]) + end, + <<"<rpc-reply xmlns=\"",?NETCONF_NAMESPACE,"\"",MsgId/binary,">\n", + Content/binary,"\n</rpc-reply>">>; +rpc_reply(Content) -> + rpc_reply(list_to_binary(Content)). + +session_id(no_session_id) -> + <<>>; +session_id(SessionId0) -> + SessionId = list_to_binary(integer_to_list(SessionId0)), + <<"<session-id>",SessionId/binary,"</session-id>\n">>. + +capabilities(undefined) -> + CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] + || C <- ?CAPABILITIES]), + <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>; +capabilities({base,Vsn}) -> + CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] + || C <- ?CAPABILITIES_VSN(Vsn)]), + <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>; +capabilities(no_base) -> + [_|Caps] = ?CAPABILITIES, + CapsXml = list_to_binary([["<capability>",C,"</capability>\n"] || C <- Caps]), + <<"<capabilities>\n",CapsXml/binary,"</capabilities>\n">>; +capabilities(no_caps) -> + <<>>. + +%%%----------------------------------------------------------------- +%%% Match received netconf message from the client. Add a new clause +%%% for each new message to recognize. The clause argument shall match +%%% the Expect argument in expect/1, expect_reply/2 or +%%% expect_do_reply/3. +%%% +%%% match(term()) -> [Match]. +%%% Match = ignore | {se,Name} | {se,Name,Attrs} | {ee,Name} | {ns,Namespace} +%%% Name = string() +%%% Attrs = [{atom(),string()}] +%%% Namespace = string() +%%% +%%% 'se' means start element, 'ee' means end element - i.e. to match +%%% an XML element you need one 'se' entry and one 'ee' entry with the +%%% same name in the match list. +match(hello) -> + [ignore,{se,"hello"},ignore,{ee,"hello"},ignore]; +match('close-session') -> + [ignore,{se,"rpc"},{se,"close-session"}, + {ee,"close-session"},{ee,"rpc"},ignore]; +match('edit-config') -> + [ignore,{se,"rpc"},{se,"edit-config"},{se,"target"},ignore,{ee,"target"}, + {se,"config"},ignore,{ee,"config"},{ee,"edit-config"},{ee,"rpc"},ignore]; +match('get') -> + match({get,subtree}); +match({'get',FilterType}) -> + [ignore,{se,"rpc"},{se,"get"},{se,"filter",[{type,atom_to_list(FilterType)}]}, + ignore,{ee,"filter"},{ee,"get"},{ee,"rpc"},ignore]; +match('get-config') -> + match({'get-config',subtree}); +match({'get-config',FilterType}) -> + [ignore,{se,"rpc"},{se,"get-config"},{se,"source"},ignore,{ee,"source"}, + {se,"filter",[{type,atom_to_list(FilterType)}]},ignore,{ee,"filter"}, + {ee,"get-config"},{ee,"rpc"},ignore]; +match('copy-config') -> + [ignore,{se,"rpc"},{se,"copy-config"},{se,"target"},ignore,{ee,"target"}, + {se,"source"},ignore,{ee,"source"},{ee,"copy-config"},{ee,"rpc"},ignore]; +match('delete-config') -> + [ignore,{se,"rpc"},{se,"delete-config"},{se,"target"},ignore,{ee,"target"}, + {ee,"delete-config"},{ee,"rpc"},ignore]; +match('lock') -> + [ignore,{se,"rpc"},{se,"lock"},{se,"target"},ignore,{ee,"target"}, + {ee,"lock"},{ee,"rpc"},ignore]; +match('unlock') -> + [ignore,{se,"rpc"},{se,"unlock"},{se,"target"},ignore,{ee,"target"}, + {ee,"unlock"},{ee,"rpc"},ignore]; +match('kill-session') -> + [ignore,{se,"rpc"},{se,"kill-session"},{se,"session-id"},ignore, + {ee,"session-id"},{ee,"kill-session"},{ee,"rpc"},ignore]; +match(action) -> + [ignore,{se,"rpc"},{ns,?ACTION_NAMESPACE},{se,"action"},{se,"data"},ignore, + {ee,"data"},{ee,"action"},{ee,"rpc"},ignore]; +match({'create-subscription',Content}) -> + [ignore,{se,"rpc"},{ns,?NETCONF_NOTIF_NAMESPACE}, + {se,"create-subscription"}] ++ + lists:flatmap(fun(X) -> + [{se,atom_to_list(X)},ignore,{ee,atom_to_list(X)}] + end, Content) ++ + [{ee,"create-subscription"},{ee,"rpc"},ignore]; +match(any) -> + [ignore]. + + + +%%%----------------------------------------------------------------- +%%% Make message to send to the client. +%%% Add a new clause for each new message that shall be sent. The +%%% clause shall match the Reply argument in expect_reply/2 or +%%% expect_do_reply/3. +make_msg({hello,SessionId,Stuff}) -> + SessionIdXml = session_id(SessionId), + CapsXml = capabilities(Stuff), + xml(<<"<hello xmlns=\"",?NETCONF_NAMESPACE,"\">\n",CapsXml/binary, + SessionIdXml/binary,"</hello>">>); +make_msg(ok) -> + xml(rpc_reply("<ok/>")); +make_msg({data,Data}) -> + xml(rpc_reply(from_simple({data,Data}))); +make_msg(event) -> + xml(<<"<notification xmlns=\"",?NETCONF_NOTIF_NAMESPACE,"\">" + "<eventTime>2012-06-14T14:50:54+02:00</eventTime>" + "<event xmlns=\"http://my.namespaces.com/event\">" + "<severity>major</severity>" + "<description>Something terrible happened</description>" + "</event>" + "</notification>">>); +make_msg(Xml) when is_binary(Xml) -> + xml(Xml); +make_msg(Simple) when is_tuple(Simple) -> + xml(from_simple(Simple)). diff --git a/lib/common_test/test/ct_priv_dir_SUITE.erl b/lib/common_test/test/ct_priv_dir_SUITE.erl index f6942d59bf..426b2d9a55 100644 --- a/lib/common_test/test/ct_priv_dir_SUITE.erl +++ b/lib/common_test/test/ct_priv_dir_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 diff --git a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl index 423cb2999b..7704a29768 100644 --- a/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl +++ b/lib/common_test/test/ct_priv_dir_SUITE_data/priv_dir_SUITE.erl @@ -1,11 +1,21 @@ -%%%------------------------------------------------------------------- -%%% @author Peter Andersson <[email protected]> -%%% @copyright (C) 2012, Peter Andersson -%%% @doc -%%% -%%% @end -%%% Created : 23 Jan 2012 by Peter Andersson <[email protected]> -%%%------------------------------------------------------------------- +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% -module(priv_dir_SUITE). -compile(export_all). diff --git a/lib/common_test/test/ct_test_server_if_1_SUITE.erl b/lib/common_test/test/ct_test_server_if_1_SUITE.erl index efc0309781..8e4852369d 100644 --- a/lib/common_test/test/ct_test_server_if_1_SUITE.erl +++ b/lib/common_test/test/ct_test_server_if_1_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -242,25 +242,28 @@ test_events(ts_if_1) -> {?eh,tc_auto_skip,{ts_if_5_SUITE,end_per_suite, {require_failed_in_suite0,{not_available,undef_variable}}}}, - {?eh,tc_start,{ts_if_6_SUITE,tc1}}, - {?eh,tc_done,{ts_if_6_SUITE,tc1,{failed,{error,{suite0_failed,{exited,suite0_byebye}}}}}}, - {?eh,test_stats,{3,5,{6,8}}}, + {?eh,tc_start,{ct_framework,init_per_suite}}, + {?eh,tc_done,{ct_framework,init_per_suite, + {failed,{error,{suite0_failed,{exited,suite0_byebye}}}}}}, + {?eh,tc_auto_skip,{ts_if_6_SUITE,tc1, + {failed,{error,{suite0_failed,{exited,suite0_byebye}}}}}}, + {?eh,test_stats,{3,5,{5,9}}}, {?eh,tc_start,{ts_if_7_SUITE,tc1}}, {?eh,tc_done,{ts_if_7_SUITE,tc1,ok}}, - {?eh,test_stats,{4,5,{6,8}}}, + {?eh,test_stats,{4,5,{5,9}}}, {?eh,tc_start,{ts_if_8_SUITE,tc1}}, {?eh,tc_done,{ts_if_8_SUITE,tc1,{failed,{error,failed_on_purpose}}}}, - {?eh,test_stats,{4,6,{6,8}}}, + {?eh,test_stats,{4,6,{5,9}}}, {?eh,tc_user_skip,{skipped_by_spec_1_SUITE,all,"should be skipped"}}, - {?eh,test_stats,{4,6,{7,8}}}, + {?eh,test_stats,{4,6,{6,9}}}, {?eh,tc_start,{skipped_by_spec_2_SUITE,init_per_suite}}, {?eh,tc_done,{skipped_by_spec_2_SUITE,init_per_suite,ok}}, {?eh,tc_user_skip,{skipped_by_spec_2_SUITE,tc1,"should be skipped"}}, - {?eh,test_stats,{4,6,{8,8}}}, + {?eh,test_stats,{4,6,{7,9}}}, {?eh,tc_start,{skipped_by_spec_2_SUITE,end_per_suite}}, {?eh,tc_done,{skipped_by_spec_2_SUITE,end_per_suite,ok}}, diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl index 6df02d12b7..80cca4a1cc 100644 --- a/lib/common_test/test/ct_test_support.erl +++ b/lib/common_test/test/ct_test_support.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,12 +29,14 @@ -export([init_per_suite/1, init_per_suite/2, end_per_suite/1, init_per_testcase/2, end_per_testcase/2, write_testspec/2, write_testspec/3, - run/2, run/4, get_opts/1, wait_for_ct_stop/1]). + run/2, run/3, run/4, get_opts/1, wait_for_ct_stop/1]). -export([handle_event/2, start_event_receiver/1, get_events/2, verify_events/3, reformat/2, log_events/4, join_abs_dirs/2]). +-export([ct_test_halt/1]). + -include_lib("kernel/include/file.hrl"). %%%----------------------------------------------------------------- @@ -223,14 +225,15 @@ get_opts(Config) -> %%%----------------------------------------------------------------- %%% -run(Opts, Config) -> +run(Opts, Config) when is_list(Opts) -> CTNode = proplists:get_value(ct_node, Config), Level = proplists:get_value(trace_level, Config), %% use ct interface test_server:format(Level, "~n[RUN #1] Calling ct:run_test(~p) on ~p~n", [Opts, CTNode]), - Result1 = rpc:call(CTNode, ct, run_test, [Opts]), - + CtRunTestResult = rpc:call(CTNode, ct, run_test, [Opts]), + test_server:format(Level, "~n[RUN #1] Got return value ~p~n", + [CtRunTestResult]), case rpc:call(CTNode, erlang, whereis, [ct_util_server]) of undefined -> ok; @@ -242,26 +245,59 @@ run(Opts, Config) -> undefined = rpc:call(CTNode, erlang, whereis, [ct_util_server]) end, %% use run_test interface (simulated) - test_server:format(Level, "Saving start opts on ~p: ~p~n", [CTNode,Opts]), - rpc:call(CTNode, application, set_env, [common_test, run_test_start_opts, Opts]), - test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n", [CTNode]), - Result2 = rpc:call(CTNode, ct_run, script_start, []), - case {Result1,Result2} of - {ok,ok} -> + Opts1 = [{halt_with,{?MODULE,ct_test_halt}} | Opts], + test_server:format(Level, "Saving start opts on ~p: ~p~n", + [CTNode, Opts1]), + rpc:call(CTNode, application, set_env, + [common_test, run_test_start_opts, Opts1]), + test_server:format(Level, "[RUN #2] Calling ct_run:script_start() on ~p~n", + [CTNode]), + ExitStatus = rpc:call(CTNode, ct_run, script_start, []), + test_server:format(Level, "[RUN #2] Got exit status value ~p~n", + [ExitStatus]), + case {CtRunTestResult,ExitStatus} of + {{_Ok,Failed,{_UserSkipped,_AutoSkipped}},1} when Failed > 0 -> ok; - {E,_} when E =/= ok -> - E; - {_,E} when E =/= ok -> - E + {{_Ok,0,{_UserSkipped,AutoSkipped}},ExitStatus} when AutoSkipped > 0 -> + case proplists:get_value(exit_status, Opts1) of + ignore_config when ExitStatus == 1 -> + {error,{wrong_exit_status,ExitStatus}}; + _ -> + ok + end; + {{error,_}=Error,ExitStatus} -> + if ExitStatus /= 2 -> + {error,{wrong_exit_status,ExitStatus}}; + ExitStatus == 2 -> + Error + end; + {{_Ok,0,{_UserSkipped,_AutoSkipped}},0} -> + ok; + Unexpected -> + {error,{unexpected_return_value,Unexpected}} end. run(M, F, A, Config) -> + run({M,F,A}, [], Config). + +run({M,F,A}, InitCalls, Config) -> CTNode = proplists:get_value(ct_node, Config), Level = proplists:get_value(trace_level, Config), - test_server:format(Level, "~nCalling ~w:~w(~p) on ~p~n", + lists:foreach( + fun({IM,IF,IA}) -> + test_server:format(Level, "~nInit call ~w:~w(~p) on ~p...~n", + [IM, IF, IA, CTNode]), + Result = rpc:call(CTNode, IM, IF, IA), + test_server:format(Level, "~n...with result: ~p~n", [Result]) + end, InitCalls), + test_server:format(Level, "~nStarting test with ~w:~w(~p) on ~p~n", [M, F, A, CTNode]), rpc:call(CTNode, M, F, A). +%% this is the last function that ct_run:script_start() calls, so the +%% return value here is what rpc:call/4 above returns +ct_test_halt(ExitStatus) -> + ExitStatus. %%%----------------------------------------------------------------- %%% wait_for_ct_stop/1 @@ -1001,6 +1037,12 @@ result_match({SkipOrFail,{ErrorInd,{Why,'_'}}}, result_match({SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,'_'}}}}, {SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,_Stack}}}}) -> true; +result_match({failed,{timetrap_timeout,{'$approx',Num}}}, + {failed,{timetrap_timeout,Value}}) -> + if Value >= trunc(Num-0.02*Num), + Value =< trunc(Num+0.02*Num) -> true; + true -> false + end; result_match(Result, Result) -> true; result_match(_, _) -> diff --git a/lib/common_test/test/ct_testspec_1_SUITE.erl b/lib/common_test/test/ct_testspec_1_SUITE.erl index b6dcf63fdf..693e8c6567 100644 --- a/lib/common_test/test/ct_testspec_1_SUITE.erl +++ b/lib/common_test/test/ct_testspec_1_SUITE.erl @@ -621,7 +621,9 @@ setup_and_execute(TCName, TestSpec, Config) -> ok = ct_test_support:run(Opts, Config), TestSpec1 = [{logdir,proplists:get_value(logdir,Opts)}, {label,proplists:get_value(label,TestTerms)} | TestSpec], - ok = ct_test_support:run(ct, run_testspec, [TestSpec1], Config), + {_Ok,_Failed,{_USkipped,_ASkipped}} = + ct_test_support:run(ct, run_testspec, [TestSpec1], Config), + Events = ct_test_support:get_events(ERPid, Config), ct_test_support:log_events(TCName, diff --git a/lib/common_test/test/ct_testspec_2_SUITE.erl b/lib/common_test/test/ct_testspec_2_SUITE.erl new file mode 100644 index 0000000000..93f3520f95 --- /dev/null +++ b/lib/common_test/test/ct_testspec_2_SUITE.erl @@ -0,0 +1,751 @@ +%% +%% %CopyrightBegin% +%% +%% 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 +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_testspec_2_SUITE +%%% +%%% Description: +%%% Test test specifications +%%% +%%% The suites used for the test are located in the data directory. +%%%------------------------------------------------------------------- +-module(ct_testspec_2_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/src/ct_util.hrl"). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(_TestCase, Config) -> + Config. + +end_per_testcase(_TestCase, _Config) -> + ok. + +%% suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [basic_compatible_no_nodes, + basic_compatible_nodes, + unknown_terms, + no_merging, + multiple_specs, + misc_config_terms, + define_names_1]. + + +%%-------------------------------------------------------------------- +%% VALID TEST SPEC TERMS (R15B02): +%% +%% {node,3} +%% {cover,2} +%% {cover,3} +%% {config,2} +%% {config,3} +%% {config,4} +%% {userconfig,2} +%% {userconfig,3} +%% {alias,3} +%% {merge_tests,2} +%% {logdir,2} +%% {logdir,3} +%% {logopts,2} +%% {logopts,3} +%% {basic_html,2} +%% {basic_html,3} +%% {label,2} +%% {label,3} +%% {event_handler,2} +%% {event_handler,3} +%% {event_handler,4} +%% {ct_hooks,2} +%% {ct_hooks,3} +%% {enable_builtin_hooks,2} +%% {noinput,2} +%% {multiply_timetraps,2} +%% {multiply_timetraps,3} +%% {scale_timetraps,2} +%% {scale_timetraps,3} +%% {include,2} +%% {include,3} +%% {auto_compile,2} +%% {auto_compile,3} +%% {stylesheet,2} +%% {stylesheet,3} +%% {suites,3} +%% {suites,4} +%% {groups,4} +%% {groups,5} +%% {groups,6} +%% {cases,4} +%% {cases,5} +%% {skip_suites,4} +%% {skip_suites,5} +%% {skip_groups,5} +%% {skip_groups,6} +%% {skip_groups,7} +%% {skip_cases,5} +%% {skip_cases,6} +%% {create_priv_dir,2} +%% +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +basic_compatible_no_nodes(_Config) -> + + AliasDir1 = "../tests/to1", + AliasDir2 = "../tests/to2", + CfgDir1 = "../cfgs/to1/x.cfg", + CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"], + LogDir = "../logs", + IncludeDir1 = "../../include", + IncludeDir2 = ["../tests/to1/include","../tests/to2/include"], + + Spec = + [ + {label,"basic_compatible_no_nodes"}, + {alias,to1,AliasDir1}, + {alias,to2,AliasDir2}, + {config,CfgDir1}, + {config,CfgDir2}, + {userconfig,{?MODULE,"cfg_str1"}}, + {userconfig,{?MODULE,"cfg_str2"}}, + {logdir,LogDir}, + {logopts,[no_nl]}, + {event_handler,evh1,[1]}, + {event_handler,[evh2,evh3],[[2,3]]}, + {ct_hooks,[{cth_mod1,[]}]}, + {ct_hooks,[{cth_mod2,[]}]}, + {multiply_timetraps,2}, + {include,IncludeDir1}, + {include,IncludeDir2}, + {suites,to1,[x_SUITE]}, + {groups,to1,y_SUITE,[g1,g2]}, + {cases,to1,y_SUITE,[tc1,tc2]}, + {skip_suites,to1,z_SUITE,"skipped"}, + {suites,to2,[x_SUITE,y_SUITE]}, + {skip_groups,to2,x_SUITE,[g1,g2],"skipped"}, + {skip_cases,to2,y_SUITE,[tc1,tc2],"skipped"} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "basic_compatible_no_nodes.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + LogDirV = get_absdir(filename:join(SpecDir,"../logs")), + Alias1V = get_absdir(filename:join(SpecDir,AliasDir1)), + Alias2V = get_absdir(filename:join(SpecDir,AliasDir2)), + CFGs = [{Node,get_absdir(filename:join(SpecDir,CfgDir))} || + CfgDir <- [CfgDir1 | CfgDir2]], + Incls = [{Node,get_absdir(filename:join(SpecDir,IncludeDir))} || + IncludeDir <- [IncludeDir1 | IncludeDir2]], + + Verify = #testspec{spec_dir = SpecDir, + nodes = [{undefined,Node}], + init = [], + label = [{Node,"basic_compatible_no_nodes"}], + logdir = [{Node,LogDirV},"."], + logopts = [{Node,[no_nl]}], + basic_html = [], + cover = [], + config = CFGs, + userconfig = [{Node,{?MODULE,"cfg_str1"}}, + {Node,{?MODULE,"cfg_str2"}}], + event_handler = [{Node,evh1,[1]}, + {Node,evh2,[[2,3]]}, + {Node,evh3,[[2,3]]}], + ct_hooks = [{Node,{cth_mod1,[]}}, + {Node,{cth_mod2,[]}}], + enable_builtin_hooks = true, + noinput = false, + include = Incls, + auto_compile = [], + stylesheet = [], + multiply_timetraps = [{Node,2}], + scale_timetraps = [], + create_priv_dir = [], + alias = [{to1,Alias1V},{to2,Alias2V}], + tests = [{{Node,Alias1V}, + [{x_SUITE,[all]}, + {y_SUITE,[{g1,all},{g2,all},tc1,tc2]}, + {z_SUITE,[{all,{skip,"skipped"}}]}]}, + {{Node,Alias2V}, + [{x_SUITE,[all, + {{g1,all},{skip,"skipped"}}, + {{g2,all},{skip,"skipped"}}]}, + {y_SUITE,[all, + {tc1,{skip,"skipped"}}, + {tc2,{skip,"skipped"}}]}]}], + merge_tests = true}, + + verify_result(Verify,ListResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +basic_compatible_nodes(_Config) -> + + Node1 = node1@host1, + Node2 = node2@host2, + TODir1 = "../tests/to1", + TODir2 = "../tests/to2", + CfgDir1 = "../cfgs/to1/x.cfg", + CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"], + LogDir = "../logs", + MasterLogDir = "../master_logs", + IncludeDir1 = "../../include", + IncludeDir2 = ["../tests/to1/include","../tests/to2/include"], + + Spec = + [ + {node,n1,Node1}, + {node,n2,Node2}, + {init,[n1],[{node_start,[{callback_module,cbm}]}]}, + {init,n2,[{node_start,[]}]}, + {init,all_nodes,{eval,{mod,func,[]}}}, + {label,"basic_compatible_nodes"}, + {label,n1,basic_compatible_nodes_1}, + {config,n1,CfgDir1}, + {config,n2,CfgDir2}, + {userconfig,{?MODULE,"cfg_str1"}}, + {userconfig,{?MODULE,"cfg_str2"}}, + {logdir,all_nodes,LogDir}, + {logdir,master,MasterLogDir}, + {logopts,node2@host2,[no_nl]}, + {event_handler,master,evh1,[1]}, + {event_handler,[n1,n2],[evh2,evh3],[[2,3]]}, + {ct_hooks,all_nodes,[{cth_mod1,[]}]}, + {ct_hooks,[{cth_mod2,[]}]}, + {multiply_timetraps,node1@host1,2}, + {include,n1,IncludeDir1}, + {include,[n1,n2],IncludeDir2}, + {suites,n1,TODir1,[x_SUITE]}, + {groups,n1,TODir1,y_SUITE,[g1,g2]}, + {cases,n1,TODir1,y_SUITE,[tc1,tc2]}, + {skip_suites,n1,TODir1,z_SUITE,"skipped"}, + {suites,n2,TODir2,[x_SUITE,y_SUITE]}, + {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"}, + {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "basic_compatible_nodes.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + LogDirV = get_absdir(filename:join(SpecDir,"../logs")), + MasterLogDirV = get_absdir(filename:join(SpecDir,"../master_logs")), + TO1V = get_absdir(filename:join(SpecDir,TODir1)), + TO2V = get_absdir(filename:join(SpecDir,TODir2)), + CFGs = [{Node1,get_absdir(filename:join(SpecDir,CfgDir1))} | + [{Node2,get_absdir(filename:join(SpecDir,CfgDir))} || CfgDir <- CfgDir2]], + Incls = [{Node1,get_absdir(filename:join(SpecDir,IncludeDir1))} | + [{Node1,get_absdir(filename:join(SpecDir,IncludeDir))} || + IncludeDir <- IncludeDir2] ++ + [{Node2,get_absdir(filename:join(SpecDir,IncludeDir))} || + IncludeDir <- IncludeDir2]], + + Verify = #testspec{spec_dir = SpecDir, + nodes = [{undefined,Node},{n1,Node1},{n2,Node2}], + init = [{Node1,[{node_start,[{callback_module,cbm}]}, + {eval,[{mod,func,[]}]}]}, + {Node2,[{node_start,[{callback_module,ct_slave}]}, + {eval,[{mod,func,[]}]}]}, + {Node,[{node_start,[]}, + {eval,[{mod,func,[]}]}]}], + label = [{Node,"basic_compatible_nodes"}, + {Node2,"basic_compatible_nodes"}, + {Node1,basic_compatible_nodes_1}], + logdir = [{Node,LogDirV},{Node1,LogDirV},{Node2,LogDirV}, + {master,MasterLogDirV},"."], + logopts = [{Node2,[no_nl]}], + basic_html = [], + cover = [], + config = CFGs, + userconfig = [{Node,{?MODULE,"cfg_str1"}}, + {Node1,{?MODULE,"cfg_str1"}}, + {Node2,{?MODULE,"cfg_str1"}}, + {Node,{?MODULE,"cfg_str2"}}, + {Node1,{?MODULE,"cfg_str2"}}, + {Node2,{?MODULE,"cfg_str2"}}], + event_handler = [{master,evh1,[1]}, + {Node1,evh2,[[2,3]]}, + {Node1,evh3,[[2,3]]}, + {Node2,evh2,[[2,3]]}, + {Node2,evh3,[[2,3]]}], + ct_hooks = [{Node,{cth_mod1,[]}}, + {Node1,{cth_mod1,[]}}, + {Node2,{cth_mod1,[]}}, + {Node,{cth_mod2,[]}}, + {Node1,{cth_mod2,[]}}, + {Node2,{cth_mod2,[]}}], + enable_builtin_hooks = true, + noinput = false, + include = Incls, + auto_compile = [], + stylesheet = [], + multiply_timetraps = [{Node1,2}], + scale_timetraps = [], + create_priv_dir = [], + tests = [{{Node1,TO1V}, + [{x_SUITE,[all]}, + {y_SUITE,[{g1,all},{g2,all},tc1,tc2]}, + {z_SUITE,[{all,{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{x_SUITE,[all, + {{g1,all},{skip,"skipped"}}, + {{g2,all},{skip,"skipped"}}]}, + {y_SUITE,[all, + {tc1,{skip,"skipped"}}, + {tc2,{skip,"skipped"}}]}]}], + merge_tests = true}, + + verify_result(Verify,ListResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +unknown_terms(Config) -> + PrivDir = ?config(priv_dir, Config), + + Spec1 = [{suites,PrivDir,all}, + {userdata,"I've got news for you"}], + {error,{undefined_term_in_spec,{userdata,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec1, false)), + true = is_record(ct_testspec:collect_tests_from_list(Spec1, true), + testspec), + + Spec2 = [{logdir,{logdir,PrivDir}}], + {error,{invalid_directory_name,_}} = + (catch ct_testspec:collect_tests_from_list(Spec2, false)), + + Spec3 = [{suite,PrivDir,all}], + {error,{undefined_term_in_spec,{suite,_,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec3, false)), + true = is_record(ct_testspec:collect_tests_from_list(Spec3, true), testspec), + + Spec4 = [{suites,PrivDir,all}, + {skip_suites,PrivDir,x_SUITE}], + {error,{bad_term_in_spec,{skip_suites,_,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec4, false)), + {error,{bad_term_in_spec,{skip_suites,_,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec4, true)), + + Spec5 = [{configs,all_nodes,PrivDir}], + {error,{undefined_term_in_spec,{configs,_,_}}} = + (catch ct_testspec:collect_tests_from_list(Spec5, false)), + true = is_record(ct_testspec:collect_tests_from_list(Spec5, true), testspec), + + ok. + +%%%----------------------------------------------------------------- +%%% +no_merging(_Config) -> + Node1 = node1@host1, + Node2 = node2@host2, + TODir1 = "../tests/to1", + TODir2 = "../tests/to2", + Spec = + [ + {merge_tests,false}, + {node,n1,Node1}, + {node,n2,Node2}, + {suites,n1,TODir1,[x_SUITE]}, + {groups,n1,TODir1,y_SUITE,[g1,g2]}, + {cases,n1,TODir1,y_SUITE,[tc1,tc2]}, + {skip_suites,n1,TODir1,z_SUITE,"skipped"}, + {suites,n2,TODir2,[x_SUITE,y_SUITE]}, + {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"}, + {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "no_merging.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + TO1V = get_absdir(filename:join(SpecDir,TODir1)), + TO2V = get_absdir(filename:join(SpecDir,TODir2)), + + Verify = #testspec{merge_tests = false, + spec_dir = SpecDir, + nodes = [{undefined,Node},{n1,Node1},{n2,Node2}], + tests = [{{Node1,TO1V}, + [{x_SUITE,[all]}]}, + {{Node1,TO1V}, + [{y_SUITE,[{g1,all},{g2,all}]}]}, + {{Node1,TO1V}, + [{y_SUITE,[tc1,tc2]}]}, + {{Node1,TO1V}, + [{z_SUITE,[{all,{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{x_SUITE,[all]}]}, + {{Node2,TO2V}, + [{y_SUITE,[all]}]}, + {{Node2,TO2V}, + [{x_SUITE,[{{g1,all},{skip,"skipped"}}, + {{g2,all},{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{y_SUITE,[{tc1,{skip,"skipped"}}, + {tc2,{skip,"skipped"}}]}]}]}, + + verify_result(Verify,ListResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +multiple_specs(_Config) -> + Node1 = node1@host1, + Node2 = node2@host2, + TODir1 = "../tests/to1", + TODir2 = "../tests/to2", + CfgDir1 = "../cfgs/to1/x.cfg", + CfgDir2 = ["../cfgs/to2/x.cfg","../cfgs/to2/y.cfg"], + LogDir = "../logs", + Spec1 = + [ + {node,n1,Node1}, + {node,n2,Node2}, + {alias,to1,TODir1}, + {alias,to2,TODir2}, + {label,"multiple_specs1"}, + {config,n1,CfgDir1}, + {config,n2,CfgDir2}, + {logdir,all_nodes,LogDir} + ], + Spec2 = + [ + {merge_tests,false}, + {label,"multiple_specs2"}, + {suites,n1,TODir1,[x_SUITE]}, + {groups,n1,TODir1,y_SUITE,[g1,g2]}, + {cases,n1,TODir1,y_SUITE,[tc1,tc2]}, + {skip_suites,n1,TODir1,z_SUITE,"skipped"}, + {suites,n2,TODir2,[x_SUITE,y_SUITE]}, + {skip_groups,n2,TODir2,x_SUITE,[g1,g2],"skipped"}, + {skip_cases,n2,TODir2,y_SUITE,[tc1,tc2],"skipped"} + ], + + {ok,SpecDir} = file:get_cwd(), + SpecFile1 = ct_test_support:write_testspec(Spec1,SpecDir, + "multiple_specs.1.spec"), + SpecFile2 = ct_test_support:write_testspec(Spec2,SpecDir, + "multiple_specs.2.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile1,SpecFile2], + false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + TO1V = get_absdir(filename:join(SpecDir,TODir1)), + TO2V = get_absdir(filename:join(SpecDir,TODir2)), + CFGs = [{Node1,get_absdir(filename:join(SpecDir,CfgDir1))} | + [{Node2,get_absdir(filename:join(SpecDir,CfgDir))} || CfgDir <- CfgDir2]], + LogDirV = get_absdir(filename:join(SpecDir,"../logs")), + + Verify = #testspec{merge_tests = false, + spec_dir = SpecDir, + nodes = [{undefined,Node},{n1,Node1},{n2,Node2}], + alias = [{to1,TO1V},{to2,TO2V}], + label = [{Node,"multiple_specs1"}, + {Node1,"multiple_specs1"}, + {Node2,"multiple_specs1"}], + logdir = [{Node,LogDirV},{Node1,LogDirV},{Node2,LogDirV},"."], + config = CFGs, + tests = [{{Node1,TO1V}, + [{x_SUITE,[all]}]}, + {{Node1,TO1V}, + [{y_SUITE,[{g1,all},{g2,all}]}]}, + {{Node1,TO1V}, + [{y_SUITE,[tc1,tc2]}]}, + {{Node1,TO1V}, + [{z_SUITE,[{all,{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{x_SUITE,[all]}]}, + {{Node2,TO2V}, + [{y_SUITE,[all]}]}, + {{Node2,TO2V}, + [{x_SUITE,[{{g1,all},{skip,"skipped"}}, + {{g2,all},{skip,"skipped"}}]}]}, + {{Node2,TO2V}, + [{y_SUITE,[{tc1,{skip,"skipped"}}, + {tc2,{skip,"skipped"}}]}]}]}, + + verify_result(Verify,FileResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +misc_config_terms(_Config) -> + CfgDir = "../cfgs/to1", + + Spec = + [{node,x,n1@h1},{node,y,n2@h2}, + + {config,CfgDir,"a.cfg"}, + {config,n1@h1,CfgDir,"b.cfg"}, + {config,all_nodes,CfgDir,"c.cfg"}, + {config,all_nodes,filename:join(CfgDir,"d.cfg")}, + + {basic_html,true}, + {basic_html,n1@h1,false}, + {basic_html,n2@h2,true}, + + {enable_builtin_hooks,false}, + + {noinput,true}, + + {auto_compile,false}, + {auto_compile,n1@h1,true}, + {auto_compile,n2@h2,false}, + + {stylesheet,"../css"}, + {stylesheet,n1@h1,"./n1/css"}, + {stylesheet,n2@h2,"./n2/css"}, + + {create_priv_dir,[auto_per_tc]}, + {create_priv_dir,n1@h1,[manual_per_tc]}, + {create_priv_dir,n2@h2,[auto_per_run]} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "misc_config_terms.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + Node = node(), + CfgA = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "a.cfg")), + CfgB = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "b.cfg")), + CfgC = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "c.cfg")), + CfgD = get_absdir(filename:join(filename:join(SpecDir,CfgDir), "d.cfg")), + CSS = get_absdir(filename:join(SpecDir,"../css")), + CSS1 = get_absdir(filename:join(SpecDir,"./n1/css")), + CSS2 = get_absdir(filename:join(SpecDir,"./n2/css")), + + Verify = #testspec{spec_dir = SpecDir, + nodes = [{undefined,Node},{x,n1@h1},{y,n2@h2}], + basic_html = [{Node,true},{n1@h1,false},{n2@h2,true}], + config = [{Node,CfgA}, + {n1@h1,CfgA}, + {n2@h2,CfgA}, + {n1@h1,CfgB}, + {Node,CfgC}, + {n1@h1,CfgC}, + {n2@h2,CfgC}, + {Node,CfgD}, + {n1@h1,CfgD}, + {n2@h2,CfgD}], + enable_builtin_hooks = false, + noinput = true, + auto_compile = [{Node,false}, + {n1@h1,true}, + {n2@h2,false}], + stylesheet = [{Node,CSS}, + {n1@h1,CSS1}, + {n2@h2,CSS2}], + create_priv_dir = [{Node,[auto_per_tc]}, + {n1@h1,[manual_per_tc]}, + {n2@h2,[auto_per_run]}] + }, + + verify_result(Verify,ListResult,FileResult). + +%%%----------------------------------------------------------------- +%%% +define_names_1(_Config) -> + Spec = + [ + {define,'HOST','eniac'}, + {define,'NODE1',testnode1}, + {define,'NODE2',testnode2}, + {define,'NODES',['NODE1@HOST', + 'NODE2@HOST']}, + {define,'TOPDIR',".."}, + {define,'TO1',"to1"}, + {define,'TO2',"to2"}, + {define,'LOGDIR',"'TOPDIR'/logdir"}, + {define,'LOGDIR1',"'TOPDIR'/logdir1"}, + {define,'LOGDIR2',"'TOPDIR'/logdir2"}, + {define,'CFGDIR',"'TOPDIR'/cfgs"}, + {define,'CFGFILES',["cfgX","cfgY"]}, + {define,'TESTDIR',"'TOPDIR'/test"}, + {define,'TO1DIR',"'TESTDIR'/'TO1'"}, + {define,'TO2DIR',"'TESTDIR'/'TO2'"}, + {define,'EXSUITE',ex_SUITE}, + {define,'EXGRS',[g1,g2]}, + + {logdir,'LOGDIR'}, + {logdir,'NODE1@HOST','LOGDIR1'}, + {logdir,'NODE2@HOST','LOGDIR2'}, + + {config,["a.cfg","b.cfg"]}, + {config,'NODES',"./'CFGDIR'/c.cfg"}, + {config,'CFGDIR',["d.cfg","e.cfg"]}, + {config,'NODE2@HOST','CFGDIR','CFGFILES'}, + + {suites,'NODE1@HOST','TO1DIR',all}, + {suites,'NODES','TO2DIR',all}, + + {groups,'TO1DIR','EXSUITE','EXGRS'} + ], + + {ok,SpecDir} = file:get_cwd(), + + ListResult = ct_testspec:collect_tests_from_list(Spec, false), + ct:pal("TESTSPEC RECORD FROM LIST:~n~p~n", [rec2proplist(ListResult)]), + SpecFile = ct_test_support:write_testspec(Spec,SpecDir, + "define_names_1.spec"), + FileResult = ct_testspec:collect_tests_from_file([SpecFile], false), + ct:pal("TESTSPEC RECORD FROM FILE:~n~p~n", [rec2proplist(FileResult)]), + + N = node(), + N1 = testnode1@eniac, + N2 = testnode2@eniac, + Join = fun(Dir) -> shorten_path(filename:join(SpecDir,Dir),SpecDir) end, + + Verify = #testspec{spec_dir = SpecDir, + nodes = [{undefined,N2}, + {undefined,N1}, + {undefined,N}], + config = [{N2,Join("a.cfg")},{N2,Join("b.cfg")}, + {N1,Join("a.cfg")},{N1,Join("b.cfg")}, + {N,Join("a.cfg")},{N,Join("b.cfg")}, + {N1,Join("../cfgs/c.cfg")},{N2,Join("../cfgs/c.cfg")}, + {N2,Join("../cfgs/d.cfg")},{N2,Join("../cfgs/e.cfg")}, + {N1,Join("../cfgs/d.cfg")},{N1,Join("../cfgs/e.cfg")}, + {N,Join("../cfgs/d.cfg")},{N,Join("../cfgs/e.cfg")}, + {N2,Join("../cfgs/cfgX")},{N2,Join("../cfgs/cfgY")}], + logdir = [{N,Join("../logdir")}, + {N1,Join("../logdir1")}, + {N2,Join("../logdir2")}, + "."], + tests = [{{N1,Join("../test/to1")},[{all,[all]}]}, + {{N1,Join("../test/to2")},[{all,[all]}]}, + {{N2,Join("../test/to2")},[{all,[all]}]}, + {{N2,Join("../test/to1")}, + [{ex_SUITE,[{g1,all},{g2,all}]}]}, + {{N,Join("../test/to1")}, + [{ex_SUITE,[{g1,all},{g2,all}]}]}] + }, + verify_result(Verify,ListResult,FileResult). + + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +verify_result(Verify,ListResult,FileResult) -> + {_,TSLTuples} = rec2proplist(ListResult), + {_,TSFTuples} = rec2proplist(FileResult), + {_,VTuples} = rec2proplist(Verify), + VResult = + (catch lists:foldl(fun({Tag,Val},{[{Tag,Val}|TSL],[{Tag,Val}|TSF]}) -> + {TSL,TSF}; + ({Tag,Val},{[{_Tag,TSLVal}|_TSL],[{Tag,Val}|_TSF]}) -> + exit({ts_list_mismatch,Tag,Val,TSLVal}); + ({Tag,Val},{[{Tag,Val}|_TSL],[{_Tag,TSFVal}|_TSF]}) -> + exit({ts_file_mismatch,Tag,Val,TSFVal}); + ({Tag,Val},{_,[{_Tag,TSFVal}|_TSF]}) -> + exit({ts_mismatch,Tag,Val,TSFVal}) + end, {TSLTuples,TSFTuples}, VTuples)), + case VResult of + {'EXIT',Reason} -> + ct:fail(Reason); + _ -> + ok + end, + ok. + + +check_parameter(S="cfg_str1") -> + {ok,{config,S}}; +check_parameter(S="cfg_str2") -> + {ok,{config,S}}. +read_config(S) -> + {ok,[{cfg,S}]}. + +rec2proplist(E={error,_What}) -> + exit({invalid_testspec_record,E}); +rec2proplist(Rec) -> + [RecName|RecList] = tuple_to_list(Rec), + FieldNames = + if RecName == testspec -> + record_info(fields, testspec); + true -> + undefined + end, + {RecName,combine_names_and_vals(FieldNames,RecList)}. + +combine_names_and_vals([FN|FNs], [V|Vs]) -> + [{FN,V} | combine_names_and_vals(FNs, Vs)]; +combine_names_and_vals([], []) -> + []; +combine_names_and_vals(_, _) -> + []. + +get_absdir(Dir) -> + shorten_path(filename:absname(Dir),Dir). + +shorten_path(Path,SpecDir) -> + case shorten_split_path(filename:split(Path),[]) of + [] -> + [Root|_] = filename:split(SpecDir), + Root; + Short -> + filename:join(Short) + end. + +shorten_split_path([".."|Path],SoFar) -> + shorten_split_path(Path,tl(SoFar)); +shorten_split_path(["."|Path],SoFar) -> + shorten_split_path(Path,SoFar); +shorten_split_path([Dir|Path],SoFar) -> + shorten_split_path(Path,[Dir|SoFar]); +shorten_split_path([],SoFar) -> + lists:reverse(SoFar). diff --git a/lib/common_test/test/ct_verbosity_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE.erl new file mode 100644 index 0000000000..349319de94 --- /dev/null +++ b/lib/common_test/test/ct_verbosity_SUITE.erl @@ -0,0 +1,244 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File: ct_verbosity_SUITE +%%% +%%% Description: +%%% Test that verbosity levels vs the importance parameter works as +%%% expected. +%%% +%%%------------------------------------------------------------------- +-module(ct_verbosity_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + +-define(eh, ct_test_support_eh). + +%%-------------------------------------------------------------------- +%% TEST SERVER CALLBACK FUNCTIONS +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Description: Since Common Test starts another Test Server +%% instance, the tests need to be performed on a separate node (or +%% there will be clashes with logging processes etc). +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config1 = ct_test_support:init_per_suite(Config), + Config1. + +end_per_suite(Config) -> + ct_test_support:end_per_suite(Config). + +init_per_testcase(TestCase, Config) -> + ct_test_support:init_per_testcase(TestCase, Config). + +end_per_testcase(TestCase, Config) -> + ct_test_support:end_per_testcase(TestCase, Config). + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + no_levels, + general_level_low, + general_level_std, + general_level_hi, + change_default, + combine_categories, + testspec_only, + merge_with_testspec + ]. + +%%-------------------------------------------------------------------- +%% TEST CASES +%%-------------------------------------------------------------------- + +%%%----------------------------------------------------------------- +%%% +no_levels(Config) -> + TC = no_levels, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +general_level_low(Config) -> + TC = general_level_low, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,0}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +general_level_std(Config) -> + TC = general_level_std, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,50}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +general_level_hi(Config) -> + TC = general_level_high, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,100}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +change_default(Config) -> + TC = change_default, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,[{default,49}]}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +combine_categories(Config) -> + TC = combine_categories, + DataDir = ?config(data_dir, Config), + Suite = filename:join(DataDir, "io_test_SUITE"), + {Opts,ERPid} = setup([{suite,Suite},{label,TC}, + {verbosity,[{error,?HI_VERBOSITY}, + {default,?LOW_VERBOSITY}]}], Config), + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +testspec_only(Config) -> + TC = testspec_only, + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + + TestSpec = [{verbosity,[{default,1},{error,75},100]}, + {suites,DataDir,[io_test_SUITE]}, + {label,TC}], + + TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir, + "verbosity_1_spec"), + {Opts,ERPid} = setup([{spec,TestSpecName}], Config), + + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% +merge_with_testspec(Config) -> + TC = merge_with_testspec, + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + + TestSpec = [{verbosity,[{default,100},{error,100}]}, + {suites,DataDir,[io_test_SUITE]}, + {label,TC}], + + TestSpecName = ct_test_support:write_testspec(TestSpec, PrivDir, + "verbosity_2_spec"), + + %% below should override verbosity categories in testspec + {Opts,ERPid} = setup([{spec,TestSpecName}, + {verbosity,[{default,0},0]}], + Config), + + ok = execute(TC, Opts, ERPid, Config). + +%%%----------------------------------------------------------------- +%%% HELP FUNCTIONS +%%%----------------------------------------------------------------- + +setup(Test, Config) -> + Opts0 = ct_test_support:get_opts(Config), + Level = ?config(trace_level, Config), + EvHArgs = [{cbm,ct_test_support},{trace_level,Level}], + Opts = Opts0 ++ [{event_handler,{?eh,EvHArgs}}|Test], + ERPid = ct_test_support:start_event_receiver(Config), + {Opts,ERPid}. + +execute(Name, Opts, ERPid, Config) -> + ok = ct_test_support:run(Opts, Config), + Events = ct_test_support:get_events(ERPid, Config), + + ct_test_support:log_events(Name, + reformat(Events, ?eh), + ?config(priv_dir, Config), + Opts), + + TestEvents = events_to_check(Name), + ct_test_support:verify_events(TestEvents, Events, Config). + +reformat(Events, EH) -> + ct_test_support:reformat(Events, EH). + +%%%----------------------------------------------------------------- +%%% TEST EVENTS +%%%----------------------------------------------------------------- +events_to_check(Test) -> + %% 2 tests (ct:run_test + script_start) is default + events_to_check(Test, 2). + +events_to_check(_, 0) -> + []; +events_to_check(Test, N) -> + test_events(Test) ++ events_to_check(Test, N-1). + + +test_events(_) -> + [ + {?eh,tc_done,{io_test_SUITE,tc1,ok}}, + {?eh,tc_done,{io_test_SUITE,tc2,ok}}, + {?eh,tc_done,{io_test_SUITE,tc3,ok}}, + + {parallel, + [ + {?eh,tc_start,{io_test_SUITE,tc1}}, + {?eh,tc_start,{io_test_SUITE,tc2}}, + {?eh,tc_start,{io_test_SUITE,tc3}}, + {?eh,tc_done,{io_test_SUITE,tc1,ok}}, + {?eh,tc_done,{io_test_SUITE,tc2,ok}}, + {?eh,tc_done,{io_test_SUITE,tc3,ok}}, + {parallel, + [ + {?eh,tc_start,{io_test_SUITE,tc1}}, + {?eh,tc_start,{io_test_SUITE,tc2}}, + {?eh,tc_start,{io_test_SUITE,tc3}}, + {?eh,tc_done,{io_test_SUITE,tc1,ok}}, + {?eh,tc_done,{io_test_SUITE,tc2,ok}}, + {?eh,tc_done,{io_test_SUITE,tc3,ok}}, + {?eh,test_stats,{9,0,{0,0}}} + ]} + ]}, + + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,stop_logging,[]} + ]. + diff --git a/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl b/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl new file mode 100644 index 0000000000..946e1c1989 --- /dev/null +++ b/lib/common_test/test/ct_verbosity_SUITE_data/io_test_SUITE.erl @@ -0,0 +1,156 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(io_test_SUITE). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +%%-------------------------------------------------------------------- +%% @spec suite() -> Info +%% Info = [tuple()] +%% @end +%%-------------------------------------------------------------------- +suite() -> + [{timetrap,{seconds,10}}]. + +%%-------------------------------------------------------------------- +%% @spec init_per_suite(Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_suite(Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_suite(Config0) -> void() | {save_config,Config1} +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_suite(_Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_group(GroupName, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_group(_GroupName, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_group(GroupName, Config0) -> +%% void() | {save_config,Config1} +%% GroupName = atom() +%% Config0 = Config1 = [tuple()] +%% @end +%%-------------------------------------------------------------------- +end_per_group(_GroupName, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec init_per_testcase(TestCase, Config0) -> +%% Config1 | {skip,Reason} | {skip_and_save,Reason,Config1} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% @spec end_per_testcase(TestCase, Config0) -> +%% void() | {save_config,Config1} | {fail,Reason} +%% TestCase = atom() +%% Config0 = Config1 = [tuple()] +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +end_per_testcase(_TestCase, _Config) -> + ok. + +%%-------------------------------------------------------------------- +%% @spec groups() -> [Group] +%% Group = {GroupName,Properties,GroupsAndTestCases} +%% GroupName = atom() +%% Properties = [parallel | sequence | Shuffle | {RepeatType,N}] +%% GroupsAndTestCases = [Group | {group,GroupName} | TestCase] +%% TestCase = atom() +%% Shuffle = shuffle | {shuffle,{integer(),integer(),integer()}} +%% RepeatType = repeat | repeat_until_all_ok | repeat_until_all_fail | +%% repeat_until_any_ok | repeat_until_any_fail +%% N = integer() | forever +%% @end +%%-------------------------------------------------------------------- +groups() -> + [{g1, [parallel], [tc1,tc2,tc3,{group,g2}]}, + {g2, [parallel], [tc1,tc2,tc3]}]. + +%%-------------------------------------------------------------------- +%% @spec all() -> GroupsAndTestCases | {skip,Reason} +%% GroupsAndTestCases = [{group,GroupName} | TestCase] +%% GroupName = atom() +%% TestCase = atom() +%% Reason = term() +%% @end +%%-------------------------------------------------------------------- +all() -> + [tc1,tc2,tc3,{group,g1}]. + +tc1(_C) -> + io:format("This is an io:format(~p)~n", [[]]), + ct:log("ct:log(default)", []), + ct:log(?STD_IMPORTANCE, "ct:log(default,~p)", [?STD_IMPORTANCE]), + ct:log(error, "ct:log(error)", []), + ct:log(error, ?STD_IMPORTANCE, "ct:log(error,~p)", [?STD_IMPORTANCE]), + ct:log(1, "ct:log(default,~p)", [1]), + ct:log(error, 1, "ct:log(error,~p)", [1]), + ct:log(99, "ct:log(default,~p)", [99]), + ct:log(error, 99, "ct:log(error,~p)", [99]), + ok. + +tc2(_C) -> + io:format("This is an io:format(~p)~n", [[]]), + ct:pal("ct:pal(default)", []), + ct:pal(?STD_IMPORTANCE, "ct:pal(default,~p)", [?STD_IMPORTANCE]), + ct:pal(error, "ct:pal(error)", []), + ct:pal(error, ?STD_IMPORTANCE, "ct:pal(error,~p)", [?STD_IMPORTANCE]), + ct:pal(1, "ct:pal(default,~p)", [1]), + ct:pal(error, 1, "ct:pal(error,~p)", [1]), + ct:pal(99, "ct:pal(default,~p)", [99]), + ct:pal(error, 99, "ct:pal(error,~p)", [99]), + ok. + +tc3(_C) -> + io:format("This is an io:format(~p)~n", [[]]), + ct:print("ct:print(default)", []), + ct:print(?STD_IMPORTANCE, "ct:print(default,~p)", [?STD_IMPORTANCE]), + ct:print(error, "ct:print(error)", []), + ct:print(error, ?STD_IMPORTANCE, "ct:print(error,~p)", [?STD_IMPORTANCE]), + ct:print(1, "ct:print(default,~p)", [1]), + ct:print(error, 1, "ct:print(error,~p)", [1]), + ct:print(99, "ct:print(default,~p)", [99]), + ct:print(error, 99, "ct:print(error,~p)", [99]), + ok. diff --git a/lib/compiler/doc/src/Makefile b/lib/compiler/doc/src/Makefile index ee41a7074f..308e9c4d02 100644 --- a/lib/compiler/doc/src/Makefile +++ b/lib/compiler/doc/src/Makefile @@ -99,14 +99,14 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/compiler/doc/src/compile.xml b/lib/compiler/doc/src/compile.xml index 0f8abf1ccf..be9eb1cd75 100644 --- a/lib/compiler/doc/src/compile.xml +++ b/lib/compiler/doc/src/compile.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -294,6 +294,12 @@ module.beam: module.erl \ describing what it is doing.</p> </item> + <tag><c>{source,FileName}</c></tag> + <item> + <p>Sets the value of the source, as returned by + <c>module_info(compile)</c>.</p> + </item> + <tag><c>{outdir,Dir}</c></tag> <item> <p>Sets a new directory for the object code. The current @@ -333,7 +339,8 @@ module.beam: module.erl \ <tag><c>{d,Macro,Value}</c></tag> <item> <p>Defines a macro <c>Macro</c> to have the value - <c>Value</c>. The default is <c>true</c>.</p> + <c>Value</c>. <c>Macro</c> is of type atom, and <c>Value</c> can be any term. + The default <c>Value</c> is <c>true</c>.</p> </item> <tag><c>{parse_transform,Module}</c></tag> diff --git a/lib/compiler/doc/src/notes.xml b/lib/compiler/doc/src/notes.xml index 3f53a71764..e2a921a6f2 100644 --- a/lib/compiler/doc/src/notes.xml +++ b/lib/compiler/doc/src/notes.xml @@ -31,6 +31,75 @@ <p>This document describes the changes made to the Compiler application.</p> +<section><title>Compiler 4.8.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + In rare circumstance, the compiler could crash when + compiling a case statement. (Thanks to Hakan Mattsson.)</p> + <p> + Own Id: OTP-9842</p> + </item> + <item> + <p>Calling a guard test (such as is_list/1) from the + top-level in a guard, would cause a compiler crash if + there was a local definition with the same name. + Corrected to reject the program with an error + message.</p> + <p> + Own Id: OTP-9866</p> + </item> + <item> + <p>Using <c>get/1</c> in a <c>try</c> block could in some + cases cause an internal compiler error. (Thanks to Eric + Merritt.)</p> + <p> + Own Id: OTP-9867</p> + </item> + <item> + <p> + An unexported on_load function would not get run if the + module was compiled with the <c>inline</c> option. + (Thanks to Yiannis Tsiouris.)</p> + <p> + Own Id: OTP-9910</p> + </item> + <item> + <p> + Fixed a discrepancy in compile_info</p> + <p> + The BEAM disassembler used the atom 'none' to signify the + absence of a compile_info chunk in a .beam file. This + clashed with the type declaration of the compile_info + field of a #beam_file{} record as containing a list. Now + [] signifies the absence of this chunk. This simplifies + the code and avoids a dialyzer warning.</p> + <p> + Own Id: OTP-9917</p> + </item> + <item> + <p> + Fix typo in `compile' doc: unmatched parenthesis (Thanks + to Ricardo Catalinas Jim�nez)</p> + <p> + Own Id: OTP-9919</p> + </item> + <item> + <p>In a <c>try</c>...<c>catch</c> statement that always + returned <c>false</c>, the compiler would remove calls to + BIFs that could not cause an exception (such as + <c>put/2</c>). Example of such code: <c>try put(K, V), + false catch _:_ -> false end.</c></p> + <p> + Own Id: OTP-9982</p> + </item> + </list> + </section> + +</section> + <section><title>Compiler 4.8</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/compiler/src/Makefile b/lib/compiler/src/Makefile index 3415517fff..56c45d369c 100644 --- a/lib/compiler/src/Makefile +++ b/lib/compiler/src/Makefile @@ -163,11 +163,11 @@ $(EBIN)/cerl_inline.beam: $(ESRC)/cerl_inline.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(EXTRA_FILES) \ - $(YRL_FILE) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(INSTALL_FILES) $(RELSYSDIR)/ebin + $(YRL_FILE) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(INSTALL_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/compiler/src/beam_type.erl b/lib/compiler/src/beam_type.erl index 6f0ffb5b25..d307d192b2 100644 --- a/lib/compiler/src/beam_type.erl +++ b/lib/compiler/src/beam_type.erl @@ -29,10 +29,17 @@ module({Mod,Exp,Attr,Fs0,Lc}, _Opts) -> {ok,{Mod,Exp,Attr,Fs,Lc}}. function({function,Name,Arity,CLabel,Asm0}) -> - Asm1 = beam_utils:live_opt(Asm0), - Asm2 = opt(Asm1, [], tdb_new()), - Asm = beam_utils:delete_live_annos(Asm2), - {function,Name,Arity,CLabel,Asm}. + try + Asm1 = beam_utils:live_opt(Asm0), + Asm2 = opt(Asm1, [], tdb_new()), + Asm = beam_utils:delete_live_annos(Asm2), + {function,Name,Arity,CLabel,Asm} + catch + Class:Error -> + Stack = erlang:get_stacktrace(), + io:fwrite("Function: ~w/~w\n", [Name,Arity]), + erlang:raise(Class, Error, Stack) + end. %% opt([Instruction], Accumulator, TypeDb) -> {[Instruction'],TypeDb'} %% Keep track of type information; try to simplify. diff --git a/lib/compiler/src/beam_utils.erl b/lib/compiler/src/beam_utils.erl index 116ede0bc9..194f089ba1 100644 --- a/lib/compiler/src/beam_utils.erl +++ b/lib/compiler/src/beam_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -741,6 +741,9 @@ live_opt([{badmatch,Src}=I|Is], _, D, Acc) -> live_opt([{case_end,Src}=I|Is], _, D, Acc) -> Regs = x_live([Src], 0), live_opt(Is, Regs, D, [I|Acc]); +live_opt([{try_case_end,Src}=I|Is], _, D, Acc) -> + Regs = x_live([Src], 0), + live_opt(Is, Regs, D, [I|Acc]); live_opt([if_end=I|Is], _, D, Acc) -> Regs = 0, live_opt(Is, Regs, D, [I|Acc]); @@ -802,8 +805,6 @@ live_opt([{deallocate,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); live_opt([{kill,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); -live_opt([{try_case_end,_}=I|Is], Regs, D, Acc) -> - live_opt(Is, Regs, D, [I|Acc]); live_opt([{try_end,_}=I|Is], Regs, D, Acc) -> live_opt(Is, Regs, D, [I|Acc]); live_opt([{loop_rec_end,_}=I|Is], Regs, D, Acc) -> diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index a52e7bb761..9f0bca9dd5 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -783,15 +783,27 @@ valfun_4({bs_utf16_size,{f,Fail},A,Dst}, Vst) -> valfun_4({bs_bits_to_bytes,{f,Fail},Src,Dst}, Vst) -> assert_term(Src, Vst), set_type_reg({integer,[]}, Dst, branch_state(Fail, Vst)); -valfun_4({bs_init2,{f,Fail},_,Heap,Live,_,Dst}, Vst0) -> +valfun_4({bs_init2,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) -> verify_live(Live, Vst0), + if + is_integer(Sz) -> + ok; + true -> + assert_term(Sz, Vst0) + end, Vst1 = heap_alloc(Heap, Vst0), Vst2 = branch_state(Fail, Vst1), Vst3 = prune_x_regs(Live, Vst2), Vst = bs_zero_bits(Vst3), set_type_reg(binary, Dst, Vst); -valfun_4({bs_init_bits,{f,Fail},_,Heap,Live,_,Dst}, Vst0) -> +valfun_4({bs_init_bits,{f,Fail},Sz,Heap,Live,_,Dst}, Vst0) -> verify_live(Live, Vst0), + if + is_integer(Sz) -> + ok; + true -> + assert_term(Sz, Vst0) + end, Vst1 = heap_alloc(Heap, Vst0), Vst2 = branch_state(Fail, Vst1), Vst3 = prune_x_regs(Live, Vst2), diff --git a/lib/compiler/src/cerl_inline.erl b/lib/compiler/src/cerl_inline.erl index 589685d72d..2e7554c1ff 100644 --- a/lib/compiler/src/cerl_inline.erl +++ b/lib/compiler/src/cerl_inline.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2010. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 9b505ad15c..7365706b94 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -146,10 +146,17 @@ env_default_opts() -> do_compile(Input, Opts0) -> Opts = expand_opts(Opts0), - Self = self(), - Serv = spawn_link(fun() -> internal(Self, Input, Opts) end), + {Pid,Ref} = + spawn_monitor(fun() -> + exit(try + internal(Input, Opts) + catch + error:Reason -> + {error,Reason} + end) + end), receive - {Serv,Rep} -> Rep + {'DOWN',Ref,process,Pid,Rep} -> Rep end. expand_opts(Opts0) -> @@ -242,15 +249,12 @@ format_error({module_name,Mod,Filename}) -> errors=[], warnings=[]}). -internal(Master, Input, Opts) -> - Master ! {self(), try internal(Input, Opts) - catch error:Reason -> {error, Reason} - end}. - -internal({forms,Forms}, Opts) -> - {_,Ps} = passes(forms, Opts), - internal_comp(Ps, "", "", #compile{code=Forms,options=Opts, - mod_options=Opts}); +internal({forms,Forms}, Opts0) -> + {_,Ps} = passes(forms, Opts0), + Source = proplists:get_value(source, Opts0, ""), + Opts1 = proplists:delete(source, Opts0), + Compile = #compile{code=Forms,options=Opts1,mod_options=Opts1}, + internal_comp(Ps, Source, "", Compile); internal({file,File}, Opts) -> {Ext,Ps} = passes(file, Opts), Compile = #compile{options=Opts,mod_options=Opts}, diff --git a/lib/compiler/src/sys_core_fold.erl b/lib/compiler/src/sys_core_fold.erl index 4e67639805..18fba7962b 100644 --- a/lib/compiler/src/sys_core_fold.erl +++ b/lib/compiler/src/sys_core_fold.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. 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 diff --git a/lib/compiler/src/sys_pre_expand.erl b/lib/compiler/src/sys_pre_expand.erl index ba9cde1de0..6cea783090 100644 --- a/lib/compiler/src/sys_pre_expand.erl +++ b/lib/compiler/src/sys_pre_expand.erl @@ -42,7 +42,7 @@ compile=[], %Compile flags attributes=[], %Attributes callbacks=[], %Callbacks - defined=[], %Defined functions + defined, %Defined functions (gb_set) vcount=0, %Variable counter func=[], %Current function arity=[], %Arity for current function @@ -83,7 +83,7 @@ module(Fs0, Opts0) -> {Efs,St2} = expand_pmod(Tfs, St1), %% Get the correct list of exported functions. Exports = case member(export_all, St2#expand.compile) of - true -> St2#expand.defined; + true -> gb_sets:to_list(St2#expand.defined); false -> St2#expand.exports end, %% Generate all functions from stored info. @@ -106,10 +106,11 @@ expand_pmod(Fs0, St0) -> true -> Ps0 end, + Def = gb_sets:to_list(St0#expand.defined), {Fs1,Xs,Ds} = sys_expand_pmod:forms(Fs0, Ps, St0#expand.exports, - St0#expand.defined), - St1 = St0#expand{exports=Xs, defined=Ds}, + Def), + St1 = St0#expand{exports=Xs,defined=gb_sets:from_list(Ds)}, {Fs2,St2} = add_instance(Ps, Fs1, St1), {Fs3,St3} = ensure_new(Base, Ps0, Fs2, St2), {Fs3,St3#expand{attributes = [{abstract, 0, [true]} @@ -118,7 +119,7 @@ expand_pmod(Fs0, St0) -> get_base(As) -> case lists:keyfind(extends, 1, As) of - {extends,[Base]} when is_atom(Base) -> + {extends,_,[Base]} when is_atom(Base) -> Base; _ -> [] @@ -159,7 +160,7 @@ add_func(Name, Args, Body, Fs, St) -> F = {function,0,Name,A,[{clause,0,Args,[],Body}]}, NA = {Name,A}, {[F|Fs],St#expand{exports=add_element(NA, St#expand.exports), - defined=add_element(NA, St#expand.defined)}}. + defined=gb_sets:add_element(NA, St#expand.defined)}}. %% define_function(Form, State) -> State. %% Add function to defined if form is a function. @@ -168,7 +169,7 @@ define_functions(Forms, #expand{defined=Predef}=St) -> Fs = foldl(fun({function,_,N,A,_Cs}, Acc) -> [{N,A}|Acc]; (_, Acc) -> Acc end, Predef, Forms), - St#expand{defined=ordsets:from_list(Fs)}. + St#expand{defined=gb_sets:from_list(Fs)}. module_attrs(#expand{attributes=Attributes}=St) -> Attrs = [{attribute,Line,Name,Val} || {Name,Line,Val} <- Attributes], @@ -187,7 +188,7 @@ module_predef_func_beh_info(#expand{callbacks=Callbacks,defined=Defined, PreDef=[{behaviour_info,1}], PreExp=PreDef, {[gen_beh_info(Callbacks)], - St#expand{defined=union(from_list(PreDef), Defined), + St#expand{defined=gb_sets:union(gb_sets:from_list(PreDef), Defined), exports=union(from_list(PreExp), Exports)}}. gen_beh_info(Callbacks) -> @@ -215,7 +216,8 @@ module_predef_funcs_mod_info(St) -> [{clause,0,[{var,0,'X'}],[], [{call,0,{remote,0,{atom,0,erlang},{atom,0,get_module_info}}, [{atom,0,St#expand.module},{var,0,'X'}]}]}]}], - St#expand{defined=union(from_list(PreDef), St#expand.defined), + St#expand{defined=gb_sets:union(gb_sets:from_list(PreDef), + St#expand.defined), exports=union(from_list(PreExp), St#expand.exports)}}. %% forms(Forms, State) -> @@ -721,4 +723,4 @@ imported(F, A, St) -> end. defined(F, A, St) -> - ordsets:is_element({F,A}, St#expand.defined). + gb_sets:is_element({F,A}, St#expand.defined). diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index 36d35a8122..be15495672 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. 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 diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index ad0a8a7654..01042cc56f 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. 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 @@ -823,6 +823,13 @@ bitstr({bin_element,_,E0,Size0,[Type,{unit,Unit}|Flags]}, St0) -> {_,_} -> throw(bad_binary) end, + case Size1 of + #c_var{} -> ok; + #c_literal{val=Sz} when is_integer(Sz), Sz >= 0 -> ok; + #c_literal{val=undefined} -> ok; + #c_literal{val=all} -> ok; + _ -> throw(bad_binary) + end, {#c_bitstr{val=E1,size=Size1, unit=#c_literal{val=Unit}, type=#c_literal{val=Type}, diff --git a/lib/compiler/src/v3_kernel.erl b/lib/compiler/src/v3_kernel.erl index abe94ad991..b184987625 100644 --- a/lib/compiler/src/v3_kernel.erl +++ b/lib/compiler/src/v3_kernel.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. 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 @@ -278,11 +278,12 @@ expr(#c_binary{anno=A,segments=Cv}, Sub, St0) -> {#k_binary{anno=A,segs=Kv},Ep,St1} catch throw:bad_element_size -> + St1 = add_warning(get_line(A), bad_segment_size, A, St0), Erl = #c_literal{val=erlang}, Name = #c_literal{val=error}, Args = [#c_literal{val=badarg}], Error = #c_call{anno=A,module=Erl,name=Name,args=Args}, - expr(Error, Sub, St0) + expr(Error, Sub, St1) end; expr(#c_fun{anno=A,vars=Cvs,body=Cb}, Sub0, #kern{ff=OldFF,func=Func}=St0) -> FA = case OldFF of @@ -1827,7 +1828,9 @@ format_error({nomatch_shadow,Line}) -> format_error(nomatch_shadow) -> "this clause cannot match because a previous clause always matches"; format_error(bad_call) -> - "invalid module and/or function name; this call will always fail". + "invalid module and/or function name; this call will always fail"; +format_error(bad_segment_size) -> + "binary construction will fail because of a type mismatch". add_warning(none, Term, Anno, #kern{ws=Ws}=St) -> File = get_file(Anno), diff --git a/lib/compiler/src/v3_kernel.hrl b/lib/compiler/src/v3_kernel.hrl index 17904c37da..fb8baf398b 100644 --- a/lib/compiler/src/v3_kernel.hrl +++ b/lib/compiler/src/v3_kernel.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. 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 diff --git a/lib/compiler/src/v3_life.erl b/lib/compiler/src/v3_life.erl index 22f3e3c2d2..2cc3493570 100644 --- a/lib/compiler/src/v3_life.erl +++ b/lib/compiler/src/v3_life.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. 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 diff --git a/lib/compiler/test/Makefile b/lib/compiler/test/Makefile index e13ad4ae90..e047166ade 100644 --- a/lib/compiler/test/Makefile +++ b/lib/compiler/test/Makefile @@ -154,12 +154,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) compiler.spec compiler.cover \ - $(EMAKEFILE) $(ERL_FILES) $(CORE_FILES) $(RELSYSDIR) + $(EMAKEFILE) $(ERL_FILES) $(CORE_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(NO_OPT_ERL_FILES) $(POST_OPT_ERL_FILES) \ - $(INLINE_ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INLINE_ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/compiler/test/bs_construct_SUITE.erl b/lib/compiler/test/bs_construct_SUITE.erl index 31c7890f26..e8b30f44ce 100644 --- a/lib/compiler/test/bs_construct_SUITE.erl +++ b/lib/compiler/test/bs_construct_SUITE.erl @@ -468,6 +468,10 @@ opt(Config) when is_list(Config) -> ?line {'EXIT',_} = (catch <<<<23,56,0,2>>:64/float>>), ?line {'EXIT',_} = (catch <<<<23,56,0,2:7>>/binary>>), + %% Test constant propagation - there should be a warning. + BadSz = 2.5, + {'EXIT',_} = (catch <<<<N,56,0,2>>:BadSz/binary>>), + case id(false) of true -> ?line opt_dont_call_me(); false -> ok diff --git a/lib/compiler/test/compilation_SUITE.erl b/lib/compiler/test/compilation_SUITE.erl index 408fd5ed53..fed7bec7d4 100644 --- a/lib/compiler/test/compilation_SUITE.erl +++ b/lib/compiler/test/compilation_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 640849f2ec..da53a6ba9c 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, app_test/1, - file_1/1, module_mismatch/1, big_file/1, outdir/1, + file_1/1, forms_2/1, module_mismatch/1, big_file/1, outdir/1, binary/1, makedep/1, cond_and_ifdef/1, listings/1, listings_big/1, other_output/1, package_forms/1, encrypted_abstr/1, bad_record_use1/1, bad_record_use2/1, strict_record/1, @@ -42,7 +42,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> test_lib:recompile(?MODULE), - [app_test, file_1, module_mismatch, big_file, outdir, + [app_test, file_1, forms_2, module_mismatch, big_file, outdir, binary, makedep, cond_and_ifdef, listings, listings_big, other_output, package_forms, encrypted_abstr, {group, bad_record_use}, strict_record, @@ -76,6 +76,9 @@ app_test(Config) when is_list(Config) -> file_1(Config) when is_list(Config) -> ?line Dog = test_server:timetrap(test_server:minutes(5)), + + process_flag(trap_exit, true), + ?line {Simple, Target} = files(Config, "file_1"), ?line {ok, Cwd} = file:get_cwd(), ?line ok = file:set_cwd(filename:dirname(Target)), @@ -102,9 +105,37 @@ file_1(Config) when is_list(Config) -> %% Cleanup. ?line ok = file:delete(Target), ?line ok = file:del_dir(filename:dirname(Target)), + + %% There should not be any messages in the messages. + receive + Any -> + ?t:fail({unexpected,Any}) + after 10 -> + ok + end, + ?line test_server:timetrap_cancel(Dog), ok. +forms_2(Config) when is_list(Config) -> + Src = "/foo/bar", + AbsSrc = filename:absname(Src), + {ok,simple,Binary} = compile:forms([{attribute,1,module,simple}], + [binary,{source,Src}]), + code:load_binary(simple, Src, Binary), + Info = simple:module_info(compile), + + %% Test that the proper source is returned. + AbsSrc = proplists:get_value(source, Info), + + %% Ensure that the options are not polluted with 'source'. + [] = proplists:get_value(options, Info), + + %% Cleanup. + true = code:delete(simple), + false = code:purge(simple), + ok. + module_mismatch(Config) when is_list(Config) -> ?line DataDir = ?config(data_dir, Config), ?line File = filename:join(DataDir, "wrong_module_name.erl"), diff --git a/lib/compiler/test/core_SUITE.erl b/lib/compiler/test/core_SUITE.erl index fd1539e7fd..06185bfc34 100644 --- a/lib/compiler/test/core_SUITE.erl +++ b/lib/compiler/test/core_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 diff --git a/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S b/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S index c0bf04ed8f..cffb792920 100644 --- a/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S +++ b/lib/compiler/test/guard_SUITE_data/guard_SUITE_tuple_size.S @@ -19,10 +19,10 @@ {get_tuple_element,{x,0},1,{x,2}}. {get_tuple_element,{x,0},2,{x,3}}. {get_tuple_element,{x,0},3,{x,4}}. - {gc_bif,'+',{f,0},5,[{x,1},{x,2}],{x,0}}. - {gc_bif,'+',{f,0},5,[{x,0},{x,3}],{x,0}}. - {gc_bif,'+',{f,0},5,[{x,0},{x,4}],{x,0}}. - {gc_bif,'+',{f,0},5,[{x,0},{x,5}],{x,0}}. + {gc_bif,'+',{f,0},6,[{x,1},{x,2}],{x,0}}. + {gc_bif,'+',{f,0},6,[{x,0},{x,3}],{x,0}}. + {gc_bif,'+',{f,0},6,[{x,0},{x,4}],{x,0}}. + {gc_bif,'+',{f,0},6,[{x,0},{x,5}],{x,0}}. return. {label,3}. {badmatch,{x,0}}. diff --git a/lib/compiler/test/misc_SUITE.erl b/lib/compiler/test/misc_SUITE.erl index 5e13a93c52..b53d0dba1d 100644 --- a/lib/compiler/test/misc_SUITE.erl +++ b/lib/compiler/test/misc_SUITE.erl @@ -190,6 +190,15 @@ silly_coverage(Config) when is_list(Config) -> {label,2}|non_proper_list]}],99}, ?line expect_error(fun() -> beam_block:module(BlockInput, []) end), + %% beam_type + TypeInput = {?MODULE,[{foo,0}],[], + [{function,foo,0,2, + [{label,1}, + {line,loc}, + {func_info,{atom,?MODULE},{atom,foo},0}, + {label,2}|non_proper_list]}],99}, + expect_error(fun() -> beam_type:module(TypeInput, []) end), + %% beam_except ExceptInput = {?MODULE,[{foo,0}],[], [{function,foo,0,2, diff --git a/lib/compiler/test/trycatch_SUITE.erl b/lib/compiler/test/trycatch_SUITE.erl index 09a23724fe..29119c0f5d 100644 --- a/lib/compiler/test/trycatch_SUITE.erl +++ b/lib/compiler/test/trycatch_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 diff --git a/lib/compiler/vsn.mk b/lib/compiler/vsn.mk index 416c2f08bb..c9c28cf9a2 100644 --- a/lib/compiler/vsn.mk +++ b/lib/compiler/vsn.mk @@ -1 +1 @@ -COMPILER_VSN = 4.8 +COMPILER_VSN = 4.8.1 diff --git a/lib/cosEvent/doc/src/Makefile b/lib/cosEvent/doc/src/Makefile index 6a9b4201d7..4a0a7429e9 100644 --- a/lib/cosEvent/doc/src/Makefile +++ b/lib/cosEvent/doc/src/Makefile @@ -137,13 +137,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/cosEvent/src/Makefile b/lib/cosEvent/src/Makefile index f8e751f218..1825beacc5 100644 --- a/lib/cosEvent/src/Makefile +++ b/lib/cosEvent/src/Makefile @@ -197,14 +197,14 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) ../info $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) ../info "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosEvent/test/Makefile b/lib/cosEvent/test/Makefile index c3f07c156f..d5350d8293 100644 --- a/lib/cosEvent/test/Makefile +++ b/lib/cosEvent/test/Makefile @@ -141,11 +141,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ - $(RELSYSDIR)/$(IDLOUTDIR) + "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/cosEventDomain/doc/src/Makefile b/lib/cosEventDomain/doc/src/Makefile index cd159c623c..4e54b0fb55 100644 --- a/lib/cosEventDomain/doc/src/Makefile +++ b/lib/cosEventDomain/doc/src/Makefile @@ -131,13 +131,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/cosEventDomain/src/Makefile b/lib/cosEventDomain/src/Makefile index 409cac47f1..41a31b332f 100644 --- a/lib/cosEventDomain/src/Makefile +++ b/lib/cosEventDomain/src/Makefile @@ -166,14 +166,14 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) ../info $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) ../info "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosEventDomain/test/Makefile b/lib/cosEventDomain/test/Makefile index 160c8565e8..9349067eb5 100644 --- a/lib/cosEventDomain/test/Makefile +++ b/lib/cosEventDomain/test/Makefile @@ -98,8 +98,8 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" diff --git a/lib/cosFileTransfer/doc/src/Makefile b/lib/cosFileTransfer/doc/src/Makefile index d9c68987e4..706dec00ce 100644 --- a/lib/cosFileTransfer/doc/src/Makefile +++ b/lib/cosFileTransfer/doc/src/Makefile @@ -134,13 +134,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/cosFileTransfer/src/Makefile b/lib/cosFileTransfer/src/Makefile index 1d51304f6b..1bb765db73 100644 --- a/lib/cosFileTransfer/src/Makefile +++ b/lib/cosFileTransfer/src/Makefile @@ -176,12 +176,12 @@ $(TARGET_FILES): IDL-GENERATED include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosFileTransfer/test/Makefile b/lib/cosFileTransfer/test/Makefile index b46fb35356..177ad7616b 100644 --- a/lib/cosFileTransfer/test/Makefile +++ b/lib/cosFileTransfer/test/Makefile @@ -126,8 +126,8 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/cosNotification/doc/src/Makefile b/lib/cosNotification/doc/src/Makefile index 5caee09ec5..d970818928 100644 --- a/lib/cosNotification/doc/src/Makefile +++ b/lib/cosNotification/doc/src/Makefile @@ -159,13 +159,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/cosNotification/src/Makefile b/lib/cosNotification/src/Makefile index 92225c6cfd..7b8b73aa11 100644 --- a/lib/cosNotification/src/Makefile +++ b/lib/cosNotification/src/Makefile @@ -367,12 +367,12 @@ $(GEN_YECC_ERL_FILES) $(GEN_YECC_HRL_FILES): cosNotification_Grammar.yrl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(YECC_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(IDL_FILES) $(YECC_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(YECC_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(IDL_FILES) $(YECC_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosNotification/test/Makefile b/lib/cosNotification/test/Makefile index f509370430..17b60a8e9f 100644 --- a/lib/cosNotification/test/Makefile +++ b/lib/cosNotification/test/Makefile @@ -128,6 +128,7 @@ ERL_COMPILE_FLAGS += \ $(ERL_IDL_FLAGS) \ -pa $(ERL_TOP)/lib/orber/include \ -pa $(ERL_TOP)/internal_tools/test_server/ebin \ + -pa $(ERL_TOP)/lib/cosEvent/ebin \ -pa $(ERL_TOP)/lib/cosNotification/ebin \ -pa $(ERL_TOP)/lib/cosNotification/test/idl_output \ -pa $(ERL_TOP)/lib/cosTime/ebin \ @@ -182,11 +183,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ - $(RELSYSDIR)/$(IDLOUTDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) + "$(RELSYSDIR)/$(IDLOUTDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" diff --git a/lib/cosProperty/doc/src/Makefile b/lib/cosProperty/doc/src/Makefile index 50f2e06f6c..9bfacc27bd 100644 --- a/lib/cosProperty/doc/src/Makefile +++ b/lib/cosProperty/doc/src/Makefile @@ -136,13 +136,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/cosProperty/src/Makefile b/lib/cosProperty/src/Makefile index 8060421b4e..cb2c56743c 100644 --- a/lib/cosProperty/src/Makefile +++ b/lib/cosProperty/src/Makefile @@ -176,12 +176,12 @@ $(TARGET_FILES): IDL-GENERATED include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosProperty/test/Makefile b/lib/cosProperty/test/Makefile index f6e0d0dbba..d85b8a655a 100644 --- a/lib/cosProperty/test/Makefile +++ b/lib/cosProperty/test/Makefile @@ -120,11 +120,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) -# $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" +# $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" # $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ -# $(RELSYSDIR)/$(IDLOUTDIR) +# "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/cosTime/doc/src/Makefile b/lib/cosTime/doc/src/Makefile index 4e97b07cf4..f89c022c4a 100644 --- a/lib/cosTime/doc/src/Makefile +++ b/lib/cosTime/doc/src/Makefile @@ -130,13 +130,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/cosTime/src/Makefile b/lib/cosTime/src/Makefile index 18c25ca8f1..35f16edcef 100644 --- a/lib/cosTime/src/Makefile +++ b/lib/cosTime/src/Makefile @@ -195,12 +195,12 @@ $(TARGET_FILES): IDL-GENERATED include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(GEN_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosTime/test/Makefile b/lib/cosTime/test/Makefile index a07b27eecb..ab4d140537 100644 --- a/lib/cosTime/test/Makefile +++ b/lib/cosTime/test/Makefile @@ -126,11 +126,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) -# $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" +# $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" # $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ -# $(RELSYSDIR)/$(IDLOUTDIR) +# "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/cosTransactions/doc/src/Makefile b/lib/cosTransactions/doc/src/Makefile index 4341ec04a3..4edad9829b 100644 --- a/lib/cosTransactions/doc/src/Makefile +++ b/lib/cosTransactions/doc/src/Makefile @@ -134,13 +134,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/cosTransactions/examples/Makefile b/lib/cosTransactions/examples/Makefile index 24cd12202a..0408cad75d 100644 --- a/lib/cosTransactions/examples/Makefile +++ b/lib/cosTransactions/examples/Makefile @@ -149,9 +149,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(JAVA_FILES) $(IDL_FILES) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(INETRC_EXAMPLE) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) $(JAVA_FILES) $(IDL_FILES) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(INETRC_EXAMPLE) "$(RELSYSDIR)/examples" @tar cf - java_output | (cd $(RELSYSDIR); tar xf -) release_docs_spec: diff --git a/lib/cosTransactions/src/Makefile b/lib/cosTransactions/src/Makefile index 3c799ca0ca..0922a21641 100644 --- a/lib/cosTransactions/src/Makefile +++ b/lib/cosTransactions/src/Makefile @@ -170,11 +170,11 @@ $(TARGET_FILES): IDL-GENERATED include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILE) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_GEN_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(GEN_ERL_FILES) $(IDL_FILE) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_GEN_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/cosTransactions/test/Makefile b/lib/cosTransactions/test/Makefile index 0bc8c007da..80f4b6c624 100644 --- a/lib/cosTransactions/test/Makefile +++ b/lib/cosTransactions/test/Makefile @@ -142,11 +142,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) \ - $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ - $(RELSYSDIR)/$(IDLOUTDIR) + "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index 285537643e..cfc9a447e0 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -85,7 +85,7 @@ DYNAMIC_CRYPTO_LIB=@SSL_DYNAMIC_ONLY@ ifeq ($(DYNAMIC_CRYPTO_LIB),yes) SSL_DED_LD_RUNTIME_LIBRARY_PATH = @SSL_DED_LD_RUNTIME_LIBRARY_PATH@ -CRYPTO_LINK_LIB=$(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -l$(SSL_CRYPTO_LIBNAME) -l$(SSL_SSL_LIBNAME) +CRYPTO_LINK_LIB=$(SSL_DED_LD_RUNTIME_LIBRARY_PATH) -L$(SSL_LIBDIR) -l$(SSL_CRYPTO_LIBNAME) else SSL_DED_LD_RUNTIME_LIBRARY_PATH= CRYPTO_LINK_LIB=$(SSL_LIBDIR)/lib$(SSL_CRYPTO_LIBNAME).a @@ -133,11 +133,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/obj - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_DATA) $(NIF_MAKEFILE) $(RELSYSDIR)/priv/obj - $(INSTALL_PROGRAM) $(OBJS) $(RELSYSDIR)/priv/obj - $(INSTALL_PROGRAM) $(NIF_LIB) $(RELSYSDIR)/priv/lib + $(INSTALL_DIR) "$(RELSYSDIR)/priv/obj" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_DATA) $(NIF_MAKEFILE) "$(RELSYSDIR)/priv/obj" + $(INSTALL_PROGRAM) $(OBJS) "$(RELSYSDIR)/priv/obj" + $(INSTALL_PROGRAM) $(NIF_LIB) "$(RELSYSDIR)/priv/lib" release_docs_spec: diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 4dc62421d2..a24747a872 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2010-2011. All Rights Reserved. + * Copyright Ericsson AB 2010-2012. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -53,6 +53,10 @@ #include <openssl/evp.h> #include <openssl/hmac.h> +#if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_SHA224) && defined(NID_sha224)\ + && !defined(OPENSSL_NO_SHA256) /* disabled like this in my sha.h (?) */ +# define HAVE_SHA224 +#endif #if OPENSSL_VERSION_NUMBER >= 0x00908000L && !defined(OPENSSL_NO_SHA256) && defined(NID_sha256) # define HAVE_SHA256 #endif @@ -135,10 +139,18 @@ static ERL_NIF_TERM sha(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha256_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha256_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha256_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha512_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha512_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -149,6 +161,10 @@ static ERL_NIF_TERM md4_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv static ERL_NIF_TERM md4_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM md5_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM hmac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); static ERL_NIF_TERM hmac_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); @@ -201,12 +217,33 @@ static void dyn_destroy_function(struct CRYPTO_dynlock_value *ptr, #endif /* OPENSSL_THREADS */ /* helpers */ +static void init_digest_types(ErlNifEnv* env); static void hmac_md5(unsigned char *key, int klen, unsigned char *dbuf, int dlen, unsigned char *hmacbuf); static void hmac_sha1(unsigned char *key, int klen, unsigned char *dbuf, int dlen, unsigned char *hmacbuf); +#ifdef HAVE_SHA224 +static void hmac_sha224(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); +#endif +#ifdef HAVE_SHA256 +static void hmac_sha256(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); +#endif +#ifdef HAVE_SHA384 +static void hmac_sha384(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); +#endif +#ifdef HAVE_SHA512 +static void hmac_sha512(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf); +#endif static int library_refc = 0; /* number of users of this dynamic library */ @@ -220,10 +257,18 @@ static ErlNifFunc nif_funcs[] = { {"sha_init", 0, sha_init}, {"sha_update", 2, sha_update}, {"sha_final", 1, sha_final}, + {"sha224_nif", 1, sha224_nif}, + {"sha224_init_nif", 0, sha224_init_nif}, + {"sha224_update_nif", 2, sha224_update_nif}, + {"sha224_final_nif", 1, sha224_final_nif}, {"sha256_nif", 1, sha256_nif}, {"sha256_init_nif", 0, sha256_init_nif}, {"sha256_update_nif", 2, sha256_update_nif}, {"sha256_final_nif", 1, sha256_final_nif}, + {"sha384_nif", 1, sha384_nif}, + {"sha384_init_nif", 0, sha384_init_nif}, + {"sha384_update_nif", 2, sha384_update_nif}, + {"sha384_final_nif", 1, sha384_final_nif}, {"sha512_nif", 1, sha512_nif}, {"sha512_init_nif", 0, sha512_init_nif}, {"sha512_update_nif", 2, sha512_update_nif}, @@ -234,6 +279,10 @@ static ErlNifFunc nif_funcs[] = { {"md4_final", 1, md4_final}, {"md5_mac_n", 3, md5_mac_n}, {"sha_mac_n", 3, sha_mac_n}, + {"sha224_mac_nif", 3, sha224_mac_nif}, + {"sha256_mac_nif", 3, sha256_mac_nif}, + {"sha384_mac_nif", 3, sha384_mac_nif}, + {"sha512_mac_nif", 3, sha512_mac_nif}, {"hmac_init", 2, hmac_init}, {"hmac_update", 2, hmac_update}, {"hmac_final", 1, hmac_final}, @@ -287,10 +336,12 @@ ERL_NIF_INIT(crypto,nif_funcs,load,reload,upgrade,unload) #define SHA_CTX_LEN (sizeof(SHA_CTX)) #define SHA_LEN 20 #define SHA_LEN_96 12 +#define SHA224_LEN (224/8) #define SHA256_LEN (256/8) #define SHA384_LEN (384/8) #define SHA512_LEN (512/8) #define HMAC_INT_LEN 64 +#define HMAC_INT2_LEN 128 #define HMAC_IPAD 0x36 #define HMAC_OPAD 0x5c @@ -300,6 +351,7 @@ static ErlNifRWLock** lock_vec = NULL; /* Static locks used by openssl */ static ERL_NIF_TERM atom_true; static ERL_NIF_TERM atom_false; static ERL_NIF_TERM atom_sha; +static ERL_NIF_TERM atom_sha224; static ERL_NIF_TERM atom_sha256; static ERL_NIF_TERM atom_sha384; static ERL_NIF_TERM atom_sha512; @@ -320,6 +372,7 @@ static ERL_NIF_TERM atom_check_failed; static ERL_NIF_TERM atom_unknown; static ERL_NIF_TERM atom_none; static ERL_NIF_TERM atom_notsup; +static ERL_NIF_TERM atom_digest; static int is_ok_load_info(ErlNifEnv* env, ERL_NIF_TERM load_info) @@ -374,6 +427,7 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_true = enif_make_atom(env,"true"); atom_false = enif_make_atom(env,"false"); atom_sha = enif_make_atom(env,"sha"); + atom_sha224 = enif_make_atom(env,"sha224"); atom_sha256 = enif_make_atom(env,"sha256"); atom_sha384 = enif_make_atom(env,"sha384"); atom_sha512 = enif_make_atom(env,"sha512"); @@ -393,6 +447,9 @@ static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) atom_unknown = enif_make_atom(env,"unknown"); atom_none = enif_make_atom(env,"none"); atom_notsup = enif_make_atom(env,"notsup"); + atom_digest = enif_make_atom(env,"digest"); + + init_digest_types(env); *priv_data = NULL; library_refc++; @@ -557,6 +614,67 @@ static ERL_NIF_TERM sha_final(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return ret; } +static ERL_NIF_TERM sha224_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data) */ +#ifdef HAVE_SHA224 + ErlNifBinary ibin; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) { + return enif_make_badarg(env); + } + SHA224((unsigned char *) ibin.data, ibin.size, + enif_make_new_binary(env,SHA224_LEN, &ret)); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha224_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ +#ifdef HAVE_SHA224 + ERL_NIF_TERM ret; + SHA224_Init((SHA256_CTX *) enif_make_new_binary(env, sizeof(SHA256_CTX), &ret)); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha224_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ +#ifdef HAVE_SHA224 + SHA256_CTX* new_ctx; + ErlNifBinary ctx_bin, data_bin; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX) + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = (SHA256_CTX*) enif_make_new_binary(env,sizeof(SHA256_CTX), &ret); + memcpy(new_ctx, ctx_bin.data, sizeof(SHA256_CTX)); + SHA224_Update(new_ctx, data_bin.data, data_bin.size); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha224_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ +#ifdef HAVE_SHA224 + ErlNifBinary ctx_bin; + SHA256_CTX ctx_clone; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA256_CTX)) { + return enif_make_badarg(env); + } + memcpy(&ctx_clone, ctx_bin.data, sizeof(SHA256_CTX)); /* writable */ + SHA224_Final(enif_make_new_binary(env, SHA224_LEN, &ret), &ctx_clone); + return ret; +#else + return atom_notsup; +#endif +} + static ERL_NIF_TERM sha256_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Data) */ #ifdef HAVE_SHA256 @@ -618,6 +736,67 @@ static ERL_NIF_TERM sha256_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER #endif } +static ERL_NIF_TERM sha384_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Data) */ +#ifdef HAVE_SHA384 + ErlNifBinary ibin; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &ibin)) { + return enif_make_badarg(env); + } + SHA384((unsigned char *) ibin.data, ibin.size, + enif_make_new_binary(env,SHA384_LEN, &ret)); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha384_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* () */ +#ifdef HAVE_SHA384 + ERL_NIF_TERM ret; + SHA384_Init((SHA512_CTX *) enif_make_new_binary(env, sizeof(SHA512_CTX), &ret)); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha384_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context, Data) */ +#ifdef HAVE_SHA384 + SHA512_CTX* new_ctx; + ErlNifBinary ctx_bin, data_bin; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX) + || !enif_inspect_iolist_as_binary(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + new_ctx = (SHA512_CTX*) enif_make_new_binary(env,sizeof(SHA512_CTX), &ret); + memcpy(new_ctx, ctx_bin.data, sizeof(SHA512_CTX)); + SHA384_Update(new_ctx, data_bin.data, data_bin.size); + return ret; +#else + return atom_notsup; +#endif +} +static ERL_NIF_TERM sha384_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Context) */ +#ifdef HAVE_SHA384 + ErlNifBinary ctx_bin; + SHA512_CTX ctx_clone; + ERL_NIF_TERM ret; + if (!enif_inspect_binary(env, argv[0], &ctx_bin) || ctx_bin.size != sizeof(SHA512_CTX)) { + return enif_make_badarg(env); + } + memcpy(&ctx_clone, ctx_bin.data, sizeof(SHA512_CTX)); /* writable */ + SHA384_Final(enif_make_new_binary(env, SHA384_LEN, &ret), &ctx_clone); + return ret; +#else + return atom_notsup; +#endif +} + static ERL_NIF_TERM sha512_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Data) */ #ifdef HAVE_SHA512 @@ -760,6 +939,95 @@ static ERL_NIF_TERM sha_mac_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ return ret; } +static ERL_NIF_TERM sha224_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ +#ifdef HAVE_SHA224 + unsigned char hmacbuf[SHA224_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA224_DIGEST_LENGTH) { + return enif_make_badarg(env); + } + hmac_sha224(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), + hmacbuf, mac_sz); + return ret; +#else + return atom_notsup; +#endif +} + +static ERL_NIF_TERM sha256_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ +#ifdef HAVE_SHA256 + unsigned char hmacbuf[SHA256_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA256_DIGEST_LENGTH) { + return enif_make_badarg(env); + } + hmac_sha256(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), + hmacbuf, mac_sz); + return ret; +#else + return atom_notsup; +#endif +} + +static ERL_NIF_TERM sha384_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ +#ifdef HAVE_SHA384 + unsigned char hmacbuf[SHA384_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA384_DIGEST_LENGTH) { + return enif_make_badarg(env); + } + hmac_sha384(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), + hmacbuf, mac_sz); + return ret; +#else + return atom_notsup; +#endif +} + + +static ERL_NIF_TERM sha512_mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Key, Data, MacSize) */ +#ifdef HAVE_SHA512 + unsigned char hmacbuf[SHA512_DIGEST_LENGTH]; + ErlNifBinary key, data; + unsigned mac_sz; + ERL_NIF_TERM ret; + + if (!enif_inspect_iolist_as_binary(env, argv[0], &key) + || !enif_inspect_iolist_as_binary(env, argv[1], &data) + || !enif_get_uint(env,argv[2],&mac_sz) || mac_sz > SHA512_DIGEST_LENGTH) { + return enif_make_badarg(env); + } + hmac_sha512(key.data, key.size, data.data, data.size, hmacbuf); + memcpy(enif_make_new_binary(env, mac_sz, &ret), + hmacbuf, mac_sz); + return ret; +#else + return atom_notsup; +#endif +} + static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type, Key) */ ErlNifBinary key; @@ -768,6 +1036,18 @@ static ERL_NIF_TERM hmac_init(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[ const EVP_MD *md; if (argv[0] == atom_sha) md = EVP_sha1(); +#ifdef HAVE_SHA224 + else if (argv[0] == atom_sha224) md = EVP_sha224(); +#endif +#ifdef HAVE_SHA256 + else if (argv[0] == atom_sha256) md = EVP_sha256(); +#endif +#ifdef HAVE_SHA384 + else if (argv[0] == atom_sha384) md = EVP_sha384(); +#endif +#ifdef HAVE_SHA512 + else if (argv[0] == atom_sha512) md = EVP_sha512(); +#endif else if (argv[0] == atom_md5) md = EVP_md5(); else if (argv[0] == atom_ripemd160) md = EVP_ripemd160(); else goto badarg; @@ -954,8 +1234,7 @@ static ERL_NIF_TERM aes_cfb_128_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TE if (!enif_inspect_iolist_as_binary(env, argv[0], &key) || key.size != 16 || !enif_inspect_binary(env, argv[1], &ivec) || ivec.size != 16 - || !enif_inspect_iolist_as_binary(env, argv[2], &text) - || text.size % 16 != 0) { + || !enif_inspect_iolist_as_binary(env, argv[2], &text)) { return enif_make_badarg(env); } @@ -1208,14 +1487,43 @@ static int inspect_mpint(ErlNifEnv* env, ERL_NIF_TERM term, ErlNifBinary* bin) } static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (DigestType,Data,Signature,Key=[P, Q, G, Y]) */ +{/* (DigestType|none, Data|{digest,Digest}, Signature,Key=[P, Q, G, Y]) */ ErlNifBinary data_bin, sign_bin; BIGNUM *dsa_p = NULL, *dsa_q = NULL, *dsa_g = NULL, *dsa_y = NULL; unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + unsigned char* digest; ERL_NIF_TERM head, tail; + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; DSA *dsa; int i; + if (argv[0] == atom_sha) { + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != SHA_DIGEST_LENGTH) { + + return enif_make_badarg(env); + } + digest = data_bin.data; + } + else { + if (!inspect_mpint(env, argv[1], &data_bin)) { + return enif_make_badarg(env); + } + SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + digest = hmacbuf; + } + } + else if (argv[0] == atom_none && enif_inspect_binary(env, argv[1], &data_bin) + && data_bin.size == SHA_DIGEST_LENGTH) { + digest = data_bin.data; + } + else { + return enif_make_badarg(env); + } + if (!inspect_mpint(env, argv[2], &sign_bin) || !enif_get_list_cell(env, argv[3], &head, &tail) || !get_bn_from_mpint(env, head, &dsa_p) @@ -1226,23 +1534,13 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv || !enif_get_list_cell(env, tail, &head, &tail) || !get_bn_from_mpint(env, head, &dsa_y) || !enif_is_empty_list(env,tail)) { - badarg: + if (dsa_p) BN_free(dsa_p); if (dsa_q) BN_free(dsa_q); if (dsa_g) BN_free(dsa_g); if (dsa_y) BN_free(dsa_y); return enif_make_badarg(env); } - if (argv[0] == atom_sha && inspect_mpint(env, argv[1], &data_bin)) { - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); - } - else if (argv[0] == atom_none && enif_inspect_binary(env, argv[1], &data_bin) - && data_bin.size == SHA_DIGEST_LENGTH) { - memcpy(hmacbuf, data_bin.data, SHA_DIGEST_LENGTH); - } - else { - goto badarg; - } dsa = DSA_new(); dsa->p = dsa_p; @@ -1250,23 +1548,134 @@ static ERL_NIF_TERM dss_verify(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv dsa->g = dsa_g; dsa->priv_key = NULL; dsa->pub_key = dsa_y; - i = DSA_verify(0, hmacbuf, SHA_DIGEST_LENGTH, + i = DSA_verify(0, digest, SHA_DIGEST_LENGTH, sign_bin.data+4, sign_bin.size-4, dsa); DSA_free(dsa); return(i > 0) ? atom_true : atom_false; } + +static void md5_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + MD5(in, in_len, out); +} +static void sha1_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA1(in, in_len, out); +} +#ifdef HAVE_SHA224 +static void sha224_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA224(in, in_len, out); +} +#endif +#ifdef HAVE_SHA256 +static void sha256_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA256(in, in_len, out); +} +#endif +#ifdef HAVE_SHA384 +static void sha384_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA384(in, in_len, out); +} +#endif +#ifdef HAVE_SHA512 +static void sha512_digest(unsigned char* in, unsigned int in_len, unsigned char* out) +{ + SHA512(in, in_len, out); +} +#endif + +struct digest_type_t { + const char* type_str; + unsigned len; /* 0 if notsup */ + int NID_type; + void (*funcp)(unsigned char* in, unsigned int in_len, unsigned char* out); + ERL_NIF_TERM type_atom; +}; + +struct digest_type_t digest_types[] = +{ + {"md5", MD5_DIGEST_LENGTH, NID_md5, md5_digest}, + {"sha", SHA_DIGEST_LENGTH, NID_sha1, sha1_digest}, + {"sha224", +#ifdef HAVE_SHA224 + SHA224_LEN, NID_sha224, sha224_digest +#else + 0 +#endif + }, + {"sha256", +#ifdef HAVE_SHA256 + SHA256_LEN, NID_sha256, sha256_digest +#else + 0 +#endif + }, + {"sha384", +#ifdef HAVE_SHA384 + SHA384_LEN, NID_sha384, sha384_digest +#else + 0 +#endif + }, + {"sha512", +#ifdef HAVE_SHA512 + SHA512_LEN, NID_sha512, sha512_digest +#else + 0 +#endif + }, + {NULL} +}; + +static void init_digest_types(ErlNifEnv* env) +{ + struct digest_type_t* p = digest_types; + + for (p = digest_types; p->type_str; p++) { + p->type_atom = enif_make_atom(env, p->type_str); + } + +} + +static struct digest_type_t* get_digest_type(ERL_NIF_TERM type) +{ + struct digest_type_t* p = NULL; + for (p = digest_types; p->type_str; p++) { + if (type == p->type_atom) { + return p; + } + } + return NULL; +} + static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Data, Signature, Key=[E,N]) */ +{/* (Type, Data|{digest,Digest}, Signature, Key=[E,N]) */ ErlNifBinary data_bin, sign_bin; unsigned char hmacbuf[SHA512_LEN]; ERL_NIF_TERM head, tail, ret; int i; - RSA* rsa = RSA_new(); + RSA* rsa; const ERL_NIF_TERM type = argv[0]; + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; + struct digest_type_t* digp = NULL; + unsigned char* digest = NULL; + + digp = get_digest_type(type); + if (!digp) { + return enif_make_badarg(env); + } + if (!digp->len) { + return atom_notsup; + } - if (!inspect_mpint(env, argv[1], &data_bin) - || !inspect_mpint(env, argv[2], &sign_bin) + rsa = RSA_new(); + + if (!inspect_mpint(env, argv[2], &sign_bin) || !enif_get_list_cell(env, argv[3], &head, &tail) || !get_bn_from_mpint(env, head, &rsa->e) || !enif_get_list_cell(env, tail, &head, &tail) @@ -1274,59 +1683,38 @@ static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM || !enif_is_empty_list(env, tail)) { ret = enif_make_badarg(env); + goto done; } - else { - if (type == atom_sha) { - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH, - sign_bin.data+4, sign_bin.size-4, rsa); - } - else if (type == atom_sha256) { - #ifdef HAVE_SHA256 - SHA256(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_sha256, hmacbuf, SHA256_LEN, - sign_bin.data+4, sign_bin.size-4, rsa); - #else - ret = atom_notsup; - goto done; - #endif - } - else if (type == atom_sha384) { - #ifdef HAVE_SHA384 - SHA384(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_sha384, hmacbuf, SHA384_LEN, - sign_bin.data+4, sign_bin.size-4, rsa); - #else - ret = atom_notsup; - goto done; - #endif - } - else if (type == atom_sha512) { - #ifdef HAVE_SHA512 - SHA512(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_sha512, hmacbuf, SHA512_LEN, - sign_bin.data+4, sign_bin.size-4, rsa); - #else - ret = atom_notsup; - goto done; - #endif - } - else if (type == atom_md5) { - MD5(data_bin.data+4, data_bin.size-4, hmacbuf); - i = RSA_verify(NID_md5, hmacbuf, MD5_DIGEST_LENGTH, - sign_bin.data+4, sign_bin.size-4, rsa); - } - else { + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != digp->len) { + ret = enif_make_badarg(env); goto done; } - ret = (i==1 ? atom_true : atom_false); - } + digest = data_bin.data; + } + else if (inspect_mpint(env, argv[1], &data_bin)) { + digest = hmacbuf; + digp->funcp(data_bin.data+4, data_bin.size-4, digest); + } + else { + ret = enif_make_badarg(env); + goto done; + } + + i = RSA_verify(digp->NID_type, digest, digp->len, + sign_bin.data+4, sign_bin.size-4, rsa); + + ret = (i==1 ? atom_true : atom_false); + done: RSA_free(rsa); return ret; } + static ERL_NIF_TERM aes_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Key, IVec, Data, IsEncrypt) */ ErlNifBinary key_bin, ivec_bin, data_bin; @@ -1456,48 +1844,88 @@ static ERL_NIF_TERM rc2_cbc_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM a return ret; } -static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type,Data,Key=[E,N,D]) */ - ErlNifBinary data_bin, ret_bin; +static int get_rsa_private_key(ErlNifEnv* env, ERL_NIF_TERM key, RSA *rsa) +{ + /* key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C] */ ERL_NIF_TERM head, tail; - unsigned char hmacbuf[SHA_DIGEST_LENGTH]; - unsigned rsa_s_len; - RSA *rsa = RSA_new(); - int i, is_sha; - if (argv[0] == atom_sha) is_sha = 1; - else if (argv[0] == atom_md5) is_sha = 0; - else goto badarg; - - if (!inspect_mpint(env,argv[1],&data_bin) - || !enif_get_list_cell(env, argv[2], &head, &tail) + if (!enif_get_list_cell(env, key, &head, &tail) || !get_bn_from_mpint(env, head, &rsa->e) || !enif_get_list_cell(env, tail, &head, &tail) || !get_bn_from_mpint(env, head, &rsa->n) || !enif_get_list_cell(env, tail, &head, &tail) || !get_bn_from_mpint(env, head, &rsa->d) - || !enif_is_empty_list(env,tail)) { - badarg: - RSA_free(rsa); + || (!enif_is_empty_list(env, tail) && + (!enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->p) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->q) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->dmp1) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->dmq1) + || !enif_get_list_cell(env, tail, &head, &tail) + || !get_bn_from_mpint(env, head, &rsa->iqmp) + || !enif_is_empty_list(env, tail)))) { + return 0; + } + return 1; +} + +static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Type, Data|{digest,Digest}, Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C]) */ + ErlNifBinary data_bin, ret_bin; + unsigned char hmacbuf[SHA_DIGEST_LENGTH]; + unsigned rsa_s_len; + RSA* rsa; + int i; + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; + struct digest_type_t *digp; + unsigned char* digest; + + digp = get_digest_type(argv[0]); + if (!digp) { return enif_make_badarg(env); } - enif_alloc_binary(RSA_size(rsa), &ret_bin); - if (is_sha) { - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); - ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, SHA_DIGEST_LENGTH); - i = RSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH, - ret_bin.data, &rsa_s_len, rsa); + if (!digp->len) { + return atom_notsup; + } + + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != digp->len) { + + return enif_make_badarg(env); + } + digest = data_bin.data; } else { - MD5(data_bin.data+4, data_bin.size-4, hmacbuf); - ERL_VALGRIND_ASSERT_MEM_DEFINED(hmacbuf, MD5_DIGEST_LENGTH); - i = RSA_sign(NID_md5, hmacbuf,MD5_DIGEST_LENGTH, - ret_bin.data, &rsa_s_len, rsa); + if (!inspect_mpint(env,argv[1],&data_bin)) { + return enif_make_badarg(env); + } + digest = hmacbuf; + digp->funcp(data_bin.data+4, data_bin.size-4, digest); } + + rsa = RSA_new(); + if (!get_rsa_private_key(env, argv[2], rsa)) { + RSA_free(rsa); + return enif_make_badarg(env); + } + + + enif_alloc_binary(RSA_size(rsa), &ret_bin); + + ERL_VALGRIND_ASSERT_MEM_DEFINED(digest, digp->len); + i = RSA_sign(digp->NID_type, digest, digp->len, + ret_bin.data, &rsa_s_len, rsa); + RSA_free(rsa); if (i) { ERL_VALGRIND_MAKE_MEM_DEFINED(ret_bin.data, rsa_s_len); - if (rsa_s_len != data_bin.size) { + if (rsa_s_len != ret_bin.size) { enif_realloc_binary(&ret_bin, rsa_s_len); ERL_VALGRIND_ASSERT_MEM_DEFINED(ret_bin.data, rsa_s_len); } @@ -1509,15 +1937,49 @@ static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar } } + static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (DigesType, Data, Key=[P,Q,G,PrivKey]) */ +{/* (DigesType|none, Data|{digest,Digest}, Key=[P,Q,G,PrivKey]) */ ErlNifBinary data_bin, ret_bin; ERL_NIF_TERM head, tail; unsigned char hmacbuf[SHA_DIGEST_LENGTH]; unsigned int dsa_s_len; - DSA* dsa = DSA_new(); + const ERL_NIF_TERM* tpl_terms; + int tpl_arity; + unsigned char* digest = NULL; + DSA* dsa; int i; + if (argv[0] == atom_sha) { + if (enif_get_tuple(env, argv[1], &tpl_arity, &tpl_terms)) { + if (tpl_arity != 2 || tpl_terms[0] != atom_digest + || !enif_inspect_binary(env, tpl_terms[1], &data_bin) + || data_bin.size != SHA_DIGEST_LENGTH) { + + return enif_make_badarg(env); + } + digest = data_bin.data; + } + else { + if (!inspect_mpint(env,argv[1],&data_bin)) { + return enif_make_badarg(env); + } + SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); + digest = hmacbuf; + } + } + else if (argv[0] == atom_none + && enif_inspect_binary(env,argv[1],&data_bin) + && data_bin.size == SHA_DIGEST_LENGTH) { + + digest = data_bin.data; + } + else { + return enif_make_badarg(env); + } + + dsa = DSA_new(); + dsa->pub_key = NULL; if (!enif_get_list_cell(env, argv[2], &head, &tail) || !get_bn_from_mpint(env, head, &dsa->p) @@ -1528,23 +1990,12 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar || !enif_get_list_cell(env, tail, &head, &tail) || !get_bn_from_mpint(env, head, &dsa->priv_key) || !enif_is_empty_list(env,tail)) { - goto badarg; - } - if (argv[0] == atom_sha && inspect_mpint(env, argv[1], &data_bin)) { - SHA1(data_bin.data+4, data_bin.size-4, hmacbuf); - } - else if (argv[0] == atom_none && enif_inspect_binary(env,argv[1],&data_bin) - && data_bin.size == SHA_DIGEST_LENGTH) { - memcpy(hmacbuf, data_bin.data, SHA_DIGEST_LENGTH); - } - else { - badarg: DSA_free(dsa); return enif_make_badarg(env); } enif_alloc_binary(DSA_size(dsa), &ret_bin); - i = DSA_sign(NID_sha1, hmacbuf, SHA_DIGEST_LENGTH, + i = DSA_sign(NID_sha1, digest, SHA_DIGEST_LENGTH, ret_bin.data, &dsa_s_len, dsa); DSA_free(dsa); if (i) { @@ -1558,6 +2009,7 @@ static ERL_NIF_TERM dss_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar } } + static int rsa_pad(ERL_NIF_TERM term, int* padding) { if (term == atom_rsa_pkcs1_padding) { @@ -1623,20 +2075,13 @@ static ERL_NIF_TERM rsa_public_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TER } static ERL_NIF_TERM rsa_private_crypt(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Data, PublKey=[E,N,D], Padding, IsEncrypt) */ +{/* (Data, Key=[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Padding, IsEncrypt) */ ErlNifBinary data_bin, ret_bin; - ERL_NIF_TERM head, tail; int padding, i; RSA* rsa = RSA_new(); if (!enif_inspect_binary(env, argv[0], &data_bin) - || !enif_get_list_cell(env, argv[1], &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->e) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->n) - || !enif_get_list_cell(env, tail, &head, &tail) - || !get_bn_from_mpint(env, head, &rsa->d) - || !enif_is_empty_list(env,tail) + || !get_rsa_private_key(env, argv[1], rsa) || !rsa_pad(argv[2], &padding)) { RSA_free(rsa); @@ -2026,3 +2471,166 @@ static void hmac_sha1(unsigned char *key, int klen, SHA1_Final((unsigned char *) hmacbuf, &ctx); } +#ifdef HAVE_SHA224 +static void hmac_sha224(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + SHA256_CTX ctx; + char ipad[HMAC_INT_LEN]; + char opad[HMAC_INT_LEN]; + unsigned char nkey[SHA224_DIGEST_LENGTH]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT_LEN) { + SHA224(key, klen, nkey); + key = nkey; + klen = SHA224_DIGEST_LENGTH; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner SHA */ + SHA224_Init(&ctx); + SHA224_Update(&ctx, ipad, HMAC_INT_LEN); + SHA224_Update(&ctx, dbuf, dlen); + SHA224_Final((unsigned char *) hmacbuf, &ctx); + /* outer SHA */ + SHA224_Init(&ctx); + SHA224_Update(&ctx, opad, HMAC_INT_LEN); + SHA224_Update(&ctx, hmacbuf, SHA224_DIGEST_LENGTH); + SHA224_Final((unsigned char *) hmacbuf, &ctx); +} +#endif + +#ifdef HAVE_SHA256 +static void hmac_sha256(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + SHA256_CTX ctx; + char ipad[HMAC_INT_LEN]; + char opad[HMAC_INT_LEN]; + unsigned char nkey[SHA256_DIGEST_LENGTH]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT_LEN) { + SHA256(key, klen, nkey); + key = nkey; + klen = SHA256_DIGEST_LENGTH; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner SHA */ + SHA256_Init(&ctx); + SHA256_Update(&ctx, ipad, HMAC_INT_LEN); + SHA256_Update(&ctx, dbuf, dlen); + SHA256_Final((unsigned char *) hmacbuf, &ctx); + /* outer SHA */ + SHA256_Init(&ctx); + SHA256_Update(&ctx, opad, HMAC_INT_LEN); + SHA256_Update(&ctx, hmacbuf, SHA256_DIGEST_LENGTH); + SHA256_Final((unsigned char *) hmacbuf, &ctx); +} +#endif + +#ifdef HAVE_SHA384 +static void hmac_sha384(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + SHA512_CTX ctx; + char ipad[HMAC_INT2_LEN]; + char opad[HMAC_INT2_LEN]; + unsigned char nkey[SHA384_DIGEST_LENGTH]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT2_LEN) { + SHA384(key, klen, nkey); + key = nkey; + klen = SHA384_DIGEST_LENGTH; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT2_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner SHA */ + SHA384_Init(&ctx); + SHA384_Update(&ctx, ipad, HMAC_INT2_LEN); + SHA384_Update(&ctx, dbuf, dlen); + SHA384_Final((unsigned char *) hmacbuf, &ctx); + /* outer SHA */ + SHA384_Init(&ctx); + SHA384_Update(&ctx, opad, HMAC_INT2_LEN); + SHA384_Update(&ctx, hmacbuf, SHA384_DIGEST_LENGTH); + SHA384_Final((unsigned char *) hmacbuf, &ctx); +} +#endif + +#ifdef HAVE_SHA512 +static void hmac_sha512(unsigned char *key, int klen, + unsigned char *dbuf, int dlen, + unsigned char *hmacbuf) +{ + SHA512_CTX ctx; + char ipad[HMAC_INT2_LEN]; + char opad[HMAC_INT2_LEN]; + unsigned char nkey[SHA512_DIGEST_LENGTH]; + int i; + + /* Change key if longer than 64 bytes */ + if (klen > HMAC_INT2_LEN) { + SHA512(key, klen, nkey); + key = nkey; + klen = SHA512_DIGEST_LENGTH; + } + + memset(ipad, '\0', sizeof(ipad)); + memset(opad, '\0', sizeof(opad)); + memcpy(ipad, key, klen); + memcpy(opad, key, klen); + + for (i = 0; i < HMAC_INT2_LEN; i++) { + ipad[i] ^= HMAC_IPAD; + opad[i] ^= HMAC_OPAD; + } + + /* inner SHA */ + SHA512_Init(&ctx); + SHA512_Update(&ctx, ipad, HMAC_INT2_LEN); + SHA512_Update(&ctx, dbuf, dlen); + SHA512_Final((unsigned char *) hmacbuf, &ctx); + /* outer SHA */ + SHA512_Init(&ctx); + SHA512_Update(&ctx, opad, HMAC_INT2_LEN); + SHA512_Update(&ctx, hmacbuf, SHA512_DIGEST_LENGTH); + SHA512_Final((unsigned char *) hmacbuf, &ctx); +} +#endif diff --git a/lib/crypto/doc/src/Makefile b/lib/crypto/doc/src/Makefile index 03aaba939b..00ae70fb4a 100644 --- a/lib/crypto/doc/src/Makefile +++ b/lib/crypto/doc/src/Makefile @@ -100,16 +100,16 @@ clean clean_docs clean_tex: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index 8cb893cd1c..045ad4c050 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -256,6 +256,57 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </desc> </func> <func> + <name>hash(Type, Data) -> Digest</name> + <fsummary></fsummary> + <type> + <v>Type = md4 | md5 | sha | sha224 | sha256 | sha384 | sha512</v> + <v>Data = iodata()</v> + <v>Digest = binary()</v> + </type> + <desc> + <p>Computes a message digest of type <c>Type</c> from <c>Data</c>.</p> + </desc> + </func> + <func> + <name>hash_init(Type) -> Context</name> + <fsummary></fsummary> + <type> + <v>Type = md4 | md5 | sha | sha224 | sha256 | sha384 | sha512</v> + </type> + <desc> + <p>Initializes the context for streaming hash operations. <c>Type</c> determines + which digest to use. The returned context should be used as argument + to <seealso marker="#hash_update/2">hash_update</seealso>.</p> + </desc> + </func> + <func> + <name>hash_update(Context, Data) -> NewContext</name> + <fsummary></fsummary> + <type> + <v>Data = iodata()</v> + </type> + <desc> + <p>Updates the digest represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> + must have been generated using <seealso marker="#hash_init/1">hash_init</seealso> + or a previous call to this function. <c>Data</c> can be any length. <c>NewContext</c> + must be passed into the next call to <c>hash_update</c> + or <seealso marker="#hash_final/1">hash_final</seealso>.</p> + </desc> + </func> + <func> + <name>hash_final(Context) -> Digest</name> + <fsummary></fsummary> + <type> + <v>Digest = binary()</v> + </type> + <desc> + <p>Finalizes the hash operation referenced by <c>Context</c> returned + from a previous call to <seealso marker="#hash_update/2">hash_update</seealso>. + The size of <c>Digest</c> is determined by the type of hash + function used to generate it.</p> + </desc> + </func> + <func> <name>md5_mac(Key, Data) -> Mac</name> <fsummary>Compute an <c>MD5 MAC</c>message authentification code</fsummary> <type> @@ -643,16 +694,14 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> <func> <name>aes_cfb_128_encrypt(Key, IVec, Text) -> Cipher</name> - <name>aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher</name> - <fsummary>Encrypt <c>Text</c>according to AES in Cipher Feedback mode or Cipher Block Chaining mode</fsummary> + <fsummary>Encrypt <c>Text</c>according to AES in Cipher Feedback mode</fsummary> <type> <v>Key = Text = iolist() | binary()</v> <v>IVec = Cipher = binary()</v> </type> <desc> <p>Encrypts <c>Text</c> according to AES in Cipher Feedback - mode (CFB) or Cipher Block Chaining mode (CBC). <c>Text</c> - must be a multiple of 128 bits (16 bytes). <c>Key</c> is the + mode (CFB). <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary initializing vector. The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits (16 bytes).</p> @@ -660,15 +709,45 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </func> <func> <name>aes_cfb_128_decrypt(Key, IVec, Cipher) -> Text</name> + <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Feedback mode</fsummary> + <type> + <v>Key = Cipher = iolist() | binary()</v> + <v>IVec = Text = binary()</v> + </type> + <desc> + <p>Decrypts <c>Cipher</c> according to AES in Cipher Feedback Mode (CFB). + <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary + initializing vector. <c>Key</c> and <c>IVec</c> must have + the same values as those used when encrypting. The lengths of + <c>Key</c> and <c>IVec</c> must be 128 bits (16 bytes).</p> + </desc> + </func> + <func> + <name>aes_cbc_128_encrypt(Key, IVec, Text) -> Cipher</name> + <fsummary>Encrypt <c>Text</c>according to AES in Cipher Block Chaining mode</fsummary> + <type> + <v>Key = Text = iolist() | binary()</v> + <v>IVec = Cipher = binary()</v> + </type> + <desc> + <p>Encrypts <c>Text</c> according to AES in Cipher Block Chaining + mode (CBC). <c>Text</c> + must be a multiple of 128 bits (16 bytes). <c>Key</c> is the + AES key, and <c>IVec</c> is an arbitrary initializing vector. + The lengths of <c>Key</c> and <c>IVec</c> must be 128 bits + (16 bytes).</p> + </desc> + </func> + <func> <name>aes_cbc_128_decrypt(Key, IVec, Cipher) -> Text</name> - <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Feedback mode or Cipher Block Chaining mode</fsummary> + <fsummary>Decrypt <c>Cipher</c>according to AES in Cipher Block Chaining mode</fsummary> <type> <v>Key = Cipher = iolist() | binary()</v> <v>IVec = Text = binary()</v> </type> <desc> - <p>Decrypts <c>Cipher</c> according to Cipher Feedback Mode (CFB) - or Cipher Block Chaining mode (CBC). + <p>Decrypts <c>Cipher</c> according to AES in Cipher Block + Chaining mode (CBC). <c>Key</c> is the AES key, and <c>IVec</c> is an arbitrary initializing vector. <c>Key</c> and <c>IVec</c> must have the same values as those used when encrypting. <c>Cipher</c> @@ -865,46 +944,56 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </func> <func> - <name>rsa_sign(Data, Key) -> Signature</name> - <name>rsa_sign(DigestType, Data, Key) -> Signature</name> + <name>rsa_sign(DataOrDigest, Key) -> Signature</name> + <name>rsa_sign(DigestType, DataOrDigest, Key) -> Signature</name> <fsummary>Sign the data using rsa with the given key.</fsummary> <type> + <v>DataOrDigest = Data | {digest,Digest}</v> <v>Data = Mpint</v> - <v>Key = [E, N, D]</v> + <v>Digest = binary()</v> + <v>Key = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v> <v>E, N, D = Mpint</v> <d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and <c>D</c> is the private exponent.</d> - <v>DigestType = md5 | sha</v> + <v>P1, P2, E1, E2, C = Mpint</v> + <d>The longer key format contains redundant information that will make + the calculation faster. <c>P1,P2</c> are first and second prime factors. + <c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient. + Terminology is taken from RFC 3447.</d> + <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v> <d>The default <c>DigestType</c> is sha.</d> <v>Mpint = binary()</v> <v>Signature = binary()</v> </type> <desc> - <p>Calculates a <c>DigestType</c> digest of the <c>Data</c> - and creates a RSA signature with the private key <c>Key</c> - of the digest.</p> + <p>Creates a RSA signature with the private key <c>Key</c> + of a digest. The digest is either calculated as a + <c>DigestType</c> digest of <c>Data</c> or a precalculated + binary <c>Digest</c>.</p> </desc> </func> <func> - <name>rsa_verify(Data, Signature, Key) -> Verified</name> - <name>rsa_verify(DigestType, Data, Signature, Key) -> Verified </name> + <name>rsa_verify(DataOrDigest, Signature, Key) -> Verified</name> + <name>rsa_verify(DigestType, DataOrDigest, Signature, Key) -> Verified </name> <fsummary>Verify the digest and signature using rsa with given public key.</fsummary> <type> <v>Verified = boolean()</v> + <v>DataOrDigest = Data | {digest|Digest}</v> <v>Data, Signature = Mpint</v> + <v>Digest = binary()</v> <v>Key = [E, N]</v> <v>E, N = Mpint</v> <d>Where <c>E</c> is the public exponent and <c>N</c> is public modulus.</d> - <v>DigestType = md5 | sha | sha256 | sha384 | sha512</v> - <d> The default <c>DigestType</c> is sha.</d> + <v>DigestType = md5 | sha | sha224 | sha256 | sha384 | sha512</v> + <d>The default <c>DigestType</c> is sha.</d> <v>Mpint = binary()</v> </type> <desc> - <p>Calculates a <c>DigestType</c> digest of the <c>Data</c> - and verifies that the digest matches the RSA signature using the + <p>Verifies that a digest matches the RSA signature using the signer's public key <c>Key</c>. - </p> + The digest is either calculated as a <c>DigestType</c> + digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p> <p>May throw exception <c>notsup</c> in case the chosen <c>DigestType</c> is not supported by the underlying OpenSSL implementation.</p> </desc> @@ -943,10 +1032,15 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> <fsummary>Decrypts ChipherText using the private Key.</fsummary> <type> <v>ChipherText = binary()</v> - <v>PrivateKey = [E, N, D]</v> + <v>PrivateKey = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v> <v>E, N, D = Mpint</v> <d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and <c>D</c> is the private exponent.</d> + <v>P1, P2, E1, E2, C = Mpint</v> + <d>The longer key format contains redundant information that will make + the calculation faster. <c>P1,P2</c> are first and second prime factors. + <c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient. + Terminology is taken from RFC 3447.</d> <v>Padding = rsa_pkcs1_padding | rsa_pkcs1_oaep_padding | rsa_no_padding</v> <v>PlainText = binary()</v> </type> @@ -965,10 +1059,15 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> <fsummary>Encrypts Msg using the private Key.</fsummary> <type> <v>PlainText = binary()</v> - <v>PrivateKey = [E, N, D]</v> - <v>E, N, D = Mpint</v> + <v>PrivateKey = [E, N, D] | [E, N, D, P1, P2, E1, E2, C]</v> + <v>E, N, D = Mpint</v> <d>Where <c>E</c> is the public exponent, <c>N</c> is public modulus and <c>D</c> is the private exponent.</d> + <v>P1, P2, E1, E2, C = Mpint</v> + <d>The longer key format contains redundant information that will make + the calculation faster. <c>P1,P2</c> are first and second prime factors. + <c>E1,E2</c> are first and second exponents. <c>C</c> is the CRT coefficient. + Terminology is taken from RFC 3447.</d> <v>Padding = rsa_pkcs1_padding | rsa_no_padding</v> <v>ChipherText = binary()</v> </type> @@ -1007,45 +1106,52 @@ Mpint() = <![CDATA[<<ByteLen:32/integer-big, Bytes:ByteLen/binary>>]]> </func> <func> - <name>dss_sign(Data, Key) -> Signature</name> - <name>dss_sign(DigestType, Data, Key) -> Signature</name> + <name>dss_sign(DataOrDigest, Key) -> Signature</name> + <name>dss_sign(DigestType, DataOrDigest, Key) -> Signature</name> <fsummary>Sign the data using dsa with given private key.</fsummary> <type> - <v>DigestType = sha | none (default is sha)</v> - <v>Data = Mpint | ShaDigest</v> + <v>DigestType = sha</v> + <v>DataOrDigest = Mpint | {digest,Digest}</v> <v>Key = [P, Q, G, X]</v> <v>P, Q, G, X = Mpint</v> <d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss parameters and <c>X</c> is the private key.</d> - <v>ShaDigest = binary() with length 20 bytes</v> + <v>Digest = binary() with length 20 bytes</v> <v>Signature = binary()</v> </type> <desc> - <p>Creates a DSS signature with the private key <c>Key</c> of a digest. - If <c>DigestType</c> is 'sha', the digest is calculated as SHA1 of <c>Data</c>. - If <c>DigestType</c> is 'none', <c>Data</c> is the precalculated SHA1 digest.</p> + <p>Creates a DSS signature with the private key <c>Key</c> of + a digest. The digest is either calculated as a SHA1 + digest of <c>Data</c> or a precalculated binary <c>Digest</c>.</p> + <p>A deprecated feature is having <c>DigestType = 'none'</c> + in which case <c>DataOrDigest</c> is a precalculated SHA1 + digest.</p> </desc> </func> <func> - <name>dss_verify(Data, Signature, Key) -> Verified</name> - <name>dss_verify(DigestType, Data, Signature, Key) -> Verified</name> + <name>dss_verify(DataOrDigest, Signature, Key) -> Verified</name> + <name>dss_verify(DigestType, DataOrDigest, Signature, Key) -> Verified</name> <fsummary>Verify the data and signature using dsa with given public key.</fsummary> <type> <v>Verified = boolean()</v> - <v>DigestType = sha | none</v> + <v>DigestType = sha</v> + <v>DataOrDigest = Mpint | {digest,Digest}</v> <v>Data = Mpint | ShaDigest</v> <v>Signature = Mpint</v> <v>Key = [P, Q, G, Y]</v> <v>P, Q, G, Y = Mpint</v> <d> Where <c>P</c>, <c>Q</c> and <c>G</c> are the dss parameters and <c>Y</c> is the public key.</d> - <v>ShaDigest = binary() with length 20 bytes</v> + <v>Digest = binary() with length 20 bytes</v> </type> <desc> - <p>Verifies that a digest matches the DSS signature using the public key <c>Key</c>. - If <c>DigestType</c> is 'sha', the digest is calculated as SHA1 of <c>Data</c>. - If <c>DigestType</c> is 'none', <c>Data</c> is the precalculated SHA1 digest.</p> + <p>Verifies that a digest matches the DSS signature using the + public key <c>Key</c>. The digest is either calculated as a SHA1 + digest of <c>Data</c> or is a precalculated binary <c>Digest</c>.</p> + <p>A deprecated feature is having <c>DigestType = 'none'</c> + in which case <c>DataOrDigest</c> is a precalculated SHA1 + digest binary.</p> </desc> </func> diff --git a/lib/crypto/src/Makefile b/lib/crypto/src/Makefile index 0e886ce8bf..ddafb9168f 100644 --- a/lib/crypto/src/Makefile +++ b/lib/crypto/src/Makefile @@ -83,11 +83,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) \ - $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(APPUP_TARGET) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index d7aac27825..0089e79a4f 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,12 +22,19 @@ -module(crypto). -export([start/0, stop/0, info/0, info_lib/0, version/0]). +-export([hash/2, hash_init/1, hash_update/2, hash_final/1]). -export([md4/1, md4_init/0, md4_update/2, md4_final/1]). -export([md5/1, md5_init/0, md5_update/2, md5_final/1]). -export([sha/1, sha_init/0, sha_update/2, sha_final/1]). +-export([sha224/1, sha224_init/0, sha224_update/2, sha224_final/1]). -export([sha256/1, sha256_init/0, sha256_update/2, sha256_final/1]). +-export([sha384/1, sha384_init/0, sha384_update/2, sha384_final/1]). -export([sha512/1, sha512_init/0, sha512_update/2, sha512_final/1]). -export([md5_mac/2, md5_mac_96/2, sha_mac/2, sha_mac/3, sha_mac_96/2]). +-export([sha224_mac/2, sha224_mac/3]). +-export([sha256_mac/2, sha256_mac/3]). +-export([sha384_mac/2, sha384_mac/3]). +-export([sha512_mac/2, sha512_mac/3]). -export([hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]). -export([des_cbc_encrypt/3, des_cbc_decrypt/3, des_cbc_ivec/1]). -export([des_ecb_encrypt/2, des_ecb_decrypt/2]). @@ -64,10 +71,13 @@ -define(FUNC_LIST, [md4, md4_init, md4_update, md4_final, md5, md5_init, md5_update, md5_final, sha, sha_init, sha_update, sha_final, - sha256, sha256_init, sha256_update, sha256_final, - sha512, sha512_init, sha512_update, sha512_final, + sha224, sha224_init, sha224_update, sha224_final, + sha256, sha256_init, sha256_update, sha256_final, + sha384, sha384_init, sha384_update, sha384_final, + sha512, sha512_init, sha512_update, sha512_final, md5_mac, md5_mac_96, sha_mac, sha_mac_96, + sha224_mac, sha256_mac, sha384_mac, sha512_mac, sha_mac_init, sha_mac_update, sha_mac_final, des_cbc_encrypt, des_cbc_decrypt, des_cfb_encrypt, des_cfb_decrypt, @@ -95,8 +105,9 @@ aes_ctr_stream_init, aes_ctr_stream_encrypt, aes_ctr_stream_decrypt, info_lib]). --type rsa_digest_type() :: 'md5' | 'sha' | 'sha256' | 'sha384' | 'sha512'. +-type rsa_digest_type() :: 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512'. -type dss_digest_type() :: 'none' | 'sha'. +-type data_or_digest() :: binary() | {digest, binary()}. -type crypto_integer() :: binary() | integer(). -define(nif_stub,nif_stub_error(?LINE)). @@ -171,7 +182,7 @@ info_lib() -> ?nif_stub. %% (no version): Driver implementation %% 2.0 : NIF implementation, requires OTP R14 version() -> ?CRYPTO_VSN. - + %% Below Key and Data are binaries or IO-lists. IVec is a binary. %% Output is always a binary. Context is a binary. @@ -179,6 +190,45 @@ version() -> ?CRYPTO_VSN. %% MESSAGE DIGESTS %% +-spec hash(_, iodata()) -> binary(). +hash(md5, Data) -> md5(Data); +hash(md4, Data) -> md4(Data); +hash(sha, Data) -> sha(Data); +hash(sha224, Data) -> sha224(Data); +hash(sha256, Data) -> sha256(Data); +hash(sha384, Data) -> sha384(Data); +hash(sha512, Data) -> sha512(Data). + +-spec hash_init('md5'|'md4'|'sha'|'sha224'|'sha256'|'sha384'|'sha512') -> any(). + +hash_init(md5) -> {md5, md5_init()}; +hash_init(md4) -> {md4, md4_init()}; +hash_init(sha) -> {sha, sha_init()}; +hash_init(sha224) -> {sha224, sha224_init()}; +hash_init(sha256) -> {sha256, sha256_init()}; +hash_init(sha384) -> {sha384, sha384_init()}; +hash_init(sha512) -> {sha512, sha512_init()}. + +-spec hash_update(_, iodata()) -> any(). + +hash_update({md5,Context}, Data) -> {md5, md5_update(Context,Data)}; +hash_update({md4,Context}, Data) -> {md4, md4_update(Context,Data)}; +hash_update({sha,Context}, Data) -> {sha, sha_update(Context,Data)}; +hash_update({sha224,Context}, Data) -> {sha224, sha224_update(Context,Data)}; +hash_update({sha256,Context}, Data) -> {sha256, sha256_update(Context,Data)}; +hash_update({sha384,Context}, Data) -> {sha384, sha384_update(Context,Data)}; +hash_update({sha512,Context}, Data) -> {sha512, sha512_update(Context,Data)}. + +-spec hash_final(_) -> binary(). + +hash_final({md5,Context}) -> md5_final(Context); +hash_final({md4,Context}) -> md4_final(Context); +hash_final({sha,Context}) -> sha_final(Context); +hash_final({sha224,Context}) -> sha224_final(Context); +hash_final({sha256,Context}) -> sha256_final(Context); +hash_final({sha384,Context}) -> sha384_final(Context); +hash_final({sha512,Context}) -> sha512_final(Context). + %% %% MD5 %% @@ -220,6 +270,40 @@ sha_update(_Context, _Data) -> ?nif_stub. sha_final(_Context) -> ?nif_stub. % +%% SHA224 +%% +-spec sha224(iodata()) -> binary(). +-spec sha224_init() -> binary(). +-spec sha224_update(binary(), iodata()) -> binary(). +-spec sha224_final(binary()) -> binary(). + +sha224(Data) -> + case sha224_nif(Data) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha224_init() -> + case sha224_init_nif() of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha224_update(Context, Data) -> + case sha224_update_nif(Context, Data) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha224_final(Context) -> + case sha224_final_nif(Context) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha224_nif(_Data) -> ?nif_stub. +sha224_init_nif() -> ?nif_stub. +sha224_update_nif(_Context, _Data) -> ?nif_stub. +sha224_final_nif(_Context) -> ?nif_stub. + +% %% SHA256 %% -spec sha256(iodata()) -> binary(). @@ -254,6 +338,40 @@ sha256_update_nif(_Context, _Data) -> ?nif_stub. sha256_final_nif(_Context) -> ?nif_stub. % +%% SHA384 +%% +-spec sha384(iodata()) -> binary(). +-spec sha384_init() -> binary(). +-spec sha384_update(binary(), iodata()) -> binary(). +-spec sha384_final(binary()) -> binary(). + +sha384(Data) -> + case sha384_nif(Data) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha384_init() -> + case sha384_init_nif() of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha384_update(Context, Data) -> + case sha384_update_nif(Context, Data) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. +sha384_final(Context) -> + case sha384_final_nif(Context) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha384_nif(_Data) -> ?nif_stub. +sha384_init_nif() -> ?nif_stub. +sha384_update_nif(_Context, _Data) -> ?nif_stub. +sha384_final_nif(_Context) -> ?nif_stub. + +% %% SHA512 %% -spec sha512(iodata()) -> binary(). @@ -336,6 +454,70 @@ sha_mac_96(Key, Data) -> sha_mac_n(_Key,_Data,_MacSz) -> ?nif_stub. %% +%% SHA224_MAC +%% +-spec sha224_mac(iodata(), iodata()) -> binary(). + +sha224_mac(Key, Data) -> + sha224_mac(Key, Data, 224 div 8). + +sha224_mac(Key, Data, Size) -> + case sha224_mac_nif(Key, Data, Size) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha224_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub. + +%% +%% SHA256_MAC +%% +-spec sha256_mac(iodata(), iodata()) -> binary(). + +sha256_mac(Key, Data) -> + sha256_mac(Key, Data, 256 div 8). + +sha256_mac(Key, Data, Size) -> + case sha256_mac_nif(Key, Data, Size) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha256_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub. + +%% +%% SHA384_MAC +%% +-spec sha384_mac(iodata(), iodata()) -> binary(). + +sha384_mac(Key, Data) -> + sha384_mac(Key, Data, 384 div 8). + +sha384_mac(Key, Data, Size) -> + case sha384_mac_nif(Key, Data, Size) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha384_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub. + +%% +%% SHA512_MAC +%% +-spec sha512_mac(iodata(), iodata()) -> binary(). + +sha512_mac(Key, Data) -> + sha512_mac(Key, Data, 512 div 8). + +sha512_mac(Key, Data, MacSz) -> + case sha512_mac_nif(Key, Data, MacSz) of + notsup -> erlang:error(notsup); + Bin -> Bin + end. + +sha512_mac_nif(_Key,_Data,_MacSz) -> ?nif_stub. + +%% %% CRYPTO FUNCTIONS %% @@ -576,10 +758,10 @@ mod_exp_nif(_Base,_Exp,_Mod) -> ?nif_stub. %% %% DSS, RSA - verify %% --spec dss_verify(binary(), binary(), [binary()]) -> boolean(). --spec dss_verify(dss_digest_type(), binary(), binary(), [binary()]) -> boolean(). --spec rsa_verify(binary(), binary(), [binary()]) -> boolean(). --spec rsa_verify(rsa_digest_type(), binary(), binary(), [binary()]) -> +-spec dss_verify(data_or_digest(), binary(), [binary()]) -> boolean(). +-spec dss_verify(dss_digest_type(), data_or_digest(), binary(), [binary()]) -> boolean(). +-spec rsa_verify(data_or_digest(), binary(), [binary()]) -> boolean(). +-spec rsa_verify(rsa_digest_type(), data_or_digest(), binary(), [binary()]) -> boolean(). %% Key = [P,Q,G,Y] P,Q,G=DSSParams Y=PublicKey @@ -590,8 +772,8 @@ dss_verify(_Type,_Data,_Signature,_Key) -> ?nif_stub. % Key = [E,N] E=PublicExponent N=PublicModulus rsa_verify(Data,Signature,Key) -> rsa_verify_nif(sha, Data,Signature,Key). -rsa_verify(Type, Data, Signature, Key) -> - case rsa_verify_nif(Type, Data, Signature, Key) of +rsa_verify(Type, DataOrDigest, Signature, Key) -> + case rsa_verify_nif(Type, DataOrDigest, Signature, Key) of notsup -> erlang:error(notsup); Bool -> Bool end. @@ -603,27 +785,27 @@ rsa_verify_nif(_Type, _Data, _Signature, _Key) -> ?nif_stub. %% DSS, RSA - sign %% %% Key = [P,Q,G,X] P,Q,G=DSSParams X=PrivateKey --spec dss_sign(binary(), [binary()]) -> binary(). --spec dss_sign(dss_digest_type(), binary(), [binary()]) -> binary(). --spec rsa_sign(binary(), [binary()]) -> binary(). --spec rsa_sign(rsa_digest_type(), binary(), [binary()]) -> binary(). - -dss_sign(Data,Key) -> - dss_sign(sha,Data,Key). -dss_sign(Type, Data, Key) -> - case dss_sign_nif(Type,Data,Key) of - error -> erlang:error(badkey, [Data, Key]); +-spec dss_sign(data_or_digest(), [binary()]) -> binary(). +-spec dss_sign(dss_digest_type(), data_or_digest(), [binary()]) -> binary(). +-spec rsa_sign(data_or_digest(), [binary()]) -> binary(). +-spec rsa_sign(rsa_digest_type(), data_or_digest(), [binary()]) -> binary(). + +dss_sign(DataOrDigest,Key) -> + dss_sign(sha,DataOrDigest,Key). +dss_sign(Type, DataOrDigest, Key) -> + case dss_sign_nif(Type,DataOrDigest,Key) of + error -> erlang:error(badkey, [DataOrDigest, Key]); Sign -> Sign end. dss_sign_nif(_Type,_Data,_Key) -> ?nif_stub. %% Key = [E,N,D] E=PublicExponent N=PublicModulus D=PrivateExponent -rsa_sign(Data,Key) -> - rsa_sign(sha, Data, Key). -rsa_sign(Type, Data, Key) -> - case rsa_sign_nif(Type,Data,Key) of - error -> erlang:error(badkey, [Type,Data,Key]); +rsa_sign(DataOrDigest,Key) -> + rsa_sign(sha, DataOrDigest, Key). +rsa_sign(Type, DataOrDigest, Key) -> + case rsa_sign_nif(Type,DataOrDigest,Key) of + error -> erlang:error(badkey, [Type,DataOrDigest,Key]); Sign -> Sign end. diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile index 3150bd472d..ec8136b455 100644 --- a/lib/crypto/test/Makefile +++ b/lib/crypto/test/Makefile @@ -75,9 +75,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: $(TEST_TARGET) - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 627c966dfb..1b5bc44dde 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -33,9 +33,12 @@ sha_update/1, hmac_update_sha/1, hmac_update_sha_n/1, + hmac_update_sha256/1, + hmac_update_sha512/1, hmac_update_md5/1, hmac_update_md5_io/1, hmac_update_md5_n/1, + hmac_rfc4231/1, sha256/1, sha256_update/1, sha512/1, @@ -61,7 +64,9 @@ rsa_verify_test/1, dsa_verify_test/1, rsa_sign_test/1, + rsa_sign_hash_test/1, dsa_sign_test/1, + dsa_sign_hash_test/1, rsa_encrypt_decrypt/1, dh/1, exor_test/1, @@ -82,13 +87,15 @@ groups() -> {rest, [], [md5, md5_update, md4, md4_update, md5_mac, md5_mac_io, sha, sha_update, - hmac_update_sha, hmac_update_sha_n, hmac_update_md5_n, - hmac_update_md5_io, hmac_update_md5, + hmac_update_sha, hmac_update_sha_n, hmac_update_sha256, hmac_update_sha512, + hmac_update_md5_n, hmac_update_md5_io, hmac_update_md5, + hmac_rfc4231, des_cbc, aes_cfb, aes_cbc, aes_cbc_iter, aes_ctr, aes_ctr_stream, des_cbc_iter, des_ecb, rand_uniform_test, strong_rand_test, rsa_verify_test, dsa_verify_test, rsa_sign_test, - dsa_sign_test, rsa_encrypt_decrypt, dh, exor_test, + rsa_sign_hash_test, dsa_sign_test, dsa_sign_hash_test, + rsa_encrypt_decrypt, dh, exor_test, rc4_test, rc4_stream_test, mod_exp_test, blowfish_cfb64, smp]}]. @@ -335,6 +342,44 @@ hmac_update_sha(Config) when is_list(Config) -> ?line Mac = crypto:hmac_final(Ctx3), ?line Exp = crypto:sha_mac(Key, lists:flatten([Data, Data2])), ?line m(Exp, Mac). + +hmac_update_sha256(doc) -> + ["Generate an SHA256 HMAC using hmac_init, hmac_update, and hmac_final. " + "Expected values for examples are generated using crypto:sha256_mac." ]; +hmac_update_sha256(suite) -> + []; +hmac_update_sha256(Config) when is_list(Config) -> + ?line Key = hexstr2bin("00010203101112132021222330313233" + "04050607141516172425262734353637" + "08090a0b18191a1b28292a2b38393a3b" + "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"), + ?line Data = "Sampl", + ?line Data2 = "e #1", + ?line Ctx = crypto:hmac_init(sha256, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), + ?line Mac = crypto:hmac_final(Ctx3), + ?line Exp = crypto:sha256_mac(Key, lists:flatten([Data, Data2])), + ?line m(Exp, Mac). + +hmac_update_sha512(doc) -> + ["Generate an SHA512 HMAC using hmac_init, hmac_update, and hmac_final. " + "Expected values for examples are generated using crypto:sha512_mac." ]; +hmac_update_sha512(suite) -> + []; +hmac_update_sha512(Config) when is_list(Config) -> + ?line Key = hexstr2bin("00010203101112132021222330313233" + "04050607141516172425262734353637" + "08090a0b18191a1b28292a2b38393a3b" + "0c0d0e0f1c1d1e1f2c2d2e2f3c3d3e3f"), + ?line Data = "Sampl", + ?line Data2 = "e #1", + ?line Ctx = crypto:hmac_init(sha512, Key), + ?line Ctx2 = crypto:hmac_update(Ctx, Data), + ?line Ctx3 = crypto:hmac_update(Ctx2, Data2), + ?line Mac = crypto:hmac_final(Ctx3), + ?line Exp = crypto:sha512_mac(Key, lists:flatten([Data, Data2])), + ?line m(Exp, Mac). hmac_update_md5(doc) -> ["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. " @@ -354,7 +399,272 @@ hmac_update_md5(Config) when is_list(Config) -> ?line Mac2 = crypto:hmac_final(CtxD), ?line Exp2 = crypto:md5_mac(Key2, lists:flatten([Long1, Long2, Long3])), ?line m(Exp2, Mac2). + +hmac_rfc4231(doc) -> + ["Generate an HMAC using crypto:shaXXX_mac and hmac_init, hmac_update, and hmac_final. " + "Testvectors are take from RFC4231." ]; +hmac_rfc4231(suite) -> + []; +hmac_rfc4231(Config) when is_list(Config) -> + %% Test Case 1 + Case1Key = binary:copy(<<16#0b>>, 20), + Case1Data = <<"Hi There">>, + Case1Exp224 = hexstr2bin("896fb1128abbdf196832107cd49df33f" + "47b4b1169912ba4f53684b22"), + Case1Exp256 = hexstr2bin("b0344c61d8db38535ca8afceaf0bf12b" + "881dc200c9833da726e9376c2e32cff7"), + Case1Exp384 = hexstr2bin("afd03944d84895626b0825f4ab46907f" + "15f9dadbe4101ec682aa034c7cebc59c" + "faea9ea9076ede7f4af152e8b2fa9cb6"), + Case1Exp512 = hexstr2bin("87aa7cdea5ef619d4ff0b4241a1d6cb0" + "2379f4e2ce4ec2787ad0b30545e17cde" + "daa833b7d6b8a702038b274eaea3f4e4" + "be9d914eeb61f1702e696c203a126854"), + + ?line Case1Ctx224 = crypto:hmac_init(sha224, Case1Key), + ?line Case1Ctx224_2 = crypto:hmac_update(Case1Ctx224, Case1Data), + ?line Case1Mac224_1 = crypto:hmac_final(Case1Ctx224_2), + ?line Case1Mac224_2 = crypto:sha224_mac(Case1Key, Case1Data), + ?line m(Case1Exp224, Case1Mac224_1), + ?line m(Case1Exp224, Case1Mac224_2), + + ?line Case1Ctx256 = crypto:hmac_init(sha256, Case1Key), + ?line Case1Ctx256_2 = crypto:hmac_update(Case1Ctx256, Case1Data), + ?line Case1Mac256_1 = crypto:hmac_final(Case1Ctx256_2), + ?line Case1Mac256_2 = crypto:sha256_mac(Case1Key, Case1Data), + ?line m(Case1Exp256, Case1Mac256_1), + ?line m(Case1Exp256, Case1Mac256_2), + + ?line Case1Ctx384 = crypto:hmac_init(sha384, Case1Key), + ?line Case1Ctx384_2 = crypto:hmac_update(Case1Ctx384, Case1Data), + ?line Case1Mac384_1 = crypto:hmac_final(Case1Ctx384_2), + ?line Case1Mac384_2 = crypto:sha384_mac(Case1Key, Case1Data), + ?line m(Case1Exp384, Case1Mac384_1), + ?line m(Case1Exp384, Case1Mac384_2), + + ?line Case1Ctx512 = crypto:hmac_init(sha512, Case1Key), + ?line Case1Ctx512_2 = crypto:hmac_update(Case1Ctx512, Case1Data), + ?line Case1Mac512_1 = crypto:hmac_final(Case1Ctx512_2), + ?line Case1Mac512_2 = crypto:sha512_mac(Case1Key, Case1Data), + ?line m(Case1Exp512, Case1Mac512_1), + ?line m(Case1Exp512, Case1Mac512_2), + + %% Test Case 2 + Case2Key = <<"Jefe">>, + Case2Data = <<"what do ya want for nothing?">>, + Case2Exp224 = hexstr2bin("a30e01098bc6dbbf45690f3a7e9e6d0f" + "8bbea2a39e6148008fd05e44"), + Case2Exp256 = hexstr2bin("5bdcc146bf60754e6a042426089575c7" + "5a003f089d2739839dec58b964ec3843"), + Case2Exp384 = hexstr2bin("af45d2e376484031617f78d2b58a6b1b" + "9c7ef464f5a01b47e42ec3736322445e" + "8e2240ca5e69e2c78b3239ecfab21649"), + Case2Exp512 = hexstr2bin("164b7a7bfcf819e2e395fbe73b56e0a3" + "87bd64222e831fd610270cd7ea250554" + "9758bf75c05a994a6d034f65f8f0e6fd" + "caeab1a34d4a6b4b636e070a38bce737"), + + ?line Case2Ctx224 = crypto:hmac_init(sha224, Case2Key), + ?line Case2Ctx224_2 = crypto:hmac_update(Case2Ctx224, Case2Data), + ?line Case2Mac224_1 = crypto:hmac_final(Case2Ctx224_2), + ?line Case2Mac224_2 = crypto:sha224_mac(Case2Key, Case2Data), + ?line m(Case2Exp224, Case2Mac224_1), + ?line m(Case2Exp224, Case2Mac224_2), + + ?line Case2Ctx256 = crypto:hmac_init(sha256, Case2Key), + ?line Case2Ctx256_2 = crypto:hmac_update(Case2Ctx256, Case2Data), + ?line Case2Mac256_1 = crypto:hmac_final(Case2Ctx256_2), + ?line Case2Mac256_2 = crypto:sha256_mac(Case2Key, Case2Data), + ?line m(Case2Exp256, Case2Mac256_1), + ?line m(Case2Exp256, Case2Mac256_2), + + ?line Case2Ctx384 = crypto:hmac_init(sha384, Case2Key), + ?line Case2Ctx384_2 = crypto:hmac_update(Case2Ctx384, Case2Data), + ?line Case2Mac384_1 = crypto:hmac_final(Case2Ctx384_2), + ?line Case2Mac384_2 = crypto:sha384_mac(Case2Key, Case2Data), + ?line m(Case2Exp384, Case2Mac384_1), + ?line m(Case2Exp384, Case2Mac384_2), + + ?line Case2Ctx512 = crypto:hmac_init(sha512, Case2Key), + ?line Case2Ctx512_2 = crypto:hmac_update(Case2Ctx512, Case2Data), + ?line Case2Mac512_1 = crypto:hmac_final(Case2Ctx512_2), + ?line Case2Mac512_2 = crypto:sha512_mac(Case2Key, Case2Data), + ?line m(Case2Exp512, Case2Mac512_1), + ?line m(Case2Exp512, Case2Mac512_2), + + %% Test Case 3 + Case3Key = binary:copy(<<16#aa>>, 20), + Case3Data = binary:copy(<<16#dd>>, 50), + Case3Exp224 = hexstr2bin("7fb3cb3588c6c1f6ffa9694d7d6ad264" + "9365b0c1f65d69d1ec8333ea"), + Case3Exp256 = hexstr2bin("773ea91e36800e46854db8ebd09181a7" + "2959098b3ef8c122d9635514ced565fe"), + Case3Exp384 = hexstr2bin("88062608d3e6ad8a0aa2ace014c8a86f" + "0aa635d947ac9febe83ef4e55966144b" + "2a5ab39dc13814b94e3ab6e101a34f27"), + Case3Exp512 = hexstr2bin("fa73b0089d56a284efb0f0756c890be9" + "b1b5dbdd8ee81a3655f83e33b2279d39" + "bf3e848279a722c806b485a47e67c807" + "b946a337bee8942674278859e13292fb"), + + ?line Case3Ctx224 = crypto:hmac_init(sha224, Case3Key), + ?line Case3Ctx224_2 = crypto:hmac_update(Case3Ctx224, Case3Data), + ?line Case3Mac224_1 = crypto:hmac_final(Case3Ctx224_2), + ?line Case3Mac224_2 = crypto:sha224_mac(Case3Key, Case3Data), + ?line m(Case3Exp224, Case3Mac224_1), + ?line m(Case3Exp224, Case3Mac224_2), + + ?line Case3Ctx256 = crypto:hmac_init(sha256, Case3Key), + ?line Case3Ctx256_2 = crypto:hmac_update(Case3Ctx256, Case3Data), + ?line Case3Mac256_1 = crypto:hmac_final(Case3Ctx256_2), + ?line Case3Mac256_2 = crypto:sha256_mac(Case3Key, Case3Data), + ?line m(Case3Exp256, Case3Mac256_1), + ?line m(Case3Exp256, Case3Mac256_2), + + ?line Case3Ctx384 = crypto:hmac_init(sha384, Case3Key), + ?line Case3Ctx384_2 = crypto:hmac_update(Case3Ctx384, Case3Data), + ?line Case3Mac384_1 = crypto:hmac_final(Case3Ctx384_2), + ?line Case3Mac384_2 = crypto:sha384_mac(Case3Key, Case3Data), + ?line m(Case3Exp384, Case3Mac384_1), + ?line m(Case3Exp384, Case3Mac384_2), + + ?line Case3Ctx512 = crypto:hmac_init(sha512, Case3Key), + ?line Case3Ctx512_2 = crypto:hmac_update(Case3Ctx512, Case3Data), + ?line Case3Mac512_1 = crypto:hmac_final(Case3Ctx512_2), + ?line Case3Mac512_2 = crypto:sha512_mac(Case3Key, Case3Data), + ?line m(Case3Exp512, Case3Mac512_1), + ?line m(Case3Exp512, Case3Mac512_2), + + %% Test Case 4 + Case4Key = list_to_binary(lists:seq(1, 16#19)), + Case4Data = binary:copy(<<16#cd>>, 50), + Case4Exp224 = hexstr2bin("6c11506874013cac6a2abc1bb382627c" + "ec6a90d86efc012de7afec5a"), + Case4Exp256 = hexstr2bin("82558a389a443c0ea4cc819899f2083a" + "85f0faa3e578f8077a2e3ff46729665b"), + Case4Exp384 = hexstr2bin("3e8a69b7783c25851933ab6290af6ca7" + "7a9981480850009cc5577c6e1f573b4e" + "6801dd23c4a7d679ccf8a386c674cffb"), + Case4Exp512 = hexstr2bin("b0ba465637458c6990e5a8c5f61d4af7" + "e576d97ff94b872de76f8050361ee3db" + "a91ca5c11aa25eb4d679275cc5788063" + "a5f19741120c4f2de2adebeb10a298dd"), + + ?line Case4Ctx224 = crypto:hmac_init(sha224, Case4Key), + ?line Case4Ctx224_2 = crypto:hmac_update(Case4Ctx224, Case4Data), + ?line Case4Mac224_1 = crypto:hmac_final(Case4Ctx224_2), + ?line Case4Mac224_2 = crypto:sha224_mac(Case4Key, Case4Data), + ?line m(Case4Exp224, Case4Mac224_1), + ?line m(Case4Exp224, Case4Mac224_2), + + ?line Case4Ctx256 = crypto:hmac_init(sha256, Case4Key), + ?line Case4Ctx256_2 = crypto:hmac_update(Case4Ctx256, Case4Data), + ?line Case4Mac256_1 = crypto:hmac_final(Case4Ctx256_2), + ?line Case4Mac256_2 = crypto:sha256_mac(Case4Key, Case4Data), + ?line m(Case4Exp256, Case4Mac256_1), + ?line m(Case4Exp256, Case4Mac256_2), + + ?line Case4Ctx384 = crypto:hmac_init(sha384, Case4Key), + ?line Case4Ctx384_2 = crypto:hmac_update(Case4Ctx384, Case4Data), + ?line Case4Mac384_1 = crypto:hmac_final(Case4Ctx384_2), + ?line Case4Mac384_2 = crypto:sha384_mac(Case4Key, Case4Data), + ?line m(Case4Exp384, Case4Mac384_1), + ?line m(Case4Exp384, Case4Mac384_2), + + ?line Case4Ctx512 = crypto:hmac_init(sha512, Case4Key), + ?line Case4Ctx512_2 = crypto:hmac_update(Case4Ctx512, Case4Data), + ?line Case4Mac512_1 = crypto:hmac_final(Case4Ctx512_2), + ?line Case4Mac512_2 = crypto:sha512_mac(Case4Key, Case4Data), + ?line m(Case4Exp512, Case4Mac512_1), + ?line m(Case4Exp512, Case4Mac512_2), + + %% Test Case 6 + Case6Key = binary:copy(<<16#aa>>, 131), + Case6Data = <<"Test Using Larger Than Block-Size Key - Hash Key First">>, + Case6Exp224 = hexstr2bin("95e9a0db962095adaebe9b2d6f0dbce2" + "d499f112f2d2b7273fa6870e"), + Case6Exp256 = hexstr2bin("60e431591ee0b67f0d8a26aacbf5b77f" + "8e0bc6213728c5140546040f0ee37f54"), + Case6Exp384 = hexstr2bin("4ece084485813e9088d2c63a041bc5b4" + "4f9ef1012a2b588f3cd11f05033ac4c6" + "0c2ef6ab4030fe8296248df163f44952"), + Case6Exp512 = hexstr2bin("80b24263c7c1a3ebb71493c1dd7be8b4" + "9b46d1f41b4aeec1121b013783f8f352" + "6b56d037e05f2598bd0fd2215d6a1e52" + "95e64f73f63f0aec8b915a985d786598"), + + ?line Case6Ctx224 = crypto:hmac_init(sha224, Case6Key), + ?line Case6Ctx224_2 = crypto:hmac_update(Case6Ctx224, Case6Data), + ?line Case6Mac224_1 = crypto:hmac_final(Case6Ctx224_2), + ?line Case6Mac224_2 = crypto:sha224_mac(Case6Key, Case6Data), + ?line m(Case6Exp224, Case6Mac224_1), + ?line m(Case6Exp224, Case6Mac224_2), + + ?line Case6Ctx256 = crypto:hmac_init(sha256, Case6Key), + ?line Case6Ctx256_2 = crypto:hmac_update(Case6Ctx256, Case6Data), + ?line Case6Mac256_1 = crypto:hmac_final(Case6Ctx256_2), + ?line Case6Mac256_2 = crypto:sha256_mac(Case6Key, Case6Data), + ?line m(Case6Exp256, Case6Mac256_1), + ?line m(Case6Exp256, Case6Mac256_2), + + ?line Case6Ctx384 = crypto:hmac_init(sha384, Case6Key), + ?line Case6Ctx384_2 = crypto:hmac_update(Case6Ctx384, Case6Data), + ?line Case6Mac384_1 = crypto:hmac_final(Case6Ctx384_2), + ?line Case6Mac384_2 = crypto:sha384_mac(Case6Key, Case6Data), + ?line m(Case6Exp384, Case6Mac384_1), + ?line m(Case6Exp384, Case6Mac384_2), + + ?line Case6Ctx512 = crypto:hmac_init(sha512, Case6Key), + ?line Case6Ctx512_2 = crypto:hmac_update(Case6Ctx512, Case6Data), + ?line Case6Mac512_1 = crypto:hmac_final(Case6Ctx512_2), + ?line Case6Mac512_2 = crypto:sha512_mac(Case6Key, Case6Data), + ?line m(Case6Exp512, Case6Mac512_1), + ?line m(Case6Exp512, Case6Mac512_2), + %% Test Case 7 + Case7Key = binary:copy(<<16#aa>>, 131), + Case7Data = <<"This is a test using a larger than block-size key and a larger t", + "han block-size data. The key needs to be hashed before being use", + "d by the HMAC algorithm.">>, + Case7Exp224 = hexstr2bin("3a854166ac5d9f023f54d517d0b39dbd" + "946770db9c2b95c9f6f565d1"), + Case7Exp256 = hexstr2bin("9b09ffa71b942fcb27635fbcd5b0e944" + "bfdc63644f0713938a7f51535c3a35e2"), + Case7Exp384 = hexstr2bin("6617178e941f020d351e2f254e8fd32c" + "602420feb0b8fb9adccebb82461e99c5" + "a678cc31e799176d3860e6110c46523e"), + Case7Exp512 = hexstr2bin("e37b6a775dc87dbaa4dfa9f96e5e3ffd" + "debd71f8867289865df5a32d20cdc944" + "b6022cac3c4982b10d5eeb55c3e4de15" + "134676fb6de0446065c97440fa8c6a58"), + + ?line Case7Ctx224 = crypto:hmac_init(sha224, Case7Key), + ?line Case7Ctx224_2 = crypto:hmac_update(Case7Ctx224, Case7Data), + ?line Case7Mac224_1 = crypto:hmac_final(Case7Ctx224_2), + ?line Case7Mac224_2 = crypto:sha224_mac(Case7Key, Case7Data), + ?line m(Case7Exp224, Case7Mac224_1), + ?line m(Case7Exp224, Case7Mac224_2), + + ?line Case7Ctx256 = crypto:hmac_init(sha256, Case7Key), + ?line Case7Ctx256_2 = crypto:hmac_update(Case7Ctx256, Case7Data), + ?line Case7Mac256_1 = crypto:hmac_final(Case7Ctx256_2), + ?line Case7Mac256_2 = crypto:sha256_mac(Case7Key, Case7Data), + ?line m(Case7Exp256, Case7Mac256_1), + ?line m(Case7Exp256, Case7Mac256_2), + + ?line Case7Ctx384 = crypto:hmac_init(sha384, Case7Key), + ?line Case7Ctx384_2 = crypto:hmac_update(Case7Ctx384, Case7Data), + ?line Case7Mac384_1 = crypto:hmac_final(Case7Ctx384_2), + ?line Case7Mac384_2 = crypto:sha384_mac(Case7Key, Case7Data), + ?line m(Case7Exp384, Case7Mac384_1), + ?line m(Case7Exp384, Case7Mac384_2), + + ?line Case7Ctx512 = crypto:hmac_init(sha512, Case7Key), + ?line Case7Ctx512_2 = crypto:hmac_update(Case7Ctx512, Case7Data), + ?line Case7Mac512_1 = crypto:hmac_final(Case7Ctx512_2), + ?line Case7Mac512_2 = crypto:sha512_mac(Case7Key, Case7Data), + ?line m(Case7Exp512, Case7Mac512_1), + ?line m(Case7Exp512, Case7Mac512_2). hmac_update_md5_io(doc) -> ["Generate an MD5 HMAC using hmac_init, hmac_update, and hmac_final. " @@ -717,10 +1027,19 @@ aes_cfb(Config) when is_list(Config) -> ?line Key = hexstr2bin("2b7e151628aed2a6abf7158809cf4f3c"), ?line IVec = hexstr2bin("000102030405060708090a0b0c0d0e0f"), ?line Plain = hexstr2bin("6bc1bee22e409f96e93d7e117393172a"), - ?line Cipher = crypto:aes_cfb_128_encrypt(Key, IVec, Plain), - ?line m(Cipher, hexstr2bin("3b3fd92eb72dad20333449f8e83cfb4a")), - ?line m(Plain, - crypto:aes_cfb_128_decrypt(Key, IVec, Cipher)). + ?line Cipher = hexstr2bin("3b3fd92eb72dad20333449f8e83cfb4a"), + + %% Try all prefixes of plain and cipher. + aes_cfb_do(byte_size(Plain), Plain, Cipher, Key, IVec). + +aes_cfb_do(N, Plain, Cipher, Key, IVec) when N >= 0 -> + <<P:N/binary, _/binary>> = Plain, + <<C:N/binary, _/binary>> = Cipher, + ?line C = crypto:aes_cfb_128_encrypt(Key, IVec, P), + ?line P = crypto:aes_cfb_128_decrypt(Key, IVec, C), + aes_cfb_do(N-1, Plain, Cipher, Key, IVec); +aes_cfb_do(_, _, _, _, _) -> ok. + %% %% @@ -1207,6 +1526,33 @@ rsa_sign_test(Config) when is_list(Config) -> ok. +rsa_sign_hash_test(doc) -> + "rsa_sign_hash testing"; +rsa_sign_hash_test(suite) -> + []; +rsa_sign_hash_test(Config) when is_list(Config) -> + PubEx = 65537, + PrivEx = 7531712708607620783801185371644749935066152052780368689827275932079815492940396744378735701395659435842364793962992309884847527234216715366607660219930945, + Mod = 7919488123861148172698919999061127847747888703039837999377650217570191053151807772962118671509138346758471459464133273114654252861270845708312601272799123, + Msg = <<"7896345786348756234 Hejsan Svejsan, erlang crypto debugger" + "09812312908312378623487263487623412039812 huagasd">>, + + PrivKey = [crypto:mpint(PubEx), crypto:mpint(Mod), crypto:mpint(PrivEx)], + PubKey = [crypto:mpint(PubEx), crypto:mpint(Mod)], + MD5 = crypto:md5(sized_binary(Msg)), + SHA = crypto:sha(sized_binary(Msg)), + ?line Sig1 = crypto:rsa_sign(sha, {digest,SHA}, PrivKey), + ?line m(crypto:rsa_verify(sha, {digest,SHA}, sized_binary(Sig1),PubKey), true), + + ?line Sig2 = crypto:rsa_sign(md5, {digest,MD5}, PrivKey), + ?line m(crypto:rsa_verify(md5, {digest,MD5}, sized_binary(Sig2),PubKey), true), + + ?line m(Sig1 =:= Sig2, false), + ?line m(crypto:rsa_verify(md5, {digest,MD5}, sized_binary(Sig1),PubKey), false), + ?line m(crypto:rsa_verify(sha, {digest,SHA}, sized_binary(Sig2),PubKey), false), + + ok. + dsa_sign_test(doc) -> "dsa_sign testing"; dsa_sign_test(suite) -> @@ -1237,6 +1583,37 @@ dsa_sign_test(Config) when is_list(Config) -> ok. +dsa_sign_hash_test(doc) -> + "dsa_sign_hash testing"; +dsa_sign_hash_test(suite) -> + []; +dsa_sign_hash_test(Config) when is_list(Config) -> + Msg = <<"7896345786348756234 Hejsan Svejsan, erlang crypto debugger" + "09812312908312378623487263487623412039812 huagasd">>, + SHA = crypto:sha(sized_binary(Msg)), + + PubKey = _Y = 25854665488880835237281628794585130313500176551981812527054397586638455298000483144002221850980183404910190346416063318160497344811383498859129095184158800144312512447497510551471331451396405348497845813002058423110442376886564659959543650802132345311573634832461635601376738282831340827591903548964194832978, + PrivKey = _X = 441502407453038284293378221372000880210588566361, + ParamP = 109799869232806890760655301608454668257695818999841877165019612946154359052535682480084145133201304812979481136659521529774182959764860329095546511521488413513097576425638476458000255392402120367876345280670101492199681798674053929238558140260669578407351853803102625390950534052428162468100618240968893110797, + ParamQ = 1349199015905534965792122312016505075413456283393, + ParamG = 18320614775012672475365915366944922415598782131828709277168615511695849821411624805195787607930033958243224786899641459701930253094446221381818858674389863050420226114787005820357372837321561754462061849169568607689530279303056075793886577588606958623645901271866346406773590024901668622321064384483571751669, + + Params = [crypto:mpint(ParamP), crypto:mpint(ParamQ), crypto:mpint(ParamG)], + ?line Sig1 = crypto:dss_sign(sha, {digest,SHA}, Params ++ [crypto:mpint(PrivKey)]), + + ?line m(crypto:dss_verify(none, SHA, sized_binary(Sig1), + Params ++ [crypto:mpint(PubKey)]), true), + + ?line m(crypto:dss_verify(sized_binary(one_bit_wrong(Msg)), sized_binary(Sig1), + Params ++ [crypto:mpint(PubKey)]), false), + + ?line m(crypto:dss_verify(sized_binary(Msg), sized_binary(one_bit_wrong(Sig1)), + Params ++ [crypto:mpint(PubKey)]), false), + + %%?line Bad = crypto:dss_sign(sized_binary(Msg), [Params, crypto:mpint(PubKey)]), + + ok. + rsa_encrypt_decrypt(doc) -> ["Test rsa_public_encrypt and rsa_private_decrypt functions."]; @@ -1421,7 +1798,9 @@ worker_loop(N, Config) -> Funcs = { md5, md5_update, md5_mac, md5_mac_io, sha, sha_update, des_cbc, aes_cfb, aes_cbc, des_cbc_iter, rand_uniform_test, strong_rand_test, rsa_verify_test, exor_test, rc4_test, rc4_stream_test, mod_exp_test, - hmac_update_md5, hmac_update_sha, aes_ctr_stream }, + hmac_update_md5, hmac_update_sha, hmac_update_sha256, hmac_update_sha512, + hmac_rfc4231, + aes_ctr_stream }, F = element(random:uniform(size(Funcs)),Funcs), %%io:format("worker ~p calling ~p\n",[self(),F]), diff --git a/lib/debugger/doc/src/Makefile b/lib/debugger/doc/src/Makefile index 1c0bbaf9d2..bc11ed7f46 100644 --- a/lib/debugger/doc/src/Makefile +++ b/lib/debugger/doc/src/Makefile @@ -110,12 +110,12 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html - (/bin/cp -rf $(HTMLDIR) $(RELSYSDIR)/doc) - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc") + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/debugger/doc/src/notes.xml b/lib/debugger/doc/src/notes.xml index 4d8bd8ebe4..593bf715bf 100644 --- a/lib/debugger/doc/src/notes.xml +++ b/lib/debugger/doc/src/notes.xml @@ -32,6 +32,22 @@ <p>This document describes the changes made to the Debugger application.</p> +<section><title>Debugger 3.2.8</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed disappearing breakpoints bug, reported by Ricardo + Catalinas Jim�nez.</p> + <p> + Own Id: OTP-9950</p> + </item> + </list> + </section> + +</section> + <section><title>Debugger 3.2.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/debugger/priv/Makefile b/lib/debugger/priv/Makefile index 1036a5666b..2f3002331b 100644 --- a/lib/debugger/priv/Makefile +++ b/lib/debugger/priv/Makefile @@ -54,8 +54,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TOOLBAR_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TOOLBAR_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/debugger/src/Makefile b/lib/debugger/src/Makefile index be9a2d13cb..1f9d6f2058 100644 --- a/lib/debugger/src/Makefile +++ b/lib/debugger/src/Makefile @@ -129,9 +129,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(TOOLBOX_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(TARGET_TOOLBOX_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(TOOLBOX_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(TARGET_TOOLBOX_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/debugger/src/dbg_iserver.erl b/lib/debugger/src/dbg_iserver.erl index 1bb73a43b9..31a856545f 100644 --- a/lib/debugger/src/dbg_iserver.erl +++ b/lib/debugger/src/dbg_iserver.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -188,10 +188,7 @@ handle_call({new_break, Point, Options}, _From, State) -> handle_call(all_breaks, _From, State) -> {reply, State#state.breaks, State}; handle_call({all_breaks, Mod}, _From, State) -> - Reply = lists:filter(fun({{M,_L}, _Options}) -> - M =/= Mod - end, - State#state.breaks), + Reply = [Break || Break = {{M, _},_} <- State#state.breaks, M =:= Mod], {reply, Reply, State}; %% From Meta process diff --git a/lib/debugger/src/dbg_ui_break_win.erl b/lib/debugger/src/dbg_ui_break_win.erl index 2894e8223b..11d810ccab 100644 --- a/lib/debugger/src/dbg_ui_break_win.erl +++ b/lib/debugger/src/dbg_ui_break_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 diff --git a/lib/debugger/src/dbg_ui_edit_win.erl b/lib/debugger/src/dbg_ui_edit_win.erl index fb40ab3812..2e9a685b57 100644 --- a/lib/debugger/src/dbg_ui_edit_win.erl +++ b/lib/debugger/src/dbg_ui_edit_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 diff --git a/lib/debugger/src/dbg_ui_filedialog_win.erl b/lib/debugger/src/dbg_ui_filedialog_win.erl index 50d198f361..52dc2012f3 100644 --- a/lib/debugger/src/dbg_ui_filedialog_win.erl +++ b/lib/debugger/src/dbg_ui_filedialog_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 diff --git a/lib/debugger/src/dbg_ui_mon_win.erl b/lib/debugger/src/dbg_ui_mon_win.erl index f446fb7fcd..8655c7697a 100644 --- a/lib/debugger/src/dbg_ui_mon_win.erl +++ b/lib/debugger/src/dbg_ui_mon_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/debugger/src/dbg_ui_trace_win.erl b/lib/debugger/src/dbg_ui_trace_win.erl index f237f30e4a..1b439cbf18 100644 --- a/lib/debugger/src/dbg_ui_trace_win.erl +++ b/lib/debugger/src/dbg_ui_trace_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/debugger/src/dbg_ui_win.erl b/lib/debugger/src/dbg_ui_win.erl index cef091ee28..1c7a80a1c4 100644 --- a/lib/debugger/src/dbg_ui_win.erl +++ b/lib/debugger/src/dbg_ui_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 diff --git a/lib/debugger/src/dbg_ui_winman.erl b/lib/debugger/src/dbg_ui_winman.erl index 8619be344b..b5433a202b 100644 --- a/lib/debugger/src/dbg_ui_winman.erl +++ b/lib/debugger/src/dbg_ui_winman.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 diff --git a/lib/debugger/src/dbg_wx_trace_win.erl b/lib/debugger/src/dbg_wx_trace_win.erl index aab8fddda7..68e8e0b844 100644 --- a/lib/debugger/src/dbg_wx_trace_win.erl +++ b/lib/debugger/src/dbg_wx_trace_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 diff --git a/lib/debugger/test/Makefile b/lib/debugger/test/Makefile index 3dfbed31ff..bf948ce8b0 100644 --- a/lib/debugger/test/Makefile +++ b/lib/debugger/test/Makefile @@ -98,10 +98,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) debugger.spec debugger.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) debugger.spec debugger.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/debugger/test/bs_construct_SUITE.erl b/lib/debugger/test/bs_construct_SUITE.erl index fa7b40ff1e..e0bda7eac8 100644 --- a/lib/debugger/test/bs_construct_SUITE.erl +++ b/lib/debugger/test/bs_construct_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -477,7 +477,6 @@ mem_leak(0, _) -> ok; mem_leak(N, B) -> ?line big_bin(B, <<23>>), ?line {'EXIT',{badarg,_}} = (catch big_bin(B, bad)), - maybe_gc(), mem_leak(N-1, B). big_bin(B1, B2) -> @@ -490,13 +489,6 @@ big_bin(B1, B2) -> make_bin(0, Acc) -> Acc; make_bin(N, Acc) -> make_bin(N-1, <<Acc/binary,Acc/binary>>). -maybe_gc() -> - case erlang:system_info(heap_type) of - shared -> erlang:garbage_collect(); - hybrid -> erlang:garbage_collect(); - private -> ok - end. - -define(COF(Int0), ?line (fun(Int) -> true = <<Int:32/float>> =:= <<(float(Int)):32/float>>, diff --git a/lib/debugger/test/bs_match_misc_SUITE.erl b/lib/debugger/test/bs_match_misc_SUITE.erl index ec3746d76a..6b66c45448 100644 --- a/lib/debugger/test/bs_match_misc_SUITE.erl +++ b/lib/debugger/test/bs_match_misc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 diff --git a/lib/debugger/test/int_break_SUITE.erl b/lib/debugger/test/int_break_SUITE.erl index 159678a1f9..7bb0fc2018 100644 --- a/lib/debugger/test/int_break_SUITE.erl +++ b/lib/debugger/test/int_break_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. 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 @@ -76,6 +76,9 @@ basic(Config) when list(Config) -> ?line S3 = [xxx,y] = ordsets1:add_element(y, S2), ?line ok = i:ib(ordsets1, union, 2), ?line [xxx,y,z] = ordsets1:union(S3, [z]), + All = [{{ordsets1,86}, _}, {{ordsets1,_},_}|_] = lists:sort(int:all_breaks()), + [] = lists:sort(int:all_breaks(foobar)), + All = lists:sort(int:all_breaks(ordsets1)), ok. cleanup(doc) -> "Make sure that the auto-attach flag is turned off."; diff --git a/lib/debugger/vsn.mk b/lib/debugger/vsn.mk index 01ff0eb9a8..5b572f5f20 100644 --- a/lib/debugger/vsn.mk +++ b/lib/debugger/vsn.mk @@ -1 +1 @@ -DEBUGGER_VSN = 3.2.7 +DEBUGGER_VSN = 3.2.8 diff --git a/lib/dialyzer/doc/src/Makefile b/lib/dialyzer/doc/src/Makefile index 45b0ffa5ff..a827281cc5 100644 --- a/lib/dialyzer/doc/src/Makefile +++ b/lib/dialyzer/doc/src/Makefile @@ -100,13 +100,13 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/dialyzer/doc/src/notes.xml b/lib/dialyzer/doc/src/notes.xml index f100865b56..934cdba437 100644 --- a/lib/dialyzer/doc/src/notes.xml +++ b/lib/dialyzer/doc/src/notes.xml @@ -31,6 +31,21 @@ <p>This document describes the changes made to the Dialyzer application.</p> +<section><title>Dialyzer 2.5.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Handle <c>nowarn_unused_function</c> the same way as + the compiler does. </p> + <p> + Own Id: OTP-9833</p> + </item> + </list> + </section> + +</section> + <section><title>Dialyzer 2.5</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile index 810f86dc21..63cc1c98f1 100644 --- a/lib/dialyzer/src/Makefile +++ b/lib/dialyzer/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2010. All Rights Reserved. +# Copyright Ericsson AB 2006-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -63,7 +63,10 @@ MODULES = \ dialyzer_plt \ dialyzer_races \ dialyzer_succ_typings \ + dialyzer_timing \ dialyzer_typesig \ + dialyzer_coordinator \ + dialyzer_worker \ dialyzer_utils HRL_FILES= dialyzer.hrl dialyzer_gui_wx.hrl @@ -86,7 +89,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) ifeq ($(NATIVE_LIBS_ENABLED),yes) ERL_COMPILE_FLAGS += +native endif -ERL_COMPILE_FLAGS += +warn_exported_vars +warn_unused_import +warn_untyped_record +warn_missing_spec +ERL_COMPILE_FLAGS += +warn_exported_vars +warn_unused_import +warn_untyped_record +warn_missing_spec +warnings_as_errors # ---------------------------------------------------- # Targets @@ -152,10 +155,10 @@ $(EBIN)/dialyzer_utils.beam: dialyzer.hrl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(EXTRA_FILES) \ - $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(INSTALL_FILES) $(RELSYSDIR)/ebin + "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(INSTALL_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index 5e089d1773..105a174e31 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -2,7 +2,7 @@ %%% %%% %CopyrightBegin% %%% -%%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%%% Copyright Ericsson AB 2006-2012. 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 @@ -110,6 +110,8 @@ -type label() :: non_neg_integer(). -type rep_mode() :: 'quiet' | 'normal' | 'verbose'. -type start_from() :: 'byte_code' | 'src_code'. +-type mfa_or_funlbl() :: label() | mfa(). +-type solver() :: 'v1' | 'v2'. %%-------------------------------------------------------------------- %% Record declarations used by various files @@ -126,11 +128,15 @@ use_contracts = true :: boolean(), race_detection = false :: boolean(), behaviours_chk = false :: boolean(), - callgraph_file = "" :: file:filename()}). + timing = false :: boolean() | 'debug', + timing_server :: dialyzer_timing:timing_server(), + callgraph_file = "" :: file:filename(), + solvers :: [solver()]}). -record(options, {files = [] :: [file:filename()], files_rec = [] :: [file:filename()], analysis_type = succ_typings :: anal_type1(), + timing = false :: boolean() | 'debug', defines = [] :: [dial_define()], from = byte_code :: start_from(), get_warnings = maybe :: boolean() | 'maybe', @@ -145,10 +151,20 @@ output_format = formatted :: format(), filename_opt = basename :: fopt(), callgraph_file = "" :: file:filename(), - check_plt = true :: boolean()}). + check_plt = true :: boolean(), + solvers = [] :: [solver()]}). -record(contract, {contracts = [] :: [contract_pair()], args = [] :: [erl_types:erl_type()], forms = [] :: [{_, _}]}). %%-------------------------------------------------------------------- + +-define(timing(Server, Msg, Var, Expr), + begin + dialyzer_timing:start_stamp(Server, Msg), + Var = Expr, + dialyzer_timing:end_stamp(Server), + Var + end). +-define(timing(Server, Msg, Expr),?timing(Server, Msg, _T, Expr)). diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl index 5be870d78f..496d317f8a 100644 --- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl @@ -2,7 +2,7 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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,6 +30,15 @@ -export([start/3]). +-export([compile_init_result/0, + add_to_result/4, + start_compilation/2, + continue_compilation/2]). + +-export_type([compile_init_data/0, + one_file_result/0, + compile_result/0]). + -include("dialyzer.hrl"). -record(analysis_state, @@ -43,7 +52,9 @@ parent :: pid(), plt :: dialyzer_plt:plt(), start_from = byte_code :: start_from(), - use_contracts = true :: boolean() + use_contracts = true :: boolean(), + timing_server :: dialyzer_timing:timing_server(), + solvers :: [solver()] }). -record(server_state, {parent :: pid(), legal_warnings :: [dial_warn_tag()]}). @@ -55,12 +66,15 @@ -spec start(pid(), [dial_warn_tag()], #analysis{}) -> 'ok'. start(Parent, LegalWarnings, Analysis) -> + TimingServer = dialyzer_timing:init(Analysis#analysis.timing), RacesOn = ordsets:is_element(?WARN_RACE_CONDITION, LegalWarnings), - Analysis0 = Analysis#analysis{race_detection = RacesOn}, + Analysis0 = + Analysis#analysis{race_detection = RacesOn, timing_server = TimingServer}, Analysis1 = expand_files(Analysis0), Analysis2 = run_analysis(Analysis1), State = #server_state{parent = Parent, legal_warnings = LegalWarnings}, - loop(State, Analysis2, none). + loop(State, Analysis2, none), + dialyzer_timing:stop(TimingServer). run_analysis(Analysis) -> Self = self(), @@ -122,7 +136,9 @@ analysis_start(Parent, Analysis) -> plt = Plt, parent = Parent, start_from = Analysis#analysis.start_from, - use_contracts = Analysis#analysis.use_contracts + use_contracts = Analysis#analysis.use_contracts, + timing_server = Analysis#analysis.timing_server, + solvers = Analysis#analysis.solvers }, Files = ordsets:from_list(Analysis#analysis.files), {Callgraph, NoWarn, TmpCServer0} = compile_and_store(Files, State), @@ -146,7 +162,8 @@ analysis_start(Parent, Analysis) -> dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, TmpCServer1), TmpCServer3 = dialyzer_utils:process_record_remote_types(TmpCServer2), - dialyzer_contracts:process_contract_remote_types(TmpCServer3) + ?timing(State#analysis_state.timing_server, "remote", + dialyzer_contracts:process_contract_remote_types(TmpCServer3)) catch throw:{error, _ErrorMsg} = Error -> exit(Error) end, @@ -174,72 +191,67 @@ analysis_start(Parent, Analysis) -> send_codeserver_plt(Parent, CServer, State3#analysis_state.plt), send_analysis_done(Parent, Plt2, State3#analysis_state.doc_plt). -analyze_callgraph(Callgraph, State) -> - Codeserver = State#analysis_state.codeserver, - Parent = State#analysis_state.parent, - DocPlt = State#analysis_state.doc_plt, +analyze_callgraph(Callgraph, #analysis_state{codeserver = Codeserver, + doc_plt = DocPlt, + timing_server = TimingServer, + parent = Parent, + solvers = Solvers} = State) -> Plt = dialyzer_plt:insert_callbacks(State#analysis_state.plt, Codeserver), - Callgraph1 = dialyzer_callgraph:finalize(Callgraph), {NewPlt, NewDocPlt} = case State#analysis_state.analysis_type of plt_build -> - {dialyzer_succ_typings:analyze_callgraph(Callgraph1, Plt, - Codeserver, Parent), - DocPlt}; + NewPlt0 = + dialyzer_succ_typings:analyze_callgraph(Callgraph, Plt, Codeserver, + TimingServer, Solvers, Parent), + {NewPlt0, DocPlt}; succ_typings -> NoWarn = State#analysis_state.no_warn_unused, {Warnings, NewPlt0, NewDocPlt0} = - dialyzer_succ_typings:get_warnings(Callgraph1, Plt, DocPlt, - Codeserver, NoWarn, Parent), + dialyzer_succ_typings:get_warnings(Callgraph, Plt, DocPlt, Codeserver, + NoWarn, TimingServer, Solvers, Parent), send_warnings(State#analysis_state.parent, Warnings), {NewPlt0, NewDocPlt0} end, - dialyzer_callgraph:delete(Callgraph1), + dialyzer_callgraph:delete(Callgraph), State#analysis_state{plt = NewPlt, doc_plt = NewDocPlt}. %%-------------------------------------------------------------------- %% Build the callgraph and fill the codeserver. %%-------------------------------------------------------------------- +-record(compile_init,{ + callgraph :: dialyzer_callgraph:callgraph(), + codeserver :: dialyzer_codeserver:codeserver(), + defines = [] :: [dial_define()], + include_dirs = [] :: [file:filename()], + start_from = byte_code :: start_from(), + use_contracts = true :: boolean() + }). + +make_compile_init(#analysis_state{codeserver = Codeserver, + defines = Defs, + include_dirs = Dirs, + use_contracts = UseContracts, + start_from = StartFrom}, Callgraph) -> + #compile_init{callgraph = Callgraph, + codeserver = Codeserver, + defines = [{d, Macro, Val} || {Macro, Val} <- Defs], + include_dirs = [{i, D} || D <- Dirs], + use_contracts = UseContracts, + start_from = StartFrom}. + compile_and_store(Files, #analysis_state{codeserver = CServer, - defines = Defs, - include_dirs = Dirs, - parent = Parent, - use_contracts = UseContracts, - start_from = StartFrom - } = State) -> + timing_server = Timing, + parent = Parent} = State) -> send_log(Parent, "Reading files and computing callgraph... "), {T1, _} = statistics(runtime), - Includes = [{i, D} || D <- Dirs], - Defines = [{d, Macro, Val} || {Macro, Val} <- Defs], Callgraph = dialyzer_callgraph:new(), - Fun = case StartFrom of - src_code -> - fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn, TmpMods}) -> - case compile_src(File, Includes, Defines, TmpCG, - TmpCServer, UseContracts) of - {error, Reason} -> - {TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn, - TmpMods}; - {ok, NewCG, NoWarn, NewCServer, Mod} -> - {NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn, - [Mod|TmpMods]} - end - end; - byte_code -> - fun(File, {TmpCG, TmpCServer, TmpFailed, TmpNoWarn, TmpMods}) -> - case compile_byte(File, TmpCG, TmpCServer, UseContracts) of - {error, Reason} -> - {TmpCG, TmpCServer, [{File, Reason}|TmpFailed], TmpNoWarn, - TmpMods}; - {ok, NewCG, NoWarn, NewCServer, Mod} -> - {NewCG, NewCServer, TmpFailed, NoWarn++TmpNoWarn, - [Mod|TmpMods]} - end - end - end, - {NewCallgraph1, NewCServer, Failed, NoWarn, Modules} = - lists:foldl(Fun, {Callgraph, CServer, [], [], []}, Files), + CompileInit = make_compile_init(State, Callgraph), + {{Failed, NoWarn, Modules}, NextLabel} = + ?timing(Timing, "compile", _C1, + dialyzer_coordinator:parallel_job(compile, Files, + CompileInit, Timing)), + CServer2 = dialyzer_codeserver:set_next_core_label(NextLabel, CServer), case Failed =:= [] of true -> NewFiles = lists:zip(lists:reverse(Modules), Files), @@ -255,11 +267,56 @@ compile_and_store(Files, #analysis_state{codeserver = CServer, {T2, _} = statistics(runtime), Msg1 = io_lib:format("done in ~.2f secs\nRemoving edges... ", [(T2-T1)/1000]), send_log(Parent, Msg1), - NewCallgraph2 = cleanup_callgraph(State, NewCServer, NewCallgraph1, Modules), + Callgraph = + ?timing(Timing, "clean", _C2, + cleanup_callgraph(State, CServer2, Callgraph, Modules)), {T3, _} = statistics(runtime), Msg2 = io_lib:format("done in ~.2f secs\n", [(T3-T2)/1000]), send_log(Parent, Msg2), - {NewCallgraph2, sets:from_list(NoWarn), NewCServer}. + {Callgraph, sets:from_list(NoWarn), CServer2}. + +-type compile_init_data() :: #compile_init{}. +-type error_reason() :: string(). +-type compile_result() :: {[{file:filename(), error_reason()}], [mfa()], + [module()]}. %%opaque +-type one_file_result() :: {error, error_reason()} | + {ok, [dialyzer_callgraph:callgraph_edge()], + [mfa_or_funlbl()], [mfa()], module()}. %%opaque +-type compile_mid_data() :: {module(), cerl:cerl(), [mfa()], + dialyzer_callgraph:callgraph(), + dialyzer_codeserver:codeserver()}. + +-spec compile_init_result() -> compile_result(). + +compile_init_result() -> {[], [], []}. + +-spec add_to_result(file:filename(), one_file_result(), compile_result(), + compile_init_data()) -> compile_result(). + +add_to_result(File, NewData, {Failed, NoWarn, Mods}, InitData) -> + case NewData of + {error, Reason} -> + {[{File, Reason}|Failed], NoWarn, Mods}; + {ok, V, E, NewNoWarn, Mod} -> + Callgraph = InitData#compile_init.callgraph, + dialyzer_callgraph:add_edges(E, V, Callgraph), + {Failed, NewNoWarn ++ NoWarn, [Mod|Mods]} + end. + +-spec start_compilation(file:filename(), compile_init_data()) -> + {error, error_reason()} |{ok, integer(), compile_mid_data()}. + +start_compilation(File, + #compile_init{callgraph = Callgraph, codeserver = Codeserver, + defines = Defines, include_dirs = IncludeD, + use_contracts = UseContracts, + start_from = StartFrom}) -> + case StartFrom of + src_code -> + compile_src(File, IncludeD, Defines, Callgraph, Codeserver, UseContracts); + byte_code -> + compile_byte(File, Callgraph, Codeserver, UseContracts) + end. cleanup_callgraph(#analysis_state{plt = InitPlt, parent = Parent, codeserver = CodeServer @@ -348,14 +405,18 @@ compile_common(File, AbstrCode, CompOpts, Callgraph, CServer, UseContracts) -> store_core(Mod, Core, NoWarn, Callgraph, CServer) -> Exp = get_exports_from_core(Core), - OldExpTypes = dialyzer_codeserver:get_temp_exported_types(CServer), - NewExpTypes = get_exported_types_from_core(Core), - MergedExpTypes = sets:union(NewExpTypes, OldExpTypes), - CServer1 = dialyzer_codeserver:insert_exports(Exp, CServer), - CServer2 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, - CServer1), - {LabeledCore, CServer3} = label_core(Core, CServer2), - store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, CServer3, NoWarn). + ExpTypes = get_exported_types_from_core(Core), + CServer = dialyzer_codeserver:insert_exports(Exp, CServer), + CServer = dialyzer_codeserver:insert_temp_exported_types(ExpTypes, CServer), + CoreTree = cerl:from_records(Core), + {ok, cerl_trees:size(CoreTree), {Mod, CoreTree, NoWarn, Callgraph, CServer}}. + +-spec continue_compilation(integer(), compile_mid_data()) -> one_file_result(). + +continue_compilation(NextLabel, {Mod, CoreTree, NoWarn, Callgraph, CServer}) -> + {LabeledTree, _NewNextLabel} = cerl_trees:label(CoreTree, NextLabel), + LabeledCore = cerl:to_records(LabeledTree), + store_code_and_build_callgraph(Mod, LabeledCore, Callgraph, NoWarn, CServer). abs_get_nowarn(Abs, M) -> Opts = lists:flatten([C || {attribute, _, compile, C} <- Abs]), @@ -388,18 +449,11 @@ get_exports_from_core(Core) -> M = cerl:atom_val(cerl:module_name(Tree)), [{M, F, A} || {F, A} <- Exports2]. -label_core(Core, CServer) -> - NextLabel = dialyzer_codeserver:get_next_core_label(CServer), - CoreTree = cerl:from_records(Core), - {LabeledTree, NewNextLabel} = cerl_trees:label(CoreTree, NextLabel), - {cerl:to_records(LabeledTree), - dialyzer_codeserver:set_next_core_label(NewNextLabel, CServer)}. - -store_code_and_build_callgraph(Mod, Core, Callgraph, CServer, NoWarn) -> +store_code_and_build_callgraph(Mod, Core, Callgraph, NoWarn, CServer) -> CoreTree = cerl:from_records(Core), - NewCallgraph = dialyzer_callgraph:scan_core_tree(CoreTree, Callgraph), - CServer2 = dialyzer_codeserver:insert(Mod, CoreTree, CServer), - {ok, NewCallgraph, NoWarn, CServer2, Mod}. + {Vertices, Edges} = dialyzer_callgraph:scan_core_tree(CoreTree, Callgraph), + CServer = dialyzer_codeserver:insert(Mod, CoreTree, CServer), + {ok, Vertices, Edges, NoWarn, Mod}. %%-------------------------------------------------------------------- %% Utilities @@ -463,8 +517,10 @@ default_includes(Dir) -> rcv_and_send_ext_types(Parent) -> Self = self(), Self ! {Self, done}, - ExtTypes = rcv_ext_types(Self, []), - Parent ! {Self, ext_types, ExtTypes}, + case rcv_ext_types(Self, []) of + [] -> ok; + ExtTypes -> Parent ! {Self, ext_types, ExtTypes} + end, ok. rcv_ext_types(Self, ExtTypes) -> diff --git a/lib/dialyzer/src/dialyzer_behaviours.erl b/lib/dialyzer/src/dialyzer_behaviours.erl index fdaa8b663c..b84071b95c 100644 --- a/lib/dialyzer/src/dialyzer_behaviours.erl +++ b/lib/dialyzer/src/dialyzer_behaviours.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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,7 +30,7 @@ -module(dialyzer_behaviours). --export([check_callbacks/4, get_behaviour_apis/1, +-export([check_callbacks/5, get_behaviour_apis/1, translate_behaviour_api_call/5, translatable_behaviours/1, translate_callgraph/3]). @@ -47,15 +47,16 @@ -record(state, {plt :: dialyzer_plt:plt(), codeserver :: dialyzer_codeserver:codeserver(), filename :: file:filename(), - behlines :: [{behaviour(), non_neg_integer()}]}). + behlines :: [{behaviour(), non_neg_integer()}], + records :: dict()}). %%-------------------------------------------------------------------- --spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], +-spec check_callbacks(module(), [{cerl:cerl(), cerl:cerl()}], dict(), dialyzer_plt:plt(), dialyzer_codeserver:codeserver()) -> [dial_warning()]. -check_callbacks(Module, Attrs, Plt, Codeserver) -> +check_callbacks(Module, Attrs, Records, Plt, Codeserver) -> {Behaviours, BehLines} = get_behaviours(Attrs), case Behaviours of [] -> []; @@ -64,7 +65,7 @@ check_callbacks(Module, Attrs, Plt, Codeserver) -> {_Var,Code} = dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver), File = get_file(cerl:get_ann(Code)), State = #state{plt = Plt, filename = File, behlines = BehLines, - codeserver = Codeserver}, + codeserver = Codeserver, records = Records}, Warnings = get_warnings(Module, Behaviours, State), [add_tag_file_line(Module, W, State) || W <- Warnings] end. @@ -92,24 +93,21 @@ get_warnings(Module, [Behaviour|Rest], State, Acc) -> check_behaviour(Module, Behaviour, #state{plt = Plt} = State, Acc) -> case dialyzer_plt:lookup_callbacks(Plt, Behaviour) of - [] -> [{callback_info_missing, [Behaviour]}|Acc]; - Callbacks -> check_all_callbacks(Module, Behaviour, Callbacks, State, Acc) + none -> [{callback_info_missing, [Behaviour]}|Acc]; + {value, Callbacks} -> + check_all_callbacks(Module, Behaviour, Callbacks, State, Acc) end. check_all_callbacks(_Module, _Behaviour, [], _State, Acc) -> Acc; check_all_callbacks(Module, Behaviour, [Cb|Rest], - #state{plt = Plt, codeserver = Codeserver} = State, Acc) -> + #state{plt = Plt, codeserver = Codeserver, + records = Records} = State, Acc) -> {{Behaviour, Function, Arity}, {{_BehFile, _BehLine}, Callback}} = Cb, CbMFA = {Module, Function, Arity}, CbReturnType = dialyzer_contracts:get_contract_return(Callback), CbArgTypes = dialyzer_contracts:get_contract_args(Callback), - Records = - case dict:find(Module, dialyzer_codeserver:get_records(Codeserver)) of - {ok, V} -> V; - error -> dict:new() - end, Acc0 = Acc, Acc1 = case dialyzer_plt:lookup(Plt, CbMFA) of @@ -282,8 +280,8 @@ translate_callgraph([{Behaviour,_}|Behaviours], Module, Callgraph) -> DirectCalls = [{From, {Module, Fun, Arity}} || {From, To} <- UsedCalls,{API, {Fun, Arity, _Ord}} <- Calls, To =:= API], - NewCallgraph = dialyzer_callgraph:add_edges(DirectCalls, Callgraph), - translate_callgraph(Behaviours, Module, NewCallgraph); + dialyzer_callgraph:add_edges(DirectCalls, Callgraph), + translate_callgraph(Behaviours, Module, Callgraph); translate_callgraph([], _Module, Callgraph) -> Callgraph. diff --git a/lib/dialyzer/src/dialyzer_callgraph.erl b/lib/dialyzer/src/dialyzer_callgraph.erl index 4767c02f77..64e0ee88af 100644 --- a/lib/dialyzer/src/dialyzer_callgraph.erl +++ b/lib/dialyzer/src/dialyzer_callgraph.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,6 +28,7 @@ -module(dialyzer_callgraph). -export([add_edges/2, + add_edges/3, all_nodes/1, delete/1, finalize/1, @@ -43,12 +44,15 @@ %% module_postorder/1, module_postorder_from_funs/2, new/0, + get_depends_on/2, + get_required_by/2, in_neighbours/2, renew_race_info/4, + renew_race_code/2, + renew_race_public_tables/2, reset_from_funs/2, scan_core_tree/2, strip_module_deps/2, - take_scc/1, remove_external/1, to_dot/2, to_ps/3]). @@ -57,15 +61,14 @@ get_race_code/1, get_race_detection/1, race_code_new/1, put_digraph/2, put_race_code/2, put_race_detection/2, put_named_tables/2, put_public_tables/2, put_behaviour_api_calls/2, - get_behaviour_api_calls/1]). + get_behaviour_api_calls/1, dispose_race_server/1, duplicate/1]). --export_type([callgraph/0, mfa_or_funlbl/0]). +-export_type([callgraph/0, mfa_or_funlbl/0, callgraph_edge/0]). -include("dialyzer.hrl"). %%---------------------------------------------------------------------- --type mfa_or_funlbl() :: label() | mfa(). -type scc() :: [mfa_or_funlbl()]. -type mfa_calls() :: [{mfa_or_funlbl(), mfa_or_funlbl()}]. @@ -78,9 +81,6 @@ %% digraph - A digraph representing the callgraph. %% Nodes are represented as MFAs or labels. %% esc - A set of all escaping functions as reported by dialyzer_dep. -%% postorder - A list of strongly connected components of the callgraph -%% sorted in a topological bottom-up order. -%% This is produced by calling finalize/1. %% name_map - A mapping from label to MFA. %% rev_name_map - A reverse mapping of the name_map. %% rec_var_map - A dict mapping from letrec bound labels to function names. @@ -91,29 +91,42 @@ %%----------------------------------------------------------------------------- -record(callgraph, {digraph = digraph:new() :: digraph(), - esc = sets:new() :: set(), - name_map = dict:new() :: dict(), - rev_name_map = dict:new() :: dict(), - postorder = [] :: [scc()], - rec_var_map = dict:new() :: dict(), - self_rec = sets:new() :: set(), - calls = dict:new() :: dict(), - race_code = dict:new() :: dict(), - public_tables = [] :: [label()], - named_tables = [] :: [string()], + active_digraph :: active_digraph(), + esc :: ets:tid(), + name_map :: ets:tid(), + rev_name_map :: ets:tid(), + rec_var_map :: ets:tid(), + self_rec :: ets:tid(), + calls :: ets:tid(), race_detection = false :: boolean(), - beh_api_calls = [] :: [{mfa(), mfa()}]}). + race_data_server = new_race_data_server() :: pid()}). + +-record(race_data_state, {race_code = dict:new() :: dict(), + public_tables = [] :: [label()], + named_tables = [] :: [string()], + beh_api_calls = [] :: [{mfa(), mfa()}]}). %% Exported Types -type callgraph() :: #callgraph{}. +-type active_digraph() :: {'d', digraph()} | {'e', ets:tid(), ets:tid()}. + %%---------------------------------------------------------------------- -spec new() -> callgraph(). new() -> - #callgraph{}. + [ETSEsc, ETSNameMap, ETSRevNameMap, ETSRecVarMap, ETSSelfRec, ETSCalls] = + [ets:new(N,[public, {read_concurrency, true}]) || + N <- [callgraph_esc, callgraph_name_map, callgraph_rev_name_map, + callgraph_rec_var_map, callgraph_self_rec, callgraph_calls]], + #callgraph{esc = ETSEsc, + name_map = ETSNameMap, + rev_name_map = ETSRevNameMap, + rec_var_map = ETSRecVarMap, + self_rec = ETSSelfRec, + calls = ETSCalls}. -spec delete(callgraph()) -> 'true'. @@ -129,32 +142,32 @@ all_nodes(#callgraph{digraph = DG}) -> lookup_rec_var(Label, #callgraph{rec_var_map = RecVarMap}) when is_integer(Label) -> - dict:find(Label, RecVarMap). + ets_lookup_dict(Label, RecVarMap). -spec lookup_call_site(label(), callgraph()) -> 'error' | {'ok', [_]}. % XXX: refine lookup_call_site(Label, #callgraph{calls = Calls}) when is_integer(Label) -> - dict:find(Label, Calls). + ets_lookup_dict(Label, Calls). -spec lookup_name(label(), callgraph()) -> 'error' | {'ok', mfa()}. lookup_name(Label, #callgraph{name_map = NameMap}) when is_integer(Label) -> - dict:find(Label, NameMap). + ets_lookup_dict(Label, NameMap). -spec lookup_label(mfa_or_funlbl(), callgraph()) -> 'error' | {'ok', integer()}. lookup_label({_,_,_} = MFA, #callgraph{rev_name_map = RevNameMap}) -> - dict:find(MFA, RevNameMap); + ets_lookup_dict(MFA, RevNameMap); lookup_label(Label, #callgraph{}) when is_integer(Label) -> {ok, Label}. -spec in_neighbours(mfa_or_funlbl(), callgraph()) -> 'none' | [mfa_or_funlbl(),...]. -in_neighbours(Label, #callgraph{digraph = Digraph, name_map = NameMap}) +in_neighbours(Label, #callgraph{digraph = Digraph} = CG) when is_integer(Label) -> - Name = case dict:find(Label, NameMap) of + Name = case lookup_name(Label, CG) of {ok, Val} -> Val; error -> Label end, @@ -165,34 +178,27 @@ in_neighbours({_, _, _} = MFA, #callgraph{digraph = Digraph}) -> -spec is_self_rec(mfa_or_funlbl(), callgraph()) -> boolean(). is_self_rec(MfaOrLabel, #callgraph{self_rec = SelfRecs}) -> - sets:is_element(MfaOrLabel, SelfRecs). + ets_lookup_set(MfaOrLabel, SelfRecs). -spec is_escaping(label(), callgraph()) -> boolean(). is_escaping(Label, #callgraph{esc = Esc}) when is_integer(Label) -> - sets:is_element(Label, Esc). + ets_lookup_set(Label, Esc). -type callgraph_edge() :: {mfa_or_funlbl(),mfa_or_funlbl()}. --spec add_edges([callgraph_edge()], callgraph()) -> callgraph(). +-spec add_edges([callgraph_edge()], callgraph()) -> ok. -add_edges([], CG) -> - CG; -add_edges(Edges, #callgraph{digraph = Digraph} = CG) -> - CG#callgraph{digraph = digraph_add_edges(Edges, Digraph)}. +add_edges([], _CG) -> + ok; +add_edges(Edges, #callgraph{digraph = Digraph}) -> + digraph_add_edges(Edges, Digraph). --spec add_edges([callgraph_edge()], [mfa_or_funlbl()], callgraph()) -> callgraph(). +-spec add_edges([callgraph_edge()], [mfa_or_funlbl()], callgraph()) -> ok. add_edges(Edges, MFAs, #callgraph{digraph = DG} = CG) -> - DG = digraph_confirm_vertices(MFAs, DG), + digraph_confirm_vertices(MFAs, DG), add_edges(Edges, CG). --spec take_scc(callgraph()) -> 'none' | {'ok', scc(), callgraph()}. - -take_scc(#callgraph{postorder = [SCC|SCCs]} = CG) -> - {ok, SCC, CG#callgraph{postorder = SCCs}}; -take_scc(#callgraph{postorder = []}) -> - none. - -spec remove_external(callgraph()) -> {callgraph(), [tuple()]}. remove_external(#callgraph{digraph = DG} = CG) -> @@ -221,13 +227,25 @@ find_non_local_calls([{Label1, Label2}|Left], Set) when is_integer(Label1), find_non_local_calls([], Set) -> sets:to_list(Set). --spec renew_race_info(callgraph(), dict(), [label()], [string()]) -> - callgraph(). +-spec get_depends_on(scc() | module(), callgraph()) -> [scc()]. + +get_depends_on(SCC, #callgraph{active_digraph = {'e', Out, _In}}) -> + case ets_lookup_dict(SCC, Out) of + {ok, Value} -> Value; + error -> [] + end; +get_depends_on(SCC, #callgraph{active_digraph = {'d', DG}}) -> + digraph:out_neighbours(DG, SCC). + +-spec get_required_by(scc() | module(), callgraph()) -> [scc()]. -renew_race_info(CG, RaceCode, PublicTables, NamedTables) -> - CG#callgraph{race_code = RaceCode, - public_tables = PublicTables, - named_tables = NamedTables}. +get_required_by(SCC, #callgraph{active_digraph = {'e', _Out, In}}) -> + case ets_lookup_dict(SCC, In) of + {ok, Value} -> Value; + error -> [] + end; +get_required_by(SCC, #callgraph{active_digraph = {'d', DG}}) -> + digraph:in_neighbours(DG, SCC). %%---------------------------------------------------------------------- %% Handling of modules & SCCs @@ -238,32 +256,37 @@ renew_race_info(CG, RaceCode, PublicTables, NamedTables) -> modules(#callgraph{digraph = DG}) -> ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]). --spec module_postorder(callgraph()) -> [[module()]]. +-spec module_postorder(callgraph()) -> {[module()], {'d', digraph()}}. module_postorder(#callgraph{digraph = DG}) -> - {MDG, _Nodes} = get_module_digraph_and_nodes(DG), - MDG1 = digraph_utils:condensation(MDG), - PostOrder = digraph_utils:postorder(MDG1), - PostOrder1 = sort_sccs_internally(PostOrder, MDG), - digraph:delete(MDG1), - digraph_delete(MDG), - PostOrder1. + Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), + Nodes = sets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), + MDG = digraph:new([acyclic]), + digraph_confirm_vertices(sets:to_list(Nodes), MDG), + Foreach = fun({M1,M2}) -> digraph:add_edge(MDG, M1, M2) end, + lists:foreach(Foreach, sets:to_list(Edges)), + {digraph_utils:topsort(MDG), {'d', MDG}}. + +edge_fold({{M1,_,_},{M2,_,_}}, Set) -> + case M1 =/= M2 of + true -> sets:add_element({M1,M2},Set); + false -> Set + end; +edge_fold(_, Set) -> Set. -get_module_digraph_and_nodes(DG) -> - Edges = digraph_edges(DG), - Nodes = ordsets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), - MDG = digraph:new(), - MDG = digraph_confirm_vertices(Nodes, MDG), - MDG = create_module_digraph(Edges, MDG), - {MDG, Nodes}. %% The module deps of a module are modules that depend on the module -spec module_deps(callgraph()) -> dict(). module_deps(#callgraph{digraph = DG}) -> - {MDG, Nodes} = get_module_digraph_and_nodes(DG), + Edges = lists:foldl(fun edge_fold/2, sets:new(), digraph_edges(DG)), + Nodes = sets:from_list([M || {M,_F,_A} <- digraph_vertices(DG)]), + MDG = digraph:new(), + digraph_confirm_vertices(sets:to_list(Nodes), MDG), + Foreach = fun({M1,M2}) -> digraph:add_edge(MDG, M1, M2) end, + lists:foreach(Foreach, sets:to_list(Edges)), Deps = [{N, ordsets:from_list(digraph:in_neighbours(MDG, N))} - || N <- Nodes], + || N <- sets:to_list(Nodes)], digraph_delete(MDG), dict:from_list(Deps). @@ -276,52 +299,42 @@ strip_module_deps(ModDeps, StripSet) -> FilterFun2 = fun(_Key, ValSet) -> ValSet =/= [] end, dict:filter(FilterFun2, ModDeps1). -sort_sccs_internally(PO, MDG) -> - sort_sccs_internally(PO, MDG, []). - -sort_sccs_internally([SCC|SCCs], MDG, Acc) -> - case SCC of - [_, _, _ | _] -> % length(SCC) >= 3 - TmpDG = digraph_utils:subgraph(MDG, SCC), - NewSCC = digraph_utils:postorder(TmpDG), - digraph_delete(TmpDG), - sort_sccs_internally(SCCs, MDG, [NewSCC|Acc]); - _ -> - sort_sccs_internally(SCCs, MDG, [SCC|Acc]) - end; -sort_sccs_internally([], _MDG, Acc) -> - lists:reverse(Acc). - -create_module_digraph([{{M, _, _}, {M, _, _}}|Left], MDG) -> - create_module_digraph(Left, MDG); -create_module_digraph([{{M1, _, _}, {M2, _, _}}|Left], MDG) -> - create_module_digraph(Left, digraph_add_edge(M1, M2, MDG)); -create_module_digraph([{_, _}|Left], MDG) -> - create_module_digraph(Left, MDG); -create_module_digraph([], MDG) -> - MDG. - --spec finalize(callgraph()) -> callgraph(). +-spec finalize(callgraph()) -> {[scc()], callgraph()}. finalize(#callgraph{digraph = DG} = CG) -> - CG#callgraph{postorder = digraph_finalize(DG)}. + {ActiveDG, Postorder} = condensation(DG), + {Postorder, CG#callgraph{active_digraph = ActiveDG}}. --spec reset_from_funs([mfa_or_funlbl()], callgraph()) -> callgraph(). +-spec reset_from_funs([mfa_or_funlbl()], callgraph()) -> {[scc()], callgraph()}. -reset_from_funs(Funs, #callgraph{digraph = DG} = CG) -> +reset_from_funs(Funs, #callgraph{digraph = DG, active_digraph = ADG} = CG) -> + active_digraph_delete(ADG), SubGraph = digraph_reaching_subgraph(Funs, DG), - Postorder = digraph_finalize(SubGraph), + {NewActiveDG, Postorder} = condensation(SubGraph), digraph_delete(SubGraph), - CG#callgraph{postorder = Postorder}. + {Postorder, CG#callgraph{active_digraph = NewActiveDG}}. --spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> [[module()]]. +-spec module_postorder_from_funs([mfa_or_funlbl()], callgraph()) -> + {[module()], callgraph()}. -module_postorder_from_funs(Funs, #callgraph{digraph = DG} = CG) -> +module_postorder_from_funs(Funs, #callgraph{digraph = DG, + active_digraph = ADG} = CG) -> + active_digraph_delete(ADG), SubGraph = digraph_reaching_subgraph(Funs, DG), - PO = module_postorder(CG#callgraph{digraph = SubGraph}), + {PO, Active} = module_postorder(CG#callgraph{digraph = SubGraph}), digraph_delete(SubGraph), - PO. - + {PO, CG#callgraph{active_digraph = Active}}. + +ets_lookup_dict(Key, Table) -> + try ets:lookup_element(Table, Key, 2) of + Val -> {ok, Val} + catch + _:_ -> error + end. + +ets_lookup_set(Key, Table) -> + ets:lookup(Table, Key) =/= []. + %%---------------------------------------------------------------------- %% Core code %%---------------------------------------------------------------------- @@ -330,36 +343,37 @@ module_postorder_from_funs(Funs, #callgraph{digraph = DG} = CG) -> %% The set of labels in the tree must be disjoint from the set of %% labels already occuring in the callgraph. --spec scan_core_tree(cerl:c_module(), callgraph()) -> callgraph(). +-spec scan_core_tree(cerl:c_module(), callgraph()) -> + {[mfa_or_funlbl()], [callgraph_edge()]}. -scan_core_tree(Tree, #callgraph{calls = OldCalls, - esc = OldEsc, - name_map = OldNameMap, - rec_var_map = OldRecVarMap, - rev_name_map = OldRevNameMap, - self_rec = OldSelfRec} = CG) -> +scan_core_tree(Tree, #callgraph{calls = ETSCalls, + esc = ETSEsc, + name_map = ETSNameMap, + rec_var_map = ETSRecVarMap, + rev_name_map = ETSRevNameMap, + self_rec = ETSSelfRec}) -> %% Build name map and recursion variable maps. - {NewNameMap, NewRevNameMap, NewRecVarMap} = - build_maps(Tree, OldRecVarMap, OldNameMap, OldRevNameMap), - + build_maps(Tree, ETSRecVarMap, ETSNameMap, ETSRevNameMap), + %% First find the module-local dependencies. {Deps0, EscapingFuns, Calls} = dialyzer_dep:analyze(Tree), - NewCalls = dict:merge(fun(_Key, Val, Val) -> Val end, OldCalls, Calls), - NewEsc = sets:union(sets:from_list(EscapingFuns), OldEsc), + true = ets:insert(ETSCalls, dict:to_list(Calls)), + true = ets:insert(ETSEsc, [{E} || E <- EscapingFuns]), + LabelEdges = get_edges_from_deps(Deps0), %% Find the self recursive functions. Named functions get both the %% key and their name for convenience. SelfRecs0 = lists:foldl(fun({Key, Key}, Acc) -> - case dict:find(Key, NewNameMap) of + case ets_lookup_dict(Key, ETSNameMap) of error -> [Key|Acc]; {ok, Name} -> [Key, Name|Acc] end; (_, Acc) -> Acc end, [], LabelEdges), - SelfRecs = sets:union(sets:from_list(SelfRecs0), OldSelfRec), + true = ets:insert(ETSSelfRec, [{S} || S <- SelfRecs0]), - NamedEdges1 = name_edges(LabelEdges, NewNameMap), + NamedEdges1 = name_edges(LabelEdges, ETSNameMap), %% We need to scan for inter-module calls since these are not tracked %% by dialyzer_dep. Note that the caller is always recorded as the @@ -378,27 +392,25 @@ scan_core_tree(Tree, #callgraph{calls = OldCalls, NewNamedEdges1 = [E || {From, To} = E <- NamedEdges1, From =/= top, To =/= top], NamedEdges3 = NewNamedEdges1 ++ NewNamedEdges2, - CG1 = add_edges(NamedEdges3, Names3, CG), - CG1#callgraph{calls = NewCalls, - esc = NewEsc, - name_map = NewNameMap, - rec_var_map = NewRecVarMap, - rev_name_map = NewRevNameMap, - self_rec = SelfRecs}. - -build_maps(Tree, RecVarMap, NameMap, RevNameMap) -> + {Names3, NamedEdges3}. + +build_maps(Tree, ETSRecVarMap, ETSNameMap, ETSRevNameMap) -> %% We only care about the named (top level) functions. The anonymous %% functions will be analysed together with their parents. Defs = cerl:module_defs(Tree), Mod = cerl:atom_val(cerl:module_name(Tree)), - lists:foldl(fun({Var, Function}, {AccNameMap, AccRevNameMap, AccRecVarMap}) -> - FunName = cerl:fname_id(Var), - Arity = cerl:fname_arity(Var), - MFA = {Mod, FunName, Arity}, - {dict:store(get_label(Function), MFA, AccNameMap), - dict:store(MFA, get_label(Function), AccRevNameMap), - dict:store(get_label(Var), MFA, AccRecVarMap)} - end, {NameMap, RevNameMap, RecVarMap}, Defs). + Fun = + fun({Var, Function}) -> + FunName = cerl:fname_id(Var), + Arity = cerl:fname_arity(Var), + MFA = {Mod, FunName, Arity}, + FunLabel = get_label(Function), + VarLabel = get_label(Var), + true = ets:insert(ETSNameMap, {FunLabel, MFA}), + true = ets:insert(ETSRevNameMap, {MFA, FunLabel}), + true = ets:insert(ETSRecVarMap, {VarLabel, MFA}) + end, + lists:foreach(Fun, Defs). get_edges_from_deps(Deps) -> %% Convert the dependencies as produced by dialyzer_dep to a list of @@ -411,22 +423,22 @@ get_edges_from_deps(Deps) -> end, [], Deps), lists:flatten(Edges). -name_edges(Edges, NameMap) -> +name_edges(Edges, ETSNameMap) -> %% If a label is present in the name map it is renamed. Otherwise %% keep the label as the identity. MapFun = fun(X) -> - case dict:find(X, NameMap) of + case ets_lookup_dict(X, ETSNameMap) of error -> X; {ok, MFA} -> MFA end end, - name_edges(Edges, MapFun, NameMap, []). + name_edges(Edges, MapFun, []). -name_edges([{From, To}|Left], MapFun, NameMap, Acc) -> +name_edges([{From, To}|Left], MapFun, Acc) -> NewFrom = MapFun(From), NewTo = MapFun(To), - name_edges(Left, MapFun, NameMap, [{NewFrom, NewTo}|Acc]); -name_edges([], _MapFun, _NameMap, Acc) -> + name_edges(Left, MapFun, [{NewFrom, NewTo}|Acc]); +name_edges([], _MapFun, Acc) -> Acc. scan_core_funs(Tree) -> @@ -478,9 +490,10 @@ get_label(T) -> %%---------------------------------------------------------------------- digraph_add_edges([{From, To}|Left], DG) -> - digraph_add_edges(Left, digraph_add_edge(From, To, DG)); -digraph_add_edges([], DG) -> - DG. + digraph_add_edge(From, To, DG), + digraph_add_edges(Left, DG); +digraph_add_edges([], _DG) -> + ok. digraph_add_edge(From, To, DG) -> case digraph:vertex(DG, From) of @@ -492,13 +505,13 @@ digraph_add_edge(From, To, DG) -> {To, _} -> ok end, digraph:add_edge(DG, {From, To}, From, To, []), - DG. + ok. digraph_confirm_vertices([MFA|Left], DG) -> digraph:add_vertex(DG, MFA, confirmed), digraph_confirm_vertices(Left, DG); -digraph_confirm_vertices([], DG) -> - DG. +digraph_confirm_vertices([], _DG) -> + ok. digraph_remove_external(DG) -> Vertices = digraph:vertices(DG), @@ -522,6 +535,12 @@ remove_unconfirmed([], DG, Unconfirmed) -> digraph_delete(DG) -> digraph:delete(DG). +active_digraph_delete({'d', DG}) -> + digraph:delete(DG); +active_digraph_delete({'e', Out, In}) -> + ets:delete(Out), + ets:delete(In). + digraph_edges(DG) -> digraph:edges(DG). @@ -534,75 +553,6 @@ digraph_in_neighbours(V, DG) -> List -> List end. -%% Pick all the independent nodes (leaves) from one module. Then try -%% to stay within the module until no more independent nodes can be -%% chosen. Then pick a new module and so on. -%% -%% Note that an SCC that ranges over more than one module is -%% considered to belong to all modules to make sure that we do not -%% lose any nodes. - -digraph_postorder(Digraph) -> - %% Remove all self-edges for SCCs. - Edges = [digraph:edge(Digraph, E) || E <- digraph:edges(Digraph)], - SelfEdges = [E || {E, V, V, _} <- Edges], - true = digraph:del_edges(Digraph, SelfEdges), - %% Determine the first module outside of the loop. - Leaves = digraph_leaves(Digraph), - case Leaves =:= [] of - true -> []; - false -> - {Module, Taken} = take_sccs_from_fresh_module(Leaves), - true = digraph:del_vertices(Digraph, Taken), - digraph_postorder(Digraph, Module, [Taken]) - end. - -digraph_postorder(Digraph, LastModule, Acc) -> - Leaves = digraph_leaves(Digraph), - case Leaves =:= [] of - true -> lists:append(lists:reverse(Acc)); - false -> - case [SCC || SCC <- Leaves, scc_belongs_to_module(SCC, LastModule)] of - [] -> - {NewModule, NewTaken} = take_sccs_from_fresh_module(Leaves), - true = digraph:del_vertices(Digraph, NewTaken), - digraph_postorder(Digraph, NewModule, [NewTaken|Acc]); - NewTaken -> - true = digraph:del_vertices(Digraph, NewTaken), - digraph_postorder(Digraph, LastModule, [NewTaken|Acc]) - end - end. - -digraph_leaves(Digraph) -> - [V || V <- digraph:vertices(Digraph), digraph:out_degree(Digraph, V) =:= 0]. - -take_sccs_from_fresh_module(Leaves) -> - NewModule = find_module(hd(Leaves)), - {NewModule, - [SCC || SCC <- Leaves, scc_belongs_to_module(SCC, NewModule)]}. - --spec scc_belongs_to_module(scc(), module()) -> boolean(). - -scc_belongs_to_module([Label|Left], Module) when is_integer(Label) -> - scc_belongs_to_module(Left, Module); -scc_belongs_to_module([{M, _, _}|Left], Module) -> - if M =:= Module -> true; - true -> scc_belongs_to_module(Left, Module) - end; -scc_belongs_to_module([], _Module) -> - false. - --spec find_module(scc()) -> module(). - -find_module([{M, _, _}|_]) -> M; -find_module([Label|Left]) when is_integer(Label) -> find_module(Left). - -digraph_finalize(DG) -> - DG1 = digraph_utils:condensation(DG), - Postorder = digraph_postorder(DG1), - digraph:delete(DG1), - Postorder. - digraph_reaching_subgraph(Funs, DG) -> Vertices = digraph_utils:reaching(Funs, DG), digraph_utils:subgraph(DG, Vertices). @@ -611,20 +561,71 @@ digraph_reaching_subgraph(Funs, DG) -> %% Races %%---------------------------------------------------------------------- +-spec renew_race_info(callgraph(), dict(), [label()], [string()]) -> + callgraph(). + +renew_race_info(#callgraph{race_data_server = RaceDataServer} = CG, + RaceCode, PublicTables, NamedTables) -> + ok = race_data_server_cast( + {renew_race_info, {RaceCode, PublicTables, NamedTables}}, + RaceDataServer), + CG. + +renew_race_info({RaceCode, PublicTables, NamedTables}, + #race_data_state{} = State) -> + State#race_data_state{race_code = RaceCode, + public_tables = PublicTables, + named_tables = NamedTables}. + +-spec renew_race_code(dialyzer_races:races(), callgraph()) -> callgraph(). + +renew_race_code(Races, #callgraph{race_data_server = RaceDataServer} = CG) -> + Fun = dialyzer_races:get_curr_fun(Races), + FunArgs = dialyzer_races:get_curr_fun_args(Races), + Code = lists:reverse(dialyzer_races:get_race_list(Races)), + ok = race_data_server_cast( + {renew_race_code, {Fun, FunArgs, Code}}, + RaceDataServer), + CG. + +renew_race_code_handler({Fun, FunArgs, Code}, + #race_data_state{race_code = RaceCode} = State) -> + State#race_data_state{race_code = dict:store(Fun, [FunArgs, Code], RaceCode)}. + +-spec renew_race_public_tables(label(), callgraph()) -> callgraph(). + +renew_race_public_tables(VarLabel, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = + race_data_server_cast({renew_race_public_tables, VarLabel}, RaceDataServer), + CG. + +renew_race_public_tables_handler(VarLabel, + #race_data_state{public_tables = PT} + = State) -> + State#race_data_state{public_tables = ordsets:add_element(VarLabel, PT)}. + -spec cleanup(callgraph()) -> callgraph(). -cleanup(#callgraph{digraph = Digraph, - name_map = NameMap, - rev_name_map = RevNameMap, - public_tables = PublicTables, - named_tables = NamedTables, - race_code = RaceCode}) -> +cleanup(#callgraph{digraph = Digraph, + name_map = NameMap, + rev_name_map = RevNameMap, + race_data_server = RaceDataServer}) -> #callgraph{digraph = Digraph, - name_map = NameMap, - rev_name_map = RevNameMap, - public_tables = PublicTables, - named_tables = NamedTables, - race_code = RaceCode}. + name_map = NameMap, + rev_name_map = RevNameMap, + race_data_server = race_data_server_call(dup, RaceDataServer)}. + +-spec duplicate(callgraph()) -> callgraph(). + +duplicate(#callgraph{race_data_server = RaceDataServer} = Callgraph) -> + Callgraph#callgraph{ + race_data_server = race_data_server_call(dup, RaceDataServer)}. + +-spec dispose_race_server(callgraph()) -> ok. + +dispose_race_server(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_cast(stop, RaceDataServer). -spec get_digraph(callgraph()) -> digraph(). @@ -633,28 +634,34 @@ get_digraph(#callgraph{digraph = Digraph}) -> -spec get_named_tables(callgraph()) -> [string()]. -get_named_tables(#callgraph{named_tables = NamedTables}) -> - NamedTables. +get_named_tables(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_named_tables, RaceDataServer). -spec get_public_tables(callgraph()) -> [label()]. -get_public_tables(#callgraph{public_tables = PT}) -> - PT. +get_public_tables(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_public_tables, RaceDataServer). -spec get_race_code(callgraph()) -> dict(). -get_race_code(#callgraph{race_code = RaceCode}) -> - RaceCode. +get_race_code(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_race_code, RaceDataServer). -spec get_race_detection(callgraph()) -> boolean(). get_race_detection(#callgraph{race_detection = RD}) -> RD. +-spec get_behaviour_api_calls(callgraph()) -> [{mfa(), mfa()}]. + +get_behaviour_api_calls(#callgraph{race_data_server = RaceDataServer}) -> + race_data_server_call(get_behaviour_api_calls, RaceDataServer). + -spec race_code_new(callgraph()) -> callgraph(). -race_code_new(Callgraph) -> - Callgraph#callgraph{race_code = dict:new()}. +race_code_new(#callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast(race_code_new, RaceDataServer), + CG. -spec put_digraph(digraph(), callgraph()) -> callgraph(). @@ -663,8 +670,9 @@ put_digraph(Digraph, Callgraph) -> -spec put_race_code(dict(), callgraph()) -> callgraph(). -put_race_code(RaceCode, Callgraph) -> - Callgraph#callgraph{race_code = RaceCode}. +put_race_code(RaceCode, #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_race_code, RaceCode}, RaceDataServer), + CG. -spec put_race_detection(boolean(), callgraph()) -> callgraph(). @@ -673,13 +681,79 @@ put_race_detection(RaceDetection, Callgraph) -> -spec put_named_tables([string()], callgraph()) -> callgraph(). -put_named_tables(NamedTables, Callgraph) -> - Callgraph#callgraph{named_tables = NamedTables}. +put_named_tables(NamedTables, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_named_tables, NamedTables}, RaceDataServer), + CG. -spec put_public_tables([label()], callgraph()) -> callgraph(). -put_public_tables(PublicTables, Callgraph) -> - Callgraph#callgraph{public_tables = PublicTables}. +put_public_tables(PublicTables, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_public_tables, PublicTables}, RaceDataServer), + CG. + +-spec put_behaviour_api_calls([{mfa(), mfa()}], callgraph()) -> callgraph(). + +put_behaviour_api_calls(Calls, + #callgraph{race_data_server = RaceDataServer} = CG) -> + ok = race_data_server_cast({put_behaviour_api_calls, Calls}, RaceDataServer), + CG. + + +new_race_data_server() -> + spawn_link(fun() -> race_data_server_loop(#race_data_state{}) end). + +race_data_server_loop(State) -> + receive + {call, From, Ref, Query} -> + Reply = race_data_server_handle_call(Query, State), + From ! {Ref, Reply}, + race_data_server_loop(State); + {cast, stop} -> + ok; + {cast, Message} -> + NewState = race_data_server_handle_cast(Message, State), + race_data_server_loop(NewState) + end. + +race_data_server_call(Query, Server) -> + Ref = make_ref(), + Server ! {call, self(), Ref, Query}, + receive + {Ref, Reply} -> Reply + end. + +race_data_server_cast(Message, Server) -> + Server ! {cast, Message}, + ok. + +race_data_server_handle_cast(race_code_new, State) -> + State#race_data_state{race_code = dict:new()}; +race_data_server_handle_cast({Tag, Data}, State) -> + case Tag of + renew_race_info -> renew_race_info(Data, State); + renew_race_code -> renew_race_code_handler(Data, State); + renew_race_public_tables -> renew_race_public_tables_handler(Data, State); + put_race_code -> State#race_data_state{race_code = Data}; + put_public_tables -> State#race_data_state{public_tables = Data}; + put_named_tables -> State#race_data_state{named_tables = Data}; + put_behaviour_api_calls -> State#race_data_state{beh_api_calls = Data} + end. + +race_data_server_handle_call(Query, + #race_data_state{race_code = RaceCode, + public_tables = PublicTables, + named_tables = NamedTables, + beh_api_calls = BehApiCalls} + = State) -> + case Query of + dup -> spawn_link(fun() -> race_data_server_loop(State) end); + get_race_code -> RaceCode; + get_public_tables -> PublicTables; + get_named_tables -> NamedTables; + get_behaviour_api_calls -> BehApiCalls + end. %%============================================================================= %% Utilities for 'dot' @@ -695,7 +769,7 @@ to_dot(#callgraph{digraph = DG, esc = Esc} = CG, File) -> end end, Escaping = [{Fun(L), {color, red}} - || L <- sets:to_list(Esc), L =/= external], + || L <- [E || {E} <- ets:tab2list(Esc)], L =/= external], Vertices = digraph_edges(DG), hipe_dot:translate_list(Vertices, File, "CG", Escaping). @@ -708,14 +782,41 @@ to_ps(#callgraph{} = CG, File, Args) -> _ = os:cmd(Command), ok. -%------------------------------------------------------------------------------- - --spec put_behaviour_api_calls([{mfa(), mfa()}], callgraph()) -> callgraph(). - -put_behaviour_api_calls(Calls, Callgraph) -> - Callgraph#callgraph{beh_api_calls = Calls}. - --spec get_behaviour_api_calls(callgraph()) -> [{mfa(), mfa()}]. - -get_behaviour_api_calls(Callgraph) -> - Callgraph#callgraph.beh_api_calls. +condensation(G) -> + SCs = digraph_utils:strong_components(G), + V2I = ets:new(condensation_v2i, []), + I2C = ets:new(condensation_i2c, []), + I2I = ets:new(condensation_i2i, [bag]), + CFun = + fun(SC, N) -> + lists:foreach(fun(V) -> true = ets:insert(V2I, {V,N}) end, SC), + true = ets:insert(I2C, {N, SC}), + N + 1 + end, + lists:foldl(CFun, 1, SCs), + Fun1 = + fun({V1, V2}) -> + I1 = ets:lookup_element(V2I, V1, 2), + I2 = ets:lookup_element(V2I, V2, 2), + case I1 =:= I2 of + true -> true; + false -> ets:insert(I2I, {I1, I2}) + end + end, + lists:foreach(Fun1, digraph:edges(G)), + Fun3 = + fun({I1, I2}, {Out, In}) -> + SC1 = ets:lookup_element(I2C, I1, 2), + SC2 = ets:lookup_element(I2C, I2, 2), + {dict:append(SC1, SC2, Out), dict:append(SC2, SC1, In)} + end, + {OutDict, InDict} = ets:foldl(Fun3, {dict:new(), dict:new()}, I2I), + [OutETS, InETS] = + [ets:new(Name,[{read_concurrency, true}]) || + Name <- [callgraph_deps_out, callgraph_deps_in]], + ets:insert(OutETS, dict:to_list(OutDict)), + ets:insert(InETS, dict:to_list(InDict)), + ets:delete(V2I), + ets:delete(I2C), + ets:delete(I2I), + {{'e', OutETS, InETS}, SCs}. diff --git a/lib/dialyzer/src/dialyzer_cl.erl b/lib/dialyzer/src/dialyzer_cl.erl index 04a0db890f..6732d96b98 100644 --- a/lib/dialyzer/src/dialyzer_cl.erl +++ b/lib/dialyzer/src/dialyzer_cl.erl @@ -2,7 +2,7 @@ %%------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -188,6 +188,7 @@ plt_common(#options{init_plts = [InitPlt]} = Opts, RemoveFiles, AddFiles) -> ok -> case Opts#options.output_plt of none -> ok; + InitPlt -> ok; OutPlt -> {ok, Binary} = file:read_file(InitPlt), ok = file:write_file(OutPlt, Binary) @@ -393,10 +394,12 @@ do_analysis(Files, Options, Plt, PltInfo) -> defines = Options#options.defines, include_dirs = Options#options.include_dirs, files = Files, - start_from = Options#options.from, + start_from = Options#options.from, + timing = Options#options.timing, plt = Plt, use_contracts = Options#options.use_contracts, - callgraph_file = Options#options.callgraph_file}, + callgraph_file = Options#options.callgraph_file, + solvers = Options#options.solvers}, State3 = start_analysis(State2, InitAnalysis), {T1, _} = statistics(wall_clock), Return = cl_loop(State3), diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index ff8fc39a5e..2ea3d3af5a 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -164,6 +164,13 @@ cl(["--src"|T]) -> cl(["--no_spec"|T]) -> put(dialyzer_options_use_contracts, false), cl(T); +cl(["--statistics"|T]) -> + put(dialyzer_timing, true), + cl(T); +cl(["--resources"|T]) -> + put(dialyzer_options_report_mode, quiet), + put(dialyzer_timing, debug), + cl(T); cl(["-v"|_]) -> io:format("Dialyzer version "++?VSN++"\n"), erlang:halt(?RET_NOTHING_SUSPICIOUS); @@ -191,6 +198,9 @@ cl(["--gui"|T]) -> cl(["--wx"|T]) -> put(dialyzer_options_mode, {gui, wx}), cl(T); +cl(["--solver",Solver|T]) -> % not documented + append_var(dialyzer_solvers, [list_to_atom(Solver)]), + cl(T); cl([H|_] = L) -> case filelib:is_file(H) orelse filelib:is_dir(H) of true -> @@ -250,6 +260,8 @@ init() -> put(dialyzer_output_format, formatted), put(dialyzer_filename_opt, basename), put(dialyzer_options_check_plt, DefaultOpts#options.check_plt), + put(dialyzer_timing, DefaultOpts#options.timing), + put(dialyzer_solvers, DefaultOpts#options.solvers), ok. append_defines([Def, Val]) -> @@ -290,6 +302,7 @@ cl_options() -> {filename_opt, get(dialyzer_filename_opt)}, {analysis_type, get(dialyzer_options_analysis_type)}, {get_warnings, get(dialyzer_options_get_warnings)}, + {timing, get(dialyzer_timing)}, {callgraph_file, get(dialyzer_callgraph_file)} |common_options()]. @@ -302,7 +315,8 @@ common_options() -> {report_mode, get(dialyzer_options_report_mode)}, {use_spec, get(dialyzer_options_use_contracts)}, {warnings, get(dialyzer_warnings)}, - {check_plt, get(dialyzer_options_check_plt)}]. + {check_plt, get(dialyzer_options_check_plt)}, + {solvers, get(dialyzer_solvers)}]. %%----------------------------------------------------------------------- @@ -351,7 +365,7 @@ help_message() -> [--apps applications] [-o outfile] [--build_plt] [--add_to_plt] [--remove_from_plt] [--check_plt] [--no_check_plt] [--plt_info] [--get_warnings] - [--no_native] [--fullpath] + [--no_native] [--fullpath] [--statistics] Options: files_or_dirs (for backwards compatibility also as: -c files_or_dirs) Use Dialyzer from the command line to detect defects in the @@ -418,6 +432,9 @@ Options: Make Dialyzer a bit more quiet. --verbose Make Dialyzer a bit more verbose. + --statistics + Prints information about the progress of execution (analysis phases, + time spent in each and size of the relative input). --build_plt The analysis starts from an empty plt and creates a new one from the files specified with -c and -r. Only works for beam files. diff --git a/lib/dialyzer/src/dialyzer_codeserver.erl b/lib/dialyzer/src/dialyzer_codeserver.erl index f1e87affbd..9989118671 100644 --- a/lib/dialyzer/src/dialyzer_codeserver.erl +++ b/lib/dialyzer/src/dialyzer_codeserver.erl @@ -52,9 +52,7 @@ new/0, set_next_core_label/2, set_temp_records/2, - store_records/3, store_temp_records/3, - store_contracts/3, store_temp_contracts/4]). -export_type([codeserver/0]). @@ -63,85 +61,159 @@ %%-------------------------------------------------------------------- --record(codeserver, {table_pid :: pid(), - exported_types = sets:new() :: set(), % set(mfa()) - temp_exported_types = sets:new() :: set(), % set(mfa()) - exports = sets:new() :: set(), % set(mfa()) - next_core_label = 0 :: label(), - records = dict:new() :: dict(), - temp_records = dict:new() :: dict(), - contracts = dict:new() :: dict(), - callbacks = dict:new() :: dict(), - temp_contracts = dict:new() :: dict(), - temp_callbacks = dict:new() :: dict() +-type dict_ets() :: ets:tid(). +-type set_ets() :: ets:tid(). + +-record(codeserver, {next_core_label = 0 :: label(), + code :: dict_ets(), + exported_types :: set_ets(), % set(mfa()) + records :: dict_ets(), + contracts :: dict_ets(), + callbacks :: dict_ets(), + exports :: 'clean' | set_ets(), % set(mfa()) + temp_exported_types :: 'clean' | set_ets(), % set(mfa()) + temp_records :: 'clean' | dict_ets(), + temp_contracts :: 'clean' | dict_ets(), + temp_callbacks :: 'clean' | dict_ets() }). -opaque codeserver() :: #codeserver{}. %%-------------------------------------------------------------------- +ets_dict_find(Key, Table) -> + try ets:lookup_element(Table, Key, 2) of + Val -> {ok, Val} + catch + _:_ -> error + end. + +ets_dict_store(Key, Element, Table) -> + true = ets:insert(Table, {Key, Element}), + Table. + +ets_dict_store_dict(Dict, Table) -> + true = ets:insert(Table, dict:to_list(Dict)). + +ets_dict_to_dict(Table) -> + Fold = fun({Key,Value}, Dict) -> dict:store(Key, Value, Dict) end, + ets:foldl(Fold, dict:new(), Table). + +ets_set_is_element(Key, Table) -> + case ets:lookup(Table, Key) of + [] -> false; + _ -> true + end. + +ets_set_insert_set(Set, Table) -> + ets_set_insert_list(sets:to_list(Set), Table). + +ets_set_insert_list(List, Table) -> + true = ets:insert(Table, [{E} || E <- List]). + +ets_set_to_set(Table) -> + Fold = fun({E}, Set) -> sets:add_element(E, Set) end, + ets:foldl(Fold, sets:new(), Table). + +ets_read_concurrent_table(Name) -> + ets:new(Name,[{read_concurrency, true}]). + +%%-------------------------------------------------------------------- + -spec new() -> codeserver(). new() -> - #codeserver{table_pid = table__new()}. + CodeOptions = [compressed, public, {read_concurrency, true}], + Code = ets:new(dialyzer_codeserver_code, CodeOptions), + TempOptions = [public, {write_concurrency, true}], + [Exports, TempExportedTypes, TempRecords, TempContracts, TempCallbacks] = + [ets:new(Name, TempOptions) || + Name <- + [dialyzer_codeserver_exports, dialyzer_codeserver_temp_exported_types, + dialyzer_codeserver_temp_records, dialyzer_codeserver_temp_contracts, + dialyzer_codeserver_temp_callbacks]], + #codeserver{code = Code, + exports = Exports, + temp_exported_types = TempExportedTypes, + temp_records = TempRecords, + temp_contracts = TempContracts, + temp_callbacks = TempCallbacks}. -spec delete(codeserver()) -> 'ok'. -delete(#codeserver{table_pid = TablePid}) -> - table__delete(TablePid). +delete(#codeserver{code = Code, exported_types = ExportedTypes, + records = Records, contracts = Contracts, + callbacks = Callbacks}) -> + lists:foreach(fun ets:delete/1, + [Code, ExportedTypes, Records, Contracts, Callbacks]). -spec insert(atom(), cerl:c_module(), codeserver()) -> codeserver(). insert(Mod, ModCode, CS) -> - NewTablePid = table__insert(CS#codeserver.table_pid, Mod, ModCode), - CS#codeserver{table_pid = NewTablePid}. + Name = cerl:module_name(ModCode), + Exports = cerl:module_exports(ModCode), + Attrs = cerl:module_attrs(ModCode), + Defs = cerl:module_defs(ModCode), + As = cerl:get_ann(ModCode), + Funs = + [{{Mod, cerl:fname_id(Var), cerl:fname_arity(Var)}, + Val} || Val = {Var, _Fun} <- Defs], + Keys = [Key || {Key, _Value} <- Funs], + ModEntry = {Mod, {Name, Exports, Attrs, Keys, As}}, + true = ets:insert(CS#codeserver.code, [ModEntry|Funs]), + CS. + +-spec get_temp_exported_types(codeserver()) -> set(). + +get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) -> + ets_set_to_set(TempExpTypes). -spec insert_temp_exported_types(set(), codeserver()) -> codeserver(). insert_temp_exported_types(Set, CS) -> - CS#codeserver{temp_exported_types = Set}. + TempExportedTypes = CS#codeserver.temp_exported_types, + true = ets_set_insert_set(Set, TempExportedTypes), + CS. -spec insert_exports([mfa()], codeserver()) -> codeserver(). insert_exports(List, #codeserver{exports = Exports} = CS) -> - Set = sets:from_list(List), - NewExports = sets:union(Exports, Set), - CS#codeserver{exports = NewExports}. + true = ets_set_insert_list(List, Exports), + CS. -spec is_exported(mfa(), codeserver()) -> boolean(). is_exported(MFA, #codeserver{exports = Exports}) -> - sets:is_element(MFA, Exports). + ets_set_is_element(MFA, Exports). -spec get_exported_types(codeserver()) -> set(). % set(mfa()) get_exported_types(#codeserver{exported_types = ExpTypes}) -> - ExpTypes. - --spec get_temp_exported_types(codeserver()) -> set(). - -get_temp_exported_types(#codeserver{temp_exported_types = TempExpTypes}) -> - TempExpTypes. + ets_set_to_set(ExpTypes). -spec get_exports(codeserver()) -> set(). % set(mfa()) get_exports(#codeserver{exports = Exports}) -> - Exports. + ets_set_to_set(Exports). -spec finalize_exported_types(set(), codeserver()) -> codeserver(). finalize_exported_types(Set, CS) -> - CS#codeserver{exported_types = Set, temp_exported_types = sets:new()}. + ExportedTypes = ets_read_concurrent_table(dialyzer_codeserver_exported_types), + true = ets_set_insert_set(Set, ExportedTypes), + TempExpTypes = CS#codeserver.temp_exported_types, + true = ets:delete(TempExpTypes), + CS#codeserver{exported_types = ExportedTypes, temp_exported_types = clean}. -spec lookup_mod_code(atom(), codeserver()) -> cerl:c_module(). lookup_mod_code(Mod, CS) when is_atom(Mod) -> - table__lookup(CS#codeserver.table_pid, Mod). + table__lookup(CS#codeserver.code, Mod). -spec lookup_mfa_code(mfa(), codeserver()) -> {cerl:c_var(), cerl:c_fun()}. lookup_mfa_code({_M, _F, _A} = MFA, CS) -> - table__lookup(CS#codeserver.table_pid, MFA). + table__lookup(CS#codeserver.code, MFA). -spec get_next_core_label(codeserver()) -> label(). @@ -153,20 +225,10 @@ get_next_core_label(#codeserver{next_core_label = NCL}) -> set_next_core_label(NCL, CS) -> CS#codeserver{next_core_label = NCL}. --spec store_records(atom(), dict(), codeserver()) -> codeserver(). - -store_records(Mod, Dict, #codeserver{records = RecDict} = CS) - when is_atom(Mod) -> - case dict:size(Dict) =:= 0 of - true -> CS; - false -> CS#codeserver{records = dict:store(Mod, Dict, RecDict)} - end. - -spec lookup_mod_records(atom(), codeserver()) -> dict(). -lookup_mod_records(Mod, #codeserver{records = RecDict}) - when is_atom(Mod) -> - case dict:find(Mod, RecDict) of +lookup_mod_records(Mod, #codeserver{records = RecDict}) when is_atom(Mod) -> + case ets_dict_find(Mod, RecDict) of error -> dict:new(); {ok, Dict} -> Dict end. @@ -174,7 +236,7 @@ lookup_mod_records(Mod, #codeserver{records = RecDict}) -spec get_records(codeserver()) -> dict(). get_records(#codeserver{records = RecDict}) -> - RecDict. + ets_dict_to_dict(RecDict). -spec store_temp_records(atom(), dict(), codeserver()) -> codeserver(). @@ -182,59 +244,58 @@ store_temp_records(Mod, Dict, #codeserver{temp_records = TempRecDict} = CS) when is_atom(Mod) -> case dict:size(Dict) =:= 0 of true -> CS; - false -> CS#codeserver{temp_records = dict:store(Mod, Dict, TempRecDict)} + false -> CS#codeserver{temp_records = ets_dict_store(Mod, Dict, TempRecDict)} end. -spec get_temp_records(codeserver()) -> dict(). get_temp_records(#codeserver{temp_records = TempRecDict}) -> - TempRecDict. + ets_dict_to_dict(TempRecDict). -spec set_temp_records(dict(), codeserver()) -> codeserver(). set_temp_records(Dict, CS) -> - CS#codeserver{temp_records = Dict}. + true = ets:delete(CS#codeserver.temp_records), + TempRecords = ets:new(dialyzer_codeserver_temp_records,[]), + true = ets_dict_store_dict(Dict, TempRecords), + CS#codeserver{temp_records = TempRecords}. -spec finalize_records(dict(), codeserver()) -> codeserver(). finalize_records(Dict, CS) -> - CS#codeserver{records = Dict, temp_records = dict:new()}. - --spec store_contracts(atom(), dict(), codeserver()) -> codeserver(). - -store_contracts(Mod, Dict, #codeserver{contracts = C} = CS) when is_atom(Mod) -> - case dict:size(Dict) =:= 0 of - true -> CS; - false -> CS#codeserver{contracts = dict:store(Mod, Dict, C)} - end. + true = ets:delete(CS#codeserver.temp_records), + Records = ets_read_concurrent_table(dialyzer_codeserver_records), + true = ets_dict_store_dict(Dict, Records), + CS#codeserver{records = Records, temp_records = clean}. -spec lookup_mod_contracts(atom(), codeserver()) -> dict(). lookup_mod_contracts(Mod, #codeserver{contracts = ContDict}) when is_atom(Mod) -> - case dict:find(Mod, ContDict) of + case ets_dict_find(Mod, ContDict) of error -> dict:new(); - {ok, Dict} -> Dict + {ok, Keys} -> + dict:from_list([get_contract_pair(Key, ContDict)|| Key <- Keys]) end. +get_contract_pair(Key, ContDict) -> + {Key, ets:lookup_element(ContDict, Key, 2)}. + -spec lookup_mfa_contract(mfa(), codeserver()) -> 'error' | {'ok', dialyzer_contracts:file_contract()}. -lookup_mfa_contract({M,_F,_A} = MFA, #codeserver{contracts = ContDict}) -> - case dict:find(M, ContDict) of - error -> error; - {ok, Dict} -> dict:find(MFA, Dict) - end. +lookup_mfa_contract(MFA, #codeserver{contracts = ContDict}) -> + ets_dict_find(MFA, ContDict). -spec get_contracts(codeserver()) -> dict(). get_contracts(#codeserver{contracts = ContDict}) -> - ContDict. + ets_dict_to_dict(ContDict). --spec get_callbacks(codeserver()) -> dict(). +-spec get_callbacks(codeserver()) -> list(). get_callbacks(#codeserver{callbacks = CallbDict}) -> - CallbDict. + ets:tab2list(CallbDict). -spec store_temp_contracts(atom(), dict(), dict(), codeserver()) -> codeserver(). @@ -246,91 +307,44 @@ store_temp_contracts(Mod, SpecDict, CallbackDict, CS1 = case dict:size(SpecDict) =:= 0 of true -> CS; - false -> CS#codeserver{temp_contracts = dict:store(Mod, SpecDict, Cn)} + false -> + CS#codeserver{temp_contracts = ets_dict_store(Mod, SpecDict, Cn)} end, case dict:size(CallbackDict) =:= 0 of true -> CS1; - false -> CS1#codeserver{temp_callbacks = dict:store(Mod, CallbackDict, Cb)} + false -> + CS1#codeserver{temp_callbacks = ets_dict_store(Mod, CallbackDict, Cb)} end. -spec get_temp_contracts(codeserver()) -> {dict(), dict()}. get_temp_contracts(#codeserver{temp_contracts = TempContDict, temp_callbacks = TempCallDict}) -> - {TempContDict, TempCallDict}. + {ets_dict_to_dict(TempContDict), ets_dict_to_dict(TempCallDict)}. -spec finalize_contracts(dict(), dict(), codeserver()) -> codeserver(). -finalize_contracts(CnDict, CbDict, CS) -> - CS#codeserver{contracts = CnDict, - callbacks = CbDict, - temp_contracts = dict:new(), - temp_callbacks = dict:new() - }. - -table__new() -> - spawn_link(fun() -> table__loop(none, dict:new()) end). - -table__delete(TablePid) -> - TablePid ! stop, - ok. - -table__lookup(TablePid, Key) -> - TablePid ! {self(), lookup, Key}, - receive - {TablePid, Key, Ans} -> Ans - end. - -table__insert(TablePid, Key, Val) -> - TablePid ! {insert, [{Key, term_to_binary(Val, [compressed])}]}, - TablePid. - -table__loop(Cached, Map) -> - receive - stop -> ok; - {Pid, lookup, {M, F, A} = MFA} -> - {NewCached, Ans} = - case Cached of - {M, Tree} -> - Val = find_fun(F, A, Tree), - {Cached, Val}; - _ -> - Tree = fetch_and_expand(M, Map), - Val = find_fun(F, A, Tree), - {{M, Tree}, Val} - end, - Pid ! {self(), MFA, Ans}, - table__loop(NewCached, Map); - {Pid, lookup, Mod} when is_atom(Mod) -> - Ans = case Cached of - {Mod, Tree} -> Tree; - _ -> fetch_and_expand(Mod, Map) - end, - Pid ! {self(), Mod, Ans}, - table__loop({Mod, Ans}, Map); - {insert, List} -> - NewMap = lists:foldl(fun({Key, Val}, AccMap) -> - dict:store(Key, Val, AccMap) - end, Map, List), - table__loop(Cached, NewMap) - end. - -fetch_and_expand(Mod, Map) -> - try - Bin = dict:fetch(Mod, Map), - binary_to_term(Bin) - catch - _:_ -> - S = atom_to_list(Mod), - Msg = "found no module named '" ++ S ++ "' in the analyzed files", - exit({error, Msg}) - end. - -find_fun(F, A, Tree) -> - Pred = - fun({Var, _Fun}) -> - (cerl:fname_id(Var) =/= F) orelse - (cerl:fname_arity(Var) =/= A) - end, - [Val|_] = lists:dropwhile(Pred, cerl:module_defs(Tree)), - Val. +finalize_contracts(SpecDict, CallbackDict, CS) -> + Contracts = ets_read_concurrent_table(dialyzer_codeserver_contracts), + Callbacks = ets_read_concurrent_table(dialyzer_codeserver_callbacks), + Contracts = dict:fold(fun decompose_spec_dict/3, Contracts, SpecDict), + Callbacks = dict:fold(fun decompose_cb_dict/3, Callbacks, CallbackDict), + CS#codeserver{contracts = Contracts, callbacks = Callbacks, + temp_contracts = clean, temp_callbacks = clean}. + +decompose_spec_dict(Mod, Dict, Table) -> + Keys = dict:fetch_keys(Dict), + true = ets:insert(Table, dict:to_list(Dict)), + true = ets:insert(Table, {Mod, Keys}), + Table. + +decompose_cb_dict(_Mod, Dict, Table) -> + true = ets:insert(Table, dict:to_list(Dict)), + Table. + +table__lookup(TablePid, M) when is_atom(M) -> + {Name, Exports, Attrs, Keys, As} = ets:lookup_element(TablePid, M, 2), + Defs = [table__lookup(TablePid, Key) || Key <- Keys], + cerl:ann_c_module(As, Name, Exports, Attrs, Defs); +table__lookup(TablePid, MFA) -> + ets:lookup_element(TablePid, MFA, 2). diff --git a/lib/dialyzer/src/dialyzer_contracts.erl b/lib/dialyzer/src/dialyzer_contracts.erl index 2b78b736ab..76f23d00b9 100644 --- a/lib/dialyzer/src/dialyzer_contracts.erl +++ b/lib/dialyzer/src/dialyzer_contracts.erl @@ -344,7 +344,7 @@ insert_constraints([{subtype, Type1, Type2}|Left], Dict) -> false -> %% A lot of things should change to add supertypes throw({error, io_lib:format("First argument of is_subtype constraint " - "must be a type variable\n", [])}) + "must be a type variable: ~p\n", [Type1])}) end; insert_constraints([], Dict) -> Dict. @@ -385,9 +385,8 @@ contract_from_form([{type, _L1, bounded_fun, RecDict, FileLine, TypeAcc, FormAcc) -> TypeFun = fun(ExpTypes, AllRecords) -> - Constr1 = [constraint_from_form(C, RecDict, ExpTypes, AllRecords) - || C <- Constr], - VarDict = insert_constraints(Constr1, dict:new()), + {Constr1, VarDict} = + process_constraints(Constr, RecDict, ExpTypes, AllRecords), Type = erl_types:t_from_form(Form, RecDict, VarDict), NewType = erl_types:t_solve_remote(Type, ExpTypes, AllRecords), {NewType, Constr1} @@ -398,18 +397,66 @@ contract_from_form([{type, _L1, bounded_fun, contract_from_form([], _RecDict, _FileLine, TypeAcc, FormAcc) -> {lists:reverse(TypeAcc), lists:reverse(FormAcc)}. -constraint_from_form({type, _, constraint, [{atom, _, is_subtype}, - [Type1, Type2]]}, RecDict, - ExpTypes, AllRecords) -> - T1 = erl_types:t_from_form(Type1, RecDict), - T2 = erl_types:t_from_form(Type2, RecDict), - T3 = erl_types:t_solve_remote(T1, ExpTypes, AllRecords), - T4 = erl_types:t_solve_remote(T2, ExpTypes, AllRecords), - {subtype, T3, T4}; -constraint_from_form({type, _, constraint, [{atom,_,Name}, List]}, _RecDict, - _ExpTypes, _AllRecords) -> - N = length(List), - throw({error, io_lib:format("Unsupported type guard ~w/~w\n", [Name, N])}). +process_constraints(Constrs, RecDict, ExpTypes, AllRecords) -> + Init = initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords), + constraints_fixpoint(Init, RecDict, ExpTypes, AllRecords). + +initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords) -> + initialize_constraints(Constrs, RecDict, ExpTypes, AllRecords, []). + +initialize_constraints([], _RecDict, _ExpTypes, _AllRecords, Acc) -> + Acc; +initialize_constraints([Constr|Rest], RecDict, ExpTypes, AllRecords, Acc) -> + case Constr of + {type, _, constraint, [{atom, _, is_subtype}, [Type1, Type2]]} -> + T1 = final_form(Type1, RecDict, ExpTypes, AllRecords, dict:new()), + Entry = {T1, Type2}, + initialize_constraints(Rest, RecDict, ExpTypes, AllRecords, [Entry|Acc]); + {type, _, constraint, [{atom,_,Name}, List]} -> + N = length(List), + throw({error, + io_lib:format("Unsupported type guard ~w/~w\n", [Name, N])}) + end. + +constraints_fixpoint(Constrs, RecDict, ExpTypes, AllRecords) -> + VarDict = + constraints_to_dict(Constrs, RecDict, ExpTypes, AllRecords, dict:new()), + constraints_fixpoint(VarDict, Constrs, RecDict, ExpTypes, AllRecords). + +constraints_fixpoint(OldVarDict, Constrs, RecDict, ExpTypes, AllRecords) -> + NewVarDict = + constraints_to_dict(Constrs, RecDict, ExpTypes, AllRecords, OldVarDict), + case NewVarDict of + OldVarDict -> + DictFold = + fun(Key, Value, Acc) -> + [{subtype, erl_types:t_var(Key), Value}|Acc] + end, + FinalConstrs = dict:fold(DictFold, [], NewVarDict), + {FinalConstrs, NewVarDict}; + _Other -> + constraints_fixpoint(NewVarDict, Constrs, RecDict, ExpTypes, AllRecords) + end. + +-define(TYPE_LIMIT, 4). + +final_form(Form, RecDict, ExpTypes, AllRecords, VarDict) -> + T1 = erl_types:t_from_form(Form, RecDict, VarDict), + T2 = erl_types:t_solve_remote(T1, ExpTypes, AllRecords), + erl_types:t_limit(T2, ?TYPE_LIMIT). + +constraints_to_dict(Constrs, RecDict, ExpTypes, AllRecords, VarDict) -> + Subtypes = + constraints_to_subs(Constrs, RecDict, ExpTypes, AllRecords, VarDict, []), + insert_constraints(Subtypes, dict:new()). + +constraints_to_subs([], _RecDict, _ExpTypes, _AllRecords, _VarDict, Acc) -> + Acc; +constraints_to_subs([C|Rest], RecDict, ExpTypes, AllRecords, VarDict, Acc) -> + {T1, Form2} = C, + T2 = final_form(Form2, RecDict, ExpTypes, AllRecords, VarDict), + NewAcc = [{subtype, T1, T2}|Acc], + constraints_to_subs(Rest, RecDict, ExpTypes, AllRecords, VarDict, NewAcc). %% Gets the most general domain of a list of domains of all %% the overloaded contracts diff --git a/lib/dialyzer/src/dialyzer_coordinator.erl b/lib/dialyzer/src/dialyzer_coordinator.erl new file mode 100644 index 0000000000..5719132215 --- /dev/null +++ b/lib/dialyzer/src/dialyzer_coordinator.erl @@ -0,0 +1,250 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File : dialyzer_coordinator.erl +%%% Authors : Stavros Aronis <[email protected]> +%%%------------------------------------------------------------------- + +-module(dialyzer_coordinator). + +%%% Export for dialyzer main process +-export([parallel_job/4]). + +%%% Exports for all possible workers +-export([wait_activation/0, job_done/3]). + +%%% Exports for the typesig and dataflow analysis workers +-export([sccs_to_pids/2, request_activation/1]). + +%%% Exports for the compilation workers +-export([get_next_label/2]). + +-export_type([coordinator/0, mode/0, init_data/0, result/0]). + +%%-------------------------------------------------------------------- + +-type collector() :: pid(). +-type regulator() :: pid(). +-type scc_to_pid() :: ets:tid() | 'unused'. + +-type coordinator() :: {collector(), regulator(), scc_to_pid()}. %%opaque +-type timing() :: dialyzer_timing:timing_server(). + +-type scc() :: [mfa_or_funlbl()]. +-type mode() :: 'typesig' | 'dataflow' | 'compile' | 'warnings'. + +-type compile_jobs() :: [file:filename()]. +-type typesig_jobs() :: [scc()]. +-type dataflow_jobs() :: [module()]. +-type warnings_jobs() :: [module()]. + +-type compile_init_data() :: dialyzer_analysis_callgraph:compile_init_data(). +-type typesig_init_data() :: dialyzer_succ_typings:typesig_init_data(). +-type dataflow_init_data() :: dialyzer_succ_typings:dataflow_init_data(). +-type warnings_init_data() :: dialyzer_succ_typings:warnings_init_data(). + +-type compile_result() :: dialyzer_analysis_callgraph:compile_result(). +-type typesig_result() :: [mfa_or_funlbl()]. +-type dataflow_result() :: [mfa_or_funlbl()]. +-type warnings_result() :: [dial_warning()]. + +-type init_data() :: compile_init_data() | typesig_init_data() | + dataflow_init_data() | warnings_init_data(). + +-type result() :: compile_result() | typesig_result() | + dataflow_result() | warnings_result(). + +-type job() :: scc() | module() | file:filename(). +-type job_result() :: dialyzer_analysis_callgraph:one_file_result() | + typesig_result() | dataflow_result() | warnings_result(). + +-record(state, {mode :: mode(), + active = 0 :: integer(), + result :: result(), + next_label = 0 :: integer(), + init_data :: init_data(), + regulator :: regulator(), + scc_to_pid :: scc_to_pid() + }). + +-include("dialyzer.hrl"). + +%%-------------------------------------------------------------------- + +-spec parallel_job('compile', compile_jobs(), compile_init_data(), timing()) -> + {compile_result(), integer()}; + ('typesig', typesig_jobs(), typesig_init_data(), timing()) -> + typesig_result(); + ('dataflow', dataflow_jobs(), dataflow_init_data(), + timing()) -> dataflow_result(); + ('warnings', warnings_jobs(), warnings_init_data(), + timing()) -> warnings_result(). + +parallel_job(Mode, Jobs, InitData, Timing) -> + State = spawn_jobs(Mode, Jobs, InitData, Timing), + collect_result(State). + +spawn_jobs(Mode, Jobs, InitData, Timing) -> + Collector = self(), + Regulator = spawn_regulator(), + TypesigOrDataflow = (Mode =:= 'typesig') orelse (Mode =:= 'dataflow'), + SCCtoPID = + case TypesigOrDataflow of + true -> ets:new(scc_to_pid, [{read_concurrency, true}]); + false -> unused + end, + Coordinator = {Collector, Regulator, SCCtoPID}, + Fold = + fun(Job, Count) -> + Pid = dialyzer_worker:launch(Mode, Job, InitData, Coordinator), + case TypesigOrDataflow of + true -> true = ets:insert(SCCtoPID, {Job, Pid}); + false -> request_activation(Regulator, Pid) + end, + Count + 1 + end, + JobCount = lists:foldl(Fold, 0, Jobs), + Unit = + case Mode of + 'typesig' -> "SCCs"; + _ -> "modules" + end, + dialyzer_timing:send_size_info(Timing, JobCount, Unit), + InitResult = + case Mode of + 'compile' -> dialyzer_analysis_callgraph:compile_init_result(); + _ -> [] + end, + #state{mode = Mode, active = JobCount, result = InitResult, next_label = 0, + init_data = InitData, regulator = Regulator, scc_to_pid = SCCtoPID}. + +collect_result(#state{mode = Mode, active = Active, result = Result, + next_label = NextLabel, init_data = InitData, + regulator = Regulator, scc_to_pid = SCCtoPID} = State) -> + receive + {next_label_request, Estimation, Pid} -> + Pid ! {next_label_reply, NextLabel}, + collect_result(State#state{next_label = NextLabel + Estimation}); + {done, Job, Data} -> + NewResult = update_result(Mode, InitData, Job, Data, Result), + case Active of + 1 -> + kill_regulator(Regulator), + case Mode of + 'compile' -> + {NewResult, NextLabel}; + X when X =:= 'typesig'; X =:= 'dataflow' -> + ets:delete(SCCtoPID), + NewResult; + 'warnings' -> + NewResult + end; + N -> + collect_result(State#state{result = NewResult, active = N - 1}) + end + end. + +update_result(Mode, InitData, Job, Data, Result) -> + case Mode of + 'compile' -> + dialyzer_analysis_callgraph:add_to_result(Job, Data, Result, + InitData); + X when X =:= 'typesig'; X =:= 'dataflow' -> + dialyzer_succ_typings:lookup_names(Data, InitData) ++ Result; + 'warnings' -> + Data ++ Result + end. + +-spec sccs_to_pids([scc() | module()], coordinator()) -> + {[dialyzer_worker:worker()], [scc() | module()]}. + +sccs_to_pids(SCCs, {_Collector, _Regulator, SCCtoPID}) -> + Fold = + fun(SCC, {Pids, Unknown}) -> + try ets:lookup_element(SCCtoPID, SCC, 2) of + Result -> {[Result|Pids], Unknown} + catch + _:_ -> {Pids, [SCC|Unknown]} + end + end, + lists:foldl(Fold, {[], []}, SCCs). + +-spec job_done(job(), job_result(), coordinator()) -> ok. + +job_done(Job, Result, {Collector, Regulator, _SCCtoPID}) -> + Regulator ! done, + Collector ! {done, Job, Result}, + ok. + +-spec get_next_label(integer(), coordinator()) -> integer(). + +get_next_label(EstimatedSize, {Collector, _Regulator, _SCCtoPID}) -> + Collector ! {next_label_request, EstimatedSize, self()}, + receive + {next_label_reply, NextLabel} -> NextLabel + end. + +-spec wait_activation() -> ok. + +wait_activation() -> + receive activate -> ok end. + +activate_pid(Pid) -> + Pid ! activate. + +-spec request_activation(coordinator()) -> ok. + +request_activation({_Collector, Regulator, _SCCtoPID}) -> + Regulator ! {req, self()}, + wait_activation(). + +request_activation(Regulator, Pid) -> + Regulator ! {req, Pid}. + +spawn_regulator() -> + InitTickets = dialyzer_utils:parallelism(), + spawn_link(fun() -> regulator_loop(InitTickets, queue:new()) end). + +regulator_loop(Tickets, Queue) -> + receive + {req, Pid} -> + case Tickets of + 0 -> + regulator_loop(0, queue:in(Pid, Queue)); + N -> + activate_pid(Pid), + regulator_loop(N-1, Queue) + end; + done -> + {Waiting, NewQueue} = queue:out(Queue), + NewTickets = + case Waiting of + empty -> Tickets + 1; + {value, Pid} -> + activate_pid(Pid), + Tickets + end, + regulator_loop(NewTickets, NewQueue); + stop -> ok + end. + +kill_regulator(Regulator) -> + Regulator ! stop. diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl index aba13278ff..7131633da1 100644 --- a/lib/dialyzer/src/dialyzer_dataflow.erl +++ b/lib/dialyzer/src/dialyzer_dataflow.erl @@ -2,7 +2,7 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -32,13 +32,11 @@ %% Data structure interfaces. -export([state__add_warning/2, state__cleanup/1, + state__duplicate/1, dispose_state/1, state__get_callgraph/1, state__get_races/1, state__get_records/1, state__put_callgraph/2, state__put_races/2, state__records_only/1]). -%% Debug and test interfaces. --export([get_top_level_signatures/2, pp/1]). - -export_type([state/0]). -include("dialyzer.hrl"). @@ -67,7 +65,7 @@ %%-define(DEBUG, true). %%-define(DEBUG_PP, true). -%%-define(DOT, true). +%%-define(DEBUG_TIME, true). -ifdef(DEBUG). -import(erl_types, [t_to_string/1]). @@ -111,7 +109,7 @@ -spec get_warnings(cerl:c_module(), dialyzer_plt:plt(), dialyzer_callgraph:callgraph(), dict(), set()) -> - {[dial_warning()], dict(), dict(), [label()], [string()]}. + {[dial_warning()], dict()}. get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) -> State1 = analyze_module(Tree, Plt, Callgraph, Records, true), @@ -119,145 +117,14 @@ get_warnings(Tree, Plt, Callgraph, Records, NoWarnUnused) -> State3 = state__renew_warnings(state__get_warnings(State2, NoWarnUnused), State2), State4 = state__get_race_warnings(State3), - Callgraph1 = State2#state.callgraph, - {State4#state.warnings, state__all_fun_types(State4), - dialyzer_callgraph:get_race_code(Callgraph1), - dialyzer_callgraph:get_public_tables(Callgraph1), - dialyzer_callgraph:get_named_tables(Callgraph1)}. + {State4#state.warnings, state__all_fun_types(State4)}. -spec get_fun_types(cerl:c_module(), dialyzer_plt:plt(), - dialyzer_callgraph:callgraph(), dict()) -> - {dict(), dict(), [label()], [string()]}. + dialyzer_callgraph:callgraph(), dict()) -> dict(). get_fun_types(Tree, Plt, Callgraph, Records) -> State = analyze_module(Tree, Plt, Callgraph, Records, false), - Callgraph1 = State#state.callgraph, - {state__all_fun_types(State), - dialyzer_callgraph:get_race_code(Callgraph1), - dialyzer_callgraph:get_public_tables(Callgraph1), - dialyzer_callgraph:get_named_tables(Callgraph1)}. - -%%-------------------------------------------------------------------- - --spec pp(file:filename()) -> 'ok'. - -pp(File) -> - {ok, Code} = dialyzer_utils:get_core_from_src(File, [no_copt]), - Plt = get_def_plt(), - AnnTree = annotate_module(Code, Plt), - io:put_chars(cerl_prettypr:format(AnnTree, [{hook, cerl_typean:pp_hook()}])), - io:nl(). - -%%-------------------------------------------------------------------- -%% This is used in the testsuite. - --spec get_top_level_signatures(cerl:c_module(), dict()) -> - [{{atom(), arity()}, erl_types:erl_type()}]. - -get_top_level_signatures(Code, Records) -> - {Tree, _} = cerl_trees:label(cerl:from_records(Code)), - Callgraph0 = dialyzer_callgraph:new(), - Callgraph1 = dialyzer_callgraph:scan_core_tree(Tree, Callgraph0), - {Callgraph2, _} = dialyzer_callgraph:remove_external(Callgraph1), - Callgraph = dialyzer_callgraph:finalize(Callgraph2), - to_dot(Callgraph), - Plt = get_def_plt(), - FunTypes = get_fun_types(Tree, Plt, Callgraph, Records), - FunTypes1 = lists:foldl(fun({V, F}, Acc) -> - Label = get_label(F), - case dict:find(Label, Acc) of - error -> - Arity = cerl:fname_arity(V), - Type = t_fun(lists:duplicate(Arity, - t_none()), - t_none()), - dict:store(Label, Type, Acc); - {ok, _} -> Acc - end - end, FunTypes, cerl:module_defs(Tree)), - dialyzer_callgraph:delete(Callgraph), - Sigs = [{{cerl:fname_id(V), cerl:fname_arity(V)}, - dict:fetch(get_label(F), FunTypes1)} - || {V, F} <- cerl:module_defs(Tree)], - ordsets:from_list(Sigs). - -get_def_plt() -> - try - dialyzer_plt:from_file(dialyzer_plt:get_default_plt()) - catch - throw:{dialyzer_error, _} -> dialyzer_plt:new() - end. - -%%% =========================================================================== -%%% -%%% Annotate all top level funs. -%%% -%%% =========================================================================== - -annotate_module(Code, Plt) -> - {Tree, _} = cerl_trees:label(cerl:from_records(Code)), - Callgraph0 = dialyzer_callgraph:new(), - Callgraph1 = dialyzer_callgraph:scan_core_tree(Tree, Callgraph0), - {Callgraph2, _} = dialyzer_callgraph:remove_external(Callgraph1), - Callgraph = dialyzer_callgraph:finalize(Callgraph2), - State = analyze_module(Tree, Plt, Callgraph), - Res = annotate(Tree, State), - dialyzer_callgraph:delete(Callgraph), - Res. - -annotate(Tree, State) -> - case cerl:subtrees(Tree) of - [] -> set_type(Tree, State); - List -> - NewSubTrees = [[annotate(Subtree, State) || Subtree <- Group] - || Group <- List], - NewTree = cerl:update_tree(Tree, NewSubTrees), - set_type(NewTree, State) - end. - -set_type(Tree, State) -> - case cerl:type(Tree) of - 'fun' -> - Type = state__fun_type(Tree, State), - case t_is_any(Type) of - true -> - cerl:set_ann(Tree, delete_ann(typesig, cerl:get_ann(Tree))); - false -> - cerl:set_ann(Tree, append_ann(typesig, Type, cerl:get_ann(Tree))) - end; - apply -> - case state__find_apply_return(Tree, State) of - unknown -> Tree; - ReturnType -> - case t_is_any(ReturnType) of - true -> - cerl:set_ann(Tree, delete_ann(type, cerl:get_ann(Tree))); - false -> - cerl:set_ann(Tree, append_ann(type, ReturnType, - cerl:get_ann(Tree))) - end - end; - _ -> - Tree - end. - -append_ann(Tag, Val, [X | Xs]) -> - if tuple_size(X) >= 1, element(1, X) =:= Tag -> - append_ann(Tag, Val, Xs); - true -> - [X | append_ann(Tag, Val, Xs)] - end; -append_ann(Tag, Val, []) -> - [{Tag, Val}]. - -delete_ann(Tag, [X | Xs]) -> - if tuple_size(X) >= 1, element(1, X) =:= Tag -> - delete_ann(Tag, Xs); - true -> - [X | delete_ann(Tag, Xs)] - end; -delete_ann(_, []) -> - []. + state__all_fun_types(State). %%% =========================================================================== %%% @@ -265,56 +132,46 @@ delete_ann(_, []) -> %%% %%% =========================================================================== -analyze_module(Tree, Plt, Callgraph) -> - analyze_module(Tree, Plt, Callgraph, dict:new(), false). - analyze_module(Tree, Plt, Callgraph, Records, GetWarnings) -> debug_pp(Tree, false), Module = cerl:atom_val(cerl:module_name(Tree)), RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceCode = dialyzer_callgraph:get_race_code(Callgraph), BehaviourTranslations = case RaceDetection of true -> dialyzer_behaviours:translatable_behaviours(Tree); false -> [] end, TopFun = cerl:ann_c_fun([{label, top}], [], Tree), - State = state__new(dialyzer_callgraph:race_code_new(Callgraph), - TopFun, Plt, Module, Records, BehaviourTranslations), + State = + state__new(Callgraph, TopFun, Plt, Module, Records, BehaviourTranslations), State1 = state__race_analysis(not GetWarnings, State), State2 = analyze_loop(State1), case GetWarnings of true -> State3 = state__set_warning_mode(State2), State4 = analyze_loop(State3), - State5 = state__restore_race_code(RaceCode, State4), %% EXPERIMENTAL: Turn all behaviour API calls into calls to the %% respective callback module's functions. case BehaviourTranslations of - [] -> dialyzer_races:race(State5); + [] -> dialyzer_races:race(State4); Behaviours -> - Callgraph2 = State5#state.callgraph, - Digraph = dialyzer_callgraph:get_digraph(Callgraph2), + Digraph = dialyzer_callgraph:get_digraph(State4#state.callgraph), TranslatedCallgraph = dialyzer_behaviours:translate_callgraph(Behaviours, Module, - Callgraph2), + Callgraph), St = - dialyzer_races:race(State5#state{callgraph = TranslatedCallgraph}), - Callgraph3 = dialyzer_callgraph:put_digraph(Digraph, - St#state.callgraph), - St#state{callgraph = Callgraph3} + dialyzer_races:race(State4#state{callgraph = TranslatedCallgraph}), + FinalCallgraph = dialyzer_callgraph:put_digraph(Digraph, + St#state.callgraph), + St#state{callgraph = FinalCallgraph} end; false -> - Callgraph1 = State2#state.callgraph, - RaceCode1 = dialyzer_callgraph:get_race_code(Callgraph1), - state__restore_race_code( - dict:merge(fun (_K, V1, _V2) -> V1 end, - RaceCode, RaceCode1), State2) + State2 end. -analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> +analyze_loop(State) -> case state__get_work(State) of none -> State; {Fun, NewState1} -> @@ -340,10 +197,9 @@ analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> Map1 = enter_type_lists(Vars, ArgTypes, Map), Body = cerl:fun_body(Fun), FunLabel = get_label(Fun), - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceAnalysis = dialyzer_races:get_race_analysis(Races), + IsRaceAnalysisEnabled = is_race_analysis_enabled(State), NewState3 = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> NewState2 = state__renew_curr_fun( state__lookup_name(FunLabel, NewState1), FunLabel, @@ -357,17 +213,8 @@ analyze_loop(#state{callgraph = Callgraph, races = Races} = State) -> [state__lookup_name(get_label(Fun), State), t_to_string(t_fun(ArgTypes, BodyType))]), NewState5 = - case RaceDetection andalso RaceAnalysis of - true -> - Races1 = NewState4#state.races, - Code = lists:reverse(dialyzer_races:get_race_list(Races1)), - Callgraph1 = - renew_code(dialyzer_races:get_curr_fun(Races1), - dialyzer_races:get_curr_fun_args(Races1), - Code, - state__warning_mode(NewState4), - NewState4#state.callgraph), - NewState4#state{callgraph = Callgraph1}; + case IsRaceAnalysisEnabled of + true -> renew_race_code(NewState4); false -> NewState4 end, NewState6 = @@ -582,9 +429,7 @@ handle_apply_or_call([{local, external}|Left], Args, ArgTypes, Map, Tree, State, ArgTypes, t_any()); handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], Args, ArgTypes, Map, Tree, - #state{callgraph = Callgraph, races = Races, - opaques = Opaques} = State, - AccArgTypes, AccRet) -> + #state{opaques = Opaques} = State, AccArgTypes, AccRet) -> Any = t_any(), AnyArgs = [Any || _ <- Args], GenSig = {AnyArgs, fun(_) -> t_any() end}, @@ -680,8 +525,7 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], ?debug("ContrRet: ~s\n", [erl_types:t_to_string(CRange(TmpArgTypes))]), ?debug("SigRet: ~s\n", [erl_types:t_to_string(SigRange)]), State1 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> Ann = cerl:get_ann(Tree), File = get_file(Ann), @@ -761,7 +605,13 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left], true -> AccArgTypes; false -> [t_sup(X, Y) || {X, Y} <- lists:zip(NewArgTypes, AccArgTypes)] end, - NewAccRet = t_sup(AccRet, t_inf(RetWithoutLocal, LocalRet, opaque)), + TotalRet = + case t_is_none(LocalRet) andalso t_is_unit(RetWithoutLocal) of + true -> RetWithoutLocal; + false -> t_inf(RetWithoutLocal, LocalRet, opaque) + end, + NewAccRet = t_sup(AccRet, TotalRet), + ?debug("NewAccRet: ~s\n", [t_to_string(NewAccRet)]), handle_apply_or_call(Left, Args, ArgTypes, Map, Tree, State3, NewAccArgTypes, NewAccRet); handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State, @@ -1041,20 +891,17 @@ handle_call(Tree, Map, State) -> %%---------------------------------------- -handle_case(Tree, Map, #state{callgraph = Callgraph} = State) -> +handle_case(Tree, Map, State) -> Arg = cerl:case_arg(Tree), Clauses = filter_match_fail(cerl:case_clauses(Tree)), {State1, Map1, ArgType} = SMA = traverse(Arg, Map, State), case t_is_none_or_unit(ArgType) of true -> SMA; false -> - Races = State1#state.races, State2 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State1), state__renew_race_list([beg_case|RaceList], RaceListSize + 1, State1); false -> State1 @@ -1088,9 +935,8 @@ handle_cons(Tree, Map, State) -> %%---------------------------------------- -handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> - RaceAnalysis = dialyzer_races:get_race_analysis(Races), - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), +handle_let(Tree, Map, State) -> + IsRaceAnalysisEnabled = is_race_analysis_enabled(State), Arg = cerl:let_arg(Tree), Vars = cerl:let_vars(Tree), {Map0, State0} = @@ -1098,10 +944,9 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> true -> [Var] = Vars, {enter_subst(Var, Arg, Map), - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list( [dialyzer_races:let_tag_new(Var, Arg)|RaceList], RaceListSize + 1, State); @@ -1111,9 +956,8 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> end, Body = cerl:let_body(Tree), {State1, Map1, ArgTypes} = SMA = traverse(Arg, Map0, State0), - Callgraph1 = State1#state.callgraph, - Callgraph2 = - case RaceDetection andalso RaceAnalysis andalso cerl:is_c_call(Arg) of + State2 = + case IsRaceAnalysisEnabled andalso cerl:is_c_call(Arg) of true -> Mod = cerl:call_module(Arg), Name = cerl:call_name(Arg), @@ -1121,16 +965,11 @@ handle_let(Tree, Map, #state{callgraph = Callgraph, races = Races} = State) -> cerl:concrete(Mod) =:= ets andalso cerl:is_literal(Name) andalso cerl:concrete(Name) =:= new of - true -> - NewTable = dialyzer_races:get_new_table(State1#state.races), - renew_public_tables(Vars, NewTable, - state__warning_mode(State1), - Callgraph1); - false -> Callgraph1 + true -> renew_race_public_tables(Vars, State1); + false -> State1 end; - false -> Callgraph1 + false -> State1 end, - State2 = State1#state{callgraph = Callgraph2}, case t_is_none_or_unit(ArgTypes) of true -> SMA; false -> @@ -1161,16 +1000,13 @@ handle_module(Tree, Map, State) -> %%---------------------------------------- -handle_receive(Tree, Map, - #state{callgraph = Callgraph, races = Races} = State) -> +handle_receive(Tree, Map, State) -> Clauses = filter_match_fail(cerl:receive_clauses(Tree)), Timeout = cerl:receive_timeout(Tree), State1 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list([beg_case|RaceList], RaceListSize + 1, State); false -> State @@ -1293,16 +1129,13 @@ handle_tuple(Tree, Map, State) -> %%---------------------------------------- %% Clauses %% -handle_clauses([C|Left], Arg, ArgType, OrigArgType, - #state{callgraph = Callgraph, races = Races} = State, - CaseTypes, MapIn, Acc, ClauseAcc) -> - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceAnalysis = dialyzer_races:get_race_analysis(Races), +handle_clauses([C|Left], Arg, ArgType, OrigArgType, State, CaseTypes, MapIn, + Acc, ClauseAcc) -> + IsRaceAnalysisEnabled = is_race_analysis_enabled(State), State1 = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> - RaceList = dialyzer_races:get_race_list(Races), - RaceListSize = dialyzer_races:get_race_list_size(Races), + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list( [dialyzer_races:beg_clause_new(Arg, cerl:clause_pats(C), cerl:clause_guard(C))| @@ -1313,11 +1146,9 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, {State2, ClauseMap, BodyType, NewArgType} = do_clause(C, Arg, ArgType, OrigArgType, MapIn, State1), {NewClauseAcc, State3} = - case RaceDetection andalso RaceAnalysis of + case IsRaceAnalysisEnabled of true -> - Races1 = State2#state.races, - RaceList1 = dialyzer_races:get_race_list(Races1), - RaceListSize1 = dialyzer_races:get_race_list_size(Races1), + {RaceList1, RaceListSize1} = get_race_list_and_size(State2), EndClause = dialyzer_races:end_clause_new(Arg, cerl:clause_pats(C), cerl:clause_guard(C)), {[EndClause|ClauseAcc], @@ -1332,30 +1163,25 @@ handle_clauses([C|Left], Arg, ArgType, OrigArgType, end, handle_clauses(Left, Arg, NewArgType, OrigArgType, State3, NewCaseTypes, MapIn, NewAcc, NewClauseAcc); -handle_clauses([], _Arg, _ArgType, _OrigArgType, - #state{callgraph = Callgraph, races = Races} = State, - CaseTypes, _MapIn, Acc, ClauseAcc) -> +handle_clauses([], _Arg, _ArgType, _OrigArgType, State, CaseTypes, _MapIn, Acc, + ClauseAcc) -> State1 = - case dialyzer_callgraph:get_race_detection(Callgraph) andalso - dialyzer_races:get_race_analysis(Races) of + case is_race_analysis_enabled(State) of true -> + {RaceList, RaceListSize} = get_race_list_and_size(State), state__renew_race_list( - [dialyzer_races:end_case_new(ClauseAcc)| - dialyzer_races:get_race_list(Races)], - dialyzer_races:get_race_list_size(Races) + 1, State); + [dialyzer_races:end_case_new(ClauseAcc)|RaceList], + RaceListSize + 1, State); false -> State end, {lists:reverse(Acc), State1, t_sup(CaseTypes)}. -do_clause(C, Arg, ArgType0, OrigArgType, Map, - #state{callgraph = Callgraph, races = Races} = State) -> +do_clause(C, Arg, ArgType0, OrigArgType, Map, State) -> Pats = cerl:clause_pats(C), Guard = cerl:clause_guard(C), Body = cerl:clause_body(C), - RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), - RaceAnalysis = dialyzer_races:get_race_analysis(Races), State1 = - case RaceDetection andalso RaceAnalysis of + case is_race_analysis_enabled(State) of true -> state__renew_fun_args(Pats, State); false -> State @@ -2642,14 +2468,18 @@ join_maps(Maps, MapOut) -> Keys = ordsets:from_list(dict:fetch_keys(Dict) ++ dict:fetch_keys(Subst)), join_maps(Keys, Maps, MapOut). -join_maps([Key|Left], Maps, MapOut) -> +join_maps(Keys, Maps, MapOut) -> + KTs = join_maps_collect(Keys, Maps, MapOut), + lists:foldl(fun({K, T}, M) -> enter_type(K, T, M) end, MapOut, KTs). + +join_maps_collect([Key|Left], Maps, MapOut) -> Type = join_maps_one_key(Maps, Key, t_none()), case t_is_equal(lookup_type(Key, MapOut), Type) of - true -> join_maps(Left, Maps, MapOut); - false -> join_maps(Left, Maps, enter_type(Key, Type, MapOut)) + true -> join_maps_collect(Left, Maps, MapOut); + false -> [{Key, Type} | join_maps_collect(Left, Maps, MapOut)] end; -join_maps([], _Maps, MapOut) -> - MapOut. +join_maps_collect([], _Maps, _MapOut) -> + []. join_maps_one_key([Map|Left], Key, AccType) -> case t_is_any(AccType) of @@ -2908,10 +2738,6 @@ state__set_warning_mode(#state{tree_map = TreeMap, fun_tab = FunTab, fun_tab = FunTab, warning_mode = true, races = dialyzer_races:put_race_analysis(true, Races)}. -state__restore_race_code(RaceCode, #state{callgraph = Callgraph} = State) -> - State#state{callgraph = dialyzer_callgraph:put_race_code(RaceCode, - Callgraph)}. - state__race_analysis(Analysis, #state{races = Races} = State) -> State#state{races = dialyzer_races:put_race_analysis(Analysis, Races)}. @@ -3109,6 +2935,7 @@ init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt, Opaques) -> NewDict = dict:store(Fun, FunEntry, Dict), init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt, Opaques); init_fun_tab([], Dict, _TreeMap, _Callgraph, _Plt, _Opaques) -> + ?debug("DICT:~p\n",[dict:to_list(Dict)]), Dict. state__update_fun_env(Tree, Map, #state{envs = Envs} = State) -> @@ -3140,7 +2967,9 @@ state__fun_type(Fun, #state{fun_tab = FunTab}) -> if is_integer(Fun) -> Fun; true -> get_label(Fun) end, - case dict:find(Label, FunTab) of + Entry = dict:find(Label, FunTab), + ?debug("FunType ~p:~p\n",[Label, Entry]), + case Entry of {ok, {not_handled, {A, R}}} -> t_fun(A, R); {ok, {A, R}} -> @@ -3248,23 +3077,9 @@ state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) -> {not_handled, {_Args, Ret}} -> Ret; {_Args, Ret} -> Ret end, + ?debug("LocalRet: ~s\n", [t_to_string(LocalRet)]), {Fun, Sig, Contract, LocalRet}. -state__find_apply_return(Tree, #state{callgraph = Callgraph} = State) -> - Apply = get_label(Tree), - case dialyzer_callgraph:lookup_call_site(Apply, Callgraph) of - error -> - unknown; - {ok, List} -> - case lists:member(external, List) of - true -> t_any(); - false -> - FunTypes = [state__fun_type(F, State) || F <- List], - Returns = [t_fun_range(F) || F <- FunTypes], - t_sup(Returns) - end - end. - forward_args(Fun, ArgTypes, #state{work = Work, fun_tab = FunTab} = State) -> {OldArgTypes, OldOut, Fixpoint} = case dict:find(Fun, FunTab) of @@ -3295,6 +3110,16 @@ state__cleanup(#state{callgraph = Callgraph, races = dialyzer_races:cleanup(Races), records = Records}. +-spec state__duplicate(state()) -> state(). + +state__duplicate(#state{callgraph = Callgraph} = State) -> + State#state{callgraph = dialyzer_callgraph:duplicate(Callgraph)}. + +-spec dispose_state(state()) -> ok. + +dispose_state(#state{callgraph = Callgraph}) -> + dialyzer_callgraph:dispose_race_server(Callgraph). + -spec state__get_callgraph(state()) -> dialyzer_callgraph:callgraph(). state__get_callgraph(#state{callgraph = Callgraph}) -> @@ -3332,26 +3157,36 @@ state__records_only(#state{records = Records}) -> %%% %%% =========================================================================== -renew_code(Fun, FunArgs, Code, WarningMode, Callgraph) -> +is_race_analysis_enabled(#state{races = Races, callgraph = Callgraph}) -> + RaceDetection = dialyzer_callgraph:get_race_detection(Callgraph), + RaceAnalysis = dialyzer_races:get_race_analysis(Races), + RaceDetection andalso RaceAnalysis. + +get_race_list_and_size(#state{races = Races}) -> + dialyzer_races:get_race_list_and_size(Races). + +renew_race_code(#state{races = Races, callgraph = Callgraph, + warning_mode = WarningMode} = State) -> case WarningMode of - true -> Callgraph; + true -> State; false -> - RaceCode = dialyzer_callgraph:get_race_code(Callgraph), - dialyzer_callgraph:put_race_code( - dict:store(Fun, [FunArgs, Code], RaceCode), Callgraph) + NewCallgraph = dialyzer_callgraph:renew_race_code(Races, Callgraph), + State#state{callgraph = NewCallgraph} end. -renew_public_tables([Var], Table, WarningMode, Callgraph) -> +renew_race_public_tables([Var], #state{races = Races, callgraph = Callgraph, + warning_mode = WarningMode} = State) -> case WarningMode of - true -> Callgraph; + true -> State; false -> + Table = dialyzer_races:get_new_table(Races), case Table of - no_t -> Callgraph; - _Other -> - VarLabel = get_label(Var), - PTables = dialyzer_callgraph:get_public_tables(Callgraph), - dialyzer_callgraph:put_public_tables( - lists:usort([VarLabel|PTables]), Callgraph) + no_t -> State; + _Other -> + VarLabel = get_label(Var), + NewCallgraph = + dialyzer_callgraph:renew_race_public_tables(VarLabel, Callgraph), + State#state{callgraph = NewCallgraph} end end. @@ -3678,17 +3513,3 @@ strip_annotations(Tree) -> debug_pp(_Tree, _UseHook) -> ok. -endif. - -%%---------------------------------------------------------------------------- - --spec to_dot(dialyzer_callgraph:callgraph()) -> 'ok'. - --ifdef(DOT). -to_dot(CG) -> - dialyzer_callgraph:to_dot(CG). --else. -to_dot(_CG) -> - ok. --endif. - -%%---------------------------------------------------------------------------- diff --git a/lib/dialyzer/src/dialyzer_gui.erl b/lib/dialyzer/src/dialyzer_gui.erl index bac659548f..ac9844c22c 100644 --- a/lib/dialyzer/src/dialyzer_gui.erl +++ b/lib/dialyzer/src/dialyzer_gui.erl @@ -2,7 +2,7 @@ %%------------------------------------------------------------------------ %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl index 9ff32bd8b1..b26aa695bd 100644 --- a/lib/dialyzer/src/dialyzer_gui_wx.erl +++ b/lib/dialyzer/src/dialyzer_gui_wx.erl @@ -2,7 +2,7 @@ %%------------------------------------------------------------------------ %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,39 +40,38 @@ %%------------------------------------------------------------------------ --type wx_object() :: any(). %% XXX: should be imported from wx - --record(menu, {file :: wx_object(), - warnings :: wx_object(), - plt :: wx_object(), - options :: wx_object(), - help :: wx_object()}). - --record(gui_state, {add :: wx_object(), - add_dir :: wx_object(), - add_rec :: wx_object(), - chosen_box :: wx_object(), +-record(menu, {file :: wx:wx_object(), + warnings :: wx:wx_object(), + plt :: wx:wx_object(), + options :: wx:wx_object(), + help :: wx:wx_object()}). +-type menu() :: #menu{}. + +-record(gui_state, {add :: wx:wx_object(), + add_dir :: wx:wx_object(), + add_rec :: wx:wx_object(), + chosen_box :: wx:wx_object(), analysis_pid :: pid(), - del_file :: wx_object(), + del_file :: wx:wx_object(), doc_plt :: dialyzer_plt:plt(), - clear_chosen :: wx_object(), - clear_log :: wx_object(), - explain_warn :: wx_object(), - clear_warn :: wx_object(), + clear_chosen :: wx:wx_object(), + clear_log :: wx:wx_object(), + explain_warn :: wx:wx_object(), + clear_warn :: wx:wx_object(), init_plt :: dialyzer_plt:plt(), - dir_entry :: wx_object(), - file_box :: wx_object(), + dir_entry :: wx:wx_object(), + file_box :: wx:wx_object(), files_to_analyze :: ordset(string()), - gui :: wx_object(), - log :: wx_object(), - menu :: #menu{}, - mode :: wx_object(), + gui :: wx:wx_object(), + log :: wx:wx_object(), + menu :: menu(), + mode :: wx:wx_object(), options :: #options{}, - run :: wx_object(), - stop :: wx_object(), - frame :: wx_object(), - warnings_box :: wx_object(), - explanation_box :: wx_object(), + run :: wx:wx_object(), + stop :: wx:wx_object(), + frame :: wx:wx_object(), + warnings_box :: wx:wx_object(), + explanation_box :: wx:wx_object(), wantedWarnings :: list(), rawWarnings :: list(), backend_pid :: pid(), @@ -824,7 +823,7 @@ build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options, 1 -> src_code end, InitPlt = - case wxMenu:isChecked(Menu#menu.plt,?menuID_PLT_INIT_EMPTY) of + case wxMenu:isChecked(Menu#menu.plt, ?menuID_PLT_INIT_EMPTY) of true -> dialyzer_plt:new(); false -> InitPlt0 end, diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl index 866650a0b2..06672e595f 100644 --- a/lib/dialyzer/src/dialyzer_options.erl +++ b/lib/dialyzer/src/dialyzer_options.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -194,6 +194,11 @@ build_options([{OptionName, Value} = Term|Rest], Options) -> callgraph_file -> assert_filename(Value), build_options(Rest, Options#options{callgraph_file = Value}); + timing -> + build_options(Rest, Options#options{timing = Value}); + solvers -> + assert_solvers(Value), + build_options(Rest, Options#options{solvers = Value}); _ -> bad_option("Unknown dialyzer command line option", Term) end; @@ -255,6 +260,15 @@ is_plt_mode(plt_remove) -> true; is_plt_mode(plt_check) -> true; is_plt_mode(succ_typings) -> false. +assert_solvers([]) -> + ok; +assert_solvers([v1|Terms]) -> + assert_solvers(Terms); +assert_solvers([v2|Terms]) -> + assert_solvers(Terms); +assert_solvers([Term|_]) -> + bad_option("Illegal value for solver", Term). + -spec build_warnings([atom()], [dial_warning()]) -> [dial_warning()]. build_warnings([Opt|Opts], Warnings) -> diff --git a/lib/dialyzer/src/dialyzer_plt.erl b/lib/dialyzer/src/dialyzer_plt.erl index 74892d1668..5f64099210 100644 --- a/lib/dialyzer/src/dialyzer_plt.erl +++ b/lib/dialyzer/src/dialyzer_plt.erl @@ -2,7 +2,7 @@ %%---------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -55,7 +55,10 @@ plt_and_info_from_file/1, get_specs/1, get_specs/4, - to_file/4]). + to_file/4, + get_mini_plt/1, + restore_full_plt/2 + ]). %% Debug utilities -export([pp_non_returning/0, pp_mod/1]). @@ -82,7 +85,13 @@ contracts = table_new() :: dict(), callbacks = table_new() :: dict(), exported_types = sets:new() :: set()}). --opaque plt() :: #plt{}. + +-record(mini_plt, {info :: ets:tid(), + contracts :: ets:tid(), + callbacks :: ets:tid() + }). + +-opaque plt() :: #plt{} | #mini_plt{}. -include("dialyzer.hrl"). @@ -132,51 +141,48 @@ delete_list(#plt{info = Info, types = Types, -spec insert_contract_list(plt(), dialyzer_contracts:plt_contracts()) -> plt(). -insert_contract_list(#plt{contracts = Contracts} = PLT, List) -> - PLT#plt{contracts = table_insert_list(Contracts, List)}. +insert_contract_list(#mini_plt{contracts = Contracts} = PLT, List) -> + true = ets:insert(Contracts, List), + PLT. -spec insert_callbacks(plt(), dialyzer_codeserver:codeserver()) -> plt(). insert_callbacks(#plt{callbacks = Callbacks} = Plt, Codeserver) -> - FunPreferNew = fun(_Key, _Val1, Val2) -> Val2 end, - FunDictMerger = - fun(_Key, Value, AccIn) -> dict:merge(FunPreferNew, Value, AccIn) end, - MergedCallbacks = dict:fold(FunDictMerger, dict:new(), - dialyzer_codeserver:get_callbacks(Codeserver)), - List = dict:to_list(MergedCallbacks), + List = dialyzer_codeserver:get_callbacks(Codeserver), Plt#plt{callbacks = table_insert_list(Callbacks, List)}. -spec lookup_contract(plt(), mfa_patt()) -> 'none' | {'value', #contract{}}. -lookup_contract(#plt{contracts = Contracts}, +lookup_contract(#mini_plt{contracts = ETSContracts}, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> - table_lookup(Contracts, MFA). + ets_table_lookup(ETSContracts, MFA). -spec lookup_callbacks(plt(), module()) -> - [{mfa(), {{Filename::string(), Line::pos_integer()}, #contract{}}}]. + 'none' | {'value', [{mfa(), {{Filename::string(), + Line::pos_integer()}, + #contract{}}}]}. -lookup_callbacks(#plt{callbacks = Callbacks}, Mod) when is_atom(Mod) -> - FunModFilter = - fun({M, _F, _A}, _Val) -> M =:= Mod; - ( _Key, _Val) -> false - end, - ModCallbacks = dict:filter(FunModFilter, Callbacks), - dict:to_list(ModCallbacks). +lookup_callbacks(#mini_plt{callbacks = ETSCallbacks}, Mod) when is_atom(Mod) -> + ets_table_lookup(ETSCallbacks, Mod). -type ret_args_types() :: {erl_types:erl_type(), [erl_types:erl_type()]}. -spec insert_list(plt(), [{mfa() | integer(), ret_args_types()}]) -> plt(). -insert_list(#plt{info = Info} = PLT, List) -> - PLT#plt{info = table_insert_list(Info, List)}. +insert_list(#mini_plt{info = Info} = PLT, List) -> + true = ets:insert(Info, List), + PLT. -spec lookup(plt(), integer() | mfa_patt()) -> 'none' | {'value', ret_args_types()}. -lookup(#plt{info = Info}, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> - table_lookup(Info, MFA); -lookup(#plt{info = Info}, Label) when is_integer(Label) -> - table_lookup(Info, Label). +lookup(Plt, {M, F, _} = MFA) when is_atom(M), is_atom(F) -> + lookup_1(Plt, MFA); +lookup(Plt, Label) when is_integer(Label) -> + lookup_1(Plt, Label). + +lookup_1(#mini_plt{info = Info}, MFAorLabel) -> + ets_table_lookup(Info, MFAorLabel). -spec insert_types(plt(), dict()) -> plt(). @@ -503,6 +509,34 @@ init_md5_list_1([], DiffList, Acc) -> init_md5_list_1(Md5List, [], Acc) -> {ok, lists:reverse(Acc, Md5List)}. +-spec get_mini_plt(plt()) -> plt(). + +get_mini_plt(#plt{info = Info, contracts = Contracts, callbacks = Callbacks}) -> + [ETSInfo, ETSContracts, ETSCallbacks] = + [ets:new(Name, [public]) || Name <- [plt_info, plt_contracts, plt_callbacks]], + CallbackList = dict:to_list(Callbacks), + CallbacksByModule = + [{M, [Cb || {{M1,_,_},_} = Cb <- CallbackList, M1 =:= M]} || + M <- lists:usort([M || {{M,_,_},_} <- CallbackList])], + [true, true] = + [ets:insert(ETS, dict:to_list(Data)) || + {ETS, Data} <- [{ETSInfo, Info}, {ETSContracts, Contracts}]], + true = ets:insert(ETSCallbacks, CallbacksByModule), + #mini_plt{info = ETSInfo, contracts = ETSContracts, callbacks = ETSCallbacks}; +get_mini_plt(undefined) -> + undefined. + +-spec restore_full_plt(plt(), plt()) -> plt(). + +restore_full_plt(#mini_plt{info = ETSInfo, contracts = ETSContracts}, Plt) -> + Info = dict:from_list(ets:tab2list(ETSInfo)), + Contracts = dict:from_list(ets:tab2list(ETSContracts)), + ets:delete(ETSContracts), + ets:delete(ETSInfo), + Plt#plt{info = Info, contracts = Contracts}; +restore_full_plt(undefined, undefined) -> + undefined. + %%--------------------------------------------------------------------------- %% Edoc @@ -595,6 +629,13 @@ table_lookup(Plt, Obj) -> {ok, Val} -> {value, Val} end. +ets_table_lookup(Plt, Obj) -> + try ets:lookup_element(Plt, Obj, 2) of + Val -> {value, Val} + catch + _:_ -> none + end. + table_lookup_module(Plt, Mod) -> List = dict:fold(fun(Key, Val, Acc) -> case Key of diff --git a/lib/dialyzer/src/dialyzer_races.erl b/lib/dialyzer/src/dialyzer_races.erl index b9594f5301..cdb9f25999 100644 --- a/lib/dialyzer/src/dialyzer_races.erl +++ b/lib/dialyzer/src/dialyzer_races.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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,6 +36,7 @@ -export([beg_clause_new/3, cleanup/1, end_case_new/1, end_clause_new/3, get_curr_fun/1, get_curr_fun_args/1, get_new_table/1, get_race_analysis/1, get_race_list/1, get_race_list_size/1, + get_race_list_and_size/1, let_tag_new/2, new/0, put_curr_fun/3, put_fun_args/2, put_race_analysis/2, put_race_list/3]). @@ -346,6 +347,7 @@ fixup_race_list(RaceWarnTag, WarnVarArgs, State) -> DepList2 = fixup_race_list_helper(NewParents, Calls, CurrFun, WarnVarArgs, RaceWarnTag, NewState), + dialyzer_dataflow:dispose_state(CleanState), lists:usort(cleanup_dep_calls(DepList1 ++ DepList2)). fixup_race_list_helper(Parents, Calls, CurrFun, WarnVarArgs, RaceWarnTag, @@ -380,13 +382,15 @@ fixup_race_forward_pullout(CurrFun, CurrFunLabel, Calls, Code, RaceList, InitFun, WarnVarArgs, RaceWarnTag, RaceVarMap, FunDefVars, FunCallVars, FunArgTypes, NestingLevel, State) -> + TState = dialyzer_dataflow:state__duplicate(State), {DepList, NewCurrFun, NewCurrFunLabel, NewCalls, NewCode, NewRaceList, NewRaceVarMap, NewFunDefVars, NewFunCallVars, NewFunArgTypes, NewNestingLevel} = fixup_race_forward(CurrFun, CurrFunLabel, Calls, Code, RaceList, InitFun, WarnVarArgs, RaceWarnTag, RaceVarMap, FunDefVars, FunCallVars, FunArgTypes, NestingLevel, - cleanup_race_code(State)), + cleanup_race_code(TState)), + dialyzer_dataflow:dispose_state(TState), case NewCode of [] -> DepList; [#fun_call{caller = NewCurrFun, callee = Call, arg_types = FunTypes, @@ -2434,6 +2438,12 @@ get_race_list(#races{race_list = RaceList}) -> get_race_list_size(#races{race_list_size = RaceListSize}) -> RaceListSize. +-spec get_race_list_and_size(races()) -> {code(), non_neg_integer()}. + +get_race_list_and_size(#races{race_list = RaceList, + race_list_size = RaceListSize}) -> + {RaceList, RaceListSize}. + -spec let_tag_new(var_to_map1(), var_to_map1()) -> #let_tag{}. let_tag_new(Var, Arg) -> diff --git a/lib/dialyzer/src/dialyzer_succ_typings.erl b/lib/dialyzer/src/dialyzer_succ_typings.erl index de4c8a32a3..84379642bf 100644 --- a/lib/dialyzer/src/dialyzer_succ_typings.erl +++ b/lib/dialyzer/src/dialyzer_succ_typings.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,15 +28,22 @@ -module(dialyzer_succ_typings). -export([analyze_callgraph/3, - analyze_callgraph/4, - get_warnings/6]). + analyze_callgraph/6, + get_warnings/8 + ]). -%% These are only intended as debug functions. --export([doit/1, - get_top_level_signatures/3]). +-export([ + find_succ_types_for_scc/2, + refine_one_module/2, + find_required_by/2, + find_depends_on/2, + collect_warnings/2, + lookup_names/2 + ]). + +-export_type([typesig_init_data/0, dataflow_init_data/0, warnings_init_data/0]). %%-define(DEBUG, true). -%%-define(DEBUG_PP, true). -ifdef(DEBUG). -define(debug(X__, Y__), io:format(X__, Y__)). @@ -54,11 +61,21 @@ %% State record -- local to this module -type parent() :: 'none' | pid(). +-type typesig_init_data() :: term(). +-type dataflow_init_data() :: term(). +-type warnings_init_data() :: term(). + +-type fixpoint_init_data() :: typesig_init_data() | dataflow_init_data(). + +-type scc() :: [mfa_or_funlbl()] | [module()]. + -record(st, {callgraph :: dialyzer_callgraph:callgraph(), codeserver :: dialyzer_codeserver:codeserver(), no_warn_unused :: set(), parent = none :: parent(), + timing_server :: dialyzer_timing:timing_server(), + solvers :: [solver()], plt :: dialyzer_plt:plt()}). %%-------------------------------------------------------------------- @@ -68,60 +85,90 @@ dialyzer_plt:plt(). analyze_callgraph(Callgraph, Plt, Codeserver) -> - analyze_callgraph(Callgraph, Plt, Codeserver, none). + analyze_callgraph(Callgraph, Plt, Codeserver, none, [], none). -spec analyze_callgraph(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(), - dialyzer_codeserver:codeserver(), parent()) -> + dialyzer_codeserver:codeserver(), + dialyzer_timing:timing_server(), + [solver()], parent()) -> dialyzer_plt:plt(). -analyze_callgraph(Callgraph, Plt, Codeserver, Parent) -> - State = #st{callgraph = Callgraph, plt = Plt, - codeserver = Codeserver, parent = Parent}, - NewState = get_refined_success_typings(State), - NewState#st.plt. +analyze_callgraph(Callgraph, Plt, Codeserver, TimingServer, Solvers, Parent) -> + NewState = + init_state_and_get_success_typings(Callgraph, Plt, Codeserver, + TimingServer, Solvers, Parent), + dialyzer_plt:restore_full_plt(NewState#st.plt, Plt). %%-------------------------------------------------------------------- -get_refined_success_typings(State) -> - case find_succ_typings(State) of +init_state_and_get_success_typings(Callgraph, Plt, Codeserver, + TimingServer, Solvers, Parent) -> + {SCCs, Callgraph1} = + ?timing(TimingServer, "order", dialyzer_callgraph:finalize(Callgraph)), + State = #st{callgraph = Callgraph1, plt = dialyzer_plt:get_mini_plt(Plt), + codeserver = Codeserver, parent = Parent, + timing_server = TimingServer, solvers = Solvers}, + get_refined_success_typings(SCCs, State). + +get_refined_success_typings(SCCs, #st{callgraph = Callgraph, + timing_server = TimingServer} = State) -> + case find_succ_typings(SCCs, State) of {fixpoint, State1} -> State1; {not_fixpoint, NotFixpoint1, State1} -> - Callgraph = State1#st.callgraph, - NotFixpoint2 = [lookup_name(F, Callgraph) || F <- NotFixpoint1], - ModulePostorder = - dialyzer_callgraph:module_postorder_from_funs(NotFixpoint2, Callgraph), - case refine_succ_typings(ModulePostorder, State1) of + {ModulePostorder, ModCallgraph} = + ?timing( + TimingServer, "order", _C1, + dialyzer_callgraph:module_postorder_from_funs(NotFixpoint1, + Callgraph)), + ModState = State1#st{callgraph = ModCallgraph}, + case refine_succ_typings(ModulePostorder, ModState) of {fixpoint, State2} -> State2; - {not_fixpoint, NotFixpoint3, State2} -> - Callgraph1 = State2#st.callgraph, + {not_fixpoint, NotFixpoint2, State2} -> %% Need to reset the callgraph. - NotFixpoint4 = [lookup_name(F, Callgraph1) || F <- NotFixpoint3], - Callgraph2 = dialyzer_callgraph:reset_from_funs(NotFixpoint4, - Callgraph1), - get_refined_success_typings(State2#st{callgraph = Callgraph2}) + {NewSCCs, Callgraph2} = + ?timing(TimingServer, "order", _C2, + dialyzer_callgraph:reset_from_funs(NotFixpoint2, + ModCallgraph)), + NewState = State2#st{callgraph = Callgraph2}, + get_refined_success_typings(NewSCCs, NewState) end end. -type doc_plt() :: 'undefined' | dialyzer_plt:plt(). -spec get_warnings(dialyzer_callgraph:callgraph(), dialyzer_plt:plt(), doc_plt(), dialyzer_codeserver:codeserver(), set(), - pid()) -> + dialyzer_timing:timing_server(), [solver()], pid()) -> {[dial_warning()], dialyzer_plt:plt(), doc_plt()}. -get_warnings(Callgraph, Plt, DocPlt, Codeserver, NoWarnUnused, Parent) -> - InitState = #st{callgraph = Callgraph, codeserver = Codeserver, - no_warn_unused = NoWarnUnused, parent = Parent, plt = Plt}, - NewState = get_refined_success_typings(InitState), +get_warnings(Callgraph, Plt, DocPlt, Codeserver, + NoWarnUnused, TimingServer, Solvers, Parent) -> + InitState = + init_state_and_get_success_typings(Callgraph, Plt, Codeserver, + TimingServer, Solvers, Parent), + NewState = InitState#st{no_warn_unused = NoWarnUnused}, Mods = dialyzer_callgraph:modules(NewState#st.callgraph), - CWarns = dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver, - NewState#st.plt), - get_warnings_from_modules(Mods, NewState, DocPlt, CWarns). - -get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) -> - send_log(State#st.parent, io_lib:format("Getting warnings for ~w\n", [M])), + MiniPlt = NewState#st.plt, + CWarns = + dialyzer_contracts:get_invalid_contract_warnings(Mods, Codeserver, MiniPlt), + MiniDocPlt = dialyzer_plt:get_mini_plt(DocPlt), + ModWarns = + ?timing(TimingServer, "warning", + get_warnings_from_modules(Mods, NewState, MiniDocPlt)), + {postprocess_warnings(CWarns ++ ModWarns, Codeserver), + dialyzer_plt:restore_full_plt(MiniPlt, Plt), + dialyzer_plt:restore_full_plt(MiniDocPlt, DocPlt)}. + +get_warnings_from_modules(Mods, State, DocPlt) -> #st{callgraph = Callgraph, codeserver = Codeserver, - no_warn_unused = NoWarnUnused, plt = Plt} = State, + no_warn_unused = NoWarnUnused, plt = Plt, + timing_server = TimingServer} = State, + Init = {Codeserver, Callgraph, NoWarnUnused, Plt, DocPlt}, + dialyzer_coordinator:parallel_job(warnings, Mods, Init, TimingServer). + +-spec collect_warnings(module(), warnings_init_data()) -> [dial_warning()]. + +collect_warnings(M, {Codeserver, Callgraph, NoWarnUnused, Plt, DocPlt}) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, Codeserver), Records = dialyzer_codeserver:lookup_mod_records(M, Codeserver), Contracts = dialyzer_codeserver:lookup_mod_contracts(M, Codeserver), @@ -129,28 +176,27 @@ get_warnings_from_modules([M|Ms], State, DocPlt, Acc) when is_atom(M) -> %% Check if there are contracts for functions that do not exist Warnings1 = dialyzer_contracts:contracts_without_fun(Contracts, AllFuns, Callgraph), - {RawWarnings2, FunTypes, RaceCode, PublicTables, NamedTables} = - dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, Records, NoWarnUnused), - {NewAcc, Warnings2} = postprocess_dataflow_warns(RawWarnings2, State, Acc), + {Warnings2, FunTypes} = + dialyzer_dataflow:get_warnings(ModCode, Plt, Callgraph, + Records, NoWarnUnused), Attrs = cerl:module_attrs(ModCode), - Warnings3 = dialyzer_behaviours:check_callbacks(M, Attrs, Plt, Codeserver), - NewDocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt), - NewCallgraph = - dialyzer_callgraph:renew_race_info(Callgraph, RaceCode, PublicTables, - NamedTables), - State1 = st__renew_state_calls(NewCallgraph, State), - get_warnings_from_modules(Ms, State1, NewDocPlt, - [Warnings1, Warnings2, Warnings3|NewAcc]); -get_warnings_from_modules([], #st{plt = Plt}, DocPlt, Acc) -> - {lists:flatten(Acc), Plt, DocPlt}. - -postprocess_dataflow_warns(RawWarnings, State, WarnAcc) -> - postprocess_dataflow_warns(RawWarnings, State, WarnAcc, []). - -postprocess_dataflow_warns([], _State, WAcc, Acc) -> - {WAcc, lists:reverse(Acc)}; + Warnings3 = + dialyzer_behaviours:check_callbacks(M, Attrs, Records, Plt, Codeserver), + DocPlt = insert_into_doc_plt(FunTypes, Callgraph, DocPlt), + lists:flatten([Warnings1, Warnings2, Warnings3]). + +postprocess_warnings(RawWarnings, Codeserver) -> + Pred = + fun({?WARN_CONTRACT_RANGE, _, _}) -> true; + (_) -> false + end, + {CRWarns, NonCRWarns} = lists:partition(Pred, RawWarnings), + postprocess_dataflow_warns(CRWarns, Codeserver, NonCRWarns, []). + +postprocess_dataflow_warns([], _Callgraph, WAcc, Acc) -> + lists:reverse(Acc, WAcc); postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest], - #st{codeserver = Codeserver} = State, WAcc, Acc) -> + Codeserver, WAcc, Acc) -> {contract_range, [Contract, M, F, A, ArgStrings, CRet]} = Msg, case dialyzer_codeserver:lookup_mfa_contract({M,F,A}, Codeserver) of {ok, {{ContrF, _ContrL} = FileLine, _C}} -> @@ -163,87 +209,66 @@ postprocess_dataflow_warns([{?WARN_CONTRACT_RANGE, {CallF, CallL}, Msg}|Rest], (_) -> true end, FilterWAcc = lists:filter(Filter, WAcc), - postprocess_dataflow_warns(Rest, State, FilterWAcc, [W|Acc]); + postprocess_dataflow_warns(Rest, Codeserver, FilterWAcc, [W|Acc]); false -> - postprocess_dataflow_warns(Rest, State, WAcc, Acc) + postprocess_dataflow_warns(Rest, Codeserver, WAcc, Acc) end; error -> %% The contract is not in a module that is currently under analysis. %% We display the warning in the file/line of the call. NewMsg = {contract_range, [Contract, M, F, ArgStrings, CallL, CRet]}, W = {?WARN_CONTRACT_RANGE, {CallF, CallL}, NewMsg}, - postprocess_dataflow_warns(Rest, State, WAcc, [W|Acc]) - end; -postprocess_dataflow_warns([W|Rest], State, Wacc, Acc) -> - postprocess_dataflow_warns(Rest, State, Wacc, [W|Acc]). + postprocess_dataflow_warns(Rest, Codeserver, WAcc, [W|Acc]) + end. -refine_succ_typings(ModulePostorder, State) -> - ?debug("Module postorder: ~p\n", [ModulePostorder]), - refine_succ_typings(ModulePostorder, State, []). - -refine_succ_typings([SCC|SCCs], State, Fixpoint) -> - Msg = io_lib:format("Dataflow of one SCC: ~w\n", [SCC]), - send_log(State#st.parent, Msg), - ?debug("~s\n", [Msg]), - {NewState, FixpointFromScc} = - case SCC of - [M] -> refine_one_module(M, State); - [_|_] -> refine_one_scc(SCC, State) - end, - NewFixpoint = ordsets:union(Fixpoint, FixpointFromScc), - refine_succ_typings(SCCs, NewState, NewFixpoint); -refine_succ_typings([], State, Fixpoint) -> - case Fixpoint =:= [] of +refine_succ_typings(Modules, #st{codeserver = Codeserver, + callgraph = Callgraph, + plt = Plt, + timing_server = Timing, + solvers = Solvers} = State) -> + ?debug("Module postorder: ~p\n", [Modules]), + Init = {Codeserver, Callgraph, Plt, Solvers}, + NotFixpoint = + ?timing(Timing, "refine", + dialyzer_coordinator:parallel_job(dataflow, Modules, Init, Timing)), + ?debug("==================== Dataflow done ====================\n\n", []), + case NotFixpoint =:= [] of true -> {fixpoint, State}; - false -> {not_fixpoint, Fixpoint, State} + false -> {not_fixpoint, NotFixpoint, State} end. --spec refine_one_module(module(), #st{}) -> {#st{}, [label()]}. % ordset +-spec find_depends_on(scc() | module(), fixpoint_init_data()) -> [scc()]. -refine_one_module(M, State) -> - #st{callgraph = Callgraph, codeserver = CodeServer, plt = PLT} = State, +find_depends_on(SCC, {_Codeserver, Callgraph, _Plt, _Solvers}) -> + dialyzer_callgraph:get_depends_on(SCC, Callgraph). + +-spec find_required_by(scc() | module(), fixpoint_init_data()) -> [scc()]. + +find_required_by(SCC, {_Codeserver, Callgraph, _Plt, _Solvers}) -> + dialyzer_callgraph:get_required_by(SCC, Callgraph). + +-spec lookup_names([label()], fixpoint_init_data()) -> [mfa_or_funlbl()]. + +lookup_names(Labels, {_Codeserver, Callgraph, _Plt, _Solvers}) -> + [lookup_name(F, Callgraph) || F <- Labels]. + +-spec refine_one_module(module(), dataflow_init_data()) -> [label()]. % ordset + +refine_one_module(M, {CodeServer, Callgraph, Plt, _Solvers}) -> ModCode = dialyzer_codeserver:lookup_mod_code(M, CodeServer), AllFuns = collect_fun_info([ModCode]), - FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, PLT), Records = dialyzer_codeserver:lookup_mod_records(M, CodeServer), - {NewFunTypes, RaceCode, PublicTables, NamedTables} = - dialyzer_dataflow:get_fun_types(ModCode, PLT, Callgraph, Records), - NewCallgraph = - dialyzer_callgraph:renew_race_info(Callgraph, RaceCode, PublicTables, - NamedTables), + FunTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt), + NewFunTypes = + dialyzer_dataflow:get_fun_types(ModCode, Plt, Callgraph, Records), case reached_fixpoint(FunTypes, NewFunTypes) of - true -> - State1 = st__renew_state_calls(NewCallgraph, State), - {State1, ordsets:new()}; + true -> []; {false, NotFixpoint} -> ?debug("Not fixpoint\n", []), - NewState = insert_into_plt(dict:from_list(NotFixpoint), State), - NewState1 = st__renew_state_calls(NewCallgraph, NewState), - {NewState1, ordsets:from_list([FunLbl || {FunLbl,_Type} <- NotFixpoint])} + Plt = insert_into_plt(dict:from_list(NotFixpoint), Callgraph, Plt), + [FunLbl || {FunLbl,_Type} <- NotFixpoint] end. -st__renew_state_calls(Callgraph, State) -> - State#st{callgraph = Callgraph}. - -refine_one_scc(SCC, State) -> - refine_one_scc(SCC, State, []). - -refine_one_scc(SCC, State, AccFixpoint) -> - {NewState, FixpointFromScc} = refine_mods_in_scc(SCC, State, []), - case FixpointFromScc =:= [] of - true -> {NewState, AccFixpoint}; - false -> - NewAccFixpoint = ordsets:union(AccFixpoint, FixpointFromScc), - refine_one_scc(SCC, NewState, NewAccFixpoint) - end. - -refine_mods_in_scc([Mod|Mods], State, Fixpoint) -> - {NewState, FixpointFromModule} = refine_one_module(Mod, State), - NewFixpoint = ordsets:union(FixpointFromModule, Fixpoint), - refine_mods_in_scc(Mods, NewState, NewFixpoint); -refine_mods_in_scc([], State, Fixpoint) -> - {State, Fixpoint}. - reached_fixpoint(OldTypes, NewTypes) -> reached_fixpoint(OldTypes, NewTypes, false). @@ -299,31 +324,22 @@ compare_types_1([], [], _Strict, NotFixpoint) -> false -> {false, NotFixpoint} end. -find_succ_typings(State) -> - find_succ_typings(State, []). - -find_succ_typings(#st{callgraph = Callgraph, parent = Parent} = State, - NotFixpoint) -> - case dialyzer_callgraph:take_scc(Callgraph) of - {ok, SCC, NewCallgraph} -> - Msg = io_lib:format("Typesig analysis for SCC: ~w\n", [format_scc(SCC)]), - ?debug("~s", [Msg]), - send_log(Parent, Msg), - {NewState, NewNotFixpoint1} = - analyze_scc(SCC, State#st{callgraph = NewCallgraph}), - NewNotFixpoint2 = ordsets:union(NewNotFixpoint1, NotFixpoint), - find_succ_typings(NewState, NewNotFixpoint2); - none -> - ?debug("==================== Typesig done ====================\n\n", []), - case NotFixpoint =:= [] of - true -> {fixpoint, State}; - false -> {not_fixpoint, NotFixpoint, State} - end +find_succ_typings(SCCs, #st{codeserver = Codeserver, callgraph = Callgraph, + plt = Plt, timing_server = Timing, + solvers = Solvers} = State) -> + Init = {Codeserver, Callgraph, Plt, Solvers}, + NotFixpoint = + ?timing(Timing, "typesig", + dialyzer_coordinator:parallel_job(typesig, SCCs, Init, Timing)), + ?debug("==================== Typesig done ====================\n\n", []), + case NotFixpoint =:= [] of + true -> {fixpoint, State}; + false -> {not_fixpoint, NotFixpoint, State} end. -analyze_scc(SCC, #st{codeserver = Codeserver, - callgraph = Callgraph, - plt = Plt} = State) -> +-spec find_succ_types_for_scc(scc(), typesig_init_data()) -> [mfa_or_funlbl()]. + +find_succ_types_for_scc(SCC, {Codeserver, Callgraph, Plt, Solvers}) -> SCC_Info = [{MFA, dialyzer_codeserver:lookup_mfa_code(MFA, Codeserver), dialyzer_codeserver:lookup_mod_records(M, Codeserver)} @@ -332,26 +348,18 @@ analyze_scc(SCC, #st{codeserver = Codeserver, || {_, _, _} = MFA <- SCC], Contracts2 = [{MFA, Contract} || {MFA, {ok, Contract}} <- Contracts1], Contracts3 = orddict:from_list(Contracts2), - NextLabel = dialyzer_codeserver:get_next_core_label(Codeserver), - {SuccTypes, PltContracts, NotFixpoint} = - find_succ_types_for_scc(SCC_Info, Contracts3, NextLabel, Callgraph, Plt), - State1 = insert_into_plt(SuccTypes, State), - ContrPlt = dialyzer_plt:insert_contract_list(State1#st.plt, PltContracts), - {State1#st{plt = ContrPlt}, NotFixpoint}. - -find_succ_types_for_scc(SCC_Info, Contracts, NextLabel, Callgraph, Plt) -> - %% Assume that the PLT contains the current propagated types + Label = dialyzer_codeserver:get_next_core_label(Codeserver), AllFuns = collect_fun_info([Fun || {_MFA, {_Var, Fun}, _Rec} <- SCC_Info]), PropTypes = get_fun_types_from_plt(AllFuns, Callgraph, Plt), - FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, NextLabel, - Callgraph, Plt, PropTypes), + %% Assume that the PLT contains the current propagated types + FunTypes = dialyzer_typesig:analyze_scc(SCC_Info, Label, Callgraph, + Plt, PropTypes, Solvers), AllFunSet = sets:from_list([X || {X, _} <- AllFuns]), - FilteredFunTypes = dict:filter(fun(X, _) -> - sets:is_element(X, AllFunSet) - end, FunTypes), + FilteredFunTypes = + dict:filter(fun(X, _) -> sets:is_element(X, AllFunSet) end, FunTypes), %% Check contracts - PltContracts = dialyzer_contracts:check_contracts(Contracts, Callgraph, - FilteredFunTypes), + PltContracts = + dialyzer_contracts:check_contracts(Contracts3, Callgraph, FilteredFunTypes), ContractFixpoint = lists:all(fun({MFA, _C}) -> %% Check the non-deleted PLT @@ -360,14 +368,14 @@ find_succ_types_for_scc(SCC_Info, Contracts, NextLabel, Callgraph, Plt) -> {value, _} -> true end end, PltContracts), + Plt = insert_into_plt(FilteredFunTypes, Callgraph, Plt), + Plt = dialyzer_plt:insert_contract_list(Plt, PltContracts), case (ContractFixpoint andalso reached_fixpoint_strict(PropTypes, FilteredFunTypes)) of - true -> - {FilteredFunTypes, PltContracts, []}; + true -> []; false -> ?debug("Not fixpoint for: ~w\n", [AllFuns]), - {FilteredFunTypes, PltContracts, - ordsets:from_list([Fun || {Fun, _Arity} <- AllFuns])} + [Fun || {Fun, _Arity} <- AllFuns] end. get_fun_types_from_plt(FunList, Callgraph, Plt) -> @@ -407,10 +415,10 @@ insert_into_doc_plt(FunTypes, Callgraph, DocPlt) -> SuccTypes = format_succ_types(FunTypes, Callgraph), dialyzer_plt:insert_list(DocPlt, SuccTypes). -insert_into_plt(SuccTypes0, #st{callgraph = Callgraph, plt = Plt} = State) -> +insert_into_plt(SuccTypes0, Callgraph, Plt) -> SuccTypes = format_succ_types(SuccTypes0, Callgraph), debug_pp_succ_typings(SuccTypes), - State#st{plt = dialyzer_plt:insert_list(Plt, SuccTypes)}. + dialyzer_plt:insert_list(Plt, SuccTypes). format_succ_types(SuccTypes, Callgraph) -> format_succ_types(dict:to_list(SuccTypes), Callgraph, []). @@ -445,131 +453,3 @@ lookup_name(F, CG) -> error -> F; {ok, Name} -> Name end. - -send_log(none, _Msg) -> - ok; -send_log(Parent, Msg) -> - Parent ! {self(), log, lists:flatten(Msg)}, - ok. - -format_scc(SCC) -> - [MFA || {_M, _F, _A} = MFA <- SCC]. - -%% ============================================================================ -%% -%% Debug interface. -%% -%% ============================================================================ - --spec doit(atom() | file:filename()) -> 'ok'. - -doit(Module) -> - {ok, AbstrCode} = dialyzer_utils:get_abstract_code_from_src(Module), - {ok, Code} = dialyzer_utils:get_core_from_abstract_code(AbstrCode), - {ok, Records} = dialyzer_utils:get_record_and_type_info(AbstrCode), - %% contract typing info in dictionary format - {ok, Contracts, _Callbacks} = - dialyzer_utils:get_spec_info(cerl:concrete(cerl:module_name(Code)), - AbstrCode, Records), - Sigs0 = get_top_level_signatures(Code, Records, Contracts), - M = if is_atom(Module) -> - list_to_atom(filename:basename(atom_to_list(Module))); - is_list(Module) -> - list_to_atom(filename:basename(Module)) - end, - Sigs1 = [{{M, F, A}, Type} || {{F, A}, Type} <- Sigs0], - Sigs = ordsets:from_list(Sigs1), - io:format("==================== Final result ====================\n\n", []), - pp_signatures(Sigs, Records), - ok. - --spec get_top_level_signatures(cerl:c_module(), dict(), dict()) -> - [{{atom(), arity()}, erl_types:erl_type()}]. - -get_top_level_signatures(Code, Records, Contracts) -> - Tree = cerl:from_records(Code), - {LabeledTree, NextLabel} = cerl_trees:label(Tree), - Plt = get_def_plt(), - ModuleName = cerl:atom_val(cerl:module_name(LabeledTree)), - Plt1 = dialyzer_plt:delete_module(Plt, ModuleName), - Plt2 = analyze_module(LabeledTree, NextLabel, Plt1, Records, Contracts), - M = cerl:concrete(cerl:module_name(Tree)), - Functions = [{M, cerl:fname_id(V), cerl:fname_arity(V)} - || {V, _F} <- cerl:module_defs(LabeledTree)], - %% First contracts check - AllContracts = dict:fetch_keys(Contracts), - ErrorContracts = AllContracts -- Functions, - lists:foreach(fun(C) -> - io:format("Contract for non-existing function: ~w\n",[C]) - end, ErrorContracts), - Types = [{MFA, dialyzer_plt:lookup(Plt2, MFA)} || MFA <- Functions], - Sigs = [{{F, A}, erl_types:t_fun(ArgT, RetT)} - || {{_M, F, A}, {value, {RetT, ArgT}}} <- Types], - ordsets:from_list(Sigs). - -get_def_plt() -> - try - dialyzer_plt:from_file(dialyzer_plt:get_default_plt()) - catch - error:no_such_file -> dialyzer_plt:new(); - throw:{dialyzer_error, _} -> dialyzer_plt:new() - end. - -pp_signatures([{{_, module_info, 0}, _}|Left], Records) -> - pp_signatures(Left, Records); -pp_signatures([{{_, module_info, 1}, _}|Left], Records) -> - pp_signatures(Left, Records); -pp_signatures([{{M, F, _A}, Type}|Left], Records) -> - TypeString = - case cerl:is_literal(Type) of -%% Commented out so that dialyzer does not complain -%% false -> -%% "fun(" ++ String = erl_types:t_to_string(Type, Records), -%% string:substr(String, 1, length(String)-1); - true -> - io_lib:format("~w", [cerl:concrete(Type)]) - end, - io:format("~w:~w~s\n", [M, F, TypeString]), - pp_signatures(Left, Records); -pp_signatures([], _Records) -> - ok. - --ifdef(DEBUG_PP). -debug_pp(Tree, _Map) -> - Tree1 = strip_annotations(Tree), - io:put_chars(cerl_prettypr:format(Tree1)), - io:nl(). - -strip_annotations(Tree) -> - cerl_trees:map(fun(T) -> - case cerl:is_literal(T) orelse cerl:is_c_values(T) of - true -> cerl:set_ann(T, []); - false -> - Label = cerl_trees:get_label(T), - cerl:set_ann(T, [{'label', Label}]) - end - end, Tree). --else. -debug_pp(_Tree, _Map) -> - ok. --endif. % DEBUG_PP - -%% -%% Analysis of a single module -%% -analyze_module(LabeledTree, NextLbl, Plt, Records, Contracts) -> - debug_pp(LabeledTree, dict:new()), - CallGraph1 = dialyzer_callgraph:new(), - CallGraph2 = dialyzer_callgraph:scan_core_tree(LabeledTree, CallGraph1), - {CallGraph3, _Ext} = dialyzer_callgraph:remove_external(CallGraph2), - CallGraph4 = dialyzer_callgraph:finalize(CallGraph3), - CodeServer1 = dialyzer_codeserver:new(), - Mod = cerl:concrete(cerl:module_name(LabeledTree)), - CodeServer2 = dialyzer_codeserver:insert(Mod, LabeledTree, CodeServer1), - CodeServer3 = dialyzer_codeserver:set_next_core_label(NextLbl, CodeServer2), - CodeServer4 = dialyzer_codeserver:store_records(Mod, Records, CodeServer3), - CodeServer5 = dialyzer_codeserver:store_contracts(Mod, Contracts, CodeServer4), - Res = analyze_callgraph(CallGraph4, Plt, CodeServer5), - dialyzer_callgraph:delete(CallGraph4), - dialyzer_codeserver:delete(CodeServer5), - Res. diff --git a/lib/dialyzer/src/dialyzer_timing.erl b/lib/dialyzer/src/dialyzer_timing.erl new file mode 100644 index 0000000000..b1a4bdc07c --- /dev/null +++ b/lib/dialyzer/src/dialyzer_timing.erl @@ -0,0 +1,133 @@ +%% -*- erlang-indent-level: 2 -*- +%%------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +%%%------------------------------------------------------------------- +%%% File : dialyzer_timing.erl +%%% Authors : Stavros Aronis <[email protected]> +%%% Description : Timing reports for Dialyzer +%%%------------------------------------------------------------------- + +-module(dialyzer_timing). + +-export([init/1, start_stamp/2, send_size_info/3, end_stamp/1, stop/1]). + +-export_type([timing_server/0]). + +-type timing_server() :: pid() | 'none'. + +-spec init(boolean() | 'debug') -> timing_server(). + +init(Active) -> + case Active of + true -> + io:format("\n"), + spawn_link(fun() -> loop(now(), 0, "") end); + debug -> + io:format("\n"), + spawn_link(fun() -> debug_loop("") end); + false -> none + end. + +loop(LastNow, Size, Unit) -> + receive + {stamp, Msg, Now} -> + io:format(" ~-10s (+~4.2fs):", [Msg, diff(Now, LastNow)]), + loop(Now, 0, ""); + {stamp, Now} -> + SizeStr = + case Size of + 0 -> ""; + _ -> + Data = io_lib:format("~p ~s",[Size, Unit]), + io_lib:format(" (~12s)",[Data]) + end, + io:format("~7.2fs~s\n", [diff(Now, LastNow), SizeStr]), + loop(Now, 0, ""); + {size, NewSize, NewUnit} -> + loop(LastNow, NewSize, NewUnit); + {Pid, stop, Now} -> + io:format(" ~-9s (+~5.2fs)\n", ["",diff(Now, LastNow)]), + Pid ! ok; + {Pid, stop} -> + Pid ! ok + end. + +debug_loop(Phase) -> + receive + Message -> + {Runtime,_} = statistics(wall_clock), + Procs = erlang:system_info(process_count), + ProcMem = erlang:memory(total), + Status = io_lib:format("~12w ~6w ~20w", [Runtime, Procs, ProcMem]), + case Message of + {stamp, Msg, _Now} -> + io:format("~s ~s_start\n", [Status, Msg]), + debug_loop(Msg); + {stamp, _Now} -> + io:format("~s ~s_stop\n", [Status, Phase]), + debug_loop(""); + {Pid, stop, _Now} -> + Pid ! ok; + {Pid, stop} -> + Pid ! ok; + _ -> + debug_loop(Phase) + end + after + 50 -> + {Runtime,_} = statistics(wall_clock), + Procs = erlang:system_info(process_count), + ProcMem = erlang:memory(total), + Status = io_lib:format("~12w ~6w ~20w", [Runtime, Procs, ProcMem]), + io:format("~s\n", [Status]), + debug_loop(Phase) + end. + + +-spec start_stamp(timing_server(), string()) -> ok. + +start_stamp(none, _) -> ok; +start_stamp(Pid, Msg) -> + Pid ! {stamp, Msg, now()}, + ok. + +-spec end_stamp(timing_server()) -> ok. + +end_stamp(none) -> ok; +end_stamp(Pid) -> + Pid ! {stamp, now()}, + ok. + +-spec send_size_info(timing_server(), integer(), string()) -> ok. + +send_size_info(none, _, _) -> ok; +send_size_info(Pid, Size, Unit) -> + Pid ! {size, Size, Unit}, + ok. + +-spec stop(timing_server()) -> ok. + +stop(none) -> ok; +stop(Pid) -> + Pid ! {self(), stop, now()}, + receive ok -> ok end. + +diff(T2, T1) -> + timer:now_diff(T2,T1) / 1000000. diff --git a/lib/dialyzer/src/dialyzer_typesig.erl b/lib/dialyzer/src/dialyzer_typesig.erl index 04ff8e4941..0df003a035 100644 --- a/lib/dialyzer/src/dialyzer_typesig.erl +++ b/lib/dialyzer/src/dialyzer_typesig.erl @@ -2,7 +2,7 @@ %%----------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ -module(dialyzer_typesig). --export([analyze_scc/5]). +-export([analyze_scc/6]). -export([get_safe_underapprox/2]). -import(erl_types, @@ -78,6 +78,8 @@ -record(constraint_list, {type :: 'conj' | 'disj', list :: [constr()], deps :: [dep()], + masks :: [{dep(),[non_neg_integer()]}] | + {'d',dict()}, id :: {'list', dep()}}). -type constraint_list() :: #constraint_list{}. @@ -91,23 +93,26 @@ -type typesig_scc() :: [{mfa(), {cerl:c_var(), cerl:c_fun()}, dict()}]. -type typesig_funmap() :: [{type_var(), type_var()}]. %% Orddict --record(state, {callgraph :: dialyzer_callgraph:callgraph(), - cs = [] :: [constr()], - cmap = dict:new() :: dict(), - fun_map = [] :: typesig_funmap(), - fun_arities = dict:new() :: dict(), - in_match = false :: boolean(), - in_guard = false :: boolean(), - module :: module(), - name_map = dict:new() :: dict(), - next_label :: label(), - self_recs :: [label()], - plt :: dialyzer_plt:plt(), - prop_types = dict:new() :: dict(), - records = dict:new() :: dict(), - opaques = [] :: [erl_types:erl_type()], - scc = [] :: [type_var()], - mfas = [] :: [dialyzer_callgraph:mfa_or_funlbl()] +-type dict_or_ets() :: {'d', dict()} | {'e', ets:tid()}. + +-record(state, {callgraph :: dialyzer_callgraph:callgraph(), + cs = [] :: [constr()], + cmap = {'d', dict:new()} :: dict_or_ets(), + fun_map = [] :: typesig_funmap(), + fun_arities = dict:new() :: dict(), + in_match = false :: boolean(), + in_guard = false :: boolean(), + module :: module(), + name_map = dict:new() :: dict(), + next_label = 0 :: label(), + self_rec :: erl_types:erl_type(), + plt :: dialyzer_plt:plt(), + prop_types = {'d', dict:new()} :: dict_or_ets(), + records = dict:new() :: dict(), + opaques = [] :: [erl_types:erl_type()], + scc = [] :: [type_var()], + mfas :: [tuple()], + solvers = [] :: [solver()] }). %%----------------------------------------------------------------------------- @@ -119,8 +124,10 @@ %%-define(DEBUG_CONSTRAINTS, true). -ifdef(DEBUG). -define(DEBUG_NAME_MAP, true). +-define(DEBUG_LOOP_DETECTION, true). -endif. %%-define(DEBUG_NAME_MAP, true). +%%-define(DEBUG_LOOP_DETECTION, true). -ifdef(DEBUG). -define(debug(__String, __Args), io:format(__String, __Args)). @@ -139,7 +146,7 @@ %%----------------------------------------------------------------------------- %% Analysis of strongly connected components. %% -%% analyze_scc(SCC, NextLabel, CallGraph, PLT, PropTypes) -> FunTypes +%% analyze_scc(SCC, NextLabel, CallGraph, PLT, PropTypes, Solvers) -> FunTypes %% %% SCC - [{MFA, Def, Records}] %% where Def = {Var, Fun} as in the Core Erlang module definitions. @@ -152,15 +159,17 @@ %% about functions that can be called by this SCC. %% PropTypes - A dictionary. %% FunTypes - A dictionary. +%% Solvers - User specified solvers. %%----------------------------------------------------------------------------- -spec analyze_scc(typesig_scc(), label(), dialyzer_callgraph:callgraph(), - dialyzer_plt:plt(), dict()) -> dict(). + dialyzer_plt:plt(), dict(), [solver()]) -> dict(). -analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes) -> +analyze_scc(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers0) -> + Solvers = solvers(Solvers0), assert_format_of_scc(SCC), - State1 = new_state(SCC, NextLabel, CallGraph, Plt, PropTypes), + State1 = new_state(SCC, NextLabel, CallGraph, Plt, PropTypes, Solvers), DefSet = add_def_list([Var || {_MFA, {Var, _Fun}, _Rec} <- SCC], sets:new()), State2 = traverse_scc(SCC, DefSet, State1), State3 = state__finalize(State2), @@ -174,6 +183,9 @@ assert_format_of_scc([{_MFA, {_Var, _Fun}, _Records}|Left]) -> assert_format_of_scc([]) -> ok. +solvers([]) -> [v2]; +solvers(Solvers) -> Solvers. + %% ============================================================================ %% %% Gets the constraints by traversing the code. @@ -1661,18 +1673,23 @@ get_bif_test_constr(Dst, Arg, Type, State) -> solve([Fun], State) -> ?debug("============ Analyzing Fun: ~w ===========\n", [debug_lookup_name(Fun)]), - solve_fun(Fun, dict:new(), State); + solve_fun(Fun, map_new(), State); solve([_|_] = SCC, State) -> ?debug("============ Analyzing SCC: ~w ===========\n", [[debug_lookup_name(F) || F <- SCC]]), - solve_scc(SCC, dict:new(), State, false). + {Parallel, NewState} = + case parallel_split(SCC) of + false -> {false, State}; + SplitSCC -> {SplitSCC, minimize_state(State)} + end, + solve_scc(SCC, Parallel, map_new(), NewState, false). solve_fun(Fun, FunMap, State) -> Cs = state__get_cs(Fun, State), Deps = get_deps(Cs), Ref = mk_constraint_ref(Fun, Deps), %% Note that functions are always considered to succeed. - {ok, _MapDict, NewMap} = solve_ref_or_list(Ref, FunMap, dict:new(), State), + NewMap = solve(Fun, Ref, FunMap, State), NewType = lookup_type(Fun, NewMap), NewFunMap1 = case state__get_rec_var(Fun, State) of error -> FunMap; @@ -1680,21 +1697,23 @@ solve_fun(Fun, FunMap, State) -> end, enter_type(Fun, NewType, NewFunMap1). -solve_scc(SCC, Map, State, TryingUnit) -> - State1 = state__mark_as_non_self_rec(SCC, State), +solve_scc(SCC, Parallel, Map, State, TryingUnit) -> Vars0 = [{Fun, state__get_rec_var(Fun, State)} || Fun <- SCC], Vars = [Var || {_, {ok, Var}} <- Vars0], Funs = [Fun || {Fun, {ok, _}} <- Vars0], Types = unsafe_lookup_type_list(Funs, Map), RecTypes = [t_limit(Type, ?TYPE_LIMIT) || Type <- Types], CleanMap = lists:foldl(fun(Fun, AccFunMap) -> - dict:erase(t_var_name(Fun), AccFunMap) + erase_type(t_var_name(Fun), AccFunMap) end, Map, SCC), Map1 = enter_type_lists(Vars, RecTypes, CleanMap), ?debug("Checking SCC: ~w\n", [[debug_lookup_name(F) || F <- SCC]]), - SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State1) end, - Map2 = lists:foldl(SolveFun, Map1, SCC), FunSet = ordsets:from_list([t_var_name(F) || F <- SCC]), + Map2 = + case Parallel of + false -> solve_whole_scc(SCC, Map1, State); + SplitSCC -> solve_whole_scc_parallel(SplitSCC, Map1, State) + end, case maps_are_equal(Map2, Map, FunSet) of true -> ?debug("SCC ~w reached fixpoint\n", [SCC]), @@ -1708,20 +1727,134 @@ solve_scc(SCC, Map, State, TryingUnit) -> 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); + solve_scc(SCC, Parallel, Map3, State, true); false -> + case Parallel of + false -> true; + _ -> dispose_state(State) + end, Map2 end; false -> ?debug("SCC ~w did not reach fixpoint\n", [SCC]), - solve_scc(SCC, Map2, State, TryingUnit) + solve_scc(SCC, Parallel, Map2, State, TryingUnit) + end. + +solve_whole_scc(SCC, Map, State) -> + SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end, + lists:foldl(SolveFun, Map, SCC). + +%%------------------------------------------------------------------------------ + +-define(worth_it, 42). + +parallel_split(SCC) -> + Length = length(SCC), + case Length > 2*?worth_it of + false -> false; + true -> + case min(dialyzer_utils:parallelism(), 8) of + 1 -> false; + CPUs -> + FullShare = Length div CPUs + 1, + Unit = max(FullShare, ?worth_it), + split(SCC, Unit, []) + end + end. + +minimize_state(#state{ + cmap = {d, CMap}, + fun_map = FunMap, + fun_arities = FunArities, + self_rec = SelfRec, + prop_types = {d, PropTypes}, + opaques = Opaques, + solvers = Solvers + }) -> + ETSCMap = ets:new(cmap,[{read_concurrency, true}]), + ETSPropTypes = ets:new(prop_types,[{read_concurrency, true}]), + true = ets:insert(ETSCMap, dict:to_list(CMap)), + true = ets:insert(ETSPropTypes, dict:to_list(PropTypes)), + #state + {cmap = {e, ETSCMap}, + fun_map = FunMap, + fun_arities = FunArities, + self_rec = SelfRec, + prop_types = {e, ETSPropTypes}, + opaques = Opaques, + solvers = Solvers + }. + +dispose_state(#state{cmap = {e, ETSCMap}, + prop_types = {e, ETSPropTypes}}) -> + true = ets:delete(ETSCMap), + true = ets:delete(ETSPropTypes). + +solve_whole_scc_parallel(SplitSCC, Map, State) -> + Workers = spawn_workers(SplitSCC, Map, State), + wait_results(Workers, Map, fold_res_fun(State)). + +spawn_workers(SplitSCC, Map, State) -> + Spawner = solve_scc_spawner(self(), Map, State), + lists:foreach(Spawner, SplitSCC), + length(SplitSCC). + +wait_results(0, Map, _FoldResFun) -> + Map; +wait_results(Pending, Map, FoldResFun) -> + Res = receive_scc_result(), + NewMap = lists:foldl(FoldResFun, Map, Res), + wait_results(Pending-1, NewMap, FoldResFun). + +solve_scc_spawner(Parent, Map, State) -> + fun(SCCPart) -> + spawn_link(fun() -> solve_scc_worker(Parent, SCCPart, Map, State) end) + end. + +split([], _Unit, Acc) -> + Acc; +split(List, Unit, Acc) -> + {Taken, Rest} = + try + lists:split(Unit, List) + catch + _:_ -> {List, []} + end, + split(Rest, Unit, [Taken|Acc]). + +solve_scc_worker(Parent, SCCPart, Map, State) -> + SolveFun = fun(X, Y) -> scc_fold_fun(X, Y, State) end, + FinalMap = lists:foldl(SolveFun, Map, SCCPart), + Res = + [{F, t_limit(unsafe_lookup_type(F, FinalMap), ?TYPE_LIMIT)} || + F <- SCCPart], + send_scc_result(Parent, Res). + +fold_res_fun(State) -> + fun({F, Type}, Map) -> + case state__get_rec_var(F, State) of + {ok, R} -> + enter_type(R, Type, enter_type(F, Type, Map)); + error -> + enter_type(F, Type, Map) + end end. +receive_scc_result() -> + receive + {scc_fun, Res} -> Res + end. + +send_scc_result(Parent, Res) -> + Parent ! {scc_fun, Res}. + +%%------------------------------------------------------------------------------ + scc_fold_fun(F, FunMap, State) -> Deps = get_deps(state__get_cs(F, State)), Cs = mk_constraint_ref(F, Deps), %% Note that functions are always considered to succeed. - {ok, _NewMapDict, Map} = solve_ref_or_list(Cs, FunMap, dict:new(), State), + Map = solve(F, Cs, FunMap, State), NewType0 = unsafe_lookup_type(F, Map), NewType = t_limit(NewType0, ?TYPE_LIMIT), NewFunMap = case state__get_rec_var(F, State) of @@ -1734,15 +1867,440 @@ scc_fold_fun(F, FunMap, State) -> format_type(NewType)]), NewFunMap. +solve(Fun, Cs, FunMap, State) -> + Solvers = State#state.solvers, + R = [solver(S, solve_fun(S, Fun, Cs, FunMap, State)) || S <- Solvers], + check_solutions(R, Fun, no_solver, no_map). + +solver(Solver, SolveFun) -> + ?debug("Start solver ~w\n", [Solver]), + try timer:tc(SolveFun) of + {Time, {ok, Map}} -> + ?debug("End solver ~w (~w microsecs)\n", [Solver, Time]), + {Solver, Map, Time}; + {_, _R} -> + ?debug("Solver ~w returned unexpected result:\n ~P\n", + [Solver, _R, 60]), + throw(error) + catch E:R -> + io:format("Solver ~w failed: ~w:~p\n ~p\n", + [Solver, E, R, erlang:get_stacktrace()]), + throw(error) + end. + +solve_fun(v1, _Fun, Cs, FunMap, State) -> + fun() -> + {ok, _MapDict, NewMap} = solve_ref_or_list(Cs, FunMap, dict:new(), State), + {ok, NewMap} + end; +solve_fun(v2, Fun, _Cs, FunMap, State) -> + fun() -> v2_solve_ref(Fun, FunMap, State) end. + +check_solutions([], _Fun, _S, Map) -> + Map; +check_solutions([{S1,Map1,_Time1}|Maps], Fun, S, Map) -> + ?debug("Solver ~w needed ~w microsecs\n", [S1, _Time1]), + case Map =:= no_map orelse sane_maps(Map, Map1, [Fun], S, S1) of + true -> + check_solutions(Maps, Fun, S1, Map1); + false -> + ?debug("Constraint solvers do not agree on ~w\n", [Fun]), + pp_map(atom_to_list(S), Map), + pp_map(atom_to_list(S1), Map1), + io:format("A bug was found. Please report it, and use the option " + "`--solver v1' until the bug has been fixed.\n"), + throw(error) + end. + +sane_maps(Map1, Map2, Keys, _S1, _S2) -> + lists:all(fun(Key) -> + V1 = unsafe_lookup_type(Key, Map1), + V2 = unsafe_lookup_type(Key, Map2), + case t_is_equal(V1, V2) of + true -> true; + false -> + ?debug("Constraint solvers do not agree on ~w\n", [Key]), + ?debug("~w: ~s\n", + [_S1, format_type(unsafe_lookup_type(Key, Map1))]), + ?debug("~w: ~s\n", + [_S2, format_type(unsafe_lookup_type(Key, Map2))]), + false + end + end, Keys). + +%% Solver v2 + +-record(v2_state, {constr_data = dict:new() :: dict(), + state :: #state{}}). + +v2_solve_ref(Fun, Map, State) -> + V2State = #v2_state{state = State}, + {ok, NewMap, _, _} = v2_solve_reference(Fun, Map, V2State), + {ok, NewMap}. + +v2_solve(#constraint{}=C, Map, V2State) -> + State = V2State#v2_state.state, + case solve_one_c(C, Map, State#state.opaques) of + error -> + report_failed_constraint(C, Map), + {error, V2State}; + {ok, {NewMap, U}} -> + {ok, NewMap, V2State, U} + end; +v2_solve(#constraint_list{type = disj}=C, Map, V2State) -> + v2_solve_disjunct(C, Map, V2State); +v2_solve(#constraint_list{type = conj}=C, Map, V2State) -> + v2_solve_conjunct(C, Map, V2State); +v2_solve(#constraint_ref{id = Id}, Map, V2State) -> + v2_solve_reference(Id, Map, V2State). + +v2_solve_reference(Id, Map, V2State0) -> + ?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]), + pp_map("Map", Map), + pp_constr_data("solve_ref", V2State0), + Map1 = restore_local_map(V2State0, Id, Map), + State = V2State0#v2_state.state, + Cs = state__get_cs(Id, State), + Res = + case state__is_self_rec(Id, State) of + true -> v2_solve_self_recursive(Cs, Map1, Id, t_none(), V2State0); + false -> v2_solve(Cs, Map1, V2State0) + end, + {FunType, V2State} = + case Res of + {error, V2State1} -> + ?debug("Error solving for function ~p\n", [debug_lookup_name(Id)]), + Arity = state__fun_arity(Id, State), + FunType0 = + case state__prop_domain(t_var_name(Id), State) of + error -> t_fun(Arity, t_none()); + {ok, Dom} -> t_fun(Dom, t_none()) + end, + {FunType0, V2State1}; + {ok, NewMap, V2State1, U} -> + ?debug("Done solving fun: ~p\n", [debug_lookup_name(Id)]), + FunType0 = lookup_type(Id, NewMap), + V2State2 = save_local_map(V2State1, Id, U, NewMap), + {FunType0, V2State2} + end, + ?debug("ref Id=~w Assigned ~s\n", [Id, format_type(FunType)]), + {NewMap1, U1} = enter_var_type(Id, FunType, Map), + {NewMap2, U2} = + case state__get_rec_var(Id, State) of + {ok, Var} -> enter_var_type(Var, FunType, NewMap1); + error -> {NewMap1, []} + end, + {ok, NewMap2, V2State, lists:umerge(U1, U2)}. + +v2_solve_self_recursive(Cs, Map, Id, RecType0, V2State0) -> + ?debug("Solving self recursive ~w\n", [debug_lookup_name(Id)]), + State = V2State0#v2_state.state, + {ok, RecVar} = state__get_rec_var(Id, State), + ?debug("OldRecType ~s\n", [format_type(RecType0)]), + RecType = t_limit(RecType0, ?TYPE_LIMIT), + {Map1, U0} = enter_var_type(RecVar, RecType, Map), + V2State1 = save_updated_vars1(V2State0, Cs, U0), % Probably not necessary + case v2_solve(Cs, Map1, V2State1) of + {error, _V2State}=Error -> + case t_is_none(RecType0) of + true -> + %% Try again and assume that this is a non-terminating function. + Arity = state__fun_arity(Id, State), + NewRecType = t_fun(lists:duplicate(Arity, t_any()), t_unit()), + v2_solve_self_recursive(Cs, Map, Id, NewRecType, V2State0); + false -> + Error + end; + {ok, NewMap, V2State, U} -> + pp_map("recursive finished", NewMap), + NewRecType = unsafe_lookup_type(Id, NewMap), + case t_is_equal(NewRecType, RecType0) of + true -> + {NewMap2, U1} = enter_var_type(RecVar, NewRecType, NewMap), + {ok, NewMap2, V2State, lists:umerge(U, U1)}; + false -> + v2_solve_self_recursive(Cs, Map, Id, NewRecType, V2State0) + end + end. + +enter_var_type(Var, Type, Map0) -> + {Map, Vs} = enter_type2(Var, Type, Map0), + {Map, [t_var_name(V) || V <- Vs]}. + +v2_solve_disjunct(Disj, Map, V2State0) -> + #constraint_list{type = disj, id = _Id, list = Cs, masks = Masks} = Disj, + ?debug("disjunct Id=~w~n", [_Id]), + pp_map("Map", Map), + pp_constr_data("disjunct", V2State0), + case get_flags(V2State0, Disj) of + {V2State1, failed_list} -> {error, V2State1}; % cannot happen + {V2State1, Flags} when Flags =/= [] -> + {ok, V2State, Eval, UL, MapL0, Uneval, Failed} = + v2_solve_disj(Flags, Cs, 1, Map, V2State1, [], [], [], [], false), + ?debug("disj ending _Id=~w Eval=~w, |Uneval|=~w |UL|=~w~n", + [_Id, Eval, length(Uneval), length(UL)]), + if Eval =:= [], Uneval =:= [] -> + {error, failed_list(Disj, V2State0)}; + true -> + {Is0, UnIds} = lists:unzip(Uneval), + MapL = [restore_local_map(V2State, Id, Map) || + Id <- UnIds] ++ MapL0, + %% If some branch has just failed every variable of the + %% non-failed branches need to be checked, not just the + %% updated ones. + U0 = case Failed of + false -> lists:umerge(UL); + true -> constrained_keys(MapL) + end, + if U0 =:= [] -> {ok, Map, V2State, []}; + true -> + NotFailed = lists:umerge(Is0, Eval), + U1 = [V || V <- U0, + var_occurs_everywhere(V, Masks, NotFailed)], + NewMap = join_maps(U1, MapL, Map), + pp_map("NewMap", NewMap), + U = updated_vars_only(U1, Map, NewMap), + ?debug("disjunct finished _Id=~w\n", [_Id]), + {ok, NewMap, V2State, U} + end + end + end. + +var_occurs_everywhere(V, Masks, NotFailed) -> + ordsets:is_subset(NotFailed, get_mask(V, Masks)). + +v2_solve_disj([I|Is], [C|Cs], I, Map0, V2State0, UL, MapL, Eval, Uneval, + Failed0) -> + Id = C#constraint_list.id, + Map1 = restore_local_map(V2State0, Id, Map0), + case v2_solve(C, Map1, V2State0) of + {error, V2State} -> + ?debug("disj error I=~w~n", [I]), + Failed = Failed0 orelse not is_failed_list(C, V2State0), + v2_solve_disj(Is, Cs, I+1, Map0, V2State, UL, MapL, Eval, Uneval, Failed); + {ok, Map, V2State1, U} -> + ?debug("disj I=~w U=~w~n", [I, U]), + V2State = save_local_map(V2State1, Id, U, Map), + pp_map("DMap", Map), + v2_solve_disj(Is, Cs, I+1, Map0, V2State, [U|UL], [Map|MapL], + [I|Eval], Uneval, Failed0) + end; +v2_solve_disj([], [], _I, _Map, V2State, UL, MapL, Eval, Uneval, Failed) -> + {ok, V2State, lists:reverse(Eval), UL, MapL, lists:reverse(Uneval), Failed}; +v2_solve_disj(Is, [C|Cs], I, Map, V2State, UL, MapL, Eval, Uneval0, Failed) -> + Uneval = [{I,C#constraint_list.id} || + not is_failed_list(C, V2State)] ++ Uneval0, + v2_solve_disj(Is, Cs, I+1, Map, V2State, UL, MapL, Eval, Uneval, Failed). + +save_local_map(#v2_state{constr_data = ConData}=V2State, Id, U, Map) -> + Part0 = [{V,dict:fetch(V, Map)} || V <- U], + Part1 = + case dict:find(Id, ConData) of + error -> []; % cannot happen + {ok, {Part2,[]}} -> Part2 + end, + ?debug("save local map Id=~w:\n", [Id]), + Part = lists:ukeymerge(1, lists:keysort(1, Part0), Part1), + pp_map("New Part", dict:from_list(Part0)), + pp_map("Old Part", dict:from_list(Part1)), + pp_map(" => Part", dict:from_list(Part)), + V2State#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)}. + +restore_local_map(#v2_state{constr_data = ConData}, Id, Map0) -> + case dict:find(Id, ConData) of + error -> Map0; + {ok, failed} -> Map0; + {ok, {[],_}} -> Map0; + {ok, {Part0,U}} -> + Part = [{K,V} || {K,V} <- Part0, not lists:member(K, U)], + ?debug("restore local map Id=~w U=~w\n", [Id, U]), + pp_map("Part", dict:from_list(Part)), + pp_map("Map0", Map0), + Map = lists:foldl(fun({K,V}, D) -> dict:store(K, V, D)end, Map0, Part), + pp_map("Map", Map), + Map + end. + +v2_solve_conjunct(Conj, Map, V2State0) -> + #constraint_list{type = conj, list = Cs} = Conj, + ?debug("conjunct Id=~w~n", [Conj#constraint_list.id]), + IsFlat = case Cs of [#constraint{}|_] -> true; _ -> false end, + case get_flags(V2State0, Conj) of + {V2State, failed_list} -> {error, V2State}; + {V2State, Flags} -> + v2_solve_conj(Flags, Cs, 1, Map, Conj, IsFlat, V2State, [], [], [], + Map, Flags) + end. + +%% LastMap and LastFlags are used for loop detection. +v2_solve_conj([I|Is], [Cs|Tail], I, Map0, Conj, IsFlat, V2State0, + UL, NewFs0, VarsUp, LastMap, LastFlags) -> + ?debug("conj Id=~w I=~w~n", [Conj#constraint_list.id, I]), + true = IsFlat =:= is_record(Cs, constraint), + pp_constr_data("conj", V2State0), + case v2_solve(Cs, Map0, V2State0) of + {error, V2State1} -> {error, failed_list(Conj, V2State1)}; + {ok, Map, V2State1, []} -> + v2_solve_conj(Is, Tail, I+1, Map, Conj, IsFlat, V2State1, + UL, NewFs0, VarsUp, LastMap, LastFlags); + {ok, Map, V2State1, U} when IsFlat -> % optimization + %% It is ensured by enumerate_constraints() that every + %% #constraint{} has a conjunct as parent, and that such a + %% parent has nothing but #constraint{}:s as children, a fact + %% which is used here to simplify the flag calculation. + Mask = lists:umerge([get_mask(V, Conj#constraint_list.masks) || V <- U]), + {Is1, NewF} = add_mask_to_flags(Is, Mask, I, []), + NewFs = [NewF|NewFs0], + v2_solve_conj(Is1, Tail, I+1, Map, Conj, IsFlat, V2State1, + [U|UL], NewFs, VarsUp, LastMap, LastFlags); + {ok, Map, V2State1, U} -> + #constraint_list{masks = Masks, list = AllCs} = Conj, + M = lists:keydelete(I, 1, vars_per_child(U, Masks)), + {V2State2, NewF0} = save_updated_vars_list(AllCs, M, V2State1), + {NewF, F} = lists:splitwith(fun(J) -> J < I end, NewF0), + Is1 = lists:umerge(Is, F), + NewFs = [NewF|NewFs0], + v2_solve_conj(Is1, Tail, I+1, Map, Conj, IsFlat, V2State2, + [U|UL], NewFs, VarsUp, LastMap, LastFlags) + end; +v2_solve_conj([], _Cs, _I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp, + LastMap, LastFlags) -> + U = lists:umerge(UL), + case lists:umerge(NewFs) of + [] -> + ?debug("conjunct finished Id=~w\n", [Conj#constraint_list.id]), + {ok, Map, V2State, lists:umerge([U|VarsUp])}; + NewFlags when NewFlags =:= LastFlags, Map =:= LastMap -> + %% A loop was detected! The cause is some bug, possibly in erl_types. + %% The evaluation continues, but the results can be wrong. + report_detected_loop(Conj), + {ok, Map, V2State, lists:umerge([U|VarsUp])}; + NewFlags -> + #constraint_list{type = conj, list = Cs} = Conj, + v2_solve_conj(NewFlags, Cs, 1, Map, Conj, IsFlat, V2State, + [], [], [U|VarsUp], Map, NewFlags) + end; +v2_solve_conj(Is, [_|Tail], I, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp, + LastMap, LastFlags) -> + v2_solve_conj(Is, Tail, I+1, Map, Conj, IsFlat, V2State, UL, NewFs, VarsUp, + LastMap, LastFlags). + +-ifdef(DEBUG_LOOP_DETECTION). +report_detected_loop(Conj) -> + io:format("A loop was detected in ~w\n", [Conj#constraint_list.id]). +-else. +report_detected_loop(_) -> + ok. +-endif. + +add_mask_to_flags(Flags, [Im|M], I, L) when I > Im -> + add_mask_to_flags(Flags, M, I, [Im|L]); +add_mask_to_flags(Flags, [_|M], _I, L) -> + {lists:umerge(Flags, M), lists:reverse(L)}. + +get_mask(V, {d, Masks}) -> + case dict:find(V, Masks) of + error -> []; + {ok, M} -> M + end; +get_mask(V, Masks) -> + case lists:keyfind(V, 1, Masks) of + false -> []; + {V, M} -> M + end. + +get_flags(#v2_state{constr_data = ConData}=V2State0, C) -> + #constraint_list{id = Id, list = Cs, masks = Masks} = C, + case dict:find(Id, ConData) of + error -> + ?debug("get_flags Id=~w Flags=all ~w\n", [Id, length(Cs)]), + V2State = V2State0#v2_state{constr_data = dict:store(Id, {[],[]}, ConData)}, + {V2State, lists:seq(1, length(Cs))}; + {ok, failed} -> + {V2State0, failed_list}; + {ok, {Part,U}} when U =/= [] -> + ?debug("get_flags Id=~w U=~w\n", [Id, U]), + V2State = V2State0#v2_state{constr_data = dict:store(Id, {Part,[]}, ConData)}, + save_updated_vars_list(Cs, vars_per_child(U, Masks), V2State) + end. + +vars_per_child(U, Masks) -> + family([{I, V} || V <- lists:usort(U), I <- get_mask(V, Masks)]). + +save_updated_vars_list(Cs, IU, V2State) -> + save_updated_vars_list1(Cs, IU, V2State, 1, []). + +save_updated_vars_list1([C|Cs], [{I,U}|IU], V2State0, I, Is) -> + V2State = save_updated_vars(C, U, V2State0), + save_updated_vars_list1(Cs, IU, V2State, I+1, [I|Is]); +save_updated_vars_list1([], [], V2State, _I, Is) -> + {V2State, lists:reverse(Is)}; +save_updated_vars_list1([_|Cs], IU, V2State, I, Is) -> + save_updated_vars_list1(Cs, IU, V2State, I+1, Is). + +save_updated_vars(#constraint{}, _, V2State) -> + V2State; +save_updated_vars(#constraint_list{}=C, U, V2State0) -> + save_updated_vars1(V2State0, C, U); +save_updated_vars(#constraint_ref{id = Id}, U, V2State) -> + Cs = state__get_cs(Id, V2State#v2_state.state), + save_updated_vars(Cs, U, V2State). + +save_updated_vars1(V2State, C, U) -> + #v2_state{constr_data = ConData} = V2State, + #constraint_list{id = Id} = C, + case dict:find(Id, ConData) of + error -> V2State; % error means everything is flagged + {ok, failed} -> V2State; + {ok, {Part,U0}} -> + %% Duplicates are not so common; let masks/2 remove them. + U1 = U ++ U0, + V2State#v2_state{constr_data = dict:store(Id, {Part,U1}, ConData)} + end. + +-ifdef(DEBUG). +pp_constr_data(_Tag, #v2_state{constr_data = D}) -> + io:format("Constr data at ~p\n", [_Tag]), + _ = [begin + case _PartU of + {_Part, _U} -> + io:format("Id: ~w Vars: ~w\n", [_Id, _U]), + [pp_map("Part", dict:from_list(_Part)) || _Part =/= []]; + failed -> + io:format("Id: ~w failed list\n", [_Id]) + end + end || + {_Id, _PartU} <- lists:keysort(1, dict:to_list(D))], + ok. + +-else. +pp_constr_data(_Tag, _V2State) -> + ok. +-endif. + +failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}=V2State) -> + ?debug("error list ~w~n", [Id]), + V2State#v2_state{constr_data = dict:store(Id, failed, D)}. + +is_failed_list(#constraint_list{id = Id}, #v2_state{constr_data = D}) -> + dict:find(Id, D) =:= {ok, failed}. + +%% Solver v1 + solve_ref_or_list(#constraint_ref{id = Id, deps = Deps}, Map, MapDict, State) -> {OldLocalMap, Check} = case dict:find(Id, MapDict) of - error -> {dict:new(), false}; + error -> {map_new(), false}; {ok, M} -> {M, true} end, ?debug("Checking ref to fun: ~w\n", [debug_lookup_name(Id)]), + %% Note: mk_constraint_ref() has already removed Id from Deps. The + %% reason for doing it there is that it makes it easy for + %% calculate_masks() to make the corresponding adjustment for + %% version v2. CheckDeps = ordsets:del_element(t_var_name(Id), Deps), + true = CheckDeps =:= Deps, case Check andalso maps_are_equal(OldLocalMap, Map, CheckDeps) of true -> ?debug("Equal\n", []), @@ -1771,6 +2329,7 @@ solve_ref_or_list(#constraint_ref{id = Id, deps = Deps}, FunType0 = lookup_type(Id, NewMap), {NewMapDict0, FunType0} end, + ?debug(" Id=~w Assigned ~s\n", [Id, format_type(FunType)]), NewMap1 = enter_type(Id, FunType, Map), NewMap2 = case state__get_rec_var(Id, State) of @@ -1783,17 +2342,21 @@ solve_ref_or_list(#constraint_list{type=Type, list = Cs, deps = Deps, id = Id}, Map, MapDict, State) -> {OldLocalMap, Check} = case dict:find(Id, MapDict) of - error -> {dict:new(), false}; + error -> {map_new(), false}; {ok, M} -> {M, true} end, ?debug("Checking ref to list: ~w\n", [Id]), - case Check andalso maps_are_equal(OldLocalMap, Map, Deps) of + if + OldLocalMap =:= error -> {error, MapDict}; true -> - ?debug("~w equal ~w\n", [Type, Id]), - {ok, MapDict, Map}; - false -> - ?debug("~w not equal: ~w. Solving\n", [Type, Id]), - solve_clist(Cs, Type, Id, Deps, MapDict, Map, State) + case Check andalso maps_are_equal(OldLocalMap, Map, Deps) of + true -> + ?debug("~w equal ~w\n", [Type, Id]), + {ok, MapDict, Map}; + false -> + ?debug("~w not equal: ~w. Solving\n", [Type, Id]), + solve_clist(Cs, Type, Id, Deps, MapDict, Map, State) + end end. solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) -> @@ -1801,8 +2364,8 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) -> {ok, RecVar} = state__get_rec_var(Id, State), ?debug("OldRecType ~s\n", [format_type(RecType0)]), RecType = t_limit(RecType0, ?TYPE_LIMIT), - Map1 = enter_type(RecVar, RecType, dict:erase(t_var_name(Id), Map)), - ?debug("\tMap in: ~p\n",[[{X, format_type(Y)}||{X, Y}<-dict:to_list(Map1)]]), + Map1 = enter_type(RecVar, RecType, erase_type(t_var_name(Id), Map)), + pp_map("Map1", Map1), case solve_ref_or_list(Cs, Map1, MapDict, State) of {error, _} = Error -> case t_is_none(RecType0) of @@ -1815,8 +2378,7 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) -> Error end; {ok, NewMapDict, NewMap} -> - ?debug("\tMap: ~p\n", - [[{X, format_type(Y)} || {X, Y} <- dict:to_list(NewMap)]]), + pp_map("NewMap", NewMap), NewRecType = unsafe_lookup_type(Id, NewMap), case t_is_equal(NewRecType, RecType0) of true -> @@ -1828,7 +2390,8 @@ solve_self_recursive(Cs, Map, MapDict, Id, RecType0, State) -> solve_clist(Cs, conj, Id, Deps, MapDict, Map, State) -> case solve_cs(Cs, Map, MapDict, State) of - {error, _} = Error -> Error; + {error, NewMapDict} -> + {error, dict:store(Id, error, NewMapDict)}; {ok, NewMapDict, NewMap} = Ret -> case Cs of [_] -> @@ -1850,7 +2413,7 @@ solve_clist(Cs, disj, Id, _Deps, MapDict, Map, State) -> end, {Maps, NewMapDict} = lists:mapfoldl(Fun, MapDict, Cs), case [X || {ok, X} <- Maps] of - [] -> {error, NewMapDict}; + [] -> {error, dict:store(Id, error, NewMapDict)}; MapList -> NewMap = join_maps(MapList), {ok, dict:store(Id, NewMap, NewMapDict), NewMap} @@ -1869,14 +2432,9 @@ solve_cs([#constraint_list{} = C|Tail], Map, MapDict, State) -> solve_cs([#constraint{} = C|Tail], Map, MapDict, State) -> case solve_one_c(C, Map, State#state.opaques) of error -> - ?debug("+++++++++++\nFailed: ~s :: ~s ~w ~s :: ~s\n+++++++++++\n", - [format_type(C#constraint.lhs), - format_type(lookup_type(C#constraint.lhs, Map)), - C#constraint.op, - format_type(C#constraint.rhs), - format_type(lookup_type(C#constraint.rhs, Map))]), + report_failed_constraint(C, Map), {error, MapDict}; - {ok, NewMap} -> + {ok, {NewMap, _U}} -> solve_cs(Tail, NewMap, MapDict, State) end; solve_cs([], Map, MapDict, _State) -> @@ -1897,7 +2455,11 @@ solve_one_c(#constraint{lhs = Lhs, rhs = Rhs, op = Op}, Map, Opaques) -> eq -> case solve_subtype(Lhs, Inf, Map, Opaques) of error -> error; - {ok, Map1} -> solve_subtype(Rhs, Inf, Map1, Opaques) + {ok, {Map1, U1}} -> + case solve_subtype(Rhs, Inf, Map1, Opaques) of + error -> error; + {ok, {Map2, U2}} -> {ok, {Map2, lists:umerge(U1, U2)}} + end end end end. @@ -1920,18 +2482,34 @@ solve_subtype(Type, Inf, Map, Opaques) -> end. %% end. +report_failed_constraint(_C, _Map) -> + ?debug("+++++++++++\nFailed: ~s :: ~s ~w ~s :: ~s\n+++++++++++\n", + [format_type(_C#constraint.lhs), + format_type(lookup_type(_C#constraint.lhs, _Map)), + _C#constraint.op, + format_type(_C#constraint.rhs), + format_type(lookup_type(_C#constraint.rhs, _Map))]). + %% ============================================================================ %% %% Maps and types. %% %% ============================================================================ +map_new() -> + dict:new(). + +join_maps([Map]) -> + Map; join_maps(Maps) -> - Keys = lists:foldl(fun(TmpMap, AccKeys) -> - [Key || Key <- AccKeys, dict:is_key(Key, TmpMap)] - end, - dict:fetch_keys(hd(Maps)), tl(Maps)), - join_maps(Keys, Maps, dict:new()). + Keys = constrained_keys(Maps), + join_maps(Keys, Maps, map_new()). + +constrained_keys(Maps) -> + lists:foldl(fun(TmpMap, AccKeys) -> + [Key || Key <- AccKeys, dict:is_key(Key, TmpMap)] + end, + dict:fetch_keys(hd(Maps)), tl(Maps)). join_maps([Key|Left], Maps = [Map|MapsLeft], AccMap) -> NewType = join_one_key(Key, MapsLeft, lookup_type(Key, Map)), @@ -1994,13 +2572,13 @@ enter_type(Key, Val, Map) when is_integer(Key) -> ?debug("Entering ~s :: ~s\n", [format_type(t_var(Key)), format_type(Val)]), case t_is_any(Val) of true -> - dict:erase(Key, Map); + erase_type(Key, Map); false -> LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT), case dict:find(Key, Map) of {ok, LimitedVal} -> Map; - {ok, _} -> dict:store(Key, LimitedVal, Map); - error -> dict:store(Key, LimitedVal, Map) + {ok, _} -> map_store(Key, LimitedVal, Map); + error -> map_store(Key, LimitedVal, Map) end end; enter_type(Key, Val, Map) -> @@ -2008,13 +2586,13 @@ enter_type(Key, Val, Map) -> KeyName = t_var_name(Key), case t_is_any(Val) of true -> - dict:erase(KeyName, Map); + erase_type(KeyName, Map); false -> LimitedVal = t_limit(Val, ?INTERNAL_TYPE_LIMIT), case dict:find(KeyName, Map) of {ok, LimitedVal} -> Map; - {ok, _} -> dict:store(KeyName, LimitedVal, Map); - error -> dict:store(KeyName, LimitedVal, Map) + {ok, _} -> map_store(KeyName, LimitedVal, Map); + error -> map_store(KeyName, LimitedVal, Map) end end. @@ -2024,11 +2602,25 @@ enter_type_lists([Key|KeyTail], [Val|ValTail], Map) -> enter_type_lists([], [], Map) -> Map. -enter_type_list([{Key, Val}|Tail], Map) -> +enter_type_list(KeyVals, Map) -> + enter_type_list(KeyVals, Map, []). + +enter_type_list([{Key, Val}|Tail], Map, U0) -> + {Map1,U1} = enter_type2(Key, Val, Map), + enter_type_list(Tail, Map1, U1++U0); +enter_type_list([], Map, U) -> + {Map, ordsets:from_list(U)}. + +enter_type2(Key, Val, Map) -> Map1 = enter_type(Key, Val, Map), - enter_type_list(Tail, Map1); -enter_type_list([], Map) -> - Map. + {Map1, [Key || not is_same(Key, Map, Map1)]}. + +map_store(Key, Val, Map) -> + ?debug("Storing ~w :: ~s\n", [Key, format_type(Val)]), + dict:store(Key, Val, Map). + +erase_type(Key, Map) -> + dict:erase(Key, Map). lookup_type_list(List, Map) -> [lookup_type(X, Map) || X <- List]. @@ -2079,22 +2671,41 @@ mk_var_no_lit(Var) -> mk_var_no_lit_list(List) -> [mk_var_no_lit(X) || X <- List]. +updated_vars_only(U, OldMap, NewMap) -> + [V || V <- U, not is_same(V, OldMap, NewMap)]. + +is_same(Key, Map1, Map2) -> + t_is_equal(lookup_type(Key, Map1), lookup_type(Key, Map2)). + +pp_map(_S, _Map) -> + ?debug("\t~s: ~p\n", + [_S, [{X, lists:flatten(format_type(Y))} || + {X, Y} <- lists:keysort(1, dict:to_list(_Map))]]). + %% ============================================================================ %% %% The State. %% %% ============================================================================ -new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes) -> +new_state(SCC0, NextLabel, CallGraph, Plt, PropTypes, Solvers) -> List = [{MFA, Var} || {MFA, {Var, _Fun}, _Rec} <- SCC0], NameMap = dict:from_list(List), MFAs = [MFA || {MFA, _Var} <- List], SCC = [mk_var(Fun) || {_MFA, {_Var, Fun}, _Rec} <- SCC0], - SelfRecs = [F || F <- SCC, - dialyzer_callgraph:is_self_rec(t_var_name(F), CallGraph)], + SelfRec = + case SCC of + [OneF] -> + Label = t_var_name(OneF), + case dialyzer_callgraph:is_self_rec(Label, CallGraph) of + true -> OneF; + false -> false + end; + _Many -> false + end, #state{callgraph = CallGraph, name_map = NameMap, next_label = NextLabel, - prop_types = PropTypes, plt = Plt, scc = ordsets:from_list(SCC), - mfas = MFAs, self_recs = ordsets:from_list(SelfRecs)}. + prop_types = {d, PropTypes}, plt = Plt, scc = ordsets:from_list(SCC), + mfas = MFAs, self_rec = SelfRec, solvers = Solvers}. state__set_rec_dict(State, RecDict) -> State#state{records = RecDict}. @@ -2193,14 +2804,21 @@ state__plt(#state{plt = PLT}) -> state__new_constraint_context(State) -> State#state{cs = []}. -state__prop_domain(FunLabel, #state{prop_types = PropTypes}) -> +state__prop_domain(FunLabel, #state{prop_types = {e, ETSPropTypes}}) -> + try ets:lookup_element(ETSPropTypes, FunLabel, 2) of + {_Range_Fun, Dom} -> {ok, Dom}; + FunType -> {ok, t_fun_args(FunType)} + catch + _:_ -> error + end; +state__prop_domain(FunLabel, #state{prop_types = {d, PropTypes}}) -> case dict:find(FunLabel, PropTypes) of error -> error; {ok, {_Range_Fun, Dom}} -> {ok, Dom}; {ok, FunType} -> {ok, t_fun_args(FunType)} end. -state__add_prop_constrs(Tree, #state{prop_types = PropTypes} = State) -> +state__add_prop_constrs(Tree, #state{prop_types = {d, PropTypes}} = State) -> Label = cerl_trees:get_label(Tree), case dict:find(Label, PropTypes) of error -> State; @@ -2263,21 +2881,17 @@ state__mk_vars(N, #state{next_label = NL} = State) -> Vars = [t_var(X) || X <- lists:seq(NL, NewLabel-1)], {State#state{next_label = NewLabel}, Vars}. -state__store_constrs(Id, Cs, #state{cmap = Dict} = State) -> +state__store_constrs(Id, Cs, #state{cmap = {d, Dict}} = State) -> NewDict = dict:store(Id, Cs, Dict), - State#state{cmap = NewDict}. + State#state{cmap = {d, NewDict}}. -state__get_cs(Var, #state{cmap = Dict}) -> +state__get_cs(Var, #state{cmap = {e, ETSDict}}) -> + ets:lookup_element(ETSDict, Var, 2); +state__get_cs(Var, #state{cmap = {d, Dict}}) -> dict:fetch(Var, Dict). -%% The functions here will not be treated as self recursive. -%% These functions will need to be handled as such manually. -state__mark_as_non_self_rec(SCC, #state{self_recs = SelfRecs} = State) -> - %% TODO: Check if the result is always empty and just set it to [] if so. - State#state{self_recs = ordsets:subtract(SelfRecs, ordsets:from_list(SCC))}. - -state__is_self_rec(Fun, #state{self_recs = SelfRecs}) -> - ordsets:is_element(Fun, SelfRecs). +state__is_self_rec(Fun, #state{self_rec = SelfRec}) -> + Fun =:= SelfRec. state__store_funs(Vars0, Funs0, #state{fun_map = Map} = State) -> debug_make_name_map(Vars0, Funs0), @@ -2314,7 +2928,7 @@ mk_constraint(Lhs, Op, Rhs) -> case Deps =:= [] of true -> %% This constraint is constant. Solve it immediately. - case solve_one_c(C, dict:new(), []) of + case solve_one_c(C, map_new(), []) of error -> throw(error); _ -> %% This is always true, keep it anyway for logistic reasons @@ -2337,8 +2951,9 @@ constraint_opnd_is_any(Type) -> t_is_any(Type). -ifdef(DEBUG). --spec mk_fun_var(fun((_) -> erl_types:erl_type()), [erl_types:erl_type()], - integer()) -> #fun_var{}. +-spec mk_fun_var(integer(), + fun((_) -> erl_types:erl_type()), + [erl_types:erl_type()]) -> #fun_var{}. mk_fun_var(Line, Fun, Types) -> Deps = [t_var_name(Var) || Var <- t_collect_vars(t_product(Types))], @@ -2386,7 +3001,9 @@ mk_constraints([], _Op, []) -> []. mk_constraint_ref(Id, Deps) -> - #constraint_ref{id = Id, deps = Deps}. + %% See also solve_ref_or_list(), #constraint_ref{}. + Ds = ordsets:del_element(t_var_name(Id), Deps), + #constraint_ref{id = Id, deps = Ds}. mk_constraint_list(Type, List) -> List1 = ordsets:from_list(lift_lists(Type, List)), @@ -2523,19 +3140,21 @@ enumerate_constraints([#constraint_ref{id = Id} = C|Tail], N, Acc, State) -> enumerate_constraints([#constraint_list{type = conj, list = List} = C|Tail], N, Acc, State) -> %% Separate the flat constraints from the deep ones to make a - %% separate fixpoint interation over the flat ones for speed. - {Flat, Deep} = lists:splitwith(fun(#constraint{}) -> true; + %% separate fixpoint iteration over the flat ones for speed. + {Flat, Deep} = lists:partition(fun(#constraint{}) -> true; (#constraint_list{}) -> false; (#constraint_ref{}) -> false end, List), {NewFlat, N1, State1} = enumerate_constraints(Flat, N, [], State), {NewDeep, N2, State2} = enumerate_constraints(Deep, N1, [], State1), {NewList, N3} = - case shorter_than_two(NewFlat) orelse (NewDeep =:= []) of - true -> {NewFlat ++ NewDeep, N2}; - false -> - {NewCLists, TmpN} = group_constraints_in_components(NewFlat, N2), - {NewCLists ++ NewDeep, TmpN} + if + NewFlat =:= [] -> {NewDeep, N2}; + NewDeep =:= [] -> {NewFlat, N2}; + true -> + TmpCList = mk_conj_constraint_list(NewFlat), + {[TmpCList#constraint_list{id = {list, N2}}| NewDeep], + N2 + 1} end, NewAcc = [C#constraint_list{list = NewList, id = {list, N3}}|Acc], enumerate_constraints(Tail, N3+1, NewAcc, State2); @@ -2549,42 +3168,6 @@ enumerate_constraints([#constraint{} = C|Tail], N, Acc, State) -> enumerate_constraints([], N, Acc, State) -> {lists:reverse(Acc), N, State}. -shorter_than_two([]) -> true; -shorter_than_two([_]) -> true; -shorter_than_two([_|_]) -> false. - -group_constraints_in_components(Cs, N) -> - DepList = [Deps || #constraint{deps = Deps} <- Cs], - case find_dep_components(DepList, []) of - [_] -> {Cs, N}; - [_|_] = Components -> - ConstrComp = [[C || #constraint{deps = D} = C <- Cs, - ordsets:is_subset(D, Comp)] - || Comp <- Components], - lists:mapfoldl(fun(CComp, TmpN) -> - TmpCList = mk_conj_constraint_list(CComp), - {TmpCList#constraint_list{id = {list, TmpN}}, - TmpN + 1} - end, N, ConstrComp) - end. - -find_dep_components([Set|Left], AccComponents) -> - {Component, Ungrouped} = find_dep_components(Left, Set, []), - case Component =:= Set of - true -> find_dep_components(Ungrouped, [Component|AccComponents]); - false -> find_dep_components([Component|Ungrouped], AccComponents) - end; -find_dep_components([], AccComponents) -> - AccComponents. - -find_dep_components([Set|Left], AccSet, Ungrouped) -> - case ordsets:intersection(Set, AccSet) of - [] -> find_dep_components(Left, AccSet, [Set|Ungrouped]); - [_|_] -> find_dep_components(Left, ordsets:union(Set, AccSet), Ungrouped) - end; -find_dep_components([], AccSet, Ungrouped) -> - {AccSet, Ungrouped}. - %% Put the fun ref constraints last in any conjunction since we need %% to separate the environment from the interior of the function. order_fun_constraints(State) -> @@ -2615,7 +3198,9 @@ order_fun_constraints([#constraint_list{list = List, type = Type} = C|Tail], end, lists:mapfoldl(FoldFun, State, List) end, - NewAcc = [update_constraint_list(C, NewList)|Acc], + C1 = update_constraint_list(C, NewList), + Masks = calculate_masks(NewList, 1, []), + NewAcc = [update_masks(C1, Masks)|Acc], order_fun_constraints(Tail, Funs, NewAcc, NewState); order_fun_constraints([#constraint{} = C|Tail], Funs, Acc, State) -> order_fun_constraints(Tail, Funs, [C|Acc], State); @@ -2623,6 +3208,22 @@ order_fun_constraints([], Funs, Acc, State) -> NewState = order_fun_constraints(Funs, State), {lists:reverse(Acc)++Funs, NewState}. +update_masks(C, Masks) -> + C#constraint_list{masks = Masks}. + +-define(VARS_LIMIT, 50). + +calculate_masks([C|Cs], I, L0) -> + calculate_masks(Cs, I+1, [{V, I} || V <- get_deps(C)] ++ L0); +calculate_masks([], _I, L) -> + M = family(L), + case length(M) > ?VARS_LIMIT of + true -> + {d, dict:from_list(M)}; + false -> + M + end. + %% ============================================================================ %% %% Utilities. @@ -2700,6 +3301,9 @@ lookup_record(Records, Tag, Arity) -> error end. +family(L) -> + sofs:to_external(sofs:rel2fam(sofs:relation(L))). + %% ============================================================================ %% %% Pretty printer and debug facilities. @@ -2714,13 +3318,24 @@ lookup_record(Records, Tag, Arity) -> -ifdef(DEBUG). format_type(#fun_var{deps = Deps, origin = Origin}) -> - io_lib:format("Fun@L~p(~s)", - [Origin, lists:flatten([format_type(t_var(X))||X<-Deps])]); + L = [format_type(t_var(X)) || X <- Deps], + io_lib:format("Fun@L~p(~s)", [Origin, join_chars(L, ",")]); format_type(Type) -> case cerl:is_literal(Type) of true -> io_lib:format("~w", [cerl:concrete(Type)]); false -> erl_types:t_to_string(Type) end. + +join_chars([], _Sep) -> + []; +join_chars([H|T], Sep) -> + [H|[[Sep,X] || X <- T]]. + +debug_lookup_name(Var) -> + case dict:find(t_var_name(Var), get(dialyzer_typesig_map)) of + error -> Var; + {ok, Name} -> Name + end. -endif. -ifdef(DEBUG_NAME_MAP). @@ -2739,12 +3354,6 @@ debug_make_name_map([Var|VarLeft], [Fun|FunLeft], Map) -> debug_make_name_map([], [], Map) -> Map. -debug_lookup_name(Var) -> - case dict:find(t_var_name(Var), get(dialyzer_typesig_map)) of - error -> Var; - {ok, Name} -> Name - end. - -else. debug_make_name_map(_Vars, _Funs) -> ok. @@ -2755,51 +3364,55 @@ pp_constrs_scc(SCC, State) -> [pp_constrs(Fun, state__get_cs(Fun, State), State) || Fun <- SCC]. pp_constrs(Fun, Cs, State) -> - io:format("Constraints for fun: ~w\n", [debug_lookup_name(Fun)]), + io:format("Constraints for fun: ~w", [debug_lookup_name(Fun)]), MaxDepth = pp_constraints(Cs, State), io:format("Depth: ~w\n", [MaxDepth]). pp_constraints(Cs, State) -> - Res = pp_constraints([Cs], none, 0, 0, State), + Res = pp_constraints([Cs], 0, 0, State), io:nl(), Res. -pp_constraints([List|Tail], Separator, Level, MaxDepth, - State) when is_list(List) -> - pp_constraints(List++Tail, Separator, Level, MaxDepth, State); -pp_constraints([#constraint_ref{id = Id}|Left], Separator, - Level, MaxDepth, State) -> +pp_constraints([List|Tail], Level, MaxDepth, State) when is_list(List) -> + pp_constraints(List++Tail, Level, MaxDepth, State); +pp_constraints([#constraint_ref{id = Id}|Left], Level, MaxDepth, State) -> Cs = state__get_cs(Id, State), + pp_indent(Level), io:format("%Ref ~w%", [t_var_name(Id)]), - pp_constraints([Cs|Left], Separator, Level, MaxDepth, State); -pp_constraints([#constraint{lhs = Lhs, op = Op, rhs = Rhs}], _Separator, - Level, MaxDepth, _State) -> - io:format("~s ~w ~s", [format_type(Lhs), Op, format_type(Rhs)]), + pp_constraints([Cs|Left], Level, MaxDepth, State); +pp_constraints([#constraint{}=C], Level, MaxDepth, _State) -> + pp_op(C, Level), erlang:max(Level, MaxDepth); -pp_constraints([#constraint{lhs = Lhs, op = Op, rhs = Rhs}|Tail], Separator, - Level, MaxDepth, State) -> - io:format("~s ~w ~s ~s ", [format_type(Lhs), Op, format_type(Rhs),Separator]), - pp_constraints(Tail, Separator, Level, MaxDepth, State); +pp_constraints([#constraint{}=C|Tail], Level, MaxDepth, State) -> + pp_op(C, Level), + pp_constraints(Tail, Level, MaxDepth, State); pp_constraints([#constraint_list{type = Type, list = List, id = Id}], - _Separator, Level, MaxDepth, State) -> - io:format("%List ~w(", [Id]), - NewSeparator = case Type of - conj -> "*"; - disj -> "+" - end, - NewMaxDepth = pp_constraints(List, NewSeparator, Level + 1, MaxDepth, State), + Level, MaxDepth, State) -> + pp_indent(Level), + case Type of + conj -> io:format("Conj ~w (", [Id]); + disj -> io:format("Disj ~w (", [Id]) + end, + NewMaxDepth = pp_constraints(List, Level + 1, MaxDepth, State), io:format(")", []), NewMaxDepth; pp_constraints([#constraint_list{type = Type, list = List, id = Id}|Tail], - Separator, Level, MaxDepth, State) -> - io:format("List ~w(", [Id]), - NewSeparator = case Type of - conj -> "*"; - disj -> "+" - end, - NewMaxDepth = pp_constraints(List, NewSeparator, Level+1, MaxDepth, State), - io:format(") ~s\n~s ", [Separator, Separator]), - pp_constraints(Tail, Separator, Level, NewMaxDepth, State). + Level, MaxDepth, State) -> + pp_indent(Level), + case Type of + conj -> io:format("Conj ~w (", [Id]); + disj -> io:format("Disj ~w (", [Id]) + end, + NewMaxDepth = pp_constraints(List, Level+1, MaxDepth, State), + io:format(")", []), + pp_constraints(Tail, Level, NewMaxDepth, State). + +pp_op(#constraint{lhs = Lhs, op = Op, rhs = Rhs}, Level) -> + pp_indent(Level), + io:format("~s ~w ~s", [format_type(Lhs), Op, format_type(Rhs)]). + +pp_indent(Level) -> + io:format("\n~*s", [Level*2, ""]). -else. pp_constrs_scc(_SCC, _State) -> ok. diff --git a/lib/dialyzer/src/dialyzer_utils.erl b/lib/dialyzer/src/dialyzer_utils.erl index 2a248fb028..149e777e1f 100644 --- a/lib/dialyzer/src/dialyzer_utils.erl +++ b/lib/dialyzer/src/dialyzer_utils.erl @@ -43,7 +43,8 @@ pp_hook/0, process_record_remote_types/1, sets_filter/2, - src_compiler_opts/0 + src_compiler_opts/0, + parallelism/0 ]). -include("dialyzer.hrl"). @@ -536,3 +537,12 @@ pp_unit(Unit, Ctxt, Cont) -> pp_atom(Atom) -> String = atom_to_list(cerl:atom_val(Atom)), prettypr:text(String). + +%%------------------------------------------------------------------------------ + +-spec parallelism() -> integer(). + +parallelism() -> + CPUs = erlang:system_info(logical_processors_available), + Schedulers = erlang:system_info(schedulers), + min(CPUs, Schedulers). diff --git a/lib/dialyzer/src/dialyzer_worker.erl b/lib/dialyzer/src/dialyzer_worker.erl new file mode 100644 index 0000000000..50b2e31ed8 --- /dev/null +++ b/lib/dialyzer/src/dialyzer_worker.erl @@ -0,0 +1,189 @@ +%% -*- erlang-indent-level: 2 -*- +%%----------------------------------------------------------------------- +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(dialyzer_worker). + +-export([launch/4, sequential/4]). + +-export_type([worker/0]). + +-type worker() :: pid(). %%opaque + +-type mode() :: dialyzer_coordinator:mode(). +-type coordinator() :: dialyzer_coordinator:coordinator(). +-type init_data() :: dialyzer_coordinator:init_data(). +-type result() :: dialyzer_coordinator:result(). + +-record(state, { + mode :: mode(), + job :: mfa_or_funlbl() | file:filename(), + coordinator :: coordinator(), + init_data :: init_data(), + depends_on = [] :: list() + }). + +-include("dialyzer.hrl"). + +%% -define(DEBUG, true). + +-ifdef(DEBUG). +-define(debug(X__, Y__), io:format(X__, Y__)). +-else. +-define(debug(X__, Y__), ok). +-endif. + +%%-------------------------------------------------------------------- + +-spec launch(mode(), [mfa_or_funlbl()], init_data(), coordinator()) -> worker(). + +launch(Mode, Job, InitData, Coordinator) -> + State = #state{mode = Mode, + job = Job, + init_data = InitData, + coordinator = Coordinator}, + InitState = + case Mode of + X when X =:= 'typesig'; X =:= 'dataflow' -> initializing; + X when X =:= 'compile'; X =:= 'warnings' -> running + end, + spawn_link(fun() -> loop(InitState, State) end). + +%%-------------------------------------------------------------------- + +loop(updating, State) -> + ?debug("Update: ~p\n",[State#state.job]), + NextStatus = + case waits_more_success_typings(State) of + true -> waiting; + false -> running + end, + loop(NextStatus, State); +loop(initializing, #state{job = SCC, init_data = InitData} = State) -> + DependsOn = dialyzer_succ_typings:find_depends_on(SCC, InitData), + ?debug("Deps ~p: ~p\n",[State#state.job, DependsOn]), + loop(updating, State#state{depends_on = DependsOn}); +loop(waiting, State) -> + ?debug("Wait: ~p\n",[State#state.job]), + NewState = wait_for_success_typings(State), + loop(updating, NewState); +loop(running, #state{mode = 'compile'} = State) -> + dialyzer_coordinator:wait_activation(), + ?debug("Compile: ~s\n",[State#state.job]), + Result = + case start_compilation(State) of + {ok, EstimatedSize, Data} -> + Label = ask_coordinator_for_label(EstimatedSize, State), + continue_compilation(Label, Data); + {error, _Reason} = Error -> + Error + end, + report_to_coordinator(Result, State); +loop(running, #state{mode = 'warnings'} = State) -> + dialyzer_coordinator:wait_activation(), + ?debug("Warning: ~s\n",[State#state.job]), + Result = collect_warnings(State), + report_to_coordinator(Result, State); +loop(running, #state{mode = Mode} = State) when + Mode =:= 'typesig'; Mode =:= 'dataflow' -> + request_activation(State), + ?debug("Run: ~p\n",[State#state.job]), + NotFixpoint = do_work(State), + ok = broadcast_done(State), + report_to_coordinator(NotFixpoint, State). + +waits_more_success_typings(#state{depends_on = Depends}) -> + Depends =/= []. + +broadcast_done(#state{job = SCC, init_data = InitData, + coordinator = Coordinator}) -> + RequiredBy = dialyzer_succ_typings:find_required_by(SCC, InitData), + {Callers, Unknown} = + dialyzer_coordinator:sccs_to_pids(RequiredBy, Coordinator), + send_done(Callers, SCC), + continue_broadcast_done(Unknown, SCC, Coordinator). + +send_done(Callers, SCC) -> + ?debug("Sending ~p: ~p\n",[SCC, Callers]), + SendSTFun = fun(PID) -> PID ! {done, SCC} end, + lists:foreach(SendSTFun, Callers). + +continue_broadcast_done([], _SCC, _Coordinator) -> ok; +continue_broadcast_done(Rest, SCC, Coordinator) -> + %% This time limit should be greater than the time required + %% by the coordinator to spawn all processes. + timer:sleep(500), + {Callers, Unknown} = dialyzer_coordinator:sccs_to_pids(Rest, Coordinator), + send_done(Callers, SCC), + continue_broadcast_done(Unknown, SCC, Coordinator). + +wait_for_success_typings(#state{depends_on = DependsOn} = State) -> + receive + {done, SCC} -> + ?debug("GOT ~p: ~p\n",[State#state.job, SCC]), + State#state{depends_on = DependsOn -- [SCC]} + after + 5000 -> + ?debug("Still Waiting ~p: ~p\n",[State#state.job, DependsOn]), + State + end. + +request_activation(#state{coordinator = Coordinator}) -> + dialyzer_coordinator:request_activation(Coordinator). + +do_work(#state{mode = Mode, job = Job, init_data = InitData}) -> + case Mode of + typesig -> dialyzer_succ_typings:find_succ_types_for_scc(Job, InitData); + dataflow -> dialyzer_succ_typings:refine_one_module(Job, InitData) + end. + +report_to_coordinator(Result, #state{job = Job, coordinator = Coordinator}) -> + ?debug("Done: ~p\n",[Job]), + dialyzer_coordinator:job_done(Job, Result, Coordinator). + +start_compilation(#state{job = Job, init_data = InitData}) -> + dialyzer_analysis_callgraph:start_compilation(Job, InitData). + +ask_coordinator_for_label(EstimatedSize, #state{coordinator = Coordinator}) -> + dialyzer_coordinator:get_next_label(EstimatedSize, Coordinator). + +continue_compilation(Label, Data) -> + dialyzer_analysis_callgraph:continue_compilation(Label, Data). + +collect_warnings(#state{job = Job, init_data = InitData}) -> + dialyzer_succ_typings:collect_warnings(Job, InitData). + +%%------------------------------------------------------------------------------ + +-type extra() :: label() | 'unused'. + +-spec sequential(mode(), [mfa_or_funlbl()], init_data(), extra()) -> result(). + +sequential('compile', Job, InitData, Extra) -> + case dialyzer_analysis_callgraph:start_compilation(Job, InitData) of + {ok, EstimatedSize, Data} -> + {EstimatedSize, continue_compilation(Extra, Data)}; + {error, _Reason} = Error -> {0, Error} + end; +sequential('typesig', Job, InitData, _Extra) -> + dialyzer_succ_typings:find_succ_types_for_scc(Job, InitData); +sequential('dataflow', Job, InitData, _Extra) -> + dialyzer_succ_typings:refine_one_module(Job, InitData); +sequential('warnings', Job, InitData, _Extra) -> + dialyzer_succ_typings:collect_warnings(Job, InitData). diff --git a/lib/dialyzer/test/Makefile b/lib/dialyzer/test/Makefile index 6a1abce943..9f8a3f1194 100644 --- a/lib/dialyzer/test/Makefile +++ b/lib/dialyzer/test/Makefile @@ -25,10 +25,10 @@ RELSYSDIR = $(RELEASE_PATH)/dialyzer_test include $(ERL_TOP)/make/otp_release_targets.mk release_tests_spec: - $(INSTALL_DIR) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - $(INSTALL_DATA) $(AUXILIARY_FILES) $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) - cd $(RELSYSDIR);\ + $(INSTALL_DIR) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + $(INSTALL_DATA) $(AUXILIARY_FILES) "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + cd "$(RELSYSDIR)";\ erlc dialyzer_common.erl file_utils.erl;\ erl -noshell -run dialyzer_common create_all_suites -s erlang halt diff --git a/lib/dialyzer/test/opaque_SUITE_data/results/queue b/lib/dialyzer/test/opaque_SUITE_data/results/queue index 59ce33f098..c3f04ea64d 100644 --- a/lib/dialyzer/test/opaque_SUITE_data/results/queue +++ b/lib/dialyzer/test/opaque_SUITE_data/results/queue @@ -5,6 +5,7 @@ queue_use.erl:27: The attempt to match a term of type queue() against the patter queue_use.erl:33: Attempt to test for equality between a term of type {[42,...],[]} and a term of opaque type queue() queue_use.erl:36: The attempt to match a term of type queue() against the pattern {F, _R} breaks the opaqueness of the term queue_use.erl:40: The call queue:out({[42,...],[]}) does not have an opaque term of type queue() as 1st argument +queue_use.erl:48: The call queue_use:add_unique(42,#db{p::[],q::queue()}) contains an opaque term as 2nd argument when terms of different types are expected in these positions queue_use.erl:51: The call queue_use:is_in_queue(E::42,DB::#db{p::[],q::queue()}) contains an opaque term as 2nd argument when terms of different types are expected in these positions queue_use.erl:56: The attempt to match a term of type #db{p::[],q::queue()} against the pattern {'db', _, {L1, L2}} breaks the opaqueness of queue() queue_use.erl:62: The call queue_use:tuple_queue({42,'gazonk'}) does not have a term of type {_,queue()} (with opaque subterms) as 1st argument diff --git a/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes new file mode 100644 index 0000000000..8dc0361b0d --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/results/contracts_with_subtypes @@ -0,0 +1,31 @@ + +contracts_with_subtypes.erl:106: The call contracts_with_subtypes:rec_arg({'a','b'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:107: The call contracts_with_subtypes:rec_arg({'b','a'}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:108: The call contracts_with_subtypes:rec_arg({'a',{'b','a'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:109: The call contracts_with_subtypes:rec_arg({'b',{'a','b'}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:110: The call contracts_with_subtypes:rec_arg({'a',{'b',{'a','b'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:111: The call contracts_with_subtypes:rec_arg({'b',{'a',{'b','a'}}}) breaks the contract (Arg) -> 'ok' when is_subtype(Arg,{'a',A} | {'b',B}), is_subtype(A,'a' | {'b',B}), is_subtype(B,'b' | {'a',A}) +contracts_with_subtypes.erl:142: The pattern 1 can never match the type binary() | string() +contracts_with_subtypes.erl:145: The pattern 'alpha' can never match the type {'ok',X} | {'ok',X,binary() | string()} +contracts_with_subtypes.erl:147: The pattern 42 can never match the type {'ok',_} | {'ok',_,binary() | string()} +contracts_with_subtypes.erl:163: The pattern 'alpha' can never match the type {'ok',X} +contracts_with_subtypes.erl:165: The pattern 42 can never match the type {'ok',X} +contracts_with_subtypes.erl:183: The pattern 'alpha' can never match the type {'ok',X} +contracts_with_subtypes.erl:185: The pattern 42 can never match the type {'ok',X} +contracts_with_subtypes.erl:202: The pattern 1 can never match the type binary() | string() +contracts_with_subtypes.erl:205: The pattern {'ok', _} can never match the type {'ok',X,binary() | string()} +contracts_with_subtypes.erl:206: The pattern 'alpha' can never match the type {'ok',X,binary() | string()} +contracts_with_subtypes.erl:207: The pattern {'ok', 42} can never match the type {'ok',X,binary() | string()} +contracts_with_subtypes.erl:208: The pattern 42 can never match the type {'ok',X,binary() | string()} +contracts_with_subtypes.erl:234: Function flat_ets_new_t/0 has no local return +contracts_with_subtypes.erl:235: The call contracts_with_subtypes:flat_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,'set' | 'ordered_set' | 'bag' | 'duplicate_bag' | 'public' | 'protected' | 'private' | 'named_table' | {'keypos',integer()} | {'heir',pid(),term()} | {'heir','none'} | {'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed') +contracts_with_subtypes.erl:23: Invalid type specification for function contracts_with_subtypes:extract2/0. The success typing is () -> 'something' +contracts_with_subtypes.erl:261: Function factored_ets_new_t/0 has no local return +contracts_with_subtypes.erl:262: The call contracts_with_subtypes:factored_ets_new(12,[]) breaks the contract (Name,Options) -> atom() when is_subtype(Name,atom()), is_subtype(Options,[Option]), is_subtype(Option,Type | Access | 'named_table' | {'keypos',Pos} | {'heir',Pid::pid(),HeirData} | {'heir','none'} | Tweaks), is_subtype(Type,type()), is_subtype(Access,access()), is_subtype(Tweaks,{'write_concurrency',boolean()} | {'read_concurrency',boolean()} | 'compressed'), is_subtype(Pos,pos_integer()), is_subtype(HeirData,term()) +contracts_with_subtypes.erl:77: The call contracts_with_subtypes:foo1(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,atom()), is_subtype(Res,atom()) +contracts_with_subtypes.erl:78: The call contracts_with_subtypes:foo2(5) breaks the contract (Arg1) -> Res when is_subtype(Arg1,Arg2), is_subtype(Arg2,atom()), is_subtype(Res,atom()) +contracts_with_subtypes.erl:79: The call contracts_with_subtypes:foo3(5) breaks the contract (Arg1) -> Res when is_subtype(Arg2,atom()), is_subtype(Arg1,Arg2), is_subtype(Res,atom()) +contracts_with_subtypes.erl:7: Invalid type specification for function contracts_with_subtypes:extract/0. The success typing is () -> 'something' +contracts_with_subtypes.erl:80: The call contracts_with_subtypes:foo4(5) breaks the contract (Type) -> Type when is_subtype(Type,atom()) +contracts_with_subtypes.erl:81: The call contracts_with_subtypes:foo5(5) breaks the contract (Type::atom()) -> Type::atom() +contracts_with_subtypes.erl:82: The call contracts_with_subtypes:foo6(5) breaks the contract (Type) -> Type when is_subtype(Type,atom()) diff --git a/lib/dialyzer/test/small_SUITE_data/results/port_info_test b/lib/dialyzer/test/small_SUITE_data/results/port_info_test index 9ee863f9eb..863a3d61df 100644 --- a/lib/dialyzer/test/small_SUITE_data/results/port_info_test +++ b/lib/dialyzer/test/small_SUITE_data/results/port_info_test @@ -3,4 +3,5 @@ port_info_test.erl:10: The pattern {'connected', 42} can never match the type 'u port_info_test.erl:14: The pattern {'registered_name', "42"} can never match the type 'undefined' | {'registered_name',atom()} port_info_test.erl:19: The pattern {'output', 42} can never match the type 'undefined' | {'connected',pid()} port_info_test.erl:24: Guard test 'links' =:= Atom::'connected' can never succeed -port_info_test.erl:28: The pattern {'gazonk', _} can never match the type 'undefined' | {'connected' | 'id' | 'input' | 'links' | 'name' | 'output' | 'registered_name',atom() | pid() | [pid() | char()] | integer()} +port_info_test.erl:28: The pattern {'gazonk', _} can never match the type 'undefined' | {'connected' | 'id' | 'input' | 'links' | 'name' | 'os_pid' | 'output' | 'registered_name',atom() | pid() | [pid() | char()] | integer()} +port_info_test.erl:32: The pattern {'os_pid', "42"} can never match the type 'undefined' | {'os_pid','undefined' | non_neg_integer()} diff --git a/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/a.erl b/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/a.erl new file mode 100644 index 0000000000..7efe870b0d --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/a.erl @@ -0,0 +1,9 @@ +-module(a). +-export([g/1]). + +-export_type([a/0, t/0]). +-type a() :: integer(). +-type t() :: a() | maybe_improper_list(t(), t()). + +-spec g(t()) -> t(). +g(X) -> X. diff --git a/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/b.erl b/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/b.erl new file mode 100644 index 0000000000..b08bc5e66c --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/collapse_lists/b.erl @@ -0,0 +1,5 @@ +-module(b). +-export([f/1]). + +-spec f(a:t()) -> a:t(). +f(X) -> a:g(X). diff --git a/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl new file mode 100644 index 0000000000..d72138d509 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/contracts_with_subtypes.erl @@ -0,0 +1,267 @@ +-module(contracts_with_subtypes). + +-compile(export_all). + +%=============================================================================== + +-spec extract() -> 'ok'. + +extract() -> + case dz_extract() of + {ok, Val} -> Val; + error -> exit(boom) + end. + +-spec dz_extract() -> RetValue when + FileList :: something, + RetValue :: {ok, FileList} | error. + +dz_extract() -> get(foo). + +%------------------------------------------------------------------------------- + +-spec extract2() -> 'ok'. + +extract2() -> + case dz_extract2() of + {ok, Val} -> Val; + error -> exit(boom) + end. + +-spec dz_extract2() -> RetValue when + RetValue :: {ok, FileList} | error, + FileList :: something. + +dz_extract2() -> get(foo). + +%=============================================================================== + +-spec foo1(Arg1) -> Res when + Arg1 :: atom(), + Res :: atom(). + +foo1(X) -> X. + +-spec foo2(Arg1) -> Res when + Arg1 :: Arg2, + Arg2 :: atom(), + Res :: atom(). + +foo2(X) -> X. + +-spec foo3(Arg1) -> Res when + Arg2 :: atom(), + Arg1 :: Arg2, + Res :: atom(). + +foo3(X) -> X. + +-spec foo4(Type) -> Type when is_subtype(Type, atom()). + +foo4(X) -> X. + +-spec foo5(Type :: atom()) -> Type :: atom(). + +foo5(X) -> X. + +-spec foo6(Type) -> Type when Type :: atom(). + +foo6(X) -> X. + +-spec foo7(Type) -> Type. + +foo7(X) -> X. + +%------------------------------------------------------------------------------- + +bar(1) -> foo1(5); +bar(2) -> foo2(5); +bar(3) -> foo3(5); +bar(4) -> foo4(5); +bar(5) -> foo5(5); +bar(6) -> foo6(5); +bar(7) -> foo7(5). + +wrong_foo6() -> + b = foo6(a). + +%=============================================================================== + +-spec rec_arg(Arg) -> ok when + Arg :: {a, A} | {b, B}, + A :: a | {b, B}, + B :: b | {a, A}. + +rec_arg(X) -> get(X). + +c(aa) -> rec_arg({a, a}); +c(bb) -> rec_arg({b, b}); +c(abb) -> rec_arg({a, {b, b}}); +c(baa) -> rec_arg({b, {a, a}}); +c(abaa) -> rec_arg({a, {b, {a, a}}}); +c(babb) -> rec_arg({b, {a, {b, b}}}); +c(ababb) -> rec_arg({a, {b, {a, {b, b}}}}); +c(babaa) -> rec_arg({b, {a, {b, {a, a}}}}). + +w(ab) -> rec_arg({a, b}); +w(ba) -> rec_arg({b, a}); +w(aba) -> rec_arg({a, {b, a}}); +w(bab) -> rec_arg({b, {a, b}}); +w(abab) -> rec_arg({a, {b, {a, b}}}); +w(baba) -> rec_arg({b, {a, {b, a}}}); +w(ababa) -> rec_arg({a, {b, {a, {b, a}}}}); +w(babab) -> rec_arg({b, {a, {b, {a, b}}}}). + +%=============================================================================== + +-type dublo(X) :: {X, X}. + +-type weird(X,Y) :: {X, Y, X, X}. + +-spec forfun(dublo(Var)) -> ok when Var :: atom(). + +forfun(_) -> ok. + +-spec forfun2(weird(Var, Var)) -> ok when Var :: atom(). + +forfun2(_) -> ok. + +%=============================================================================== + +-spec shallow(X) -> {ok, X} | {ok, X, file:filename()} | err1 | err2. + +shallow(X) -> get(X). + +st(X) when is_atom(X) -> + case shallow(X) of + err1 -> ok; + err2 -> ok; + {ok, X} -> ok; + {ok, X, Res} -> + case Res of + 1 -> bad; + _Other -> ok + end; + alpha -> bad; + {ok, 42} -> bad; + 42 -> bad + end. + +%------------------------------------------------------------------------------- + +-spec deep(X) -> Ret when + Ret :: {ok, X} | Err, + Err :: err1 | err2. + +deep(X) -> get(X). + +dt(X) when is_atom(X) -> + case deep(X) of + err1 -> ok; + err2 -> ok; + {ok, X} -> ok; + alpha -> bad; + {ok, 42} -> bad; + 42 -> bad + end. + +%------------------------------------------------------------------------------- + +-type local_errors() :: err1 | err2. + +-spec deep2(X) -> Ret when + Ret :: {ok, X} | Err, + Err :: local_errors(). + +deep2(X) -> get(X). + +dt2(X) when is_atom(X) -> + case deep2(X) of + err1 -> ok; + err2 -> ok; + {ok, X} -> ok; + alpha -> bad; + {ok, 42} -> bad; + 42 -> bad + end. + +%------------------------------------------------------------------------------- + +-spec deep3(X) -> Ret when + Ret :: {ok, X, file:filename()} | Err, + Err :: local_errors(). + +deep3(X) -> get(X). + +dt3(X) when is_atom(X) -> + case deep3(X) of + err1 -> ok; + err2 -> ok; + {ok, X, Res} -> + case Res of + 1 -> bad; + _Other -> ok + end; + {ok, X} -> bad; + alpha -> bad; + {ok, 42} -> bad; + 42 -> bad + end. + +%=============================================================================== + +-spec flat_ets_new(Name, Options) -> atom() when + Name :: atom(), + Options :: [Option], + Option :: set + | ordered_set + | bag + | duplicate_bag + | public + | protected + | private + | named_table + | {keypos, integer()} + | {heir, pid(), term()} + | {heir, none} + | {write_concurrency, boolean()} + | {read_concurrency, boolean()} + | compressed. + +flat_ets_new(Name, Options) -> + get({Name, Options}). + +flat_ets_new_t() -> + flat_ets_new(12,[]), + flat_ets_new({a,b},[]), + flat_ets_new(name,[foo]), + flat_ets_new(name,{bag}), + flat_ets_new(name,bag), + ok. + +-type access() :: public | protected | private. +-type type() :: set | ordered_set | bag | duplicate_bag. + +-spec factored_ets_new(Name, Options) -> atom() when + Name :: atom(), + Options :: [Option], + Option :: Type | Access | named_table | {keypos,Pos} + | {heir, Pid :: pid(), HeirData} | {heir, none} | Tweaks, + Type :: type(), + Access :: access(), + Tweaks :: {write_concurrency, boolean()} + | {read_concurrency, boolean()} + | compressed, + Pos :: pos_integer(), + HeirData :: term(). + +factored_ets_new(Name, Options) -> + get({Name, Options}). + +factored_ets_new_t() -> + factored_ets_new(12,[]), + factored_ets_new({a,b},[]), + factored_ets_new(name,[foo]), + factored_ets_new(name,{bag}), + factored_ets_new(name,bag), + ok. diff --git a/lib/dialyzer/test/small_SUITE_data/src/deep_lc.erl b/lib/dialyzer/test/small_SUITE_data/src/deep_lc.erl new file mode 100644 index 0000000000..d9ca0817d9 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/deep_lc.erl @@ -0,0 +1,14 @@ +-module(deep_lc). + +-export([t/0]). + +%% This is compile/test/lc_SUITE:deeply_nested/1 +%% +%% Used to be _very_ slow. Unknown how slow, but more than 15 hours. + +t() -> + [[X1,X2,X3,X4,X5,X6,X7(),X8,X9,X10,X11,X12,X13,X14,X15,X16,X17,X18(),X19,X20] || + X1 <- [99],X2 <- [98],X3 <- [97],X4 <- [96],X5 <- [42],X6 <- [17], + X7 <- [fun() -> X5*X5 end],X8 <- [12],X9 <- [11],X10 <- [10], + X11 <- [9],X12 <- [8],X13 <- [7],X14 <- [6],X15 <- [5], + X16 <- [4],X17 <- [3],X18 <- [fun() -> X16+X17 end],X19 <- [2],X20 <- [1]]. diff --git a/lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl b/lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl new file mode 100644 index 0000000000..237f43b1a6 --- /dev/null +++ b/lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl @@ -0,0 +1,31 @@ +-module(maybe_servers). + +-export([maybe_server/2, mirror_maybe_server/2]). + +maybe_server(O, I) -> + case O of + no -> + maybe_loop(fun(_) -> fin end, I); + yes -> + maybe_loop(fun(X) -> {ok, X} end, I) + end. + +maybe_loop(F, X)-> + case F(X) of + {ok, Y} -> maybe_loop(F, Y); + fin -> exit(n) + end. + +mirror_maybe_loop(F, X)-> + case F(X) of + {ok, Y} -> mirror_maybe_loop(F, Y); + fin -> exit(n) + end. + +mirror_maybe_server(O, I) -> + case O of + no -> + mirror_maybe_loop(fun(_) -> fin end, I); + yes -> + mirror_maybe_loop(fun(X) -> {ok, X} end, I) + end. diff --git a/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl b/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl index 2ee9a3a6e2..07f22256c9 100644 --- a/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl +++ b/lib/dialyzer/test/small_SUITE_data/src/port_info_test.erl @@ -3,7 +3,7 @@ %% and the quality of the warnings that Dialyzer spits out %% -module(port_info_test). --export([t1/1, t2/1, t3/1, t4/1, t5/2, buggy/1]). +-export([t1/1, t2/1, t3/1, t4/1, t5/2, t6/1, buggy/1]). %% The following errors are correctly caught, but the messages are a bit weird t1(X) when is_port(X) -> @@ -28,6 +28,10 @@ t5(X, Atom) when is_port(X) -> {gazonk, _} = erlang:port_info(X, Atom); t5(_, _) -> ok. +t6(X) when is_port(X) -> + {os_pid, "42"} = erlang:port_info(X, os_pid); +t6(_) -> ok. + %% The type system is not strong enough to catch the following errors buggy(X) when is_atom(X) -> {links, X} = erlang:port_info(foo, X). diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk index 622e51b859..edafcc4afb 100644 --- a/lib/dialyzer/vsn.mk +++ b/lib/dialyzer/vsn.mk @@ -1 +1 @@ -DIALYZER_VSN = 2.5 +DIALYZER_VSN = 2.5.1 diff --git a/lib/diameter/autoconf/vxworks/sed.general b/lib/diameter/autoconf/vxworks/sed.general index 77b306aa0a..9199983e16 100644 --- a/lib/diameter/autoconf/vxworks/sed.general +++ b/lib/diameter/autoconf/vxworks/sed.general @@ -66,7 +66,6 @@ s|@HCLIBS@|| s|@ENABLE_ALLOC_TYPE_VARS@|| s|@TERMCAP_LIB@|| s|@ERTS_BUILD_SMP_EMU@|no| -s|@ERTS_BUILD_HYBRID_EMU@|no| s|@HAVE_VALGRIND@|no| s|@EXEEXT@|| s|@WITH_SCTP@|| diff --git a/lib/diameter/doc/src/Makefile b/lib/diameter/doc/src/Makefile index bc3e649e6b..cdf63e7fb6 100644 --- a/lib/diameter/doc/src/Makefile +++ b/lib/diameter/doc/src/Makefile @@ -162,18 +162,18 @@ include $(DIAMETER_TOP)/make/release_targets.mk endif release_docs_spec: $(LOCAL)docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html - $(INSTALL_DIR) $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man4 - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(HTMLDIR)/*.* $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN4_FILES) $(RELEASE_PATH)/man/man4 - [ -z "$(LOCAL)" ] || cp -r $(HTMLDIR)/js $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(HTMLDIR)/*.* "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4" + [ -z "$(LOCAL)" ] || cp -r $(HTMLDIR)/js "$(RELSYSDIR)/doc/html" echo $(LOCAL) release_spec: diff --git a/lib/diameter/examples/code/GNUmakefile b/lib/diameter/examples/code/GNUmakefile index a0669119d2..98e36a99e3 100644 --- a/lib/diameter/examples/code/GNUmakefile +++ b/lib/diameter/examples/code/GNUmakefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2011. All Rights Reserved. +# Copyright Ericsson AB 2010-2012. 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 diff --git a/lib/diameter/examples/code/client.erl b/lib/diameter/examples/code/client.erl index 9e65f98de0..bfe71b0e56 100644 --- a/lib/diameter/examples/code/client.erl +++ b/lib/diameter/examples/code/client.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -38,7 +38,7 @@ -module(client). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl"). +-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). -export([start/1, %% start a service connect/2, %% add a connecting transport diff --git a/lib/diameter/examples/code/client_cb.erl b/lib/diameter/examples/code/client_cb.erl index 524a8f94a1..ee3dcb2fec 100644 --- a/lib/diameter/examples/code/client_cb.erl +++ b/lib/diameter/examples/code/client_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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,7 +20,7 @@ -module(client_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl"). +-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). %% diameter callbacks -export([peer_up/3, diff --git a/lib/diameter/examples/code/peer.erl b/lib/diameter/examples/code/peer.erl index 89203e15c3..b07cd32b98 100644 --- a/lib/diameter/examples/code/peer.erl +++ b/lib/diameter/examples/code/peer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,7 +25,7 @@ -module(peer). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl"). +-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). -export([start/2, listen/2, diff --git a/lib/diameter/examples/code/redirect.erl b/lib/diameter/examples/code/redirect.erl index b54701243f..d4d94ab23a 100644 --- a/lib/diameter/examples/code/redirect.erl +++ b/lib/diameter/examples/code/redirect.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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,7 +20,7 @@ -module(redirect). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl"). +-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). -export([start/1, listen/2, diff --git a/lib/diameter/examples/code/redirect_cb.erl b/lib/diameter/examples/code/redirect_cb.erl index ea7ad38749..da31add70d 100644 --- a/lib/diameter/examples/code/redirect_cb.erl +++ b/lib/diameter/examples/code/redirect_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 diff --git a/lib/diameter/examples/code/relay.erl b/lib/diameter/examples/code/relay.erl index deecb1cfc0..d3438f83f3 100644 --- a/lib/diameter/examples/code/relay.erl +++ b/lib/diameter/examples/code/relay.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -32,7 +32,7 @@ -module(relay). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl"). +-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). -export([start/1, listen/2, diff --git a/lib/diameter/examples/code/relay_cb.erl b/lib/diameter/examples/code/relay_cb.erl index 9ed6517d5c..9f9cd8d5ae 100644 --- a/lib/diameter/examples/code/relay_cb.erl +++ b/lib/diameter/examples/code/relay_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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,7 +20,7 @@ -module(relay_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl"). +-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). %% diameter callbacks -export([peer_up/3, diff --git a/lib/diameter/examples/code/server.erl b/lib/diameter/examples/code/server.erl index ebb408e501..3959461cec 100644 --- a/lib/diameter/examples/code/server.erl +++ b/lib/diameter/examples/code/server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,7 +35,7 @@ -module(server). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl"). +-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). -export([start/1, %% start a service listen/2, %% add a listening transport diff --git a/lib/diameter/examples/code/server_cb.erl b/lib/diameter/examples/code/server_cb.erl index 43b8e24b5c..0f6eb32ed6 100644 --- a/lib/diameter/examples/code/server_cb.erl +++ b/lib/diameter/examples/code/server_cb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -24,7 +24,7 @@ -module(server_cb). -include_lib("diameter/include/diameter.hrl"). --include_lib("diameter/src/app/diameter_gen_base_rfc3588.hrl"). +-include_lib("diameter/include/diameter_gen_base_rfc3588.hrl"). %% diameter callbacks -export([peer_up/3, @@ -76,7 +76,7 @@ handle_request(#diameter_packet{msg = Req, errors = []}, _SvcName, {_, Caps}) %% ... or one that wasn't. 3xxx errors are answered by diameter itself %% but these are 5xxx errors for which we must contruct a reply. %% diameter will set Result-Code and Failed-AVP's. -handle_request(#diameter_packet{msg = Req} = Pkt, _SvcName, {_, Caps}) +handle_request(#diameter_packet{msg = Req}, _SvcName, {_, Caps}) when is_record(Req, diameter_base_RAR) -> #diameter_caps{origin_host = {OH,_}, origin_realm = {OR,_}} diff --git a/lib/diameter/src/Makefile b/lib/diameter/src/Makefile index dbfaa4e140..de2eca0279 100644 --- a/lib/diameter/src/Makefile +++ b/lib/diameter/src/Makefile @@ -195,27 +195,27 @@ endif release_spec: opt for d in bin ebin include src/dict; do \ - $(INSTALL_DIR) $(RELSYSDIR)/$$d; \ + $(INSTALL_DIR) "$(RELSYSDIR)/$$d"; \ done - $(INSTALL_SCRIPT) $(BINS:%=../bin/%) $(RELSYSDIR)/bin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_SCRIPT) $(BINS:%=../bin/%) "$(RELSYSDIR)/bin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(EXTERNAL_HRLS:%=../include/%) $(DICT_HRLS) \ - $(RELSYSDIR)/include - $(INSTALL_DATA) $(DICTS:%=dict/%.dia) $(RELSYSDIR)/src/dict + "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(DICTS:%=dict/%.dia) "$(RELSYSDIR)/src/dict" $(MAKE) $(TARGET_DIRS:%/=release_src_%) $(MAKE) $(EXAMPLE_DIRS:%/=release_examples_%) $(TARGET_DIRS:%/=release_src_%): release_src_%: - $(INSTALL_DIR) $(RELSYSDIR)/src/$* + $(INSTALL_DIR) "$(RELSYSDIR)/src/$*" $(INSTALL_DATA) $(filter $*/%, $(TARGET_MODULES:%=%.erl) \ $(INTERNAL_HRLS)) \ $(filter $*/%, compiler/$(DICT_YRL).yrl) \ - $(RELSYSDIR)/src/$* + "$(RELSYSDIR)/src/$*" $(EXAMPLE_DIRS:%/=release_examples_%): release_examples_%: - $(INSTALL_DIR) $(RELSYSDIR)/examples/$* + $(INSTALL_DIR) "$(RELSYSDIR)/examples/$*" $(INSTALL_DATA) $(patsubst %, ../examples/%, $(filter $*/%, $(EXAMPLES))) \ - $(RELSYSDIR)/examples/$* + "$(RELSYSDIR)/examples/$*" release_docs_spec: diff --git a/lib/diameter/src/base/diameter.appup.src b/lib/diameter/src/base/diameter.appup.src index b1c94d4cc8..2ebdad598f 100644 --- a/lib/diameter/src/base/diameter.appup.src +++ b/lib/diameter/src/base/diameter.appup.src @@ -21,10 +21,14 @@ {"%VSN%", [ {"0.9", [{restart_application, diameter}]}, - {"0.10", [{restart_application, diameter}]} + {"0.10", [{restart_application, diameter}]}, + {"1.0", [{update, diameter_service}, + {update, diameter_watchdog}]} ], [ {"0.9", [{restart_application, diameter}]}, - {"0.10", [{restart_application, diameter}]} + {"0.10", [{restart_application, diameter}]}, + {"1.0", [{update, diameter_watchdog}, + {update, diameter_service}]} ] }. diff --git a/lib/diameter/test/Makefile b/lib/diameter/test/Makefile index ab5b45ff3d..1659330a91 100644 --- a/lib/diameter/test/Makefile +++ b/lib/diameter/test/Makefile @@ -141,7 +141,7 @@ log: # ---------------------------------------------------- /%: % force - sed -f release.sed $< > $(RELSYSDIR)$@ + sed -f release.sed $< > "$(RELSYSDIR)$@" ifeq ($(ERL_TOP),) include $(DIAMETER_TOP)/make/release_targets.mk @@ -152,17 +152,17 @@ endif release_spec release_docs_spec: release_tests_spec: - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(TEST_SPEC_FILE) \ $(COVER_SPEC_FILE) \ $(HRL_FILES) \ - $(RELSYSDIR) + "$(RELSYSDIR)" $(MAKE) $(DATA_DIRS:%/=release_data_%) $(MAKE) $(ERL_FILES:%=/%) $(DATA_DIRS:%/=release_data_%): release_data_%: - $(INSTALL_DIR) $(RELSYSDIR)/$* - $(INSTALL_DATA) $(filter $*/%, $(DATA)) $(RELSYSDIR)/$* + $(INSTALL_DIR) "$(RELSYSDIR)/$*" + $(INSTALL_DATA) $(filter $*/%, $(DATA)) "$(RELSYSDIR)/$*" force: diff --git a/lib/diameter/vsn.mk b/lib/diameter/vsn.mk index 0c240798cc..f6dc786417 100644 --- a/lib/diameter/vsn.mk +++ b/lib/diameter/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = diameter -DIAMETER_VSN = 1.0 +DIAMETER_VSN = 1.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(DIAMETER_VSN)$(PRE_VSN)" diff --git a/lib/edoc/doc/src/Makefile b/lib/edoc/doc/src/Makefile index b933094464..1f92d36f12 100644 --- a/lib/edoc/doc/src/Makefile +++ b/lib/edoc/doc/src/Makefile @@ -124,13 +124,13 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/edoc/include/Makefile b/lib/edoc/include/Makefile index 5b2ad38c9d..cf2e051632 100644 --- a/lib/edoc/include/Makefile +++ b/lib/edoc/include/Makefile @@ -49,8 +49,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(INCLUDE_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(INCLUDE_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/edoc/priv/Makefile b/lib/edoc/priv/Makefile index 13225e6d1a..73c42c05eb 100644 --- a/lib/edoc/priv/Makefile +++ b/lib/edoc/priv/Makefile @@ -45,9 +45,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(PRIV_FILES) $(RELSYSDIR)/priv - $(INSTALL_SCRIPT) $(GEN_SCRIPT) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(PRIV_FILES) "$(RELSYSDIR)/priv" + $(INSTALL_SCRIPT) $(GEN_SCRIPT) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/edoc/src/Makefile b/lib/edoc/src/Makefile index fcb0b61292..72354ac711 100644 --- a/lib/edoc/src/Makefile +++ b/lib/edoc/src/Makefile @@ -85,10 +85,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(OBJECTS) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(SOURCES) $(HRL_FILES) $(YRL_FILE) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(OBJECTS) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(SOURCES) $(HRL_FILES) $(YRL_FILE) "$(RELSYSDIR)/src" release_docs_spec: diff --git a/lib/edoc/src/edoc_data.erl b/lib/edoc/src/edoc_data.erl index aad0b14371..624f9177a2 100644 --- a/lib/edoc/src/edoc_data.erl +++ b/lib/edoc/src/edoc_data.erl @@ -167,7 +167,10 @@ callbacks(Es, Module, Env, Opts) -> case lists:any(fun (#entry{name = {behaviour_info, 1}}) -> true; (_) -> false end, - Es) of + Es) + orelse + lists:keymember(callback, 1, Module#module.attributes) + of true -> try (Module#module.name):behaviour_info(callbacks) of Fs -> diff --git a/lib/edoc/src/edoc_lib.erl b/lib/edoc/src/edoc_lib.erl index 7fd8358add..90fb8a679c 100644 --- a/lib/edoc/src/edoc_lib.erl +++ b/lib/edoc/src/edoc_lib.erl @@ -469,6 +469,10 @@ uri_get("ftp:" ++ Path) -> uri_get("//" ++ Path) -> Msg = io_lib:format("cannot access network-path: '//~s'.", [Path]), {error, Msg}; +uri_get([C, $:, $/ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z -> + uri_get_file(Path); % special case for Windows +uri_get([C, $:, $\ | _]=Path) when C >= $A, C =< $Z; C >= $a, C =< $z -> + uri_get_file(Path); % special case for Windows uri_get(URI) -> case is_relative_uri(URI) of true -> diff --git a/lib/edoc/test/Makefile b/lib/edoc/test/Makefile index 2dbdb77eff..2033e003b3 100644 --- a/lib/edoc/test/Makefile +++ b/lib/edoc/test/Makefile @@ -57,10 +57,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) edoc.spec edoc.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) edoc.spec edoc.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/edoc/vsn.mk b/lib/edoc/vsn.mk index b8f33894f1..2f403212c8 100644 --- a/lib/edoc/vsn.mk +++ b/lib/edoc/vsn.mk @@ -1 +1 @@ -EDOC_VSN = 0.7.9.1 +EDOC_VSN = 0.7.10 diff --git a/lib/eldap/.gitignore b/lib/eldap/.gitignore new file mode 100644 index 0000000000..d3dd8228d5 --- /dev/null +++ b/lib/eldap/.gitignore @@ -0,0 +1,4 @@ +*.beam +*.asn1db +ebin/ELDAPv3.hrl +ebin/ELDAPv3.erl diff --git a/lib/eldap/AUTHORS b/lib/eldap/AUTHORS new file mode 100644 index 0000000000..8f1e399306 --- /dev/null +++ b/lib/eldap/AUTHORS @@ -0,0 +1,7 @@ +Original author: + Torbjorn Tornkvist + +With patches from: + Simon MacMullen + Manuel Durán Aguete + voluntas
\ No newline at end of file diff --git a/lib/eldap/LICENSE b/lib/eldap/LICENSE new file mode 100644 index 0000000000..1f6200918f --- /dev/null +++ b/lib/eldap/LICENSE @@ -0,0 +1,21 @@ + +Copyright (c) 2010, Torbjorn Tornkvist + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/lib/eldap/Makefile b/lib/eldap/Makefile new file mode 100644 index 0000000000..3635ec759d --- /dev/null +++ b/lib/eldap/Makefile @@ -0,0 +1,39 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2012. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Macros +# ---------------------------------------------------- + +SUB_DIRECTORIES = src doc/src + +include vsn.mk +VSN = $(ELDAP_VSN) + +SPECIAL_TARGETS = + +# ---------------------------------------------------- +# Default Subdir Targets +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_subdir.mk + diff --git a/lib/eldap/README b/lib/eldap/README new file mode 100644 index 0000000000..e1bde9d658 --- /dev/null +++ b/lib/eldap/README @@ -0,0 +1,33 @@ +Hi, + +This is 'eldap', the Erlang LDAP library. + +It exports an API that can do all possible operations +you may want to do against an LDAP server. The code has +been tested to work at some point, but only the bind +and search operations are running daily in our products, +so there may be bugs lurking in some parts of the code. + +To just use eldap for doing authentication, do like in: + + {ok,X} = eldap:open(["ldap.mycorp.com"], []). + eldap:simple_bind(X, "uid=tobbe,ou=People,dc=mycorp,dc=com", "passwd"). + +In the doc/README.example you'll find a trace from a +Erlang shell session as an example on how to setup a +connection, authenticate (bind) and perform a search. +Note that by using the option {ssl, true}, you should +be able to setup an SSL tunnel (LDAPS) if your Erlang +system has been configured with SSL. + +In the test directory there are some hints and examples +on how to test the code and how to setup and populate +an OpenLDAP server. The 'eldap' code has been tested +agains OpenLDAP, IPlanet and ActiveDirectory servers. + +If you plan to incorporate this code into your system +I suggest that you build a server/supervisor harnesk +that uses 'eldap' (as we have done in our products). + +Good luck ! +/Tobbe diff --git a/lib/eldap/asn1/ELDAPv3.asn1 b/lib/eldap/asn1/ELDAPv3.asn1 new file mode 100644 index 0000000000..72b87d7221 --- /dev/null +++ b/lib/eldap/asn1/ELDAPv3.asn1 @@ -0,0 +1,278 @@ +-- Lightweight-Directory-Access-Protocol-V3 {1 3 6 1 1 18} +-- Copyright (C) The Internet Society (2006). This version of +-- this ASN.1 module is part of RFC 4511; see the RFC itself +-- for full legal notices. +ELDAPv3 DEFINITIONS +IMPLICIT TAGS +EXTENSIBILITY IMPLIED ::= + +BEGIN + +LDAPMessage ::= SEQUENCE { + messageID MessageID, + protocolOp CHOICE { + bindRequest BindRequest, + bindResponse BindResponse, + unbindRequest UnbindRequest, + searchRequest SearchRequest, + searchResEntry SearchResultEntry, + searchResDone SearchResultDone, + searchResRef SearchResultReference, + modifyRequest ModifyRequest, + modifyResponse ModifyResponse, + addRequest AddRequest, + addResponse AddResponse, + delRequest DelRequest, + delResponse DelResponse, + modDNRequest ModifyDNRequest, + modDNResponse ModifyDNResponse, + compareRequest CompareRequest, + compareResponse CompareResponse, + abandonRequest AbandonRequest, + extendedReq ExtendedRequest, + extendedResp ExtendedResponse, + ..., + intermediateResponse IntermediateResponse }, + controls [0] Controls OPTIONAL } + +MessageID ::= INTEGER (0 .. maxInt) + +maxInt INTEGER ::= 2147483647 -- (2^^31 - 1) -- + +LDAPString ::= OCTET STRING -- UTF-8 encoded, + -- [ISO10646] characters + +LDAPOID ::= OCTET STRING -- Constrained to <numericoid> + -- [RFC4512] + +LDAPDN ::= LDAPString -- Constrained to <distinguishedName> + -- [RFC4514] + +RelativeLDAPDN ::= LDAPString -- Constrained to <name-component> + -- [RFC4514] + +AttributeDescription ::= LDAPString + -- Constrained to <attributedescription> + -- [RFC4512] + +AttributeValue ::= OCTET STRING + +AttributeValueAssertion ::= SEQUENCE { + attributeDesc AttributeDescription, + assertionValue AssertionValue } + +AssertionValue ::= OCTET STRING + +PartialAttribute ::= SEQUENCE { + type AttributeDescription, + vals SET OF value AttributeValue } + +Attribute ::= PartialAttribute(WITH COMPONENTS { + ..., + vals (SIZE(1..MAX))}) + +MatchingRuleId ::= LDAPString + +LDAPResult ::= SEQUENCE { + resultCode ENUMERATED { + success (0), + operationsError (1), + protocolError (2), + timeLimitExceeded (3), + sizeLimitExceeded (4), + compareFalse (5), + compareTrue (6), + authMethodNotSupported (7), + strongerAuthRequired (8), + -- 9 reserved -- + referral (10), + adminLimitExceeded (11), + unavailableCriticalExtension (12), + confidentialityRequired (13), + saslBindInProgress (14), + + noSuchAttribute (16), + undefinedAttributeType (17), + inappropriateMatching (18), + constraintViolation (19), + attributeOrValueExists (20), + invalidAttributeSyntax (21), + -- 22-31 unused -- + noSuchObject (32), + aliasProblem (33), + invalidDNSyntax (34), + -- 35 reserved for undefined isLeaf -- + aliasDereferencingProblem (36), + -- 37-47 unused -- + inappropriateAuthentication (48), + invalidCredentials (49), + insufficientAccessRights (50), + busy (51), + unavailable (52), + unwillingToPerform (53), + loopDetect (54), + -- 55-63 unused -- + namingViolation (64), + objectClassViolation (65), + notAllowedOnNonLeaf (66), + notAllowedOnRDN (67), + entryAlreadyExists (68), + objectClassModsProhibited (69), + -- 70 reserved for CLDAP -- + affectsMultipleDSAs (71), + -- 72-79 unused -- + other (80), + ... }, + matchedDN LDAPDN, + diagnosticMessage LDAPString, + referral [3] Referral OPTIONAL } + +Referral ::= SEQUENCE SIZE (1..MAX) OF uri URI + +URI ::= LDAPString -- limited to characters permitted in + -- URIs + +Controls ::= SEQUENCE OF control Control + +Control ::= SEQUENCE { + controlType LDAPOID, + criticality BOOLEAN DEFAULT FALSE, + controlValue OCTET STRING OPTIONAL } + +BindRequest ::= [APPLICATION 0] SEQUENCE { + version INTEGER (1 .. 127), + name LDAPDN, + authentication AuthenticationChoice } + +AuthenticationChoice ::= CHOICE { + simple [0] OCTET STRING, + -- 1 and 2 reserved + sasl [3] SaslCredentials, + ... } + +SaslCredentials ::= SEQUENCE { + mechanism LDAPString, + credentials OCTET STRING OPTIONAL } + +BindResponse ::= [APPLICATION 1] SEQUENCE { + COMPONENTS OF LDAPResult, + serverSaslCreds [7] OCTET STRING OPTIONAL } + +UnbindRequest ::= [APPLICATION 2] NULL + +SearchRequest ::= [APPLICATION 3] SEQUENCE { + baseObject LDAPDN, + scope ENUMERATED { + baseObject (0), + singleLevel (1), + wholeSubtree (2), + ... }, + derefAliases ENUMERATED { + neverDerefAliases (0), + derefInSearching (1), + derefFindingBaseObj (2), + derefAlways (3) }, + sizeLimit INTEGER (0 .. maxInt), + timeLimit INTEGER (0 .. maxInt), + typesOnly BOOLEAN, + filter Filter, + attributes AttributeSelection } + +AttributeSelection ::= SEQUENCE OF selector LDAPString + -- The LDAPString is constrained to + -- <attributeSelector> in Section 4.5.1.8 + +Filter ::= CHOICE { + and [0] SET SIZE (1..MAX) OF filter Filter, + or [1] SET SIZE (1..MAX) OF filter Filter, + not [2] Filter, + equalityMatch [3] AttributeValueAssertion, + substrings [4] SubstringFilter, + greaterOrEqual [5] AttributeValueAssertion, + lessOrEqual [6] AttributeValueAssertion, + present [7] AttributeDescription, + approxMatch [8] AttributeValueAssertion, + extensibleMatch [9] MatchingRuleAssertion, + ... } + +SubstringFilter ::= SEQUENCE { + type AttributeDescription, + substrings SEQUENCE SIZE (1..MAX) OF substring CHOICE { + initial [0] AssertionValue, -- can occur at most once + any [1] AssertionValue, + final [2] AssertionValue } -- can occur at most once + } + +MatchingRuleAssertion ::= SEQUENCE { + matchingRule [1] MatchingRuleId OPTIONAL, + type [2] AttributeDescription OPTIONAL, + matchValue [3] AssertionValue, + dnAttributes [4] BOOLEAN DEFAULT FALSE } + +SearchResultEntry ::= [APPLICATION 4] SEQUENCE { + objectName LDAPDN, + attributes PartialAttributeList } + +PartialAttributeList ::= SEQUENCE OF + partialAttribute PartialAttribute + +SearchResultReference ::= [APPLICATION 19] SEQUENCE + SIZE (1..MAX) OF uri URI + +SearchResultDone ::= [APPLICATION 5] LDAPResult + +ModifyRequest ::= [APPLICATION 6] SEQUENCE { + object LDAPDN, + changes SEQUENCE OF change SEQUENCE { + operation ENUMERATED { + add (0), + delete (1), + replace (2), + ... }, + modification PartialAttribute } } + +ModifyResponse ::= [APPLICATION 7] LDAPResult + +AddRequest ::= [APPLICATION 8] SEQUENCE { + entry LDAPDN, + attributes AttributeList } + +AttributeList ::= SEQUENCE OF attribute Attribute + +AddResponse ::= [APPLICATION 9] LDAPResult + +DelRequest ::= [APPLICATION 10] LDAPDN + +DelResponse ::= [APPLICATION 11] LDAPResult + +ModifyDNRequest ::= [APPLICATION 12] SEQUENCE { + entry LDAPDN, + newrdn RelativeLDAPDN, + deleteoldrdn BOOLEAN, + newSuperior [0] LDAPDN OPTIONAL } + +ModifyDNResponse ::= [APPLICATION 13] LDAPResult + +CompareRequest ::= [APPLICATION 14] SEQUENCE { + entry LDAPDN, + ava AttributeValueAssertion } + +CompareResponse ::= [APPLICATION 15] LDAPResult + +AbandonRequest ::= [APPLICATION 16] MessageID + +ExtendedRequest ::= [APPLICATION 23] SEQUENCE { + requestName [0] LDAPOID, + requestValue [1] OCTET STRING OPTIONAL } + +ExtendedResponse ::= [APPLICATION 24] SEQUENCE { + COMPONENTS OF LDAPResult, + responseName [10] LDAPOID OPTIONAL, + responseValue [11] OCTET STRING OPTIONAL } + +IntermediateResponse ::= [APPLICATION 25] SEQUENCE { + responseName [0] LDAPOID OPTIONAL, + responseValue [1] OCTET STRING OPTIONAL } + +END + diff --git a/lib/eldap/doc/html/.gitignore b/lib/eldap/doc/html/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/eldap/doc/html/.gitignore diff --git a/lib/eldap/doc/man3/.gitignore b/lib/eldap/doc/man3/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/eldap/doc/man3/.gitignore diff --git a/lib/eldap/doc/pdf/.gitignore b/lib/eldap/doc/pdf/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/eldap/doc/pdf/.gitignore diff --git a/lib/eldap/doc/src/Makefile b/lib/eldap/doc/src/Makefile new file mode 100644 index 0000000000..a4827793f4 --- /dev/null +++ b/lib/eldap/doc/src/Makefile @@ -0,0 +1,115 @@ +# ``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$ +# +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../../vsn.mk +VSN=$(ELDAP_VSN) +APPLICATION=eldap + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- +XML_APPLICATION_FILES = ref_man.xml +XML_REF3_FILES = eldap.xml + +XML_PART_FILES = release_notes.xml usersguide.xml +XML_CHAPTER_FILES = notes.xml + +BOOK_FILES = book.xml + +XML_FILES = $(BOOK_FILES) $(XML_APPLICATION_FILES) $(XML_REF3_FILES) $(XML_REF6_FILES) \ + $(XML_PART_FILES) $(XML_CHAPTER_FILES) + +GIF_FILES = + +# ---------------------------------------------------- + +HTML_FILES = $(XML_APPLICATION_FILES:%.xml=$(HTMLDIR)/%.html) \ + $(XML_PART_FILES:%.xml=$(HTMLDIR)/%.html) + +INFO_FILE = ../../info + +MAN3_FILES = $(XML_REF3_FILES:%.xml=$(MAN3DIR)/%.3) +MAN6_FILES = $(XML_REF6_FILES:%_app.xml=$(MAN6DIR)/%.6) + +HTML_REF_MAN_FILE = $(HTMLDIR)/index.html + +TOP_PDF_FILE = $(PDFDIR)/$(APPLICATION)-$(VSN).pdf + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +XML_FLAGS += + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- +$(HTMLDIR)/%.gif: %.gif + $(INSTALL_DATA) $< $@ + + +docs: pdf html man + +$(TOP_PDF_FILE): $(XML_FILES) + +pdf: $(TOP_PDF_FILE) + +html: gifs $(HTML_REF_MAN_FILE) + +man: $(MAN3_FILES) $(MAN6_FILES) + +gifs: $(GIF_FILES:%=$(HTMLDIR)/%) + +debug opt valgrind: + +clean clean_docs clean_tex: + rm -rf $(HTMLDIR)/* + rm -f $(MAN3DIR)/* + rm -f $(MAN6DIR)/* + rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f errs core *~ + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_docs_spec: docs + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(HTMLDIR)/* \ + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" +# $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" +# $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" + + +release_spec: + diff --git a/lib/eldap/doc/src/book.xml b/lib/eldap/doc/src/book.xml new file mode 100644 index 0000000000..74f1c37cdc --- /dev/null +++ b/lib/eldap/doc/src/book.xml @@ -0,0 +1,47 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE book SYSTEM "book.dtd"> + +<book xmlns:xi="http://www.w3.org/2001/XInclude"> + <header titlestyle="normal"> + <copyright> + <year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>eldap</title> + <prepared>OTP team</prepared> + <docno></docno> + <date>1999-01-21</date> + <rev>A</rev> + <file>book.xml</file> + </header> + <pagetext>eldap</pagetext> + <preamble> + <contents level="2"></contents> + </preamble> + <parts lift="no"> + <xi:include href="usersguide.xml"/> + </parts> + <applications> + <xi:include href="ref_man.xml"/> + </applications> + <releasenotes> + <xi:include href="notes.xml"/> + </releasenotes> + <listofterms></listofterms> + <index></index> +</book> + diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml new file mode 100644 index 0000000000..04dad2eee7 --- /dev/null +++ b/lib/eldap/doc/src/eldap.xml @@ -0,0 +1,342 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>eldap</title> + <prepared>Peter Högfeldt</prepared> + <docno></docno> + <date>2000-06-20</date> + <rev>B</rev> + </header> + <module>eldap</module> + <modulesummary>Eldap Functions</modulesummary> + <description> + <p>This module provides a client api to the Lightweight Directory Access Protocol (LDAP). + </p> + <p>References:</p> + <list type="bulleted"> + <item> <p>RFC 4510 - RFC 4519</p> </item> + </list> + <p>The above publications can be found at <url href="http://www.ietf.org">IETF</url>. + </p> + <p><em>Types</em></p> + <pre> +handle() Connection handle +attribute() {Type = string(), Values=[string()]} +modify_op() See mod_add/2, mod_delete/2, mod_replace/2 +scope() See baseObject/0, singleLevel/0, wholeSubtree/0 +dereference() See neverDerefAliases/0, derefInSearching/0, derefFindingBaseObj/0, derefAlways/0 +filter() See present/1, substrings/2, + equalityMatch/2, greaterOrEqual/2, lessOrEqual/2, + approxMatch/2, + 'and'/1, 'or'/1, 'not'/1. + </pre> + <p></p> + </description> + <funcs> + <func> + <name>open([Host]) -> {ok, Handle} | {error, Reason}</name> + <fsummary>Open a connection to an LDAP server.</fsummary> + <type> + <v>Handle = handle()</v> + </type> + <desc> + <p>Setup a connection to an LDAP server, the <c>HOST</c>'s are tried in order. </p> + </desc> + </func> + <func> + <name>open([Host], [Option]) -> {ok, Handle} | {error, Reason}</name> + <fsummary>Open a connection to an LDAP server.</fsummary> + <type> + <v>Handle = handle()</v> + <v>Option = {port, integer()} | {log, function()} | {timeout, integer()} | {ssl, boolean()}</v> + </type> + <desc> + <p>Setup a connection to an LDAP server, the <c>HOST</c>'s are tried in order.</p> + <p>The log function takes three arguments, <c>fun(Level, FormatString, [FormatArg]) end</c>.</p> + <p>Timeout set the maximum time in milliseconds that each server request may take.</p> + </desc> + </func> + <func> + <name>close(Handle) -> ok</name> + <fsummary>Shutdown the connection.</fsummary> + <type> + <v>Handle = handle()</v> + </type> + <desc> + <p>Shutdown the connection.</p> + </desc> + </func> + <func> + <name>simple_bind(Handle, Dn, Password) -> ok | {error, Reason}</name> + <fsummary>Authenticate the connection.</fsummary> + <type> + <v>Handle = handle()</v> + <v>Dn = string()</v> + <v>Password = string()</v> + </type> + <desc> + <p>Authenticate the connection using simple authentication.</p> + </desc> + </func> + <func> + <name>add(Handle, Dn, [Attribute]) -> ok | {error, Reason}</name> + <fsummary>Add an entry.</fsummary> + <type> + <v>Handle = handle()</v> + <v>Dn = string()</v> + <v>Attribute = attribute()</v> + </type> + <desc> + <p> Add an entry. The entry must not exist.</p> + <pre> + add(Handle, + "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com", + [{"objectclass", ["person"]}, + {"cn", ["Bill Valentine"]}, + {"sn", ["Valentine"]}, + {"telephoneNumber", ["545 555 00"]}] + ) + </pre> + </desc> + </func> + <func> + <name>delete(Handle, Dn) -> ok | {error, Reason}</name> + <fsummary>Delete an entry.</fsummary> + <type> + <v>Dn = string()</v> + </type> + <desc> + <p> Delete an entry.</p> + <pre> + delete(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com") + </pre> + </desc> + </func> + + <func> + <name>mod_add(Type, [Value]) -> modify_op()</name> + <fsummary>Create a modification operation.</fsummary> + <type> + <v>Type = string()</v> + <v>Value = string()</v> + </type> + <desc> <p> Create an add modification operation.</p> </desc> + </func> + <func> + <name>mod_delete(Type, [Value]) -> modify_op()</name> + <fsummary>Create a modification operation.</fsummary> + <type> + <v>Type = string()</v> + <v>Value = string()</v> + </type> + <desc> <p> Create a delete modification operation.</p> </desc> + </func> + <func> + <name>mod_replace(Type, [Value]) -> modify_op()</name> + <fsummary>Create a modification operation.</fsummary> + <type> + <v>Type = string()</v> + <v>Value = string()</v> + </type> + <desc> <p> Create a replace modification operation.</p> </desc> + </func> + + <func> + <name>modify(Handle, Dn, [ModifyOp]) -> ok | {error, Reason}</name> + <fsummary>Modify an entry.</fsummary> + <type> + <v>Dn = string()</v> + <v>ModifyOp = modify_op()</v> + </type> + <desc> + <p> Modify an entry.</p> + <pre> + modify(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com", + [eldap:mod_replace("telephoneNumber", ["555 555 00"]), + eldap:mod_add("description", ["LDAP Hacker"]) ]) + </pre> + </desc> + </func> + <func> + <name>modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> ok | {error, Reason}</name> + <fsummary>Modify the DN of an entry.</fsummary> + <type> + <v>Dn = string()</v> + <v>NewRDN = string()</v> + <v>DeleteOldRDN = boolean()</v> + <v>NewSupDN = string()</v> + </type> + <desc> + <p> Modify the DN of an entry. <c>DeleteOldRDN</c> indicates + whether the current RDN should be removed after operation. + <c>NewSupDN</c> should be "" if the RDN should not be moved or the new parent which + the RDN will be moved to.</p> + <pre> + modify_dn(Handle, "cn=Bill Valentine, ou=people, o=Example Org, dc=example, dc=com ", + "cn=Bill Jr Valentine", true, "") + </pre> + </desc> + </func> + <func> + <name>search(Handle, SearchOptions) -> {ok, #eldap_search_result{}} | {error, Reason}</name> + <fsummary>Search the Directory</fsummary> + <type> + <v>SearchOptions = #eldap_search{} | [SearchOption]</v> + <v>SearchOption = {base, string()} | {filter, filter()} | {scope, scope()} + | {attributes, [string()]} | {deref, dereference()} | + | {types_only, boolean()} | {timeout, integer()} + </v> + </type> + <desc> + <p>Search the directory with the supplied the SearchOptions. + The base and filter options must be supplied. + Default values: scope is <c>wholeSubtree()</c>, deref is <c>derefAlways()</c>, + types_only is <c>false</c> and timeout is <c>0</c> (meaning infinity). + </p> + <pre> + Filter = eldap:substrings("cn", [{any,"V"}]), + search(Handle, [{base, "dc=example, dc=com"}, {filter, Filter}, {attributes, ["cn"]}]), + </pre> + </desc> + </func> + + <func> + <name>baseObject() -> scope()</name> + <fsummary>Create search scope.</fsummary> + <desc> <p> Search baseobject only.</p> </desc> + </func> + <func> + <name>singleLevel() -> scope()</name> + <fsummary>Create search scope.</fsummary> + <desc> <p> Search the specified level only, i.e. do not recurse.</p> </desc> + </func> + <func> + <name>wholeSubtree() -> scope()</name> + <fsummary>Create search scope.</fsummary> + <desc> <p> Search the entire subtree.</p> </desc> + </func> + + <func> + <name>neverDerefAliases() -> dereference()</name> + <fsummary>Create search option.</fsummary> + <desc> <p>Never derefrence aliases, treat aliases as entries.</p> </desc> + </func> + <func> + <name>derefAlways() -> dereference()</name> + <fsummary>Create search option.</fsummary> + <desc> <p>Always derefrence aliases.</p> </desc> + </func> + <func> + <name>derefInSearching() -> dereference()</name> + <fsummary>Create search option.</fsummary> + <desc> <p>Derefrence aliases only when searching.</p> </desc> + </func> + <func> + <name>derefFindingBaseObj() -> dereference()</name> + <fsummary>Create search option.</fsummary> + <desc> <p>Derefrence aliases only in finding the base.</p> </desc> + </func> + + <func> + <name>present(Type) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>Type = string()</v> + </type> + <desc> <p>Create a filter which filters on attribute type presence.</p> </desc> + </func> + <func> + <name>substrings(Type, [SubString]) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>Type = string()</v> + <v>SubString = {StringPart, string()}</v> + <v>StringPart = initial | any | final</v> + </type> + <desc> <p>Create a filter which filters on substrings.</p> </desc> + </func> + <func> + <name>equalityMatch(Type, Value) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>Type = string()</v> + <v>Value = string()</v> + </type> + <desc> <p>Create a equality filter.</p> </desc> + </func> + <func> + <name>greaterOrEqual(Type, Value) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>Type = string()</v> + <v>Value = string()</v> + </type> + <desc> <p>Create a greater or equal filter.</p> </desc> + </func> + <func> + <name>lessOrEqual(Type, Value) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>Type = string()</v> + <v>Value = string()</v> + </type> + <desc> <p>Create a less or equal filter.</p> </desc> + </func> + <func> + <name>approxMatch(Type, Value) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>Type = string()</v> + <v>Value = string()</v> + </type> + <desc> <p>Create a approximation match filter.</p> </desc> + </func> + <func> + <name>'and'([Filter]) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>Filter = filter()</v> + </type> + <desc> <p>Creates a filter where all <c>Filter</c> must be true.</p> </desc> + </func> + <func> + <name>'or'([Filter]) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>Filter = filter()</v> + </type> + <desc> <p>Create a filter where at least one of the <c>Filter</c> must be true.</p> </desc> + </func> + <func> + <name>'not'(Filter) -> filter()</name> + <fsummary>Create search filter option.</fsummary> + <type> + <v>Filter = filter()</v> + </type> + <desc> <p>Negate a filter.</p> </desc> + </func> + + </funcs> + +</erlref> + diff --git a/lib/eldap/doc/src/fascicules.xml b/lib/eldap/doc/src/fascicules.xml new file mode 100644 index 0000000000..8fc250bc75 --- /dev/null +++ b/lib/eldap/doc/src/fascicules.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE fascicules SYSTEM "fascicules.dtd"> + +<fascicules> + <fascicule file="usersguide" href="usersguide_frame.html" entry="no"> + User's Guide + </fascicule> + <fascicule file="ref_man" href="ref_man_frame.html" entry="yes"> + Reference Manual + </fascicule> + <fascicule file="release_notes" href="release_notes_frame.html" entry="no"> + Release Notes + </fascicule> + <fascicule file="" href="../../../../doc/print.html" entry="no"> + Off-Print + </fascicule> +</fascicules> + diff --git a/lib/eldap/doc/src/note.gif b/lib/eldap/doc/src/note.gif Binary files differnew file mode 100644 index 0000000000..6fffe30419 --- /dev/null +++ b/lib/eldap/doc/src/note.gif diff --git a/lib/eldap/doc/src/notes.xml b/lib/eldap/doc/src/notes.xml new file mode 100644 index 0000000000..d9b11875de --- /dev/null +++ b/lib/eldap/doc/src/notes.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE chapter SYSTEM "chapter.dtd"> + +<chapter> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Eldap Release Notes</title> + <prepared>OTP team</prepared> + <docno></docno> + <date>2003-06-06</date> + <rev>B</rev> + <file>notes.xml</file> + </header> + <p>This document describes the changes made to the Eldap application.</p> + + <section> + <title>Eldap 1.0</title> + <p>New application. </p> + </section> +</chapter> + diff --git a/lib/eldap/doc/src/ref_man.xml b/lib/eldap/doc/src/ref_man.xml new file mode 100644 index 0000000000..70c4cee16d --- /dev/null +++ b/lib/eldap/doc/src/ref_man.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE application SYSTEM "application.dtd"> + +<application xmlns:xi="http://www.w3.org/2001/XInclude"> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Eldap Reference Manual</title> + <prepared>OTP team</prepared> + <docno></docno> + <date>2003-06-01</date> + <rev>B</rev> + <file>refman.sgml</file> + </header> + <description> + <p>The <em>Eldap</em> application provides an api for accessing an LDAP server.</p> + <p>The original code was developed by Torbjörn Törnkvist.</p> + </description> + <xi:include href="eldap.xml"/> +</application> + diff --git a/lib/eldap/doc/src/release_notes.xml b/lib/eldap/doc/src/release_notes.xml new file mode 100644 index 0000000000..778a49b894 --- /dev/null +++ b/lib/eldap/doc/src/release_notes.xml @@ -0,0 +1,37 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<part> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB, All Rights Reserved</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + The Initial Developer of the Original Code is Ericsson AB. + </legalnotice> + + <title>Eldap Release Notes</title> + <prepared>OTP team</prepared> + <docno></docno> + <date>2003-06-01</date> + <rev>B</rev> + <file>release_notes.xml</file> + </header> + <description> + <p></p> + </description> + <include file="notes"></include> +</part> + diff --git a/lib/eldap/doc/src/usersguide.xml b/lib/eldap/doc/src/usersguide.xml new file mode 100644 index 0000000000..828588e88e --- /dev/null +++ b/lib/eldap/doc/src/usersguide.xml @@ -0,0 +1,38 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE part SYSTEM "part.dtd"> + +<part xmlns:xi="http://www.w3.org/2001/XInclude"> + <header> + <copyright> + <year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>Eldap User's Guide</title> + <prepared>OTP Team</prepared> + <docno></docno> + <date>2003-06-01</date> + <rev>C</rev> + <file>usersguide.xml</file> + </header> + <description> + <p>The <em>Eldap</em> application provides an api for accessing an LDAP server.</p> + <p>The original code was developed by Torbjörn Törnkvist.</p> + </description> + + +</part> + diff --git a/lib/eldap/doc/src/warning.gif b/lib/eldap/doc/src/warning.gif Binary files differnew file mode 100644 index 0000000000..96af52360e --- /dev/null +++ b/lib/eldap/doc/src/warning.gif diff --git a/lib/eldap/ebin/.gitignore b/lib/eldap/ebin/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/eldap/ebin/.gitignore diff --git a/lib/eldap/include/eldap.hrl b/lib/eldap/include/eldap.hrl new file mode 100644 index 0000000000..7c12cd4f2b --- /dev/null +++ b/lib/eldap/include/eldap.hrl @@ -0,0 +1,33 @@ +-ifndef( _ELDAP_HRL ). +-define( _ELDAP_HRL , 1 ). + +%%% +%%% Search input parameters +%%% +-record(eldap_search, { + base = [], % Baseobject + filter = [], % Search conditions + scope=wholeSubtree, % Search scope + deref=derefAlways, % Dereference + attributes = [], % Attributes to be returned + types_only = false, % Return types+values or types + timeout = 0 % Timelimit for search + }). + +%%% +%%% Returned search result +%%% +-record(eldap_search_result, { + entries = [], % List of #eldap_entry{} records + referrals = [] % List of referrals + }). + +%%% +%%% LDAP entry +%%% +-record(eldap_entry, { + object_name = "", % The DN for the entry + attributes = [] % List of {Attribute, Value} pairs + }). + +-endif. diff --git a/lib/eldap/info b/lib/eldap/info new file mode 100644 index 0000000000..463f9af6ea --- /dev/null +++ b/lib/eldap/info @@ -0,0 +1,2 @@ +group: comm +short: eldap - Erlang LDAP library diff --git a/lib/eldap/src/Makefile b/lib/eldap/src/Makefile new file mode 100644 index 0000000000..39a41d08e2 --- /dev/null +++ b/lib/eldap/src/Makefile @@ -0,0 +1,110 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2012. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# +# +include $(ERL_TOP)/make/target.mk + +include $(ERL_TOP)/make/$(TARGET)/otp.mk + +# ---------------------------------------------------- +# Application version +# ---------------------------------------------------- +include ../vsn.mk + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/lib/eldap-$(ELDAP_VSN) + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= eldap + +ASN1_FILES = ELDAPv3.asn1 +ASN1_HRL = $(EBIN)/$(ASN1_FILES:%.asn1=%.hrl) + +ERL_FILES= $(MODULES:%=%.erl) + +TARGET_FILES = $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(ASN1_FILES:%.asn1=$(EBIN)/%.$(EMULATOR)) + +EXTERNAL_HRL_FILES = ../include/eldap.hrl + +HRL_FILES = $(EXTERNAL_HRL_FILES) $(ASN1_HRL) + +APPUP_FILE = eldap.appup +APPUP_SRC = $(APPUP_FILE).src +APPUP_TARGET = $(EBIN)/$(APPUP_FILE) + +APP_FILE = eldap.app +APP_SRC = $(APP_FILE).src +APP_TARGET = $(EBIN)/$(APP_FILE) + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_COMPILE_FLAGS += -I../include -I../ebin + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- +opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) + +debug: + @${MAKE} TYPE=debug opt + +clean: + rm -f $(TARGET_FILES) $(GEN_FILES) $(APP_TARGET) $(APPUP_TARGET) + rm -f $(ASN1_FILES:%.asn1=$(EBIN)/%.*) + rm -f errs core *~ + +$(APP_TARGET): $(APP_SRC) ../vsn.mk + sed -e 's;%VSN%;$(ELDAP_VSN);' $< > $@ + +$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk + sed -e 's;%VSN%;$(ELDAP_VSN);' $< > $@ + +docs: + +$(TARGET_FILES): $(HRL_FILES) + +# ---------------------------------------------------- +# Special Build Targets +# ---------------------------------------------------- +$(ASN1_HRL): ../asn1/$(ASN1_FILES) + $(ERLC) -o $(EBIN) -bber_bin +optimize +nif $(ERL_COMPILE_FLAGS) ../asn1/ELDAPv3.asn1 + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/asn1" + $(INSTALL_DATA) ../asn1/$(ASN1_FILES) "$(RELSYSDIR)/asn1" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) "$(RELSYSDIR)/include" + +release_docs_spec: + + diff --git a/lib/eldap/src/eldap.app.src b/lib/eldap/src/eldap.app.src new file mode 100644 index 0000000000..8215328910 --- /dev/null +++ b/lib/eldap/src/eldap.app.src @@ -0,0 +1,8 @@ +{application, eldap, + [{description, "Ldap api"}, + {vsn, "%VSN%"}, + {modules, [eldap, 'ELDAPv3']}, + {registered, []}, + {applications, [kernel, stdlib]}, + {env, []} +]}. diff --git a/lib/eldap/src/eldap.appup.src b/lib/eldap/src/eldap.appup.src new file mode 100644 index 0000000000..8d33482f11 --- /dev/null +++ b/lib/eldap/src/eldap.appup.src @@ -0,0 +1,6 @@ +%% -*- erlang -*- +{"%VSN%", + [ + ], + [ + ]}. diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl new file mode 100644 index 0000000000..699dfc8791 --- /dev/null +++ b/lib/eldap/src/eldap.erl @@ -0,0 +1,1114 @@ +-module(eldap). +%%% -------------------------------------------------------------------- +%%% Created: 12 Oct 2000 by Tobbe <[email protected]> +%%% Function: Erlang client LDAP implementation according RFC 2251,2253 +%%% and 2255. The interface is based on RFC 1823, and +%%% draft-ietf-asid-ldap-c-api-00.txt +%%% +%%% Copyright (c) 2010 Torbjorn Tornkvist +%%% See MIT-LICENSE at the top dir for licensing information. +%%% -------------------------------------------------------------------- +-vc('$Id$ '). +-export([open/1,open/2,simple_bind/3,controlling_process/2, + baseObject/0,singleLevel/0,wholeSubtree/0,close/1, + equalityMatch/2,greaterOrEqual/2,lessOrEqual/2, + approxMatch/2,search/2,substrings/2,present/1, + 'and'/1,'or'/1,'not'/1,modify/3, mod_add/2, mod_delete/2, + mod_replace/2, add/3, delete/2, modify_dn/5,parse_dn/1, + parse_ldap_url/1]). + +-export([neverDerefAliases/0, derefInSearching/0, + derefFindingBaseObj/0, derefAlways/0]). + +%% for upgrades +-export([loop/2]). + +-import(lists,[concat/1]). + +-include("ELDAPv3.hrl"). +-include("eldap.hrl"). + +-define(LDAP_VERSION, 3). +-define(LDAP_PORT, 389). +-define(LDAPS_PORT, 636). + +-record(eldap, {version = ?LDAP_VERSION, + host, % Host running LDAP server + port = ?LDAP_PORT, % The LDAP server port + fd, % Socket filedescriptor. + binddn = "", % Name of the entry to bind as + passwd, % Password for (above) entry + id = 0, % LDAP Request ID + log, % User provided log function + timeout = infinity, % Request timeout + anon_auth = false, % Allow anonymous authentication + use_tls = false % LDAP/LDAPS + }). + +%%% For debug purposes +%%-define(PRINT(S, A), io:fwrite("~w(~w): " ++ S, [?MODULE,?LINE|A])). +-define(PRINT(S, A), true). + +-define(elog(S, A), error_logger:info_msg("~w(~w): "++S,[?MODULE,?LINE|A])). + +%%% ==================================================================== +%%% Exported interface +%%% ==================================================================== + +%%% -------------------------------------------------------------------- +%%% open(Hosts [,Opts] ) +%%% -------------------- +%%% Setup a connection to on of the Hosts in the argument +%%% list. Stop at the first successful connection attempt. +%%% Valid Opts are: Where: +%%% +%%% {port, Port} - Port is the port number +%%% {log, F} - F(LogLevel, FormatString, ListOfArgs) +%%% {timeout, milliSec} - Server request timeout +%%% +%%% -------------------------------------------------------------------- +open(Hosts) -> + open(Hosts, []). + +open(Hosts, Opts) when is_list(Hosts), is_list(Opts) -> + Self = self(), + Pid = spawn_link(fun() -> init(Hosts, Opts, Self) end), + recv(Pid). + +%%% -------------------------------------------------------------------- +%%% Shutdown connection (and process) asynchronous. +%%% -------------------------------------------------------------------- + +close(Handle) when is_pid(Handle) -> + send(Handle, close). + +%%% -------------------------------------------------------------------- +%%% Set who we should link ourselves to +%%% -------------------------------------------------------------------- + +controlling_process(Handle, Pid) when is_pid(Handle), is_pid(Pid) -> + link(Pid), + send(Handle, {cnt_proc, Pid}), + recv(Handle). + +%%% -------------------------------------------------------------------- +%%% Authenticate ourselves to the Directory +%%% using simple authentication. +%%% +%%% Dn - The name of the entry to bind as +%%% Passwd - The password to be used +%%% +%%% Returns: ok | {error, Error} +%%% -------------------------------------------------------------------- +simple_bind(Handle, Dn, Passwd) when is_pid(Handle) -> + send(Handle, {simple_bind, Dn, Passwd}), + recv(Handle). + +%%% -------------------------------------------------------------------- +%%% Add an entry. The entry field MUST NOT exist for the AddRequest +%%% to succeed. The parent of the entry MUST exist. +%%% Example: +%%% +%%% add(Handle, +%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com", +%%% [{"objectclass", ["person"]}, +%%% {"cn", ["Bill Valentine"]}, +%%% {"sn", ["Valentine"]}, +%%% {"telephoneNumber", ["545 555 00"]}] +%%% ) +%%% -------------------------------------------------------------------- +add(Handle, Entry, Attributes) when is_pid(Handle),is_list(Entry),is_list(Attributes) -> + send(Handle, {add, Entry, add_attrs(Attributes)}), + recv(Handle). + +%%% Do sanity check ! +add_attrs(Attrs) -> + F = fun({Type,Vals}) when is_list(Type),is_list(Vals) -> + %% Confused ? Me too... :-/ + {'AddRequest_attributes',Type, Vals} + end, + case catch lists:map(F, Attrs) of + {'EXIT', _} -> throw({error, attribute_values}); + Else -> Else + end. + +%%% -------------------------------------------------------------------- +%%% Delete an entry. The entry consists of the DN of +%%% the entry to be deleted. +%%% Example: +%%% +%%% delete(Handle, +%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com" +%%% ) +%%% -------------------------------------------------------------------- +delete(Handle, Entry) when is_pid(Handle), is_list(Entry) -> + send(Handle, {delete, Entry}), + recv(Handle). + +%%% -------------------------------------------------------------------- +%%% Modify an entry. Given an entry a number of modification +%%% operations can be performed as one atomic operation. +%%% Example: +%%% +%%% modify(Handle, +%%% "cn=Torbjorn Tornkvist, ou=people, o=Bluetail AB, dc=bluetail, dc=com", +%%% [mod_replace("telephoneNumber", ["555 555 00"]), +%%% mod_add("description", ["LDAP hacker"])] +%%% ) +%%% -------------------------------------------------------------------- +modify(Handle, Object, Mods) when is_pid(Handle), is_list(Object), is_list(Mods) -> + send(Handle, {modify, Object, Mods}), + recv(Handle). + +%%% +%%% Modification operations. +%%% Example: +%%% mod_replace("telephoneNumber", ["555 555 00"]) +%%% +mod_add(Type, Values) when is_list(Type), is_list(Values) -> m(add, Type, Values). +mod_delete(Type, Values) when is_list(Type), is_list(Values) -> m(delete, Type, Values). +mod_replace(Type, Values) when is_list(Type), is_list(Values) -> m(replace, Type, Values). + +m(Operation, Type, Values) -> + #'ModifyRequest_changes_SEQOF'{ + operation = Operation, + modification = #'PartialAttribute'{ + type = Type, + vals = Values}}. + +%%% -------------------------------------------------------------------- +%%% Modify an entry. Given an entry a number of modification +%%% operations can be performed as one atomic operation. +%%% Example: +%%% +%%% modify_dn(Handle, +%%% "cn=Bill Valentine, ou=people, o=Bluetail AB, dc=bluetail, dc=com", +%%% "cn=Ben Emerson", +%%% true, +%%% "" +%%% ) +%%% -------------------------------------------------------------------- +modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup) + when is_pid(Handle),is_list(Entry),is_list(NewRDN),is_atom(DelOldRDN),is_list(NewSup) -> + send(Handle, {modify_dn, Entry, NewRDN, + bool_p(DelOldRDN), optional(NewSup)}), + recv(Handle). + +%%% Sanity checks ! + +bool_p(Bool) when Bool==true;Bool==false -> Bool. + +optional([]) -> asn1_NOVALUE; +optional(Value) -> Value. + +%%% -------------------------------------------------------------------- +%%% Synchronous search of the Directory returning a +%%% requested set of attributes. +%%% +%%% Example: +%%% +%%% Filter = eldap:substrings("cn", [{any,"o"}]), +%%% eldap:search(S, [{base, "dc=bluetail, dc=com"}, +%%% {filter, Filter}, +%%% {attributes,["cn"]}])), +%%% +%%% Returned result: {ok, #eldap_search_result{}} +%%% +%%% Example: +%%% +%%% {ok,{eldap_search_result, +%%% [{eldap_entry, +%%% "cn=Magnus Froberg, dc=bluetail, dc=com", +%%% [{"cn",["Magnus Froberg"]}]}, +%%% {eldap_entry, +%%% "cn=Torbjorn Tornkvist, dc=bluetail, dc=com", +%%% [{"cn",["Torbjorn Tornkvist"]}]}], +%%% []}} +%%% +%%% -------------------------------------------------------------------- +search(Handle, A) when is_pid(Handle), is_record(A, eldap_search) -> + call_search(Handle, A); +search(Handle, L) when is_pid(Handle), is_list(L) -> + case catch parse_search_args(L) of + {error, Emsg} -> {error, Emsg}; + A when is_record(A, eldap_search) -> call_search(Handle, A) + end. + +call_search(Handle, A) -> + send(Handle, {search, A}), + recv(Handle). + +parse_search_args(Args) -> + parse_search_args(Args, + #eldap_search{scope = wholeSubtree, + deref = derefAlways}). + +parse_search_args([{base, Base}|T],A) -> + parse_search_args(T,A#eldap_search{base = Base}); +parse_search_args([{filter, Filter}|T],A) -> + parse_search_args(T,A#eldap_search{filter = Filter}); +parse_search_args([{scope, Scope}|T],A) -> + parse_search_args(T,A#eldap_search{scope = Scope}); +parse_search_args([{deref, Deref}|T],A) -> + parse_search_args(T,A#eldap_search{deref = Deref}); +parse_search_args([{attributes, Attrs}|T],A) -> + parse_search_args(T,A#eldap_search{attributes = Attrs}); +parse_search_args([{types_only, TypesOnly}|T],A) -> + parse_search_args(T,A#eldap_search{types_only = TypesOnly}); +parse_search_args([{timeout, Timeout}|T],A) when is_integer(Timeout) -> + parse_search_args(T,A#eldap_search{timeout = Timeout}); +parse_search_args([H|_],_) -> + throw({error,{unknown_arg, H}}); +parse_search_args([],A) -> + A. + +%%% +%%% The Scope parameter +%%% +baseObject() -> baseObject. +singleLevel() -> singleLevel. +wholeSubtree() -> wholeSubtree. + +%% +%% The derefAliases parameter +%% +neverDerefAliases() -> neverDerefAliases. +derefInSearching() -> derefInSearching. +derefFindingBaseObj() -> derefFindingBaseObj. +derefAlways() -> derefAlways. + +%%% +%%% Boolean filter operations +%%% +'and'(ListOfFilters) when is_list(ListOfFilters) -> {'and',ListOfFilters}. +'or'(ListOfFilters) when is_list(ListOfFilters) -> {'or', ListOfFilters}. +'not'(Filter) when is_tuple(Filter) -> {'not',Filter}. + +%%% +%%% The following Filter parameters consist of an attribute +%%% and an attribute value. Example: F("uid","tobbe") +%%% +equalityMatch(Desc, Value) -> {equalityMatch, av_assert(Desc, Value)}. +greaterOrEqual(Desc, Value) -> {greaterOrEqual, av_assert(Desc, Value)}. +lessOrEqual(Desc, Value) -> {lessOrEqual, av_assert(Desc, Value)}. +approxMatch(Desc, Value) -> {approxMatch, av_assert(Desc, Value)}. + +av_assert(Desc, Value) -> + #'AttributeValueAssertion'{attributeDesc = Desc, + assertionValue = Value}. + +%%% +%%% Filter to check for the presence of an attribute +%%% +present(Attribute) when is_list(Attribute) -> + {present, Attribute}. + + +%%% +%%% A substring filter seem to be based on a pattern: +%%% +%%% InitValue*AnyValue*FinalValue +%%% +%%% where all three parts seem to be optional (at least when +%%% talking with an OpenLDAP server). Thus, the arguments +%%% to substrings/2 looks like this: +%%% +%%% Type ::= string( <attribute> ) +%%% SubStr ::= listof( {initial,Value} | {any,Value}, {final,Value}) +%%% +%%% Example: substrings("sn",[{initial,"To"},{any,"kv"},{final,"st"}]) +%%% will match entries containing: 'sn: Tornkvist' +%%% +substrings(Type, SubStr) when is_list(Type), is_list(SubStr) -> + Ss = {'SubstringFilter_substrings',v_substr(SubStr)}, + {substrings,#'SubstringFilter'{type = Type, + substrings = Ss}}. + +%%% -------------------------------------------------------------------- +%%% Worker process. We keep track of a controlling process to +%%% be able to terminate together with it. +%%% -------------------------------------------------------------------- + +init(Hosts, Opts, Cpid) -> + Data = parse_args(Opts, Cpid, #eldap{}), + case try_connect(Hosts, Data) of + {ok,Data2} -> + send(Cpid, {ok,self()}), + ?MODULE:loop(Cpid, Data2); + Else -> + send(Cpid, Else), + unlink(Cpid), + exit(Else) + end. + +parse_args([{port, Port}|T], Cpid, Data) when is_integer(Port) -> + parse_args(T, Cpid, Data#eldap{port = Port}); +parse_args([{timeout, Timeout}|T], Cpid, Data) when is_integer(Timeout),Timeout>0 -> + parse_args(T, Cpid, Data#eldap{timeout = Timeout}); +parse_args([{anon_auth, true}|T], Cpid, Data) -> + parse_args(T, Cpid, Data#eldap{anon_auth = false}); +parse_args([{anon_auth, _}|T], Cpid, Data) -> + parse_args(T, Cpid, Data); +parse_args([{ssl, true}|T], Cpid, Data) -> + parse_args(T, Cpid, Data#eldap{use_tls = true}); +parse_args([{ssl, _}|T], Cpid, Data) -> + parse_args(T, Cpid, Data); +parse_args([{log, F}|T], Cpid, Data) when is_function(F) -> + parse_args(T, Cpid, Data#eldap{log = F}); +parse_args([{log, _}|T], Cpid, Data) -> + parse_args(T, Cpid, Data); +parse_args([H|_], Cpid, _) -> + send(Cpid, {error,{wrong_option,H}}), + exit(wrong_option); +parse_args([], _, Data) -> + Data. + +%%% Try to connect to the hosts in the listed order, +%%% and stop with the first one to which a successful +%%% connection is made. + +try_connect([Host|Hosts], Data) -> + TcpOpts = [{packet, asn1}, {active,false}], + try do_connect(Host, Data, TcpOpts) of + {ok,Fd} -> {ok,Data#eldap{host = Host, fd = Fd}}; + Err -> + log2(Data, "Connect: ~p failed ~p~n",[Host, Err]), + try_connect(Hosts, Data) + catch _:Err -> + log2(Data, "Connect: ~p failed ~p~n",[Host, Err]), + try_connect(Hosts, Data) + end; +try_connect([],_) -> + {error,"connect failed"}. + +do_connect(Host, Data, Opts) when Data#eldap.use_tls == false -> + gen_tcp:connect(Host, Data#eldap.port, Opts, Data#eldap.timeout); +do_connect(Host, Data, Opts) when Data#eldap.use_tls == true -> + ssl:connect(Host, Data#eldap.port, [{verify,0}|Opts]). + + +loop(Cpid, Data) -> + receive + + {From, {search, A}} -> + {Res,NewData} = do_search(Data, A), + send(From,Res), + ?MODULE:loop(Cpid, NewData); + + {From, {modify, Obj, Mod}} -> + {Res,NewData} = do_modify(Data, Obj, Mod), + send(From,Res), + ?MODULE:loop(Cpid, NewData); + + {From, {modify_dn, Obj, NewRDN, DelOldRDN, NewSup}} -> + {Res,NewData} = do_modify_dn(Data, Obj, NewRDN, DelOldRDN, NewSup), + send(From,Res), + ?MODULE:loop(Cpid, NewData); + + {From, {add, Entry, Attrs}} -> + {Res,NewData} = do_add(Data, Entry, Attrs), + send(From,Res), + ?MODULE:loop(Cpid, NewData); + + {From, {delete, Entry}} -> + {Res,NewData} = do_delete(Data, Entry), + send(From,Res), + ?MODULE:loop(Cpid, NewData); + + {From, {simple_bind, Dn, Passwd}} -> + {Res,NewData} = do_simple_bind(Data, Dn, Passwd), + send(From,Res), + ?MODULE:loop(Cpid, NewData); + + {From, {cnt_proc, NewCpid}} -> + unlink(Cpid), + send(From,ok), + ?PRINT("New Cpid is: ~p~n",[NewCpid]), + ?MODULE:loop(NewCpid, Data); + + {_From, close} -> + unlink(Cpid), + exit(closed); + + {Cpid, 'EXIT', Reason} -> + ?PRINT("Got EXIT from Cpid, reason=~p~n",[Reason]), + exit(Reason); + + _XX -> + ?PRINT("loop got: ~p~n",[_XX]), + ?MODULE:loop(Cpid, Data) + + end. + +%%% -------------------------------------------------------------------- +%%% bindRequest +%%% -------------------------------------------------------------------- + +%%% Authenticate ourselves to the directory using +%%% simple authentication. + +do_simple_bind(Data, anon, anon) -> %% For testing + do_the_simple_bind(Data, "", ""); +do_simple_bind(Data, Dn, _Passwd) when Dn=="",Data#eldap.anon_auth==false -> + {{error,anonymous_auth},Data}; +do_simple_bind(Data, _Dn, Passwd) when Passwd=="",Data#eldap.anon_auth==false -> + {{error,anonymous_auth},Data}; +do_simple_bind(Data, Dn, Passwd) -> + do_the_simple_bind(Data, Dn, Passwd). + +do_the_simple_bind(Data, Dn, Passwd) -> + case catch exec_simple_bind(Data#eldap{binddn = Dn, + passwd = Passwd, + id = bump_id(Data)}) of + {ok,NewData} -> {ok,NewData}; + {error,Emsg} -> {{error,Emsg},Data}; + Else -> {{error,Else},Data} + end. + +exec_simple_bind(Data) -> + Req = #'BindRequest'{version = Data#eldap.version, + name = Data#eldap.binddn, + authentication = {simple, Data#eldap.passwd}}, + log2(Data, "bind request = ~p~n", [Req]), + Reply = request(Data#eldap.fd, Data, Data#eldap.id, {bindRequest, Req}), + log2(Data, "bind reply = ~p~n", [Reply]), + exec_simple_bind_reply(Data, Reply). + +exec_simple_bind_reply(Data, {ok,Msg}) when + Msg#'LDAPMessage'.messageID == Data#eldap.id -> + case Msg#'LDAPMessage'.protocolOp of + {bindResponse, Result} -> + case Result#'BindResponse'.resultCode of + success -> {ok,Data}; + Error -> {error, Error} + end; + Other -> {error, Other} + end; +exec_simple_bind_reply(_, Error) -> + {error, Error}. + + +%%% -------------------------------------------------------------------- +%%% searchRequest +%%% -------------------------------------------------------------------- + +do_search(Data, A) -> + case catch do_search_0(Data, A) of + {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; + {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; + {ok,Res,Ref,NewData} -> {{ok,polish(Res, Ref)},NewData}; + {{error,Reason},NewData} -> {{error,Reason},NewData}; + Else -> {ldap_closed_p(Data, Else),Data} + end. + +%%% +%%% Polish the returned search result +%%% + +polish(Res, Ref) -> + R = polish_result(Res), + %%% No special treatment of referrals at the moment. + #eldap_search_result{entries = R, + referrals = Ref}. + +polish_result([H|T]) when is_record(H, 'SearchResultEntry') -> + ObjectName = H#'SearchResultEntry'.objectName, + F = fun({_,A,V}) -> {A,V} end, + Attrs = lists:map(F, H#'SearchResultEntry'.attributes), + [#eldap_entry{object_name = ObjectName, + attributes = Attrs}| + polish_result(T)]; +polish_result([]) -> + []. + +do_search_0(Data, A) -> + Req = #'SearchRequest'{baseObject = A#eldap_search.base, + scope = v_scope(A#eldap_search.scope), + derefAliases = v_deref(A#eldap_search.deref), + sizeLimit = 0, % no size limit + timeLimit = v_timeout(A#eldap_search.timeout), + typesOnly = v_bool(A#eldap_search.types_only), + filter = v_filter(A#eldap_search.filter), + attributes = v_attributes(A#eldap_search.attributes) + }, + Id = bump_id(Data), + collect_search_responses(Data#eldap{id=Id}, Req, Id). + +%%% The returned answers cames in one packet per entry +%%% mixed with possible referals + +collect_search_responses(Data, Req, ID) -> + S = Data#eldap.fd, + log2(Data, "search request = ~p~n", [Req]), + send_request(S, Data, ID, {searchRequest, Req}), + Resp = recv_response(S, Data), + log2(Data, "search reply = ~p~n", [Resp]), + collect_search_responses(Data, S, ID, Resp, [], []). + +collect_search_responses(Data, S, ID, {ok,Msg}, Acc, Ref) + when is_record(Msg,'LDAPMessage') -> + case Msg#'LDAPMessage'.protocolOp of + {'searchResDone',R} -> + case R#'LDAPResult'.resultCode of + success -> + log2(Data, "search reply = searchResDone ~n", []), + {ok,Acc,Ref,Data}; + Reason -> + {{error,Reason},Data} + end; + {'searchResEntry',R} when is_record(R,'SearchResultEntry') -> + Resp = recv_response(S, Data), + log2(Data, "search reply = ~p~n", [Resp]), + collect_search_responses(Data, S, ID, Resp, [R|Acc], Ref); + {'searchResRef',R} -> + %% At the moment we don't do anyting sensible here since + %% I haven't been able to trigger the server to generate + %% a response like this. + Resp = recv_response(S, Data), + log2(Data, "search reply = ~p~n", [Resp]), + collect_search_responses(Data, S, ID, Resp, Acc, [R|Ref]); + Else -> + throw({error,Else}) + end; +collect_search_responses(_, _, _, Else, _, _) -> + throw({error,Else}). + +%%% -------------------------------------------------------------------- +%%% addRequest +%%% -------------------------------------------------------------------- + +do_add(Data, Entry, Attrs) -> + case catch do_add_0(Data, Entry, Attrs) of + {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; + {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; + {ok,NewData} -> {ok,NewData}; + Else -> {ldap_closed_p(Data, Else),Data} + end. + +do_add_0(Data, Entry, Attrs) -> + Req = #'AddRequest'{entry = Entry, + attributes = Attrs}, + S = Data#eldap.fd, + Id = bump_id(Data), + log2(Data, "add request = ~p~n", [Req]), + Resp = request(S, Data, Id, {addRequest, Req}), + log2(Data, "add reply = ~p~n", [Resp]), + check_reply(Data#eldap{id = Id}, Resp, addResponse). + + +%%% -------------------------------------------------------------------- +%%% deleteRequest +%%% -------------------------------------------------------------------- + +do_delete(Data, Entry) -> + case catch do_delete_0(Data, Entry) of + {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; + {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; + {ok,NewData} -> {ok,NewData}; + Else -> {ldap_closed_p(Data, Else),Data} + end. + +do_delete_0(Data, Entry) -> + S = Data#eldap.fd, + Id = bump_id(Data), + log2(Data, "del request = ~p~n", [Entry]), + Resp = request(S, Data, Id, {delRequest, Entry}), + log2(Data, "del reply = ~p~n", [Resp]), + check_reply(Data#eldap{id = Id}, Resp, delResponse). + + +%%% -------------------------------------------------------------------- +%%% modifyRequest +%%% -------------------------------------------------------------------- + +do_modify(Data, Obj, Mod) -> + case catch do_modify_0(Data, Obj, Mod) of + {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; + {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; + {ok,NewData} -> {ok,NewData}; + Else -> {ldap_closed_p(Data, Else),Data} + end. + +do_modify_0(Data, Obj, Mod) -> + v_modifications(Mod), + Req = #'ModifyRequest'{object = Obj, + changes = Mod}, + S = Data#eldap.fd, + Id = bump_id(Data), + log2(Data, "modify request = ~p~n", [Req]), + Resp = request(S, Data, Id, {modifyRequest, Req}), + log2(Data, "modify reply = ~p~n", [Resp]), + check_reply(Data#eldap{id = Id}, Resp, modifyResponse). + +%%% -------------------------------------------------------------------- +%%% modifyDNRequest +%%% -------------------------------------------------------------------- + +do_modify_dn(Data, Entry, NewRDN, DelOldRDN, NewSup) -> + case catch do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup) of + {error,Emsg} -> {ldap_closed_p(Data, Emsg),Data}; + {'EXIT',Error} -> {ldap_closed_p(Data, Error),Data}; + {ok,NewData} -> {ok,NewData}; + Else -> {ldap_closed_p(Data, Else),Data} + end. + +do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup) -> + Req = #'ModifyDNRequest'{entry = Entry, + newrdn = NewRDN, + deleteoldrdn = DelOldRDN, + newSuperior = NewSup}, + S = Data#eldap.fd, + Id = bump_id(Data), + log2(Data, "modify DN request = ~p~n", [Req]), + Resp = request(S, Data, Id, {modDNRequest, Req}), + log2(Data, "modify DN reply = ~p~n", [Resp]), + check_reply(Data#eldap{id = Id}, Resp, modDNResponse). + +%%% -------------------------------------------------------------------- +%%% Send an LDAP request and receive the answer +%%% -------------------------------------------------------------------- + +request(S, Data, ID, Request) -> + send_request(S, Data, ID, Request), + recv_response(S, Data). + +send_request(S, Data, ID, Request) -> + Message = #'LDAPMessage'{messageID = ID, + protocolOp = Request}, + {ok,Bytes} = asn1rt:encode('ELDAPv3', 'LDAPMessage', Message), + case do_send(S, Data, Bytes) of + {error,Reason} -> throw({gen_tcp_error,Reason}); + Else -> Else + end. + +do_send(S, Data, Bytes) when Data#eldap.use_tls == false -> + gen_tcp:send(S, Bytes); +do_send(S, Data, Bytes) when Data#eldap.use_tls == true -> + ssl:send(S, Bytes). + +do_recv(S, #eldap{use_tls=false, timeout=Timeout}, Len) -> + gen_tcp:recv(S, Len, Timeout); +do_recv(S, #eldap{use_tls=true, timeout=Timeout}, Len) -> + ssl:recv(S, Len, Timeout). + +recv_response(S, Data) -> + case do_recv(S, Data, 0) of + {ok, Packet} -> + check_tag(Packet), + case asn1rt:decode('ELDAPv3', 'LDAPMessage', Packet) of + {ok,Resp} -> {ok,Resp}; + Error -> throw(Error) + end; + {error,Reason} -> + throw({gen_tcp_error, Reason}); + Error -> + throw(Error) + end. + +%%% Sanity check of received packet +check_tag(Data) -> + case asn1rt_ber_bin:decode_tag(l2b(Data)) of + {_Tag, Data1, _Rb} -> + case asn1rt_ber_bin:decode_length(l2b(Data1)) of + {{_Len, _Data2}, _Rb2} -> ok; + _ -> throw({error,decoded_tag_length}) + end; + _ -> throw({error,decoded_tag}) + end. + +%%% Check for expected kind of reply +check_reply(Data, {ok,Msg}, Op) when + Msg#'LDAPMessage'.messageID == Data#eldap.id -> + case Msg#'LDAPMessage'.protocolOp of + {Op, Result} -> + case Result#'LDAPResult'.resultCode of + success -> {ok,Data}; + Error -> {error, Error} + end; + Other -> {error, Other} + end; +check_reply(_, Error, _) -> + {error, Error}. + + +%%% -------------------------------------------------------------------- +%%% Verify the input data +%%% -------------------------------------------------------------------- + +v_filter({'and',L}) -> {'and',L}; +v_filter({'or', L}) -> {'or',L}; +v_filter({'not',L}) -> {'not',L}; +v_filter({equalityMatch,AV}) -> {equalityMatch,AV}; +v_filter({greaterOrEqual,AV}) -> {greaterOrEqual,AV}; +v_filter({lessOrEqual,AV}) -> {lessOrEqual,AV}; +v_filter({approxMatch,AV}) -> {approxMatch,AV}; +v_filter({present,A}) -> {present,A}; +v_filter({substrings,S}) when is_record(S,'SubstringFilter') -> {substrings,S}; +v_filter(_Filter) -> throw({error,concat(["unknown filter: ",_Filter])}). + +v_modifications(Mods) -> + F = fun({_,Op,_}) -> + case lists:member(Op,[add,delete,replace]) of + true -> true; + _ -> throw({error,{mod_operation,Op}}) + end + end, + lists:foreach(F, Mods). + +v_substr([{Key,Str}|T]) when is_list(Str),Key==initial;Key==any;Key==final -> + [{Key,Str}|v_substr(T)]; +v_substr([H|_]) -> + throw({error,{substring_arg,H}}); +v_substr([]) -> + []. +v_scope(baseObject) -> baseObject; +v_scope(singleLevel) -> singleLevel; +v_scope(wholeSubtree) -> wholeSubtree; +v_scope(_Scope) -> throw({error,concat(["unknown scope: ",_Scope])}). + +v_deref(DR = neverDerefAliases) -> DR; +v_deref(DR = derefInSearching) -> DR; +v_deref(DR = derefFindingBaseObj) -> DR; +v_deref(DR = derefAlways ) -> DR. + +v_bool(true) -> true; +v_bool(false) -> false; +v_bool(_Bool) -> throw({error,concat(["not Boolean: ",_Bool])}). + +v_timeout(I) when is_integer(I), I>=0 -> I; +v_timeout(_I) -> throw({error,concat(["timeout not positive integer: ",_I])}). + +v_attributes(Attrs) -> + F = fun(A) when is_list(A) -> A; + (A) -> throw({error,concat(["attribute not String: ",A])}) + end, + lists:map(F,Attrs). + + +%%% -------------------------------------------------------------------- +%%% Log routines. Call a user provided log routine F. +%%% -------------------------------------------------------------------- + +%log1(Data, Str, Args) -> log(Data, Str, Args, 1). +log2(Data, Str, Args) -> log(Data, Str, Args, 2). + +log(Data, Str, Args, Level) when is_function(Data#eldap.log) -> + catch (Data#eldap.log)(Level, Str, Args); +log(_, _, _, _) -> + ok. + + +%%% -------------------------------------------------------------------- +%%% Misc. routines +%%% -------------------------------------------------------------------- + +send(To,Msg) -> To ! {self(),Msg}. +recv(From) -> + receive + {From,Msg} -> Msg; + {'EXIT', From, Reason} -> + {error, {internal_error, Reason}} + end. + +ldap_closed_p(Data, Emsg) when Data#eldap.use_tls == true -> + %% Check if the SSL socket seems to be alive or not + case catch ssl:sockname(Data#eldap.fd) of + {error, _} -> + ssl:close(Data#eldap.fd), + {error, ldap_closed}; + {ok, _} -> + {error, Emsg}; + _ -> + %% sockname crashes if the socket pid is not alive + {error, ldap_closed} + end; +ldap_closed_p(Data, Emsg) -> + %% non-SSL socket + case inet:port(Data#eldap.fd) of + {error,_} -> {error, ldap_closed}; + _ -> {error,Emsg} + end. + +bump_id(Data) -> Data#eldap.id + 1. + + +%%% -------------------------------------------------------------------- +%%% parse_dn/1 - Implementation of RFC 2253: +%%% +%%% "UTF-8 String Representation of Distinguished Names" +%%% +%%% Test cases: +%%% +%%% The simplest case: +%%% +%%% 1> eldap:parse_dn("CN=Steve Kille,O=Isode Limited,C=GB"). +%%% {ok,[[{attribute_type_and_value,"CN","Steve Kille"}], +%%% [{attribute_type_and_value,"O","Isode Limited"}], +%%% [{attribute_type_and_value,"C","GB"}]]} +%%% +%%% The first RDN is multi-valued: +%%% +%%% 2> eldap:parse_dn("OU=Sales+CN=J. Smith,O=Widget Inc.,C=US"). +%%% {ok,[[{attribute_type_and_value,"OU","Sales"}, +%%% {attribute_type_and_value,"CN","J. Smith"}], +%%% [{attribute_type_and_value,"O","Widget Inc."}], +%%% [{attribute_type_and_value,"C","US"}]]} +%%% +%%% Quoting a comma: +%%% +%%% 3> eldap:parse_dn("CN=L. Eagle,O=Sue\\, Grabbit and Runn,C=GB"). +%%% {ok,[[{attribute_type_and_value,"CN","L. Eagle"}], +%%% [{attribute_type_and_value,"O","Sue\\, Grabbit and Runn"}], +%%% [{attribute_type_and_value,"C","GB"}]]} +%%% +%%% A value contains a carriage return: +%%% +%%% 4> eldap:parse_dn("CN=Before +%%% 4> After,O=Test,C=GB"). +%%% {ok,[[{attribute_type_and_value,"CN","Before\nAfter"}], +%%% [{attribute_type_and_value,"O","Test"}], +%%% [{attribute_type_and_value,"C","GB"}]]} +%%% +%%% 5> eldap:parse_dn("CN=Before\\0DAfter,O=Test,C=GB"). +%%% {ok,[[{attribute_type_and_value,"CN","Before\\0DAfter"}], +%%% [{attribute_type_and_value,"O","Test"}], +%%% [{attribute_type_and_value,"C","GB"}]]} +%%% +%%% An RDN in OID form: +%%% +%%% 6> eldap:parse_dn("1.3.6.1.4.1.1466.0=#04024869,O=Test,C=GB"). +%%% {ok,[[{attribute_type_and_value,"1.3.6.1.4.1.1466.0","#04024869"}], +%%% [{attribute_type_and_value,"O","Test"}], +%%% [{attribute_type_and_value,"C","GB"}]]} +%%% +%%% +%%% -------------------------------------------------------------------- + +parse_dn("") -> % empty DN string + {ok,[]}; +parse_dn([H|_] = Str) when H=/=$, -> % 1:st name-component ! + case catch parse_name(Str,[]) of + {'EXIT',Reason} -> {parse_error,internal_error,Reason}; + Else -> Else + end. + +parse_name("",Acc) -> + {ok,lists:reverse(Acc)}; +parse_name([$,|T],Acc) -> % N:th name-component ! + parse_name(T,Acc); +parse_name(Str,Acc) -> + {Rest,NameComponent} = parse_name_component(Str), + parse_name(Rest,[NameComponent|Acc]). + +parse_name_component(Str) -> + parse_name_component(Str,[]). + +parse_name_component(Str,Acc) -> + case parse_attribute_type_and_value(Str) of + {[$+|Rest], ATV} -> + parse_name_component(Rest,[ATV|Acc]); + {Rest,ATV} -> + {Rest,lists:reverse([ATV|Acc])} + end. + +parse_attribute_type_and_value(Str) -> + case parse_attribute_type(Str) of + {_Rest,[]} -> + parse_error(expecting_attribute_type,Str); + {Rest,Type} -> + Rest2 = parse_equal_sign(Rest), + {Rest3,Value} = parse_attribute_value(Rest2), + {Rest3,{attribute_type_and_value,Type,Value}} + end. + +-define(IS_ALPHA(X) , X>=$a,X=<$z;X>=$A,X=<$Z ). +-define(IS_DIGIT(X) , X>=$0,X=<$9 ). +-define(IS_SPECIAL(X) , X==$,;X==$=;X==$+;X==$<;X==$>;X==$#;X==$; ). +-define(IS_QUOTECHAR(X) , X=/=$\\,X=/=$" ). +-define(IS_STRINGCHAR(X) , + X=/=$,,X=/=$=,X=/=$+,X=/=$<,X=/=$>,X=/=$#,X=/=$;,?IS_QUOTECHAR(X) ). +-define(IS_HEXCHAR(X) , ?IS_DIGIT(X);X>=$a,X=<$f;X>=$A,X=<$F ). + +parse_attribute_type([H|T]) when ?IS_ALPHA(H) -> + %% NB: It must be an error in the RFC in the definition + %% of 'attributeType', should be: (ALPHA *keychar) + {Rest,KeyChars} = parse_keychars(T), + {Rest,[H|KeyChars]}; +parse_attribute_type([H|_] = Str) when ?IS_DIGIT(H) -> + parse_oid(Str); +parse_attribute_type(Str) -> + parse_error(invalid_attribute_type,Str). + + + +%%% Is a hexstring ! +parse_attribute_value([$#,X,Y|T]) when ?IS_HEXCHAR(X),?IS_HEXCHAR(Y) -> + {Rest,HexString} = parse_hexstring(T), + {Rest,[$#,X,Y|HexString]}; +%%% Is a "quotation-sequence" ! +parse_attribute_value([$"|T]) -> + {Rest,Quotation} = parse_quotation(T), + {Rest,[$"|Quotation]}; +%%% Is a stringchar , pair or Empty ! +parse_attribute_value(Str) -> + parse_string(Str). + +parse_hexstring(Str) -> + parse_hexstring(Str,[]). + +parse_hexstring([X,Y|T],Acc) when ?IS_HEXCHAR(X),?IS_HEXCHAR(Y) -> + parse_hexstring(T,[Y,X|Acc]); +parse_hexstring(T,Acc) -> + {T,lists:reverse(Acc)}. + +parse_quotation([$"|T]) -> % an empty: "" is ok ! + {T,[$"]}; +parse_quotation(Str) -> + parse_quotation(Str,[]). + +%%% Parse to end of quotation +parse_quotation([$"|T],Acc) -> + {T,lists:reverse([$"|Acc])}; +parse_quotation([X|T],Acc) when ?IS_QUOTECHAR(X) -> + parse_quotation(T,[X|Acc]); +parse_quotation([$\\,X|T],Acc) when ?IS_SPECIAL(X) -> + parse_quotation(T,[X,$\\|Acc]); +parse_quotation([$\\,$\\|T],Acc) -> + parse_quotation(T,[$\\,$\\|Acc]); +parse_quotation([$\\,$"|T],Acc) -> + parse_quotation(T,[$",$\\|Acc]); +parse_quotation([$\\,X,Y|T],Acc) when ?IS_HEXCHAR(X),?IS_HEXCHAR(Y) -> + parse_quotation(T,[Y,X,$\\|Acc]); +parse_quotation(T,_) -> + parse_error(expecting_double_quote_mark,T). + +parse_string(Str) -> + parse_string(Str,[]). + +parse_string("",Acc) -> + {"",lists:reverse(Acc)}; +parse_string([H|T],Acc) when ?IS_STRINGCHAR(H) -> + parse_string(T,[H|Acc]); +parse_string([$\\,X|T],Acc) when ?IS_SPECIAL(X) -> % is a pair ! + parse_string(T,[X,$\\|Acc]); +parse_string([$\\,$\\|T],Acc) -> % is a pair ! + parse_string(T,[$\\,$\\|Acc]); +parse_string([$\\,$" |T],Acc) -> % is a pair ! + parse_string(T,[$" ,$\\|Acc]); +parse_string([$\\,X,Y|T],Acc) when ?IS_HEXCHAR(X),?IS_HEXCHAR(Y) -> % is a pair! + parse_string(T,[Y,X,$\\|Acc]); +parse_string(T,Acc) -> + {T,lists:reverse(Acc)}. + +parse_equal_sign([$=|T]) -> T; +parse_equal_sign(T) -> parse_error(expecting_equal_sign,T). + +parse_keychars(Str) -> parse_keychars(Str,[]). + +parse_keychars([H|T],Acc) when ?IS_ALPHA(H) -> parse_keychars(T,[H|Acc]); +parse_keychars([H|T],Acc) when ?IS_DIGIT(H) -> parse_keychars(T,[H|Acc]); +parse_keychars([$-|T],Acc) -> parse_keychars(T,[$-|Acc]); +parse_keychars(T,Acc) -> {T,lists:reverse(Acc)}. + +parse_oid(Str) -> parse_oid(Str,[]). + +parse_oid([H,$.|T], Acc) when ?IS_DIGIT(H) -> + parse_oid(T,[$.,H|Acc]); +parse_oid([H|T], Acc) when ?IS_DIGIT(H) -> + parse_oid(T,[H|Acc]); +parse_oid(T, Acc) -> + {T,lists:reverse(Acc)}. + +parse_error(Emsg,Rest) -> + throw({parse_error,Emsg,Rest}). + + +%%% -------------------------------------------------------------------- +%%% Parse LDAP url according to RFC 2255 +%%% +%%% Test case: +%%% +%%% 2> eldap:parse_ldap_url("ldap://10.42.126.33:389/cn=Administrative%20CA,o=Post%20Danmark,c=DK?certificateRevokationList;binary"). +%%% {ok,{{10,42,126,33},389}, +%%% [[{attribute_type_and_value,"cn","Administrative%20CA"}], +%%% [{attribute_type_and_value,"o","Post%20Danmark"}], +%%% [{attribute_type_and_value,"c","DK"}]], +%%% {attributes,["certificateRevokationList;binary"]}} +%%% +%%% -------------------------------------------------------------------- + +parse_ldap_url("ldap://" ++ Rest1 = Str) -> + {Rest2,HostPort} = parse_hostport(Rest1), + %% Split the string into DN and Attributes+etc + {Sdn,Rest3} = split_string(rm_leading_slash(Rest2),$?), + case parse_dn(Sdn) of + {parse_error,internal_error,_Reason} -> + {parse_error,internal_error,{Str,[]}}; + {parse_error,Emsg,Tail} -> + Head = get_head(Str,Tail), + {parse_error,Emsg,{Head,Tail}}; + {ok,DN} -> + %% We stop parsing here for now and leave + %% 'scope', 'filter' and 'extensions' to + %% be implemented later if needed. + {_Rest4,Attributes} = parse_attributes(Rest3), + {ok,HostPort,DN,Attributes} + end. + +rm_leading_slash([$/|Tail]) -> Tail; +rm_leading_slash(Tail) -> Tail. + +parse_attributes([$?|Tail]) -> + case split_string(Tail,$?) of + {[],Attributes} -> + {[],{attributes,string:tokens(Attributes,",")}}; + {Attributes,Rest} -> + {Rest,{attributes,string:tokens(Attributes,",")}} + end. + +parse_hostport(Str) -> + {HostPort,Rest} = split_string(Str,$/), + case split_string(HostPort,$:) of + {Shost,[]} -> + {Rest,{parse_host(Rest,Shost),?LDAP_PORT}}; + {Shost,[$:|Sport]} -> + {Rest,{parse_host(Rest,Shost), + parse_port(Rest,Sport)}} + end. + +parse_port(Rest,Sport) -> + try list_to_integer(Sport) + catch _:_ -> parse_error(parsing_port,Rest) + end. + +parse_host(Rest,Shost) -> + case catch validate_host(Shost) of + {parse_error,Emsg,_} -> parse_error(Emsg,Rest); + Host -> Host + end. + +validate_host(Shost) -> + case inet_parse:address(Shost) of + {ok,Host} -> Host; + _ -> + case inet_parse:domain(Shost) of + true -> Shost; + _ -> parse_error(parsing_host,Shost) + end + end. + + +split_string(Str,Key) -> + Pred = fun(X) when X==Key -> false; (_) -> true end, + lists:splitwith(Pred, Str). + +get_head(Str,Tail) -> + get_head(Str,Tail,[]). + +%%% Should always succeed ! +get_head([H|Tail],Tail,Rhead) -> lists:reverse([H|Rhead]); +get_head([H|Rest],Tail,Rhead) -> get_head(Rest,Tail,[H|Rhead]). + +l2b(B) when is_binary(B) -> B; +l2b(L) when is_list(L) -> list_to_binary(L). + diff --git a/lib/eldap/test/Makefile b/lib/eldap/test/Makefile new file mode 100644 index 0000000000..3c5810eece --- /dev/null +++ b/lib/eldap/test/Makefile @@ -0,0 +1,83 @@ +# +# %CopyrightBegin% +# +# Copyright Ericsson AB 2012. All Rights Reserved. +# +# The contents of this file are subject to the Erlang Public License, +# Version 1.1, (the "License"); you may not use this file except in +# compliance with the License. You should have received a copy of the +# Erlang Public License along with this software. If not, it can be +# retrieved online at http://www.erlang.org/. +# +# Software distributed under the License is distributed on an "AS IS" +# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +# the License for the specific language governing rights and limitations +# under the License. +# +# %CopyrightEnd% +# + +include $(ERL_TOP)/make/target.mk +include $(ERL_TOP)/make/$(TARGET)/otp.mk + + +INCLUDES= -I. -I ../include + +# ---------------------------------------------------- +# Target Specs +# ---------------------------------------------------- + +MODULES= \ + eldap_basic_SUITE + +ERL_FILES= $(MODULES:%=%.erl) + +HRL_FILES= + +TARGET_FILES= \ + $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +SPEC_FILES = eldap.spec + +# COVER_FILE = eldap.cover + + +# ---------------------------------------------------- +# Release directory specification +# ---------------------------------------------------- +RELSYSDIR = $(RELEASE_PATH)/eldap_test + +# ---------------------------------------------------- +# FLAGS +# ---------------------------------------------------- +ERL_COMPILE_FLAGS += $(INCLUDES) -pa $(ERL_TOP)/lib/eldap/ebin + +EBIN = . + +# ---------------------------------------------------- +# Targets +# ---------------------------------------------------- + +tests debug opt: $(TARGET_FILES) + + +clean: + rm -f $(TARGET_FILES) + rm -f core + +docs: + +# ---------------------------------------------------- +# Release Target +# ---------------------------------------------------- +include $(ERL_TOP)/make/otp_release_targets.mk + +release_spec: opt + +release_tests_spec: opt + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" +# @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) +release_docs_spec: diff --git a/lib/eldap/test/eldap.spec b/lib/eldap/test/eldap.spec new file mode 100644 index 0000000000..b14e9a655a --- /dev/null +++ b/lib/eldap/test/eldap.spec @@ -0,0 +1 @@ +{suites,"../eldap_test",all}. diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl new file mode 100644 index 0000000000..c7e3052b29 --- /dev/null +++ b/lib/eldap/test/eldap_basic_SUITE.erl @@ -0,0 +1,222 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(eldap_basic_SUITE). + +-compile(export_all). + +%%-include_lib("common_test/include/ct.hrl"). +-include_lib("test_server/include/test_server.hrl"). +-include_lib("eldap/include/eldap.hrl"). + +-define(TIMEOUT, 120000). % 2 min + +init_per_suite(Config0) -> + {{EldapHost,Port}, Config1} = + case catch ct:get_config(eldap_server, undefined) of + undefined -> %% Dev test only + Server = {"localhost", 9876}, + {Server, [{eldap_server, {"localhost", 9876}}|Config0]}; + {'EXIT', _} -> %% Dev test only + Server = {"localhost", 9876}, + {Server, [{eldap_server, {"localhost", 9876}}|Config0]}; + Server -> + {Server, [{eldap_server, Server}|Config0]} + end, + %% Add path for this test run + try + {ok, Handle} = eldap:open([EldapHost], [{port, Port}]), + ok = eldap:simple_bind(Handle, "cn=Manager,dc=ericsson,dc=se", "hejsan"), + {ok, MyHost} = inet:gethostname(), + Path = "dc="++MyHost++",dc=ericsson,dc=se", + Config = [{eldap_path,Path}|Config1], + eldap:add(Handle,"dc=ericsson,dc=se", + [{"objectclass", ["dcObject", "organization"]}, + {"dc", ["ericsson"]}, {"o", ["Testing"]}]), + eldap:add(Handle,Path, + [{"objectclass", ["dcObject", "organization"]}, + {"dc", [MyHost]}, {"o", ["Test machine"]}]), + Config + catch error:{badmatch,Error} -> + io:format("Eldap init error ~p~n ~p~n",[Error, erlang:get_stacktrace()]), + {skip, lists:flatten(io_lib:format("Ldap init failed with host ~p", [EldapHost]))} + end. +end_per_suite(Config) -> + %% Cleanup everything + {EHost, Port} = proplists:get_value(eldap_server, Config), + Path = proplists:get_value(eldap_path, Config), + {ok, H} = eldap:open([EHost], [{port, Port}]), + ok = eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"), + case eldap:search(H, [{base, Path}, + {filter, eldap:present("objectclass")}, + {scope, eldap:wholeSubtree()}]) + of + {ok, {eldap_search_result, Entries, _}} -> + [ok = eldap:delete(H, Entry) || {eldap_entry, Entry, _} <- Entries]; + _ -> ignore + end, + ok. + +init_per_testcase(_TestCase, Config) -> Config. +end_per_testcase(_TestCase, _Config) -> ok. + +%% suite() -> + +all() -> + [app, + api]. + +app(doc) -> "Test that the eldap app file is ok"; +app(suite) -> []; +app(Config) when is_list(Config) -> + ok = test_server:app_test(public_key). + +api(doc) -> "Basic test that all api functions works as expected"; +api(suite) -> []; +api(Config) -> + {Host,Port} = proplists:get_value(eldap_server, Config), + {ok, H} = eldap:open([Host], [{port,Port}]), + %% {ok, H} = eldap:open([Host], [{port,Port+1}, {ssl, true}]), + BasePath = proplists:get_value(eldap_path, Config), + All = fun(Where) -> + eldap:search(H, #eldap_search{base=Where, + filter=eldap:present("objectclass"), + scope= eldap:wholeSubtree()}) + end, + Search = fun(Filter) -> + eldap:search(H, #eldap_search{base=BasePath, + filter=Filter, + scope=eldap:singleLevel()}) + end, + {ok, #eldap_search_result{entries=[_]}} = All(BasePath), + {error, noSuchObject} = All("cn=Bar,"++BasePath), + + {error, _} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, + [{"objectclass", ["person"]}, + {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), + eldap:simple_bind(H, "cn=Manager,dc=ericsson,dc=se", "hejsan"), + + %% Add + ok = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, + [{"objectclass", ["person"]}, + {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), + ok = eldap:add(H, "cn=Foo Bar," ++ BasePath, + [{"objectclass", ["person"]}, + {"cn", ["Foo Bar"]}, {"sn", ["Bar"]}, {"telephoneNumber", ["555-1232", "555-5432"]}]), + ok = eldap:add(H, "ou=Team," ++ BasePath, + [{"objectclass", ["organizationalUnit"]}, + {"ou", ["Team"]}]), + + %% Search + JJSR = {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(eldap:equalityMatch("sn", "Jonsson")), + JJSR = Search(eldap:substrings("sn", [{any, "ss"}])), + FBSR = {ok, #eldap_search_result{entries=[#eldap_entry{object_name=FB}]}} = + Search(eldap:substrings("sn", [{any, "a"}])), + FBSR = Search(eldap:substrings("sn", [{initial, "B"}])), + FBSR = Search(eldap:substrings("sn", [{final, "r"}])), + + F_AND = eldap:'and'([eldap:present("objectclass"), eldap:present("ou")]), + {ok, #eldap_search_result{entries=[#eldap_entry{}]}} = Search(F_AND), + F_NOT = eldap:'and'([eldap:present("objectclass"), eldap:'not'(eldap:present("ou"))]), + {ok, #eldap_search_result{entries=[#eldap_entry{}, #eldap_entry{}]}} = Search(F_NOT), + + %% MODIFY + Mod = [eldap:mod_replace("telephoneNumber", ["555-12345"]), + eldap:mod_add("description", ["Nice guy"])], + %% io:format("MOD ~p ~p ~n",[FB, Mod]), + ok = eldap:modify(H, FB, Mod), + %% DELETE ATTR + ok = eldap:modify(H, FB, [eldap:mod_delete("telephoneNumber", [])]), + + %% DELETE + {error, entryAlreadyExists} = eldap:add(H, "cn=Jonas Jonsson," ++ BasePath, + [{"objectclass", ["person"]}, + {"cn", ["Jonas Jonsson"]}, {"sn", ["Jonsson"]}]), + ok = eldap:delete(H, "cn=Jonas Jonsson," ++ BasePath), + {error, noSuchObject} = eldap:delete(H, "cn=Jonas Jonsson," ++ BasePath), + + %% MODIFY_DN + ok = eldap:modify_dn(H, FB, "cn=Niclas Andre", true, ""), + %%io:format("Res ~p~n ~p~n",[R, All(BasePath)]), + + eldap:close(H), + ok. + +add(H, Attr, Value, Path0, Attrs, Class) -> + Path = case Path0 of + [] -> Attr ++ "=" ++ Value; + _ -> Attr ++ "=" ++ Value ++ "," ++ Path0 + end, + case eldap:add(H, Path, [{"objectclass", Class}, {Attr, [Value]}] ++ Attrs) + of + ok -> {ok, Path}; + {error, E = entryAlreadyExists} -> {E, Path}; + R = {error, Reason} -> + io:format("~p:~p: ~s,~s =>~n ~p~n", + [?MODULE,?LINE, Attr, Value, R]), + exit({ldap, add, Reason}) + end. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Develop +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +test() -> + run(). + +run() -> + Cases = all(), + run(Cases). + +run(Case) when is_atom(Case) -> + run([Case]); +run(Cases) when is_list(Cases) -> + Run = fun(Test, Config0) -> + Config = init_per_testcase(Test, Config0), + try + io:format("~nTest ~p ... ",[Test]), + ?MODULE:Test(Config), + end_per_testcase(Test, Config), + io:format("ok~n",[]) + catch _:Reason -> + io:format("~n FAIL (~p): ~p~n ~p~n", + [Test, Reason, erlang:get_stacktrace()]) + end + end, + process_flag(trap_exit, true), + Pid = spawn_link(fun() -> + case init_per_suite([]) of + {skip, Reason} -> io:format("Skip ~s~n",[Reason]); + Config -> + try + [Run(Test, Config) || Test <- Cases] + catch _:Err -> + io:format("Error ~p in ~p~n",[Err, erlang:get_stacktrace()]) + end, + end_per_suite(Config) + end + end), + receive + {'EXIT', Pid, normal} -> ok; + Msg -> io:format("Received ~p (~p)~n",[Msg, Pid]) + after 100 -> ok end, + process_flag(trap_exit, false), + ok. diff --git a/lib/eldap/test/ldap_server/slapd.conf b/lib/eldap/test/ldap_server/slapd.conf new file mode 100644 index 0000000000..87be676d9f --- /dev/null +++ b/lib/eldap/test/ldap_server/slapd.conf @@ -0,0 +1,14 @@ +include /etc/ldap/schema/core.schema +pidfile /tmp/openldap-data/slapd.pid +argsfile /tmp/openldap-data/slapd.args + +database bdb +suffix "dc=ericsson,dc=se" +rootdn "cn=Manager,dc=ericsson,dc=se" +rootpw hejsan +# The database must exist before running slapd +directory /tmp/openldap-data +# Indices to maintain +index objectClass eq +# URI "ldap://0.0.0.0:9876 ldaps://0.0.0.0:9870" +# servers/slapd/slapd -d 255 -h "ldap://0.0.0.0:9876 ldaps://0.0.0.0:9870" -f /ldisk/dgud/src/otp/lib/eldap/test/ldap_server/slapd.conf
\ No newline at end of file diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk new file mode 100644 index 0000000000..c9d6e4e324 --- /dev/null +++ b/lib/eldap/vsn.mk @@ -0,0 +1 @@ +ELDAP_VSN = 1.0 diff --git a/lib/erl_docgen/doc/src/Makefile b/lib/erl_docgen/doc/src/Makefile index ff50c12895..a254c4fb6d 100644 --- a/lib/erl_docgen/doc/src/Makefile +++ b/lib/erl_docgen/doc/src/Makefile @@ -124,13 +124,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/erl_docgen/doc/src/notes.xml b/lib/erl_docgen/doc/src/notes.xml index 23f64b876a..8ca11b1cf5 100644 --- a/lib/erl_docgen/doc/src/notes.xml +++ b/lib/erl_docgen/doc/src/notes.xml @@ -30,7 +30,60 @@ </header> <p>This document describes the changes made to the <em>erl_docgen</em> application.</p> - <section><title>Erl_Docgen 0.3</title> + <section><title>Erl_Docgen 0.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Some links in C libraries were not generated + correctly. This bug has been fixed. </p> + <p> + Own Id: OTP-9832</p> + </item> + <item> + <p> + Set `font-family: Courier, monospace' in OTP doc CSS</p> + <p> + left Courier as the primary original font and also added + monospace as secondary for people which in Linux does not + have it installed.</p> + <p> + Also adds minor cosmetic changes to the CSS. (Thanks to + Ricardo Catalinas Jim�nez)</p> + <p> + Own Id: OTP-9918</p> + </item> + <item> + <p> + When generating from edoc it is now possible to use + ranges in specs and <img> tags in the description.</p> + <p> + Own Id: OTP-9970</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> Optimize and improve handling of multiple func:s. + </p> + <p> + Own Id: OTP-9877</p> + </item> + <item> + <p> The generation of the libraries' HTML documentation + has been optimized. </p> + <p> + Own Id: OTP-9893</p> + </item> + </list> + </section> + +</section> + +<section><title>Erl_Docgen 0.3</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/erl_docgen/priv/bin/Makefile b/lib/erl_docgen/priv/bin/Makefile index 95ad36216a..449cc6691c 100644 --- a/lib/erl_docgen/priv/bin/Makefile +++ b/lib/erl_docgen/priv/bin/Makefile @@ -64,8 +64,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(ESCRIPT_FILES) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_SCRIPT) $(ESCRIPT_FILES) "$(RELSYSDIR)/priv/bin" release_docs_spec: diff --git a/lib/erl_docgen/priv/css/Makefile b/lib/erl_docgen/priv/css/Makefile index 81124fb111..0faca019f3 100644 --- a/lib/erl_docgen/priv/css/Makefile +++ b/lib/erl_docgen/priv/css/Makefile @@ -64,13 +64,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/css - $(INSTALL_DATA) $(CSS_FILES) $(RELSYSDIR)/priv/css + $(INSTALL_DIR) "$(RELSYSDIR)/priv/css" + $(INSTALL_DATA) $(CSS_FILES) "$(RELSYSDIR)/priv/css" release_docs_spec: - $(INSTALL_DIR) $(RELEASE_PATH)/doc - $(INSTALL_DATA) $(CSS_FILES) ../nyi.html $(RELEASE_PATH)/doc + $(INSTALL_DIR) "$(RELEASE_PATH)/doc" + $(INSTALL_DATA) $(CSS_FILES) ../nyi.html "$(RELEASE_PATH)/doc" release_tests_spec: diff --git a/lib/erl_docgen/priv/dtd/Makefile b/lib/erl_docgen/priv/dtd/Makefile index 65c68fcca7..314374d34b 100644 --- a/lib/erl_docgen/priv/dtd/Makefile +++ b/lib/erl_docgen/priv/dtd/Makefile @@ -89,8 +89,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/dtd - $(INSTALL_DATA) $(DTD_FILES) $(ENT_FILES) $(RELSYSDIR)/priv/dtd + $(INSTALL_DIR) "$(RELSYSDIR)/priv/dtd" + $(INSTALL_DATA) $(DTD_FILES) $(ENT_FILES) "$(RELSYSDIR)/priv/dtd" release_docs_spec: diff --git a/lib/erl_docgen/priv/dtd_html_entities/Makefile b/lib/erl_docgen/priv/dtd_html_entities/Makefile index d57302bd1b..9cd3f38c4b 100644 --- a/lib/erl_docgen/priv/dtd_html_entities/Makefile +++ b/lib/erl_docgen/priv/dtd_html_entities/Makefile @@ -63,8 +63,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/dtd_html_entities - $(INSTALL_DATA) $(ENTITY_FILES) $(RELSYSDIR)/priv/dtd_html_entities + $(INSTALL_DIR) "$(RELSYSDIR)/priv/dtd_html_entities" + $(INSTALL_DATA) $(ENTITY_FILES) "$(RELSYSDIR)/priv/dtd_html_entities" release_docs_spec: diff --git a/lib/erl_docgen/priv/dtd_man_entities/Makefile b/lib/erl_docgen/priv/dtd_man_entities/Makefile index 5651d8bc29..b6f29af6b4 100644 --- a/lib/erl_docgen/priv/dtd_man_entities/Makefile +++ b/lib/erl_docgen/priv/dtd_man_entities/Makefile @@ -63,8 +63,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/dtd_man_entities - $(INSTALL_DATA) $(ENTITY_FILES) $(RELSYSDIR)/priv/dtd_man_entities + $(INSTALL_DIR) "$(RELSYSDIR)/priv/dtd_man_entities" + $(INSTALL_DATA) $(ENTITY_FILES) "$(RELSYSDIR)/priv/dtd_man_entities" release_docs_spec: diff --git a/lib/erl_docgen/priv/images/Makefile b/lib/erl_docgen/priv/images/Makefile index 8b858fefb3..389f9f98ae 100644 --- a/lib/erl_docgen/priv/images/Makefile +++ b/lib/erl_docgen/priv/images/Makefile @@ -66,13 +66,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/images - $(INSTALL_DATA) $(GIF_FILES) $(PNG_FILES) $(RELSYSDIR)/priv/images + $(INSTALL_DIR) "$(RELSYSDIR)/priv/images" + $(INSTALL_DATA) $(GIF_FILES) $(PNG_FILES) "$(RELSYSDIR)/priv/images" release_docs_spec: - $(INSTALL_DIR) $(RELEASE_PATH)/doc - $(INSTALL_DATA) $(PNG_FILES) $(RELEASE_PATH)/doc + $(INSTALL_DIR) "$(RELEASE_PATH)/doc" + $(INSTALL_DATA) $(PNG_FILES) "$(RELEASE_PATH)/doc" release_tests_spec: diff --git a/lib/erl_docgen/priv/js/flipmenu/Makefile b/lib/erl_docgen/priv/js/flipmenu/Makefile index 65c5c91f35..682d186608 100644 --- a/lib/erl_docgen/priv/js/flipmenu/Makefile +++ b/lib/erl_docgen/priv/js/flipmenu/Makefile @@ -68,13 +68,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/js/flipmenu - $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) $(RELSYSDIR)/priv/js/flipmenu + $(INSTALL_DIR) "$(RELSYSDIR)/priv/js/flipmenu" + $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELSYSDIR)/priv/js/flipmenu" release_docs_spec: - $(INSTALL_DIR) $(RELEASE_PATH)/doc/js/flipmenu - $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) $(RELEASE_PATH)/doc/js/flipmenu + $(INSTALL_DIR) "$(RELEASE_PATH)/doc/js/flipmenu" + $(INSTALL_DATA) $(JS_FILES) $(GIF_FILES) "$(RELEASE_PATH)/doc/js/flipmenu" release_tests_spec: diff --git a/lib/erl_docgen/priv/xsl/Makefile b/lib/erl_docgen/priv/xsl/Makefile index 92a53b8c0b..ca7a4e8157 100644 --- a/lib/erl_docgen/priv/xsl/Makefile +++ b/lib/erl_docgen/priv/xsl/Makefile @@ -68,8 +68,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/xsl - $(INSTALL_DATA) $(XSL_FILES) $(RELSYSDIR)/priv/xsl + $(INSTALL_DIR) "$(RELSYSDIR)/priv/xsl" + $(INSTALL_DATA) $(XSL_FILES) "$(RELSYSDIR)/priv/xsl" release_docs_spec: diff --git a/lib/erl_docgen/priv/xsl/db_html.xsl b/lib/erl_docgen/priv/xsl/db_html.xsl index 7cf5465f90..4bc5abb364 100644 --- a/lib/erl_docgen/priv/xsl/db_html.xsl +++ b/lib/erl_docgen/priv/xsl/db_html.xsl @@ -1817,7 +1817,14 @@ <xsl:choose> <xsl:when test="ancestor::cref"> - <a name="{substring-before(nametext, '(')}"><span class="bold_code"><xsl:value-of select="ret"/><xsl:text> </xsl:text><xsl:value-of select="nametext"/></span></a><br/> + <a name="{substring-before(nametext, '(')}"> + <span class="bold_code"> + <xsl:value-of select="ret"/> + <xsl:call-template name="maybe-space-after-ret"> + <xsl:with-param name="s" select="ret"/> + </xsl:call-template> + <xsl:value-of select="nametext"/> + </span></a><br/> </xsl:when> <xsl:when test="ancestor::erlref"> <xsl:variable name="fname"> @@ -1845,6 +1852,18 @@ </xsl:template> + <xsl:template name="maybe-space-after-ret"> + <xsl:param name="s"/> + <xsl:variable name="last_char" + select="substring($s, string-length($s), 1)"/> + <xsl:choose> + <xsl:when test="$last_char != '*'"> + <xsl:text> </xsl:text> + </xsl:when> + </xsl:choose> + </xsl:template> + + <!-- Type --> <xsl:template match="type"> <xsl:param name="partnum"/> diff --git a/lib/erl_docgen/priv/xsl/db_man.xsl b/lib/erl_docgen/priv/xsl/db_man.xsl index 5234ba6bd0..33808859c7 100644 --- a/lib/erl_docgen/priv/xsl/db_man.xsl +++ b/lib/erl_docgen/priv/xsl/db_man.xsl @@ -3,7 +3,7 @@ # # %CopyrightBegin% # - # Copyright Ericsson AB 2009-2011. All Rights Reserved. + # Copyright Ericsson AB 2009-2012. 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 @@ -758,10 +758,32 @@ <xsl:template name="name"> <xsl:text> .B </xsl:text> - <xsl:apply-templates/> + <xsl:choose> + <xsl:when test="ancestor::cref"> + <xsl:value-of select="ret"/> + <xsl:call-template name="maybe-space-after-ret"> + <xsl:with-param name="s" select="ret"/> + </xsl:call-template> + <xsl:value-of select="nametext"/> + </xsl:when> + <xsl:otherwise> + <xsl:apply-templates/> + </xsl:otherwise> + </xsl:choose> <xsl:text> .br</xsl:text> </xsl:template> + <xsl:template name="maybe-space-after-ret"> + <xsl:param name="s"/> + <xsl:variable name="last_char" + select="substring($s, string-length($s), 1)"/> + <xsl:choose> + <xsl:when test="$last_char != '*'"> + <xsl:text> </xsl:text> + </xsl:when> + </xsl:choose> + </xsl:template> + <!-- Type --> <xsl:template match="type"> diff --git a/lib/erl_docgen/priv/xsl/db_pdf.xsl b/lib/erl_docgen/priv/xsl/db_pdf.xsl index 4ed4fa14c4..da96052462 100644 --- a/lib/erl_docgen/priv/xsl/db_pdf.xsl +++ b/lib/erl_docgen/priv/xsl/db_pdf.xsl @@ -3,7 +3,7 @@ # # %CopyrightBegin% # - # Copyright Ericsson AB 2009-2011. All Rights Reserved. + # Copyright Ericsson AB 2009-2012. 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 @@ -1424,7 +1424,13 @@ <xsl:param name="partnum"/> <xsl:choose> <xsl:when test="ancestor::cref"> - <fo:block id="{generate-id(nametext)}"><xsl:value-of select="ret"/><xsl:text></xsl:text><xsl:value-of select="nametext"/></fo:block> + <fo:block id="{generate-id(nametext)}"> + <xsl:value-of select="ret"/> + <xsl:call-template name="maybe-space-after-ret"> + <xsl:with-param name="s" select="ret"/> + </xsl:call-template> + <xsl:value-of select="nametext"/> + </fo:block> </xsl:when> <xsl:otherwise> <fo:block id="{generate-id(.)}"><xsl:value-of select="."/></fo:block> @@ -1432,6 +1438,16 @@ </xsl:choose> </xsl:template> + <xsl:template name="maybe-space-after-ret"> + <xsl:param name="s"/> + <xsl:variable name="last_char" + select="substring($s, string-length($s), 1)"/> + <xsl:choose> + <xsl:when test="$last_char != '*'"> + <xsl:text> </xsl:text> + </xsl:when> + </xsl:choose> + </xsl:template> <!-- Type --> <xsl:template match="type"> @@ -1564,7 +1580,7 @@ </xsl:variable> <fo:block xsl:use-attribute-sets="image"> - <fo:external-graphic content-width="60%" src="{@file}"/> + <fo:external-graphic content-width="scale-down-to-fit" inline-progression-dimension.maximum="100%" src="{@file}"/> <xsl:apply-templates> <xsl:with-param name="chapnum" select="$chapnum"/> diff --git a/lib/erl_docgen/src/Makefile b/lib/erl_docgen/src/Makefile index cbaf6e4627..4d8f43bfed 100644 --- a/lib/erl_docgen/src/Makefile +++ b/lib/erl_docgen/src/Makefile @@ -89,10 +89,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/erl_docgen/vsn.mk b/lib/erl_docgen/vsn.mk index dbd7e017b0..bf10591f34 100644 --- a/lib/erl_docgen/vsn.mk +++ b/lib/erl_docgen/vsn.mk @@ -1,2 +1,2 @@ -ERL_DOCGEN_VSN = 0.3 +ERL_DOCGEN_VSN = 0.3.1 diff --git a/lib/erl_interface/doc/src/Makefile b/lib/erl_interface/doc/src/Makefile index e05b647cb2..2bad8976b5 100644 --- a/lib/erl_interface/doc/src/Makefile +++ b/lib/erl_interface/doc/src/Makefile @@ -113,16 +113,16 @@ clean clean_docs clean_tex: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man1 - $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/erl_interface/doc/src/notes.xml b/lib/erl_interface/doc/src/notes.xml index 4cb9532880..48ac0be22d 100644 --- a/lib/erl_interface/doc/src/notes.xml +++ b/lib/erl_interface/doc/src/notes.xml @@ -30,6 +30,21 @@ </header> <p>This document describes the changes made to the Erl_interface application.</p> +<section><title>Erl_Interface 3.7.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Minor suppressions and fixes of compilation + warnings</p> + <p> + Own Id: OTP-10016</p> + </item> + </list> + </section> + +</section> + <section><title>Erl_Interface 3.7.6</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/erl_interface/src/Makefile.in b/lib/erl_interface/src/Makefile.in index d6b0ca1f16..661dbb68ac 100644 --- a/lib/erl_interface/src/Makefile.in +++ b/lib/erl_interface/src/Makefile.in @@ -843,32 +843,32 @@ EXTRA = \ $(TARGET)/eidefs.mk release: opt - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/lib - $(INSTALL_DIR) $(RELSYSDIR)/bin - $(INSTALL_DIR) $(RELSYSDIR)/src/auxdir - $(INSTALL_DIR) $(RELSYSDIR)/src/connect - $(INSTALL_DIR) $(RELSYSDIR)/src/decode - $(INSTALL_DIR) $(RELSYSDIR)/src/encode - $(INSTALL_DIR) $(RELSYSDIR)/src/epmd - $(INSTALL_DIR) $(RELSYSDIR)/src/legacy - $(INSTALL_DIR) $(RELSYSDIR)/src/misc - $(INSTALL_DIR) $(RELSYSDIR)/src/prog - $(INSTALL_DIR) $(RELSYSDIR)/src/registry - $(INSTALL_DATA) $(HEADERS) $(RELSYSDIR)/include - $(INSTALL_DATA) $(OBJ_TARGETS) $(RELSYSDIR)/lib + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/lib" + $(INSTALL_DIR) "$(RELSYSDIR)/bin" + $(INSTALL_DIR) "$(RELSYSDIR)/src/auxdir" + $(INSTALL_DIR) "$(RELSYSDIR)/src/connect" + $(INSTALL_DIR) "$(RELSYSDIR)/src/decode" + $(INSTALL_DIR) "$(RELSYSDIR)/src/encode" + $(INSTALL_DIR) "$(RELSYSDIR)/src/epmd" + $(INSTALL_DIR) "$(RELSYSDIR)/src/legacy" + $(INSTALL_DIR) "$(RELSYSDIR)/src/misc" + $(INSTALL_DIR) "$(RELSYSDIR)/src/prog" + $(INSTALL_DIR) "$(RELSYSDIR)/src/registry" + $(INSTALL_DATA) $(HEADERS) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(OBJ_TARGETS) "$(RELSYSDIR)/lib" ifneq ($(EXE_TARGETS),) - $(INSTALL_PROGRAM) $(EXE_TARGETS) $(RELSYSDIR)/bin -endif - $(INSTALL_DATA) $(EXTRA) $(RELSYSDIR)/src - $(INSTALL_DATA) connect/*.[ch] $(RELSYSDIR)/src/connect - $(INSTALL_DATA) decode/*.[ch] $(RELSYSDIR)/src/decode - $(INSTALL_DATA) encode/*.[ch] $(RELSYSDIR)/src/encode - $(INSTALL_DATA) epmd/*.[ch] $(RELSYSDIR)/src/epmd - $(INSTALL_DATA) misc/*.[ch] $(RELSYSDIR)/src/misc - $(INSTALL_DATA) registry/*.[ch] $(RELSYSDIR)/src/registry - $(INSTALL_DATA) legacy/*.[ch] $(RELSYSDIR)/src/legacy - $(INSTALL_DATA) prog/*.[ch] $(RELSYSDIR)/src/prog + $(INSTALL_PROGRAM) $(EXE_TARGETS) "$(RELSYSDIR)/bin" +endif + $(INSTALL_DATA) $(EXTRA) "$(RELSYSDIR)/src" + $(INSTALL_DATA) connect/*.[ch] "$(RELSYSDIR)/src/connect" + $(INSTALL_DATA) decode/*.[ch] "$(RELSYSDIR)/src/decode" + $(INSTALL_DATA) encode/*.[ch] "$(RELSYSDIR)/src/encode" + $(INSTALL_DATA) epmd/*.[ch] "$(RELSYSDIR)/src/epmd" + $(INSTALL_DATA) misc/*.[ch] "$(RELSYSDIR)/src/misc" + $(INSTALL_DATA) registry/*.[ch] "$(RELSYSDIR)/src/registry" + $(INSTALL_DATA) legacy/*.[ch] "$(RELSYSDIR)/src/legacy" + $(INSTALL_DATA) prog/*.[ch] "$(RELSYSDIR)/src/prog" release_docs: diff --git a/lib/erl_interface/src/misc/ei_format.c b/lib/erl_interface/src/misc/ei_format.c index c1e5d998e4..281a192535 100644 --- a/lib/erl_interface/src/misc/ei_format.c +++ b/lib/erl_interface/src/misc/ei_format.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2001-2011. All Rights Reserved. + * Copyright Ericsson AB 2001-2012. 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 diff --git a/lib/erl_interface/test/Makefile b/lib/erl_interface/test/Makefile index 4faf89c0d6..d11a138844 100644 --- a/lib/erl_interface/test/Makefile +++ b/lib/erl_interface/test/Makefile @@ -71,9 +71,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/erl_interface/test/all_SUITE_data/Makefile.src b/lib/erl_interface/test/all_SUITE_data/Makefile.src index 70652e47c5..39085def2d 100644 --- a/lib/erl_interface/test/all_SUITE_data/Makefile.src +++ b/lib/erl_interface/test/all_SUITE_data/Makefile.src @@ -32,7 +32,7 @@ all: $(ALL_OBJS) $(EI_COMMON_OBJS): gccifier@exe@ -@IFEQ@ (@erl_interface_cross_compile@, true) +@IFEQ@ (@cross@, yes) gccifier@exe@: $(CP) gccifier.sh gccifier@exe@ $(CHMOD) a+x gccifier@exe@ diff --git a/lib/erl_interface/test/all_SUITE_data/gccifier.c b/lib/erl_interface/test/all_SUITE_data/gccifier.c index 9f556fc4ed..a1019f9a72 100644 --- a/lib/erl_interface/test/all_SUITE_data/gccifier.c +++ b/lib/erl_interface/test/all_SUITE_data/gccifier.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2005-2009. All Rights Reserved. + * Copyright Ericsson AB 2004-2009. All Rights Reserved. * * The contents of this file are subject to the Erlang Public License, * Version 1.1, (the "License"); you may not use this file except in @@ -16,7 +16,6 @@ * * %CopyrightEnd% * - */ /* @@ -74,17 +73,23 @@ save_arg(args_t *args, char *arg1, ...) args->vec = (char **) (args->no ? realloc((void *) args->vec, (sizeof(char *) - *(args->no + ARGS_INCR + 1))) + *(args->no + ARGS_INCR + 2))) : malloc((sizeof(char *) - *(args->no + ARGS_INCR + 1)))); + *(args->no + ARGS_INCR + 2)))); if (!args->vec) enomem(); args->no += ARGS_INCR; } + if (carg == arg1) { + args->vec[args->ix++] = "\""; + args->chars++; + } args->vec[args->ix++] = carg; args->chars += strlen(carg); carg = va_arg(argp, char *); } + args->vec[args->ix++] = "\""; + args->chars++; args->vec[args->ix++] = " "; args->chars++; va_end(argp); @@ -232,6 +237,9 @@ main(int argc, char *argv[]) CHECK_FIRST_LINK_ARG; save_arg(&link_args, "-libpath:", arg, NULL); } + else if (strcmp("-link",arg) == 0) { + CHECK_FIRST_LINK_ARG; + } #endif /* #ifdef __WIN32__ */ else if (is_prefix("-l", &arg)) { CHECK_FIRST_LINK_ARG; diff --git a/lib/erl_interface/test/all_SUITE_data/init_tc.erl b/lib/erl_interface/test/all_SUITE_data/init_tc.erl index 7a599994fc..60c965eda3 100644 --- a/lib/erl_interface/test/all_SUITE_data/init_tc.erl +++ b/lib/erl_interface/test/all_SUITE_data/init_tc.erl @@ -48,8 +48,7 @@ run1(Name) -> generate(TcName, Cases) -> Hrl = TcName ++ "_cases.hrl", {ok, HrlFile} = file:open(Hrl, write), - {ok, Dir} = file:get_cwd(), - generate_hrl(Cases, HrlFile, {filename:join(Dir, TcName), 0}), + generate_hrl(Cases, HrlFile, {TcName, 0}), file:close(HrlFile), C = TcName ++ "_decl.c", {ok, CFile} = file:open(C, write), @@ -57,7 +56,7 @@ generate(TcName, Cases) -> file:close(CFile). generate_hrl([Case|Rest], File, {Name, Number}) -> - io:format(File, "-define(~s, {\"~s\", ~w}).~n", [Case, Name, Number]), + io:format(File, "-define(~s, {filename:join(proplists:get_value(data_dir,Config),\"~s\"), ~w}).~n", [Case, Name, Number]), generate_hrl(Rest, File, {Name, Number+1}); generate_hrl([], _, _) -> ok. diff --git a/lib/erl_interface/test/ei_tmo_SUITE.erl b/lib/erl_interface/test/ei_tmo_SUITE.erl index 7ff8c08280..cc22cb7440 100644 --- a/lib/erl_interface/test/ei_tmo_SUITE.erl +++ b/lib/erl_interface/test/ei_tmo_SUITE.erl @@ -88,12 +88,12 @@ ei_recv_tmo(doc) -> ei_recv_tmo(suite) -> []; ei_recv_tmo(Config) when is_list(Config) -> - ?line do_one_recv(c_node_recv_tmo_1), - ?line do_one_recv_failure(c_node_recv_tmo_2), + ?line do_one_recv(Config,c_node_recv_tmo_1), + ?line do_one_recv_failure(Config,c_node_recv_tmo_2), ok. -do_one_recv(CNode) -> +do_one_recv(Config,CNode) -> ?line {_,Host} = split(node()), ?line P1 = runner:start(?recv_tmo), ?line runner:send_term(P1,{CNode, @@ -107,7 +107,7 @@ do_one_recv(CNode) -> ?line {term, Term1} = runner:get_term(P1, 10000), ?line runner:recv_eot(P1). -do_one_recv_failure(CNode) -> +do_one_recv_failure(Config,CNode) -> ?line P1 = runner:start(?recv_tmo), ?line runner:send_term(P1,{CNode, erlang:get_cookie(), @@ -128,14 +128,14 @@ ei_send_tmo(Config) when is_list(Config) -> %dbg:p(self()), VxSim = ?config(vxsim, Config), ?line register(ei_send_tmo_1,self()), - ?line do_one_send(self(),c_node_send_tmo_1), - ?line do_one_send(ei_send_tmo_1,c_node_send_tmo_2), - ?line do_one_send_failure(self(),cccc1,c_nod_send_tmo_3,VxSim), - ?line do_one_send_failure(ei_send_tmo_1,cccc2,c_nod_send_tmo_4,VxSim), + ?line do_one_send(Config,self(),c_node_send_tmo_1), + ?line do_one_send(Config,ei_send_tmo_1,c_node_send_tmo_2), + ?line do_one_send_failure(Config,self(),cccc1,c_nod_send_tmo_3,VxSim), + ?line do_one_send_failure(Config,ei_send_tmo_1,cccc2,c_nod_send_tmo_4,VxSim), ok. -do_one_send(From,CNode) -> +do_one_send(Config,From,CNode) -> ?line {_,Host} = split(node()), ?line P1 = runner:start(?send_tmo), ?line runner:send_term(P1,{CNode, @@ -155,7 +155,7 @@ do_one_send(From,CNode) -> ?line {term, 0} = runner:get_term(P1, 10000), ?line runner:recv_eot(P1). -do_one_send_failure(From,FakeName,CName,VxSim) -> +do_one_send_failure(Config,From,FakeName,CName,VxSim) -> ?line {_,Host} = split(node()), ?line OurName = join(FakeName,Host), ?line Node = join(CName,Host), diff --git a/lib/erl_interface/test/erl_match_SUITE.erl b/lib/erl_interface/test/erl_match_SUITE.erl index e019fecca8..edbb17a8c1 100644 --- a/lib/erl_interface/test/erl_match_SUITE.erl +++ b/lib/erl_interface/test/erl_match_SUITE.erl @@ -29,7 +29,7 @@ bind/1, integers/1, floats/1, binaries/1, strings/1]). %% For interactive running of matcher. --export([start_matcher/0, erl_match/3]). +-export([start_matcher/1, erl_match/3]). %% This test suite tests the erl_match() function. @@ -57,7 +57,7 @@ end_per_group(_GroupName, Config) -> atoms(suite) -> []; atoms(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line eq(P, '', ''), ?line eq(P, a, a), @@ -74,7 +74,7 @@ atoms(Config) when is_list(Config) -> lists(suite) -> []; lists(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line eq(P, [], []), ?line ne(P, [], [a]), @@ -101,7 +101,7 @@ lists(Config) when is_list(Config) -> tuples(suite) -> []; tuples(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line ne(P, {}, {a, b}), ?line ne(P, {a, b}, {}), @@ -129,7 +129,7 @@ tuples(Config) when is_list(Config) -> references(suite) -> []; references(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line Ref1 = make_ref(), ?line Ref2 = make_ref(), @@ -144,7 +144,7 @@ references(Config) when is_list(Config) -> pids(suite) -> []; pids(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line Pid1 = c:pid(0,1,2), ?line Pid2 = c:pid(0,1,3), @@ -163,8 +163,8 @@ ports(Config) when is_list(Config) -> vxworks -> {skipped,"not on vxworks, pucko"}; _ -> - ?line P = start_matcher(), - ?line P2 = start_matcher(), + ?line P = start_matcher(Config), + ?line P2 = start_matcher(Config), ?line eq(P, P, P), ?line ne(P, P, P2), @@ -176,7 +176,7 @@ ports(Config) when is_list(Config) -> integers(suite) -> []; integers(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line I1 = 123, ?line I2 = 12345, ?line I3 = -123, @@ -195,7 +195,7 @@ integers(Config) when is_list(Config) -> floats(suite) -> []; floats(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line F1 = 3.1414, ?line F2 = 3.1415, ?line F3 = 3.1416, @@ -218,7 +218,7 @@ floats(Config) when is_list(Config) -> binaries(suite) -> []; binaries(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line Bin1 = term_to_binary({kalle, 146015, {kungsgatan, 23}}), ?line Bin2 = term_to_binary(sune), ?line Bin3 = list_to_binary("sune"), @@ -237,7 +237,7 @@ binaries(Config) when is_list(Config) -> strings(suite) -> []; strings(Config) when is_list(Config) -> - ?line P = start_matcher(), + ?line P = start_matcher(Config), ?line S1 = "string", ?line S2 = "streng", @@ -254,7 +254,7 @@ strings(Config) when is_list(Config) -> bind(suite) -> []; bind(Config) when is_list(Config) -> - ?line P = start_bind(), + ?line P = start_bind(Config), ?line S = "[X,Y,Z]", ?line L1 = [301,302,302], ?line L2 = [65,66,67], @@ -265,7 +265,7 @@ bind(Config) when is_list(Config) -> ?line runner:finish(P), ok. -start_bind() -> +start_bind(Config) -> runner:start(?erl_match_bind). bind_ok(Port, Bind, Term) -> @@ -287,7 +287,7 @@ erl_bind(Port, Pattern, Term) -> -start_matcher() -> +start_matcher(Config) -> runner:start(?erl_match_server). eq(Port, Pattern, Term) -> diff --git a/lib/erl_interface/vsn.mk b/lib/erl_interface/vsn.mk index 2c402bba6c..0e249d2483 100644 --- a/lib/erl_interface/vsn.mk +++ b/lib/erl_interface/vsn.mk @@ -1 +1 @@ -EI_VSN = 3.7.6 +EI_VSN = 3.7.7 diff --git a/lib/et/doc/src/Makefile b/lib/et/doc/src/Makefile index 6bb8164e91..facd335449 100644 --- a/lib/et/doc/src/Makefile +++ b/lib/et/doc/src/Makefile @@ -97,14 +97,14 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/et/doc/src/et_intro.xml b/lib/et/doc/src/et_intro.xml index 0c5fb14d55..60da289721 100644 --- a/lib/et/doc/src/et_intro.xml +++ b/lib/et/doc/src/et_intro.xml @@ -40,8 +40,8 @@ ports or files.</p> <section> - <title>Scope and Purpose</title>' - + <title>Scope and Purpose</title> + <p>This manual describes the <c>Event Tracer (ET)</c> application, as a component of the Erlang/Open Telecom Platform development environment. It is assumed that the reader is familiar with the diff --git a/lib/et/doc/src/notes.xml b/lib/et/doc/src/notes.xml index acc6120fcd..42790e0cdf 100644 --- a/lib/et/doc/src/notes.xml +++ b/lib/et/doc/src/notes.xml @@ -36,6 +36,26 @@ one section in this document. The title of each section is the version number of <c>Event Tracer (ET)</c>.</p> +<section><title>ET 1.4.4.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>The GS applications is now deprecated and will be + removed in the R16 release. The following GS-based + applications have been superseded by the Observer + application and will removed in R16: Appmon, Pman, + Tv.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9907</p> + </item> + </list> + </section> + +</section> + <section><title>ET 1.4.4</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/et/examples/Makefile b/lib/et/examples/Makefile index 67a6536fdf..190d5e2d6a 100644 --- a/lib/et/examples/Makefile +++ b/lib/et/examples/Makefile @@ -73,8 +73,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/et/src/Makefile b/lib/et/src/Makefile index bb6632ee91..7693571c5c 100644 --- a/lib/et/src/Makefile +++ b/lib/et/src/Makefile @@ -104,14 +104,14 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/et/src/et_gs_contents_viewer.erl b/lib/et/src/et_gs_contents_viewer.erl index 5331c7697a..2d414f10b4 100644 --- a/lib/et/src/et_gs_contents_viewer.erl +++ b/lib/et/src/et_gs_contents_viewer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 diff --git a/lib/et/src/et_gs_viewer.erl b/lib/et/src/et_gs_viewer.erl index 4e6f0e3f64..ce8634d09f 100644 --- a/lib/et/src/et_gs_viewer.erl +++ b/lib/et/src/et_gs_viewer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 diff --git a/lib/et/src/et_wx_contents_viewer.erl b/lib/et/src/et_wx_contents_viewer.erl index 86f46f25d0..b559da8807 100644 --- a/lib/et/src/et_wx_contents_viewer.erl +++ b/lib/et/src/et_wx_contents_viewer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2011. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -245,12 +245,7 @@ handle_event(#wx{id = Id, Data when is_record(Data, filter) -> F = Data, ChildState= S#state{active_filter = F#filter.name}, - case wx_object:start_link(?MODULE, [ChildState], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid); - _ -> - ignore - end; + wx_object:start_link(?MODULE, [ChildState], []); {hide, Actors} -> send_viewer_event(S, {delete_actors, Actors}); {show, Actors} -> @@ -356,12 +351,7 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) -> case lists:keysearch(?DEFAULT_FILTER_NAME, #filter.name, S#state.filters) of {value, F} when is_record(F, filter) -> ChildState= S#state{active_filter = F#filter.name}, - case wx_object:start_link(?MODULE, [ChildState], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid); - _ -> - ignore - end; + wx_object:start_link(?MODULE, [ChildState], []); false -> ignore end, @@ -370,12 +360,7 @@ handle_event(#wx{event = #wxKey{rawCode = KeyCode}}, S) -> case catch lists:nth(Int-$0, S#state.filters) of F when is_record(F, filter) -> ChildState= S#state{active_filter = F#filter.name}, - case wx_object:start_link(?MODULE, [ChildState], []) of - {ok, Pid} when S#state.parent_pid =/= self() -> - unlink(Pid); - _ -> - ignore - end; + wx_object:start_link(?MODULE, [ChildState], []); {'EXIT', _} -> ignore end, diff --git a/lib/et/test/Makefile b/lib/et/test/Makefile index e10a2a1587..7b4db7b0c3 100644 --- a/lib/et/test/Makefile +++ b/lib/et/test/Makefile @@ -70,12 +70,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) et.spec et.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_SCRIPT) ett $(RELSYSDIR) - $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR) -# chmod -R u+w $(RELSYSDIR) -# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) et.spec et.cover $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_SCRIPT) ett "$(RELSYSDIR)" + $(INSTALL_DATA) $(INSTALL_PROGS) "$(RELSYSDIR)" +# chmod -R u+w "$(RELSYSDIR)" +# @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/et/vsn.mk b/lib/et/vsn.mk index 239a72ad73..090d43d52f 100644 --- a/lib/et/vsn.mk +++ b/lib/et/vsn.mk @@ -1 +1 @@ -ET_VSN = 1.4.4 +ET_VSN = 1.4.4.1 diff --git a/lib/eunit/doc/overview.edoc b/lib/eunit/doc/overview.edoc index ad449cb6fc..b4af31ae6a 100644 --- a/lib/eunit/doc/overview.edoc +++ b/lib/eunit/doc/overview.edoc @@ -723,8 +723,12 @@ A <em>simple test object</em> is one of the following: ```fun some_function/0''' ```fun some_module:some_function/0''' </li> - <li>A pair of atoms `{ModuleName, FunctionName}', referring to the - function `ModuleName:FunctionName/0'</li> + <li>A tuple `{test, ModuleName, FunctionName}', where `ModuleName' and + `FunctionName' are atoms, referring to the function + `ModuleName:FunctionName/0'</li> + <li>(Obsolete) A pair of atoms `{ModuleName, FunctionName}', equivalent to + `{test, ModuleName, FunctionName}' if nothing else matches first. This + might be removed in a future version.</li> <li>A pair `{LineNumber, SimpleTest}', where `LineNumber' is a nonnegative integer and `SimpleTest' is another simple test object. `LineNumber' should indicate the source line of the test. diff --git a/lib/eunit/doc/src/Makefile b/lib/eunit/doc/src/Makefile index 2cdc579275..cbcd4c1507 100644 --- a/lib/eunit/doc/src/Makefile +++ b/lib/eunit/doc/src/Makefile @@ -157,14 +157,14 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/eunit/examples/Makefile b/lib/eunit/examples/Makefile index 48ec2ebf2b..cfa0a7a645 100644 --- a/lib/eunit/examples/Makefile +++ b/lib/eunit/examples/Makefile @@ -49,8 +49,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLE_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/eunit/include/eunit.hrl b/lib/eunit/include/eunit.hrl index db68d8ae60..fba840c3bd 100644 --- a/lib/eunit/include/eunit.hrl +++ b/lib/eunit/include/eunit.hrl @@ -25,11 +25,12 @@ %% will become undefined. NODEBUG also implies NOASSERT, unless testing %% is enabled. %% -%% If including this file causes TEST to be defined, then NOASSERT will -%% be undefined, even if it was previously defined and even if NODEBUG -%% is defined. If both ASSERT and NOASSERT are defined before the file -%% is included, then ASSERT takes precedence, and NOASSERT will become -%% undefined regardless of TEST. +%% Defining NOASSERT disables asserts. NODEBUG implies NOASSERT unless +%% testing is enabled. If including this file causes TEST to be defined, +%% then NOASSERT will be undefined, even if it was previously defined and +%% even if NODEBUG is defined. If both ASSERT and NOASSERT are defined +%% before the file is included, then ASSERT takes precedence, and NOASSERT +%% will become undefined regardless of TEST. %% %% After including this file, EUNIT will be defined if and only if TEST %% is defined. @@ -127,9 +128,9 @@ current_function)))). -endif. --ifdef(NOASSERT). %% The plain assert macro should be defined to do nothing if this file %% is included when debugging/testing is turned off. +-ifdef(NOASSERT). -ifndef(assert). -define(assert(BoolExpr),ok). -endif. diff --git a/lib/eunit/src/Makefile b/lib/eunit/src/Makefile index bec2fdbe0b..0a2e71cf7b 100644 --- a/lib/eunit/src/Makefile +++ b/lib/eunit/src/Makefile @@ -28,6 +28,9 @@ ERL_COMPILE_FLAGS += -pa $(EBIN) -I$(INCLUDE) +warn_unused_vars +nowarn_shadow_v PARSE_TRANSFORM = eunit_autoexport.erl +BEHAVIOUR_SOURCES= \ + eunit_listener.erl + SOURCES= \ eunit_striptests.erl \ eunit.erl \ @@ -40,13 +43,16 @@ SOURCES= \ eunit_data.erl \ eunit_tty.erl \ eunit_surefire.erl \ - eunit_listener.erl INCLUDE_FILES = eunit.hrl PARSE_TRANSFORM_BIN = $(PARSE_TRANSFORM:%.erl=$(EBIN)/%.$(EMULATOR)) -OBJECTS=$(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET) +TARGET_FILES= $(SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) + +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_SOURCES:%.erl=$(EBIN)/%.$(EMULATOR)) + +OBJECTS= $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) INCLUDE_DELIVERABLES = $(INCLUDE_FILES:%=$(INCLUDE)/%) @@ -62,6 +68,8 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) # Targets # ---------------------------------------------------- +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) + debug opt: $(PARSE_TRANSFORM_BIN) $(OBJECTS) docs: @@ -107,12 +115,12 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(PARSE_TRANSFORM_BIN) $(OBJECTS) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(PARSE_TRANSFORM) $(SOURCES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(INCLUDE_DELIVERABLES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(PARSE_TRANSFORM_BIN) $(OBJECTS) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(PARSE_TRANSFORM) $(SOURCES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(INCLUDE_DELIVERABLES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/eunit/src/eunit.app.src b/lib/eunit/src/eunit.app.src index 5e16dfa2ce..431abac98b 100644 --- a/lib/eunit/src/eunit.app.src +++ b/lib/eunit/src/eunit.app.src @@ -14,7 +14,6 @@ eunit_striptests, eunit_surefire, eunit_test, - eunit_tests, eunit_tty]}, {registered,[]}, {applications, [kernel,stdlib]}, diff --git a/lib/eunit/src/eunit.erl b/lib/eunit/src/eunit.erl index 95857e83c8..51846d73b3 100644 --- a/lib/eunit/src/eunit.erl +++ b/lib/eunit/src/eunit.erl @@ -139,7 +139,7 @@ test(Tests, Options) -> %% @private %% @doc See {@link test/2}. test(Server, Tests, Options) -> - Listeners = [eunit_tty:start(Options) | listeners(Options)], + Listeners = listeners(Options), Serial = eunit_serial:start(Listeners), case eunit_server:start_test(Server, Serial, Tests, Options) of {ok, Reference} -> test_run(Reference, Listeners); @@ -194,7 +194,10 @@ submit(Server, T, Options) -> eunit_server:start_test(Server, Dummy, T, Options). listeners(Options) -> - Ps = start_listeners(proplists:get_all_values(report, Options)), + %% note that eunit_tty must always run, because it sends the final + %% {result,...} message that the test_run() function is waiting for + Ls = [{eunit_tty, Options} | proplists:get_all_values(report, Options)], + Ps = start_listeners(Ls), %% the event_log option is for debugging, to view the raw events case proplists:get_value(event_log, Options) of undefined -> diff --git a/lib/eunit/src/eunit_data.erl b/lib/eunit/src/eunit_data.erl index 392d378a0e..0350f9bf6e 100644 --- a/lib/eunit/src/eunit_data.erl +++ b/lib/eunit/src/eunit_data.erl @@ -83,6 +83,7 @@ %% SimpleTest = TestFunction | {Line::integer(), SimpleTest} %% %% TestFunction = () -> any() +%% | {test, M::moduleName(), F::functionName()} %% | {M::moduleName(), F::functionName()}. %% %% AbstractTestFunction = (X::any()) -> any() @@ -95,7 +96,6 @@ %% %% @type moduleName() = atom() %% @type functionName() = atom() -%% @type arity() = integer() %% @type appName() = atom() %% @type fileName() = string() @@ -156,8 +156,9 @@ iter_prev(#iter{prev = [T | Ts]} = I) -> %% @spec (tests()) -> none | {testItem(), tests()} %% @type testItem() = #test{} | #group{} %% @throws {bad_test, term()} -%% | {generator_failed, exception()} -%% | {no_such_function, eunit_lib:mfa()} +%% | {generator_failed, {{M::atom(),F::atom(),A::integer()}, +%% exception()}} +%% | {no_such_function, mfa()} %% | {module_not_found, moduleName()} %% | {application_not_found, appName()} %% | {file_read_error, {Reason::atom(), Message::string(), @@ -221,17 +222,27 @@ parse({foreachx, P, S1, C1, Ps} = T) [] -> {data, []} end; -parse({generator, F} = T) when is_function(F) -> +parse({generator, F}) when is_function(F) -> + {module, M} = erlang:fun_info(F, module), + {name, N} = erlang:fun_info(F, name), + {arity, A} = erlang:fun_info(F, arity), + parse({generator, F, {M,N,A}}); +parse({generator, F, {M,N,A}} = T) + when is_function(F), is_atom(M), is_atom(N), is_integer(A) -> check_arity(F, 0, T), %% use run_testfun/1 to handle wrapper exceptions case eunit_test:run_testfun(F) of {ok, T1} -> + case eunit_lib:is_not_test(T1) of + true -> throw({bad_generator, {{M,N,A}, T1}}); + false -> ok + end, {data, T1}; {error, {Class, Reason, Trace}} -> - throw({generator_failed, {Class, Reason, Trace}}) + throw({generator_failed, {{M,N,A}, {Class, Reason, Trace}}}) end; parse({generator, M, F}) when is_atom(M), is_atom(F) -> - parse({generator, eunit_test:function_wrapper(M, F)}); + parse({generator, eunit_test:mf_wrapper(M, F), {M,F,0}}); parse({inorder, T}) -> group(#group{tests = T, order = inorder}); parse({inparallel, T}) -> @@ -421,8 +432,11 @@ parse_simple(F) -> parse_function(F) when is_function(F) -> check_arity(F, 0, F), #test{f = F, location = eunit_lib:fun_parent(F)}; -parse_function({M,F}) when is_atom(M), is_atom(F) -> - #test{f = eunit_test:function_wrapper(M, F), location = {M, F, 0}}; +parse_function({test, M, F}) when is_atom(M), is_atom(F) -> + #test{f = eunit_test:mf_wrapper(M, F), location = {M, F, 0}}; +parse_function({M, F}) when is_atom(M), is_atom(F) -> + %% {M,F} is now considered obsolete; use {test,M,F} instead + parse_function({test, M, F}); parse_function(F) -> bad_test(F). @@ -580,7 +594,7 @@ testfuns(Es, M, TestSuffix, GeneratorSuffix) -> N = atom_to_list(F), case lists:suffix(TestSuffix, N) of true -> - [{M,F} | Fs]; + [{test, M, F} | Fs]; false -> case lists:suffix(GeneratorSuffix, N) of true -> @@ -723,6 +737,7 @@ data_test_() -> Tests = [T,T,T], [?_assertMatch(ok, eunit:test(T)), ?_assertMatch(error, eunit:test(Fail)), + ?_assertMatch(ok, eunit:test({test, ?MODULE, trivial_test})), ?_assertMatch(ok, eunit:test({generator, fun () -> Tests end})), ?_assertMatch(ok, eunit:test({generator, fun generator/0})), ?_assertMatch(ok, eunit:test({generator, ?MODULE, generator_exported_})), @@ -740,6 +755,12 @@ data_test_() -> %%?_test({foreach, Setup, [T, T, T]}) ]. +trivial_test() -> + ok. + +trivial_generator_test_() -> + [?_test(ok)]. + lazy_test_() -> {spawn, [?_test(undefined = put(count, 0)), lazy_gen(7), diff --git a/lib/eunit/src/eunit_lib.erl b/lib/eunit/src/eunit_lib.erl index 1c41e229c5..ea9e944d7e 100644 --- a/lib/eunit/src/eunit_lib.erl +++ b/lib/eunit/src/eunit_lib.erl @@ -30,7 +30,8 @@ -export([dlist_next/1, uniq/1, fun_parent/1, is_string/1, command/1, command/2, command/3, trie_new/0, trie_store/2, trie_match/2, split_node/1, consult_file/1, list_dir/1, format_exit_term/1, - format_exception/1, format_exception/2, format_error/1]). + format_exception/1, format_exception/2, format_error/1, + is_not_test/1]). %% Type definitions for describing exceptions @@ -39,13 +40,10 @@ %% %% @type exceptionClass() = error | exit | throw %% -%% @type stackTrace() = [{moduleName(), functionName(), -%% arity() | argList()}] +%% @type stackTrace() = [{moduleName(), functionName(), arity() | argList()}] %% %% @type moduleName() = atom() %% @type functionName() = atom() -%% @type arity() = integer() -%% @type mfa() = {moduleName(), functionName(), arity()} %% @type argList() = [term()] %% @type fileName() = string() @@ -59,8 +57,9 @@ format_exception({Class,Term,Trace}, Depth) when is_atom(Class), is_list(Trace) -> case is_stacktrace(Trace) of true -> - io_lib:format("~w:~P\n~s", - [Class, Term, Depth, format_stacktrace(Trace)]); + io_lib:format("~s**~w:~s", + [format_stacktrace(Trace), Class, + format_term(Term, Depth)]); false -> format_term(Term, Depth) end; @@ -86,6 +85,12 @@ analyze_exit_term(Term) -> is_stacktrace([]) -> true; +is_stacktrace([{M,F,A,L}|Fs]) + when is_atom(M), is_atom(F), is_integer(A), is_list(L) -> + is_stacktrace(Fs); +is_stacktrace([{M,F,As,L}|Fs]) + when is_atom(M), is_atom(F), is_list(As), is_list(L) -> + is_stacktrace(Fs); is_stacktrace([{M,F,A}|Fs]) when is_atom(M), is_atom(F), is_integer(A) -> is_stacktrace(Fs); is_stacktrace([{M,F,As}|Fs]) when is_atom(M), is_atom(F), is_list(As) -> @@ -96,10 +101,11 @@ is_stacktrace(_) -> format_stacktrace(Trace) -> format_stacktrace(Trace, "in function", "in call from"). -format_stacktrace([{M,F,A}|Fs], Pre, Pre1) when is_integer(A) -> - [io_lib:fwrite(" ~s ~w:~w/~w\n", [Pre, M, F, A]) +format_stacktrace([{M,F,A,L}|Fs], Pre, Pre1) when is_integer(A) -> + [io_lib:fwrite("~s ~w:~w/~w~s\n", + [Pre, M, F, A, format_stacktrace_location(L)]) | format_stacktrace(Fs, Pre1, Pre1)]; -format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) -> +format_stacktrace([{M,F,As,L}|Fs], Pre, Pre1) when is_list(As) -> A = length(As), C = case is_op(M,F,A) of true when A =:= 1 -> @@ -112,12 +118,23 @@ format_stacktrace([{M,F,As}|Fs], Pre, Pre1) when is_list(As) -> false -> io_lib:fwrite("~w(~s)", [F,format_arglist(As)]) end, - [io_lib:fwrite(" ~s ~w:~w/~w\n called as ~s\n", - [Pre,M,F,A,C]) + [io_lib:fwrite("~s ~w:~w/~w~s\n called as ~s\n", + [Pre,M,F,A,format_stacktrace_location(L),C]) | format_stacktrace(Fs,Pre1,Pre1)]; +format_stacktrace([{M,F,As}|Fs], Pre, Pre1) -> + format_stacktrace([{M,F,As,[]}|Fs], Pre, Pre1); format_stacktrace([],_Pre,_Pre1) -> "". +format_stacktrace_location(Location) -> + File = proplists:get_value(file, Location), + Line = proplists:get_value(line, Location), + if File =/= undefined, Line =/= undefined -> + io_lib:format(" (~s, line ~w)", [File, Line]); + true -> + "" + end. + format_arg(A) -> io_lib:format("~P",[A,15]). @@ -139,9 +156,13 @@ is_op(_M, _F, _A) -> format_error({bad_test, Term}) -> error_msg("bad test descriptor", "~P", [Term, 15]); -format_error({generator_failed, Exception}) -> - error_msg("test generator failed", "~s", - [format_exception(Exception)]); +format_error({bad_generator, {{M,F,A}, Term}}) -> + error_msg(io_lib:format("result from generator ~w:~w/~w is not a test", + [M,F,A]), + "~P", [Term, 15]); +format_error({generator_failed, {{M,F,A}, Exception}}) -> + error_msg(io_lib:format("test generator ~w:~w/~w failed",[M,F,A]), + "~s", [format_exception(Exception)]); format_error({no_such_function, {M,F,A}}) when is_atom(M), is_atom(F), is_integer(A) -> error_msg(io_lib:format("no such function: ~w:~w/~w", [M,F,A]), @@ -158,14 +179,55 @@ format_error({setup_failed, Exception}) -> format_error({cleanup_failed, Exception}) -> error_msg("context cleanup failed", "~s", [format_exception(Exception)]); +format_error({{bad_instantiator, {{M,F,A}, Term}}, _DummyException}) -> + error_msg(io_lib:format("result from instantiator ~w:~w/~w is not a test", + [M,F,A]), + "~P", [Term, 15]); format_error({instantiation_failed, Exception}) -> error_msg("instantiation of subtests failed", "~s", [format_exception(Exception)]). error_msg(Title, Fmt, Args) -> - Msg = io_lib:format("::"++Fmt, Args), % gets indentation right + Msg = io_lib:format("**"++Fmt, Args), % gets indentation right io_lib:fwrite("*** ~s ***\n~s\n\n", [Title, Msg]). +-ifdef(TEST). +format_exception_test_() -> + [?_assertMatch( + "\nymmud:rorre"++_, + lists:reverse(lists:flatten( + format_exception(try erlang:error(dummy) + catch C:R -> {C, R, erlang:get_stacktrace()} + end)))), + ?_assertMatch( + "\nymmud:rorre"++_, + lists:reverse(lists:flatten( + format_exception(try erlang:error(dummy, [a]) + catch C:R -> {C, R, erlang:get_stacktrace()} + end))))]. +-endif. + +%% --------------------------------------------------------------------- +%% detect common return values that are definitely not tests + +is_not_test(T) -> + case T of + ok -> true; + error -> true; + true -> true; + false -> true; + undefined -> true; + {ok, _} -> true; + {error, _} -> true; + {'EXIT', _} -> true; + N when is_number(N) -> true; + [N|_] when is_number(N) -> true; + X when is_binary(X) -> true; + X when is_pid(X) -> true; + X when is_port(X) -> true; + X when is_reference(X) -> true; + _ -> false + end. %% --------------------------------------------------------------------- %% Deep list iterator; accepts improper lists/sublists, and also accepts diff --git a/lib/eunit/src/eunit_surefire.erl b/lib/eunit/src/eunit_surefire.erl index 2a6cbca14d..46b8c8b503 100644 --- a/lib/eunit/src/eunit_surefire.erl +++ b/lib/eunit/src/eunit_surefire.erl @@ -156,9 +156,33 @@ handle_end(test, Data, St) -> St#state{testsuites=store_suite(NewTestSuite, TestSuites)}. %% Cancel group does not give information on the individual cancelled test case -%% We ignore this event -handle_cancel(group, _Data, St) -> - St; +%% We ignore this event... +handle_cancel(group, Data, St) -> + %% ...except when it tells us that a fixture setup or cleanup failed. + case proplists:get_value(reason, Data) of + {abort, {SomethingFailed, Exception}} + when SomethingFailed =:= setup_failed; + SomethingFailed =:= cleanup_failed -> + [GroupId|_] = proplists:get_value(id, Data), + TestSuites = St#state.testsuites, + TestSuite = lookup_suite_by_group_id(GroupId, TestSuites), + + %% We don't have any proper name. Let's give all the + %% clues that we have. + Name = case SomethingFailed of + setup_failed -> "fixture setup "; + cleanup_failed -> "fixture cleanup " + end + ++ io_lib:format("~p", [proplists:get_value(id, Data)]), + Desc = format_desc(proplists:get_value(desc, Data)), + TestCase = #testcase{ + name = Name, description = Desc, + time = 0, output = <<>>}, + NewTestSuite = add_testcase_to_testsuite({error, Exception}, TestCase, TestSuite), + St#state{testsuites=store_suite(NewTestSuite, TestSuites)}; + _ -> + St + end; handle_cancel(test, Data, St) -> %% Retrieve existing test suite: [GroupId|_] = proplists:get_value(id, Data), @@ -232,7 +256,7 @@ write_reports(TestSuites, XmlDir) -> 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 + case file:open(Filename, [write,{encoding,utf8}]) of {ok, FileDescriptor} -> try write_report_to(TestSuite, FileDescriptor) diff --git a/lib/eunit/src/eunit_test.erl b/lib/eunit/src/eunit_test.erl index bca49ae626..9cf40a738d 100644 --- a/lib/eunit/src/eunit_test.erl +++ b/lib/eunit/src/eunit_test.erl @@ -21,8 +21,7 @@ -module(eunit_test). --export([run_testfun/1, function_wrapper/2, enter_context/4, - multi_setup/1]). +-export([run_testfun/1, mf_wrapper/2, enter_context/4, multi_setup/1]). -include("eunit.hrl"). @@ -43,8 +42,12 @@ get_stacktrace(Ts) -> prune_trace([{eunit_data, _, _} | Rest], Tail) -> prune_trace(Rest, Tail); +prune_trace([{eunit_data, _, _, _} | Rest], Tail) -> + prune_trace(Rest, Tail); prune_trace([{?MODULE, _, _} | _Rest], Tail) -> Tail; +prune_trace([{?MODULE, _, _, _} | _Rest], Tail) -> + Tail; prune_trace([T | Ts], Tail) -> [T | prune_trace(Ts, Tail)]; prune_trace([], Tail) -> @@ -258,7 +261,7 @@ macro_test_() -> %% @type wrapperError() = {no_such_function, mfa()} %% | {module_not_found, moduleName()} -function_wrapper(M, F) -> +mf_wrapper(M, F) -> fun () -> try M:F() catch @@ -289,12 +292,12 @@ fail(Term) -> wrapper_test_() -> {"error handling in function wrapper", [?_assertException(throw, {module_not_found, eunit_nonexisting}, - run_testfun(function_wrapper(eunit_nonexisting,test))), + run_testfun(mf_wrapper(eunit_nonexisting,test))), ?_assertException(throw, {no_such_function, {?MODULE,nonexisting_test,0}}, - run_testfun(function_wrapper(?MODULE,nonexisting_test))), + run_testfun(mf_wrapper(?MODULE,nonexisting_test))), ?_test({error, {error, undef, _T}} - = run_testfun(function_wrapper(?MODULE,wrapper_test_exported_))) + = run_testfun(mf_wrapper(?MODULE,wrapper_test_exported_))) ]}. %% this must be exported (done automatically by the autoexport transform) @@ -319,6 +322,17 @@ enter_context(Setup, Cleanup, Instantiate, Callback) -> R -> try Instantiate(R) of T -> + case eunit_lib:is_not_test(T) of + true -> + catch throw(error), % generate a stack trace + {module,M} = erlang:fun_info(Instantiate, module), + {name,N} = erlang:fun_info(Instantiate, name), + {arity,A} = erlang:fun_info(Instantiate, arity), + context_error({bad_instantiator, {{M,N,A},T}}, + error, badarg); + false -> + ok + end, try Callback(T) %% call back to client code after %% Always run cleanup; client may be an idiot diff --git a/lib/eunit/src/eunit_tty.erl b/lib/eunit/src/eunit_tty.erl index e3e7b710b2..f21b2da3d3 100644 --- a/lib/eunit/src/eunit_tty.erl +++ b/lib/eunit/src/eunit_tty.erl @@ -44,6 +44,7 @@ start(Options) -> init(Options) -> St = #state{verbose = proplists:get_bool(verbose, Options)}, + put(no_tty, proplists:get_bool(no_tty, Options)), receive {start, _Reference} -> if St#state.verbose -> print_header(); @@ -59,30 +60,30 @@ terminate({ok, Data}, St) -> Cancel = proplists:get_value(cancel, Data, 0), if Fail =:= 0, Skip =:= 0, Cancel =:= 0 -> if Pass =:= 0 -> - io:fwrite(" There were no tests to run.\n"); + fwrite(" There were no tests to run.\n"); true -> if St#state.verbose -> print_bar(); true -> ok end, if Pass =:= 1 -> - io:fwrite(" Test passed.\n"); + fwrite(" Test passed.\n"); true -> - io:fwrite(" All ~w tests passed.\n", [Pass]) + fwrite(" All ~w tests passed.\n", [Pass]) end end, sync_end(ok); true -> print_bar(), - io:fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n", - [Fail, Skip, Pass]), + fwrite(" Failed: ~w. Skipped: ~w. Passed: ~w.\n", + [Fail, Skip, Pass]), if Cancel =/= 0 -> - io:fwrite("One or more tests were cancelled.\n"); + fwrite("One or more tests were cancelled.\n"); true -> ok end, sync_end(error) end; terminate({error, Reason}, _St) -> - io:fwrite("Internal error: ~P.\n", [Reason, 25]), + fwrite("Internal error: ~P.\n", [Reason, 25]), sync_end(error). sync_end(Result) -> @@ -93,10 +94,10 @@ sync_end(Result) -> end. print_header() -> - io:fwrite("======================== EUnit ========================\n"). + fwrite("======================== EUnit ========================\n"). print_bar() -> - io:fwrite("=======================================================\n"). + fwrite("=======================================================\n"). handle_begin(group, Data, St) -> @@ -170,18 +171,18 @@ handle_cancel(test, Data, St) -> indent(N) when is_integer(N), N >= 1 -> - io:put_chars(lists:duplicate(N * 2, $\s)); + fwrite(lists:duplicate(N * 2, $\s)); indent(_N) -> ok. print_group_start(I, Desc) -> indent(I), - io:fwrite("~s\n", [Desc]). + fwrite("~s\n", [Desc]). print_group_end(I, Time) -> if Time > 0 -> indent(I), - io:fwrite("[done in ~.3f s]\n", [Time/1000]); + fwrite("[done in ~.3f s]\n", [Time/1000]); true -> ok end. @@ -198,9 +199,9 @@ print_test_begin(I, Data) -> end, case proplists:get_value(source, Data) of {Module, Name, _Arity} -> - io:fwrite("~s:~s ~s~s...", [Module, L, Name, D]); + fwrite("~s:~s ~s~s...", [Module, L, Name, D]); _ -> - io:fwrite("~s~s...", [L, D]) + fwrite("~s~s...", [L, D]) end. print_test_end(Data) -> @@ -208,36 +209,35 @@ print_test_end(Data) -> T = if Time > 0 -> io_lib:fwrite("[~.3f s] ", [Time/1000]); true -> "" end, - io:fwrite("~sok\n", [T]). + fwrite("~sok\n", [T]). print_test_error({error, Exception}, Data) -> Output = proplists:get_value(output, Data), - io:fwrite("*failed*\n::~s", - [eunit_lib:format_exception(Exception)]), + fwrite("*failed*\n~s", [eunit_lib:format_exception(Exception)]), case Output of <<>> -> - io:put_chars("\n\n"); + fwrite("\n\n"); <<Text:800/binary, _:1/binary, _/binary>> -> - io:fwrite(" output:<<\"~s\">>...\n\n", [Text]); + fwrite(" output:<<\"~s\">>...\n\n", [Text]); _ -> - io:fwrite(" output:<<\"~s\">>\n\n", [Output]) + fwrite(" output:<<\"~s\">>\n\n", [Output]) end; print_test_error({skipped, Reason}, _) -> - io:fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]). + fwrite("*did not run*\n::~s\n", [format_skipped(Reason)]). format_skipped({module_not_found, M}) -> - io_lib:format("missing module: ~w", [M]); + io_lib:fwrite("missing module: ~w", [M]); format_skipped({no_such_function, {M,F,A}}) -> - io_lib:format("no such function: ~w:~w/~w", [M,F,A]). + io_lib:fwrite("no such function: ~w:~w/~w", [M,F,A]). print_test_cancel(Reason) -> - io:fwrite(format_cancel(Reason)). + fwrite(format_cancel(Reason)). print_group_cancel(_I, {blame, _}) -> ok; print_group_cancel(I, Reason) -> indent(I), - io:fwrite(format_cancel(Reason)). + fwrite(format_cancel(Reason)). format_cancel(undefined) -> "*skipped*\n"; @@ -253,3 +253,12 @@ format_cancel({exit, Reason}) -> [Reason, 15]); format_cancel({abort, Reason}) -> eunit_lib:format_error(Reason). + +fwrite(String) -> + fwrite(String, []). + +fwrite(String, Args) -> + case get(no_tty) of + false -> io:fwrite(String, Args); + true -> ok + end. diff --git a/lib/eunit/test/Makefile b/lib/eunit/test/Makefile index a2d276f619..1586dd8857 100644 --- a/lib/eunit/test/Makefile +++ b/lib/eunit/test/Makefile @@ -74,9 +74,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) eunit.spec $(EMAKEFILE) \ $(COVERFILE) $(ERL_FILES) \ - $(RELSYSDIR) + "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/eunit/vsn.mk b/lib/eunit/vsn.mk index 445c070e96..174d197117 100644 --- a/lib/eunit/vsn.mk +++ b/lib/eunit/vsn.mk @@ -1 +1 @@ -EUNIT_VSN = 2.2.2 +EUNIT_VSN = 2.2.3 diff --git a/lib/gs/contribs/bonk/Makefile b/lib/gs/contribs/bonk/Makefile index be096824dd..dc92149784 100644 --- a/lib/gs/contribs/bonk/Makefile +++ b/lib/gs/contribs/bonk/Makefile @@ -94,14 +94,14 @@ $(EBIN)/$(TOOLNAME).tool: $(TOOLNAME).tool include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/bonk/bitmaps - $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) $(RELSYSDIR)/bonk/bitmaps - $(INSTALL_DIR) $(RELSYSDIR)/bonk/sounds - $(INSTALL_DATA) $(SOUNDS) $(RELSYSDIR)/bonk/sounds - $(INSTALL_DIR) $(RELSYSDIR)/bonk - $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/bonk + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/bonk/bitmaps" + $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) "$(RELSYSDIR)/bonk/bitmaps" + $(INSTALL_DIR) "$(RELSYSDIR)/bonk/sounds" + $(INSTALL_DATA) $(SOUNDS) "$(RELSYSDIR)/bonk/sounds" + $(INSTALL_DIR) "$(RELSYSDIR)/bonk" + $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/bonk" release_docs_spec: diff --git a/lib/gs/contribs/bonk/bonk.erl b/lib/gs/contribs/bonk/bonk.erl index 7e68a4ddc5..3d1e8ce34b 100644 --- a/lib/gs/contribs/bonk/bonk.erl +++ b/lib/gs/contribs/bonk/bonk.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/contribs/cols/Makefile b/lib/gs/contribs/cols/Makefile index 75ca75ffc0..4ce4204c92 100644 --- a/lib/gs/contribs/cols/Makefile +++ b/lib/gs/contribs/cols/Makefile @@ -91,12 +91,12 @@ $(EBIN)/help.gif: help.gif include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/cols/bitmaps - $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) $(RELSYSDIR)/cols/bitmaps - $(INSTALL_DIR) $(RELSYSDIR)/cols - $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/cols + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/cols/bitmaps" + $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) "$(RELSYSDIR)/cols/bitmaps" + $(INSTALL_DIR) "$(RELSYSDIR)/cols" + $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/cols" release_docs_spec: diff --git a/lib/gs/contribs/cols/cols.erl b/lib/gs/contribs/cols/cols.erl index 9c44837f3e..d1a1b791c3 100644 --- a/lib/gs/contribs/cols/cols.erl +++ b/lib/gs/contribs/cols/cols.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/contribs/cols/highscore.erl b/lib/gs/contribs/cols/highscore.erl index 8f984aaa83..7f80debb07 100644 --- a/lib/gs/contribs/cols/highscore.erl +++ b/lib/gs/contribs/cols/highscore.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/contribs/mandel/Makefile b/lib/gs/contribs/mandel/Makefile index 61a7a612e0..451ece2c8f 100644 --- a/lib/gs/contribs/mandel/Makefile +++ b/lib/gs/contribs/mandel/Makefile @@ -90,11 +90,11 @@ $(EBIN)/help.gif: help.gif include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/mandel/bitmaps - $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) $(RELSYSDIR)/mandel/bitmaps - $(INSTALL_DIR) $(RELSYSDIR)/mandel - $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/mandel + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/mandel/bitmaps" + $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) "$(RELSYSDIR)/mandel/bitmaps" + $(INSTALL_DIR) "$(RELSYSDIR)/mandel" + $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/mandel" release_docs_spec: diff --git a/lib/gs/contribs/mandel/mandel.erl b/lib/gs/contribs/mandel/mandel.erl index 6293f867e2..8ecd649532 100644 --- a/lib/gs/contribs/mandel/mandel.erl +++ b/lib/gs/contribs/mandel/mandel.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/contribs/othello/Makefile b/lib/gs/contribs/othello/Makefile index b81b35fb55..5f09f2ed13 100644 --- a/lib/gs/contribs/othello/Makefile +++ b/lib/gs/contribs/othello/Makefile @@ -89,12 +89,12 @@ $(EBIN)/$(TOOLNAME).tool: $(TOOLNAME).tool include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/othello - $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/othello - $(INSTALL_DIR) $(RELSYSDIR)/othello/priv - $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) $(RELSYSDIR)/othello/priv + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/othello" + $(INSTALL_DATA) $(ERL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/othello" + $(INSTALL_DIR) "$(RELSYSDIR)/othello/priv" + $(INSTALL_DATA) $(BITMAPS) $(TOOLBOX_FILES) "$(RELSYSDIR)/othello/priv" release_docs_spec: diff --git a/lib/gs/contribs/othello/othello_board.erl b/lib/gs/contribs/othello/othello_board.erl index c52211a495..aaa4062ba8 100644 --- a/lib/gs/contribs/othello/othello_board.erl +++ b/lib/gs/contribs/othello/othello_board.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/doc/src/Makefile b/lib/gs/doc/src/Makefile index 192dd67a52..14204c52fc 100644 --- a/lib/gs/doc/src/Makefile +++ b/lib/gs/doc/src/Makefile @@ -144,13 +144,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html - (/bin/cp -rf $(HTMLDIR) $(RELSYSDIR)/doc) - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc") + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/gs/doc/src/gs.xml b/lib/gs/doc/src/gs.xml index c61e8c4938..417163e963 100644 --- a/lib/gs/doc/src/gs.xml +++ b/lib/gs/doc/src/gs.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2000</year> - <year>2010</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/gs/doc/src/notes.xml b/lib/gs/doc/src/notes.xml index cd63104346..d1b74ecfb1 100644 --- a/lib/gs/doc/src/notes.xml +++ b/lib/gs/doc/src/notes.xml @@ -30,7 +30,27 @@ </header> <p>This document describes the changes made to the GS application.</p> - <section><title>GS 1.5.15</title> + <section><title>GS 1.5.15.1</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>The GS applications is now deprecated and will be + removed in the R16 release. The following GS-based + applications have been superseded by the Observer + application and will removed in R16: Appmon, Pman, + Tv.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9907</p> + </item> + </list> + </section> + +</section> + +<section><title>GS 1.5.15</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/gs/examples/Makefile b/lib/gs/examples/Makefile index 10b166b207..d214cd2256 100644 --- a/lib/gs/examples/Makefile +++ b/lib/gs/examples/Makefile @@ -87,9 +87,9 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" release_docs_spec: diff --git a/lib/gs/examples/ball.erl b/lib/gs/examples/ball.erl index 03fac3b5fc..42e3522fc7 100644 --- a/lib/gs/examples/ball.erl +++ b/lib/gs/examples/ball.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/browser.erl b/lib/gs/examples/browser.erl index 0f8b3fc7e6..dd01e0299b 100644 --- a/lib/gs/examples/browser.erl +++ b/lib/gs/examples/browser.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/calc.erl b/lib/gs/examples/calc.erl index ca29ae1cb9..9ed8ed4dbd 100644 --- a/lib/gs/examples/calc.erl +++ b/lib/gs/examples/calc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/calc2.erl b/lib/gs/examples/calc2.erl index 83e1870b7a..51f9c31d28 100644 --- a/lib/gs/examples/calc2.erl +++ b/lib/gs/examples/calc2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/color_demo.erl b/lib/gs/examples/color_demo.erl index a2f4d0eb87..5ebfe36d75 100644 --- a/lib/gs/examples/color_demo.erl +++ b/lib/gs/examples/color_demo.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/color_demo2.erl b/lib/gs/examples/color_demo2.erl index 3b0b36221d..1c8db7a851 100644 --- a/lib/gs/examples/color_demo2.erl +++ b/lib/gs/examples/color_demo2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/distrib_draw.erl b/lib/gs/examples/distrib_draw.erl index 8f76bc6650..fc821c6df4 100644 --- a/lib/gs/examples/distrib_draw.erl +++ b/lib/gs/examples/distrib_draw.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/entry_demo.erl b/lib/gs/examples/entry_demo.erl index 4bb64e949e..4cdbe7e279 100644 --- a/lib/gs/examples/entry_demo.erl +++ b/lib/gs/examples/entry_demo.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/event_test.erl b/lib/gs/examples/event_test.erl index 8c0a109df4..3231df00e1 100644 --- a/lib/gs/examples/event_test.erl +++ b/lib/gs/examples/event_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/file_dialog.erl b/lib/gs/examples/file_dialog.erl index c1ef6ed8b1..37b19a322c 100644 --- a/lib/gs/examples/file_dialog.erl +++ b/lib/gs/examples/file_dialog.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/focus_demo.erl b/lib/gs/examples/focus_demo.erl index 71ca9c6ff5..990140d9bc 100644 --- a/lib/gs/examples/focus_demo.erl +++ b/lib/gs/examples/focus_demo.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/frac.erl b/lib/gs/examples/frac.erl index 46af83de8a..be77c8f5d4 100644 --- a/lib/gs/examples/frac.erl +++ b/lib/gs/examples/frac.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/line_demo.erl b/lib/gs/examples/line_demo.erl index ba88605118..32c2920878 100644 --- a/lib/gs/examples/line_demo.erl +++ b/lib/gs/examples/line_demo.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/man.erl b/lib/gs/examples/man.erl index bddd6930ab..093ce854f8 100644 --- a/lib/gs/examples/man.erl +++ b/lib/gs/examples/man.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/menu_demo.erl b/lib/gs/examples/menu_demo.erl index 6b2fc4113a..60e335c40b 100644 --- a/lib/gs/examples/menu_demo.erl +++ b/lib/gs/examples/menu_demo.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/examples/rubber.erl b/lib/gs/examples/rubber.erl index da4aa57391..a9fe403b12 100644 --- a/lib/gs/examples/rubber.erl +++ b/lib/gs/examples/rubber.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/Makefile b/lib/gs/src/Makefile index 43b530295b..a9904161a4 100644 --- a/lib/gs/src/Makefile +++ b/lib/gs/src/Makefile @@ -107,13 +107,13 @@ $(GSTK_GENERIC_TARGET): gstk_generic.hrl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(APP_SRC) $(ERL_FILES) $(HRL_FILES) $(GEN_HRL_FILES) \ - $(GSTK_GENERIC) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/priv/bitmap - $(INSTALL_DATA) $(IMAGES) $(RELSYSDIR)/priv/bitmap + $(GSTK_GENERIC) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bitmap" + $(INSTALL_DATA) $(IMAGES) "$(RELSYSDIR)/priv/bitmap" release_docs_spec: diff --git a/lib/gs/src/gs.erl b/lib/gs/src/gs.erl index 92dce12580..c25c75766e 100644 --- a/lib/gs/src/gs.erl +++ b/lib/gs/src/gs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gs_frontend.erl b/lib/gs/src/gs_frontend.erl index 73954baa8d..37f91cf37c 100644 --- a/lib/gs/src/gs_frontend.erl +++ b/lib/gs/src/gs_frontend.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gs_make.erl b/lib/gs/src/gs_make.erl index bf8a66001f..9acffcd62e 100644 --- a/lib/gs/src/gs_make.erl +++ b/lib/gs/src/gs_make.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gse.erl b/lib/gs/src/gse.erl index c62badcc27..b63841bc20 100644 --- a/lib/gs/src/gse.erl +++ b/lib/gs/src/gse.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/gs/src/gstk.erl b/lib/gs/src/gstk.erl index ee974a5f63..04ca9b4722 100644 --- a/lib/gs/src/gstk.erl +++ b/lib/gs/src/gstk.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_arc.erl b/lib/gs/src/gstk_arc.erl index 3c6fe54d36..1441bb0b32 100644 --- a/lib/gs/src/gstk_arc.erl +++ b/lib/gs/src/gstk_arc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_canvas.erl b/lib/gs/src/gstk_canvas.erl index 5d15529995..34e4be0667 100644 --- a/lib/gs/src/gstk_canvas.erl +++ b/lib/gs/src/gstk_canvas.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_editor.erl b/lib/gs/src/gstk_editor.erl index 2686997036..e918d93147 100644 --- a/lib/gs/src/gstk_editor.erl +++ b/lib/gs/src/gstk_editor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_entry.erl b/lib/gs/src/gstk_entry.erl index 432ccd5fde..b10b6460f2 100644 --- a/lib/gs/src/gstk_entry.erl +++ b/lib/gs/src/gstk_entry.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_generic.erl b/lib/gs/src/gstk_generic.erl index 50c3da8dc5..9b0efd07c1 100644 --- a/lib/gs/src/gstk_generic.erl +++ b/lib/gs/src/gstk_generic.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -323,7 +323,7 @@ handle_external_opt_call([Opt|Options],Gstkid,TkW,DB,ExtraArg,ExtRes,S,P,C) -> end. handle_external_read(Res) -> - case Res of + _ = case Res of {bad_result,{Objtype,Reason,Option}} -> {error,{Objtype,Reason,Option}}; _ -> ok diff --git a/lib/gs/src/gstk_grid.erl b/lib/gs/src/gstk_grid.erl index f703dad074..5f4f4a24f0 100644 --- a/lib/gs/src/gstk_grid.erl +++ b/lib/gs/src/gstk_grid.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_gridline.erl b/lib/gs/src/gstk_gridline.erl index 4585c7e043..914a7f1c6d 100644 --- a/lib/gs/src/gstk_gridline.erl +++ b/lib/gs/src/gstk_gridline.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_image.erl b/lib/gs/src/gstk_image.erl index 2b7a324e28..7dbcce3cf9 100644 --- a/lib/gs/src/gstk_image.erl +++ b/lib/gs/src/gstk_image.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_menu.erl b/lib/gs/src/gstk_menu.erl index e878669de6..09f3208299 100644 --- a/lib/gs/src/gstk_menu.erl +++ b/lib/gs/src/gstk_menu.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_menuitem.erl b/lib/gs/src/gstk_menuitem.erl index 8abf5d13aa..03aca75b80 100644 --- a/lib/gs/src/gstk_menuitem.erl +++ b/lib/gs/src/gstk_menuitem.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_port_handler.erl b/lib/gs/src/gstk_port_handler.erl index c6ca2c0f5b..3ecc6ac5d6 100644 --- a/lib/gs/src/gstk_port_handler.erl +++ b/lib/gs/src/gstk_port_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_rectangle.erl b/lib/gs/src/gstk_rectangle.erl index 14be16c990..e26db2ed15 100644 --- a/lib/gs/src/gstk_rectangle.erl +++ b/lib/gs/src/gstk_rectangle.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/gstk_window.erl b/lib/gs/src/gstk_window.erl index 4b4706eb88..d9ab16df37 100644 --- a/lib/gs/src/gstk_window.erl +++ b/lib/gs/src/gstk_window.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/tcl2erl.erl b/lib/gs/src/tcl2erl.erl index d159681c5c..d93f589656 100644 --- a/lib/gs/src/tcl2erl.erl +++ b/lib/gs/src/tcl2erl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/gs/src/tool_file_dialog.erl b/lib/gs/src/tool_file_dialog.erl index cfcfcd1bf6..f47b4b9e3f 100644 --- a/lib/gs/src/tool_file_dialog.erl +++ b/lib/gs/src/tool_file_dialog.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/gs/src/tool_utils.erl b/lib/gs/src/tool_utils.erl index 69919b18bd..c6b75466be 100644 --- a/lib/gs/src/tool_utils.erl +++ b/lib/gs/src/tool_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/gs/tcl/Makefile.in b/lib/gs/tcl/Makefile.in index dce34a4baa..0bccb60c7b 100644 --- a/lib/gs/tcl/Makefile.in +++ b/lib/gs/tcl/Makefile.in @@ -74,10 +74,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TCL_FILES) $(EXTRA_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TCL_FILES) $(EXTRA_FILES) "$(RELSYSDIR)/priv" ifneq ($(TCL_TAR),) - gzip -dc $(TCL_TAR) | (cd $(RELSYSDIR)/priv && tar -xf -) + gzip -dc $(TCL_TAR) | (cd "$(RELSYSDIR)/priv" && tar -xf -) endif release_docs_spec: diff --git a/lib/gs/vsn.mk b/lib/gs/vsn.mk index 41a2561809..48249cb3d0 100644 --- a/lib/gs/vsn.mk +++ b/lib/gs/vsn.mk @@ -1,2 +1,2 @@ -GS_VSN = 1.5.15 +GS_VSN = 1.5.15.1 diff --git a/lib/hipe/Makefile b/lib/hipe/Makefile index f2be75f0dd..2294f98158 100644 --- a/lib/hipe/Makefile +++ b/lib/hipe/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. 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 diff --git a/lib/hipe/amd64/Makefile b/lib/hipe/amd64/Makefile index 397cc0539e..50cd024d72 100644 --- a/lib/hipe/amd64/Makefile +++ b/lib/hipe/amd64/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2010. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. 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 @@ -105,8 +105,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/arm/Makefile b/lib/hipe/arm/Makefile index 6187b7e925..651b82f1ed 100644 --- a/lib/hipe/arm/Makefile +++ b/lib/hipe/arm/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2010. All Rights Reserved. +# Copyright Ericsson AB 2005-2012. 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 @@ -106,8 +106,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/cerl/Makefile b/lib/hipe/cerl/Makefile index d0a0ea1f59..506e993ff4 100644 --- a/lib/hipe/cerl/Makefile +++ b/lib/hipe/cerl/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2010. All Rights Reserved. +# Copyright Ericsson AB 2003-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -42,7 +42,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/hipe-$(VSN) # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- -MODULES = cerl_cconv cerl_closurean cerl_hipeify cerl_hybrid_transform \ +MODULES = cerl_cconv cerl_closurean cerl_hipeify \ cerl_lib cerl_messagean cerl_pmatch cerl_prettypr cerl_to_icode \ cerl_typean erl_bif_types erl_types @@ -100,10 +100,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/cerl - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/cerl - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/cerl" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/cerl" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/cerl/cerl_hipe_primops.hrl b/lib/hipe/cerl/cerl_hipe_primops.hrl index 1d82f7bfc7..a777cd0108 100644 --- a/lib/hipe/cerl/cerl_hipe_primops.hrl +++ b/lib/hipe/cerl/cerl_hipe_primops.hrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 diff --git a/lib/hipe/cerl/cerl_hipeify.erl b/lib/hipe/cerl/cerl_hipeify.erl index 89b4ec147d..3351be77f2 100644 --- a/lib/hipe/cerl/cerl_hipeify.erl +++ b/lib/hipe/cerl/cerl_hipeify.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 diff --git a/lib/hipe/cerl/cerl_hybrid_transform.erl b/lib/hipe/cerl/cerl_hybrid_transform.erl deleted file mode 100644 index b248b0ccd0..0000000000 --- a/lib/hipe/cerl/cerl_hybrid_transform.erl +++ /dev/null @@ -1,153 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. -%% -%% The contents of this file are subject to the Erlang Public License, -%% Version 1.1, (the "License"); you may not use this file except in -%% compliance with the License. You should have received a copy of the -%% Erlang Public License along with this software. If not, it can be -%% retrieved online at http://www.erlang.org/. -%% -%% Software distributed under the License is distributed on an "AS IS" -%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See -%% the License for the specific language governing rights and limitations -%% under the License. -%% -%% %CopyrightEnd% -%% - --module(cerl_hybrid_transform). - -%% Use compile option `{core_transform, cerl_hybrid_transform}' to -%% insert this as a compilation pass. - --export([transform/2, core_transform/2]). - --spec core_transform(cerl:cerl(), [term()]) -> cerl:cerl(). - -core_transform(Code, Opts) -> - cerl:to_records(transform(cerl:from_records(Code), Opts)). - --spec transform(cerl:cerl(), [term()]) -> cerl:cerl(). - -transform(Code, _Opts) -> - Code0 = cerl_trees:map(fun unfold_literal/1, Code), - {Code1, _} = cerl_trees:label(Code0), - io:fwrite("Running hybrid heap analysis..."), - {T1,_} = statistics(runtime), - {Code2, _, Vars} = cerl_messagean:annotate(Code1), - {T2,_} = statistics(runtime), - io:fwrite("(~w ms), transform...", [T2 - T1]), - Code3 = rewrite(Code2, Vars), - io:fwrite("done.\n"), - cerl_trees:map(fun fold_literal/1, Code3). - -unfold_literal(T) -> - cerl:unfold_literal(T). - -fold_literal(T) -> - cerl:fold_literal(T). - -%% If escape-annotated: -%% {...} => hybrid:tuple([...]) -%% [H | T] => hybrid:cons(H, T) -%% -%% Wrapper for args to hybrid:cons/hybrid:tuple that may need copying: -%% hybrid:copy(A) - -rewrite(Node, Vars) -> - case cerl:type(Node) of - tuple -> - Es = rewrite_list(cerl:tuple_es(Node), Vars), - case is_escaping(Node) of - false -> - cerl:update_c_tuple(Node, Es); - true -> - Es1 = wrap(Es, Node, Vars), - cerl:update_c_call(Node, - cerl:abstract(hybrid), - cerl:abstract(tuple), - [cerl:make_list(Es1)]) -%%% cerl:update_c_call(Node, cerl:abstract(hybrid), -%%% cerl:abstract(tuple), Es1) - end; - cons -> - H = rewrite(cerl:cons_hd(Node), Vars), - T = rewrite(cerl:cons_tl(Node), Vars), - case is_escaping(Node) of - false -> - cerl:update_c_cons(Node, H, T); - true -> - Es = wrap([H, T], Node, Vars), - cerl:update_c_call(Node, - cerl:abstract(hybrid), - cerl:abstract(cons), - Es) - end; -%%% call -> -%%% M = rewrite(cerl:call_module(Node)), -%%% F = rewrite(cerl:call_name(Node)), -%%% As = rewrite_list(cerl:call_args(Node)), -%%% case cerl:is_c_atom(M) andalso cerl:is_c_atom(F) of -%%% true -> -%%% case {cerl:atom_val(M), cerl:atom_val(F), length(As)} of -%%% {erlang, '!', 2} -> -%%% cerl:update_c_call(Node, -%%% cerl:abstract(hipe_bifs), -%%% cerl:abstract(send), -%%% [cerl:make_list(As)]); -%%% _ -> -%%% cerl:update_c_call(Node, M, F, As) -%%% end; -%%% false -> -%%% cerl:update_c_call(Node, M, F, As) -%%% end; - clause -> - B = rewrite(cerl:clause_body(Node), Vars), - cerl:update_c_clause(Node, cerl:clause_pats(Node), - cerl:clause_guard(Node), B); - primop -> - case cerl:atom_val(cerl:primop_name(Node)) of - match_fail -> - Node; - _ -> - As = rewrite_list(cerl:primop_args(Node), Vars), - cerl:update_c_primop(Node, cerl:primop_name(Node), As) - end; - _T -> - case cerl:subtrees(Node) of - [] -> - Node; - Gs -> - cerl:update_tree(Node, [rewrite_list(Ns, Vars) - || Ns <- Gs]) - end - end. - -rewrite_list([N | Ns], Vars) -> - [rewrite(N, Vars) | rewrite_list(Ns, Vars)]; -rewrite_list([], _) -> - []. - -is_escaping(T) -> - lists:member(escapes, cerl:get_ann(T)). - -wrap(Es, Node, Vars) -> - L = cerl_trees:get_label(Node), - Xs = dict:fetch(L, Vars), - wrap(Es, Xs). - -wrap([E | Es], [{S, _} | Xs]) -> - case ordsets:is_element(unsafe, S) of -%% case cerl:type(E) =/= literal of - true -> - [cerl:c_call(cerl:abstract(hybrid), - cerl:abstract(copy), - [E]) - | wrap(Es, Xs)]; - false -> - [E | wrap(Es, Xs)] - end; -wrap([], _) -> - []. diff --git a/lib/hipe/cerl/cerl_messagean.erl b/lib/hipe/cerl/cerl_messagean.erl index b9f82f2a43..ca812a0f0d 100644 --- a/lib/hipe/cerl/cerl_messagean.erl +++ b/lib/hipe/cerl/cerl_messagean.erl @@ -1,7 +1,7 @@ %% ===================================================================== %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2010. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 diff --git a/lib/hipe/cerl/cerl_to_icode.erl b/lib/hipe/cerl/cerl_to_icode.erl index 089e3f021e..1c1c10d9b0 100644 --- a/lib/hipe/cerl/cerl_to_icode.erl +++ b/lib/hipe/cerl/cerl_to_icode.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2009. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 diff --git a/lib/hipe/cerl/erl_bif_types.erl b/lib/hipe/cerl/erl_bif_types.erl index 845df0ca61..1ef73da1be 100644 --- a/lib/hipe/cerl/erl_bif_types.erl +++ b/lib/hipe/cerl/erl_bif_types.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -241,6 +241,7 @@ type(erl_ddll, try_unload, 2, Xs) -> %%-- erlang ------------------------------------------------------------------- type(erlang, halt, 0, _) -> t_none(); type(erlang, halt, 1, _) -> t_none(); +type(erlang, halt, 2, _) -> t_none(); type(erlang, exit, 1, _) -> t_none(); %% Note that exit/2 sends an exit signal to another process. type(erlang, exit, 2, _) -> t_atom('true'); @@ -709,6 +710,27 @@ type(erlang, display_nl, 0, _) -> t_atom('true'); type(erlang, dist_exit, 3, Xs) -> strict(arg_types(erlang, dist_exit, 3), Xs, fun (_) -> t_atom('true') end); +type(erlang, dt_append_vm_tag_data, 1, Xs) -> + strict(arg_types(erlang, dt_append_vm_tag_data, 1), + Xs, + fun(_) -> t_iodata() end); +type(erlang, dt_get_tag, 0, _) -> + t_sup(t_binary(), t_atom('undefined')); +type(erlang, dt_get_tag_data, 0, _) -> + t_sup(t_binary(), t_atom('undefined')); +type(erlang, dt_prepend_vm_tag_data, 1, Xs) -> + strict(arg_types(erlang, dt_prepend_vm_tag_data, 1), + Xs, + fun(_) -> t_iodata() end); +type(erlang, dt_put_tag, 1, Xs) -> + strict(arg_types(erlang, dt_put_tag, 1), Xs, + fun(_) -> t_sup(t_binary(), t_atom('undefined')) end); +type(erlang, dt_restore_tag, 1, Xs) -> + strict(arg_types(erlang, dt_restore_tag, 1), Xs, fun(_) -> t_atom('true') end); +type(erlang, dt_spread_tag, 1, Xs) -> + strict(arg_types(erlang, dt_spread_tag, 1), Xs, + fun(_) -> t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]), + t_atom('true')) end); type(erlang, element, 2, Xs) -> strict(arg_types(erlang, element, 2), Xs, fun ([X1, X2]) -> @@ -1164,6 +1186,7 @@ type(erlang, port_info, 2, Xs) -> ['links'] -> t_tuple([Item, t_list(t_pid())]); ['name'] -> t_tuple([Item, t_string()]); ['output'] -> t_tuple([Item, t_integer()]); + ['os_pid'] -> t_tuple([Item, t_sup(t_non_neg_integer(),t_atom('undefined'))]); ['registered_name'] -> t_tuple([Item, t_atom()]); List when is_list(List) -> t_tuple([t_sup([t_atom(A) || A <- List]), @@ -1246,7 +1269,6 @@ type(erlang, process_info, 2, Xs) -> ['links'] -> t_tuple([InfoItem, t_list(t_pid())]); ['memory'] -> t_tuple([InfoItem, t_non_neg_integer()]); - ['message_binary'] -> t_tuple([InfoItem, t_list()]); ['message_queue_len'] -> t_tuple([InfoItem, t_non_neg_integer()]); ['messages'] -> t_tuple([InfoItem, t_list()]); @@ -1571,14 +1593,10 @@ type(erlang, system_info, 1, Xs) -> t_tuple([t_atom('fullsweep_after'), t_non_neg_integer()]); ['garbage_collection'] -> t_list(); - ['global_heaps_size'] -> - t_non_neg_integer(); ['heap_sizes'] -> t_list(t_integer()); ['heap_type'] -> - t_sup([t_atom('private'), - t_atom('shared'), - t_atom('hybrid')]); + t_atom('private'); ['hipe_architecture'] -> t_atoms(['amd64', 'arm', 'powerpc', 'ppc64', 'undefined', 'ultrasparc', 'x86']); @@ -3494,6 +3512,20 @@ arg_types(erlang, display_string, 1) -> [t_string()]; arg_types(erlang, dist_exit, 3) -> [t_pid(), t_dist_exit(), t_sup(t_pid(), t_port())]; +arg_types(erlang, dt_append_vm_tag_data, 1) -> + [t_iodata()]; +arg_types(erlang, dt_get_tag, 0) -> + []; +arg_types(erlang, dt_get_tag_data, 0) -> + []; +arg_types(erlang, dt_prepend_vm_tag_data, 1) -> + [t_iodata()]; +arg_types(erlang, dt_put_tag, 1) -> + [t_sup(t_binary(), t_atom('undefined'))]; +arg_types(erlang, dt_restore_tag, 1) -> + [t_sup(t_tuple([t_non_neg_integer(), t_sup(t_binary(), t_nil())]), t_atom('true'))]; +arg_types(erlang, dt_spread_tag, 1) -> + [t_boolean()]; arg_types(erlang, element, 2) -> [t_pos_fixnum(), t_tuple()]; arg_types(erlang, erase, 0) -> @@ -3553,7 +3585,10 @@ arg_types(erlang, group_leader, 2) -> arg_types(erlang, halt, 0) -> []; arg_types(erlang, halt, 1) -> - [t_sup(t_non_neg_fixnum(), t_string())]; + [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()])]; +arg_types(erlang, halt, 2) -> + [t_sup([t_non_neg_fixnum(), t_atom('abort'), t_string()]), + t_list(t_tuple([t_atom('flush'), t_boolean()]))]; arg_types(erlang, hash, 2) -> [t_any(), t_integer()]; arg_types(erlang, hd, 1) -> @@ -3750,7 +3785,7 @@ arg_types(erlang, port_info, 1) -> arg_types(erlang, port_info, 2) -> [t_sup(t_port(), t_atom()), t_atoms(['registered_name', 'id', 'connected', - 'links', 'name', 'input', 'output'])]; + 'links', 'name', 'input', 'output', 'os_pid'])]; arg_types(erlang, port_to_list, 1) -> [t_port()]; arg_types(erlang, ports, 0) -> @@ -4703,7 +4738,6 @@ t_pinfo_item() -> t_atom('last_calls'), t_atom('links'), t_atom('memory'), - t_atom('message_binary'), % for hybrid heap only t_atom('message_queue_len'), t_atom('messages'), t_atom('monitored_by'), diff --git a/lib/hipe/cerl/erl_types.erl b/lib/hipe/cerl/erl_types.erl index 65b9a057de..410e29d269 100644 --- a/lib/hipe/cerl/erl_types.erl +++ b/lib/hipe/cerl/erl_types.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -2318,10 +2318,14 @@ t_inf(?product(_), _, _Mode) -> ?none; t_inf(_, ?product(_), _Mode) -> ?none; -t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Mode) -> T; -t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Mode) -> T; -t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Mode) -> T; -t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Mode) -> T; +t_inf(?tuple(?any, ?any, ?any), ?tuple(_, _, _) = T, _Mode) -> + subst_all_vars_to_any(T); +t_inf(?tuple(_, _, _) = T, ?tuple(?any, ?any, ?any), _Mode) -> + subst_all_vars_to_any(T); +t_inf(?tuple(?any, ?any, ?any), ?tuple_set(_) = T, _Mode) -> + subst_all_vars_to_any(T); +t_inf(?tuple_set(_) = T, ?tuple(?any, ?any, ?any), _Mode) -> + subst_all_vars_to_any(T); t_inf(?tuple(Elements1, Arity, _Tag1), ?tuple(Elements2, Arity, _Tag2), Mode) -> case t_inf_lists_strict(Elements1, Elements2, Mode) of bottom -> ?none; @@ -2555,8 +2559,8 @@ t_subst_dict(?list(Contents, Termination, Size), Dict) -> ?nil -> ?list(NewContents, ?nil, Size); ?any -> ?list(NewContents, ?any, Size); Other -> - ?list(NewContents, NewTermination, _) = t_cons(NewContents, Other), - ?list(NewContents, NewTermination, Size) + ?list(NewContents2, NewTermination, _) = t_cons(NewContents, Other), + ?list(NewContents2, NewTermination, Size) end end; t_subst_dict(?function(Domain, Range), Dict) -> @@ -2597,8 +2601,8 @@ t_subst_aux(?list(Contents, Termination, Size), VarMap) -> ?nil -> ?list(NewContents, ?nil, Size); ?any -> ?list(NewContents, ?any, Size); Other -> - ?list(NewContents, NewTermination, _) = t_cons(NewContents, Other), - ?list(NewContents, NewTermination, Size) + ?list(NewContents2, NewTermination, _) = t_cons(NewContents, Other), + ?list(NewContents2, NewTermination, Size) end end; t_subst_aux(?function(Domain, Range), VarMap) -> @@ -3186,8 +3190,8 @@ t_abstract_records(?list(Contents, Termination, Size), RecDict) -> ?nil -> ?list(NewContents, ?nil, Size); ?any -> ?list(NewContents, ?any, Size); Other -> - ?list(NewContents, NewTermination, _) = t_cons(NewContents, Other), - ?list(NewContents, NewTermination, Size) + ?list(NewContents2, NewTermination, _) = t_cons(NewContents, Other), + ?list(NewContents2, NewTermination, Size) end end; t_abstract_records(?function(Domain, Range), RecDict) -> diff --git a/lib/hipe/doc/Makefile b/lib/hipe/doc/Makefile index e28b16c12e..4ea0137202 100644 --- a/lib/hipe/doc/Makefile +++ b/lib/hipe/doc/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2010. All Rights Reserved. +# Copyright Ericsson AB 2006-2012. 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 diff --git a/lib/hipe/doc/src/Makefile b/lib/hipe/doc/src/Makefile index 065854e8a7..fbfd4ca327 100644 --- a/lib/hipe/doc/src/Makefile +++ b/lib/hipe/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2006-2010. All Rights Reserved. +# Copyright Ericsson AB 2006-2012. 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 @@ -106,12 +106,12 @@ realclean: clean include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" release_spec: diff --git a/lib/hipe/doc/src/notes.xml b/lib/hipe/doc/src/notes.xml index 3f28cf9959..ed8cf0b8d3 100644 --- a/lib/hipe/doc/src/notes.xml +++ b/lib/hipe/doc/src/notes.xml @@ -30,6 +30,52 @@ </header> <p>This document describes the changes made to HiPE.</p> +<section><title>Hipe 3.9.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + References to <c>is_constant/1</c> (which was removed in + the R12 release) has been removed from documentation and + code.</p> + <p> + Own Id: OTP-6454 Aux Id: seq10407 </p> + </item> + <item> + <p> + Fixed a discrepancy in compile_info</p> + <p> + The BEAM disassembler used the atom 'none' to signify the + absence of a compile_info chunk in a .beam file. This + clashed with the type declaration of the compile_info + field of a #beam_file{} record as containing a list. Now + [] signifies the absence of this chunk. This simplifies + the code and avoids a dialyzer warning.</p> + <p> + Own Id: OTP-9917</p> + </item> + <item> + <p> + Make dialyzer recognize the process_flag option sensitive + add missing specs to documentation (Thanks to Tobias + Schlager)</p> + <p> + Own Id: OTP-9923</p> + </item> + <item> + <p> + Remove hipe_ceach from hipe.app.src to fix + reltool-generated release startup. (Thanks to Tim + Stewart)</p> + <p> + Own Id: OTP-9939</p> + </item> + </list> + </section> + +</section> + <section><title>Hipe 3.9</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/hipe/flow/Makefile b/lib/hipe/flow/Makefile index e8d570ac48..75e156b542 100644 --- a/lib/hipe/flow/Makefile +++ b/lib/hipe/flow/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. 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 @@ -100,10 +100,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/flow - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(INC_FILES) $(RELSYSDIR)/flow - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/flow" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(INC_FILES) "$(RELSYSDIR)/flow" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/flow/cfg.hrl b/lib/hipe/flow/cfg.hrl index 62f47a707a..95bf5f7194 100644 --- a/lib/hipe/flow/cfg.hrl +++ b/lib/hipe/flow/cfg.hrl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 diff --git a/lib/hipe/icode/Makefile b/lib/hipe/icode/Makefile index e57daecfbc..0f2d6db39b 100644 --- a/lib/hipe/icode/Makefile +++ b/lib/hipe/icode/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. 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 @@ -118,10 +118,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/icode - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/icode - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/icode" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/icode" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/icode/hipe_beam_to_icode.erl b/lib/hipe/icode/hipe_beam_to_icode.erl index 2d2b414a70..a44171b2f5 100644 --- a/lib/hipe/icode/hipe_beam_to_icode.erl +++ b/lib/hipe/icode/hipe_beam_to_icode.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 diff --git a/lib/hipe/icode/hipe_icode_inline_bifs.erl b/lib/hipe/icode/hipe_icode_inline_bifs.erl index 9a2fb7846a..6ef15b9a18 100644 --- a/lib/hipe/icode/hipe_icode_inline_bifs.erl +++ b/lib/hipe/icode/hipe_icode_inline_bifs.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 diff --git a/lib/hipe/icode/hipe_icode_type.erl b/lib/hipe/icode/hipe_icode_type.erl index cae4325b9e..f98d859822 100644 --- a/lib/hipe/icode/hipe_icode_type.erl +++ b/lib/hipe/icode/hipe_icode_type.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 diff --git a/lib/hipe/main/Makefile b/lib/hipe/main/Makefile index d49dd6f424..673431a175 100644 --- a/lib/hipe/main/Makefile +++ b/lib/hipe/main/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -115,10 +115,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DATA) ../vsn.mk $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/main - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/main - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DATA) ../vsn.mk "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/main" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/main" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src index c05aec4c4a..7db4db8a57 100644 --- a/lib/hipe/main/hipe.app.src +++ b/lib/hipe/main/hipe.app.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 @@ -24,7 +24,6 @@ {modules, [cerl_cconv, cerl_closurean, cerl_hipeify, - cerl_hybrid_transform, cerl_lib, cerl_messagean, cerl_pmatch, diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl index 1be679a13f..b2789978a4 100644 --- a/lib/hipe/main/hipe.erl +++ b/lib/hipe/main/hipe.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -482,12 +482,7 @@ compile(Name, File, Opts0) when is_atom(Name) -> compile_core(Name, Core0, File, Opts) -> Core = cerl:from_records(Core0), - Core1 = case (erlang:system_info(heap_type) =:= hybrid) - andalso proplists:get_bool(hybrid, Opts) of - true -> cerl_hybrid_transform:transform(Core, Opts); - false -> Core - end, - compile(Name, Core1, File, Opts). + compile(Name, Core, File, Opts). %% @spec compile(Name, Core, File, options()) -> %% {ok, {Target, Binary}} | {error, Reason} diff --git a/lib/hipe/misc/Makefile b/lib/hipe/misc/Makefile index 98a69d62c7..1204ab7c0b 100644 --- a/lib/hipe/misc/Makefile +++ b/lib/hipe/misc/Makefile @@ -100,10 +100,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/misc - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/misc - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/misc" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/misc" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/opt/Makefile b/lib/hipe/opt/Makefile index 8e244ab360..a21c543574 100644 --- a/lib/hipe/opt/Makefile +++ b/lib/hipe/opt/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -97,8 +97,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/ppc/Makefile b/lib/hipe/ppc/Makefile index 83376e8d9d..8bd2a8226c 100644 --- a/lib/hipe/ppc/Makefile +++ b/lib/hipe/ppc/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2010. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -108,8 +108,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/regalloc/Makefile b/lib/hipe/regalloc/Makefile index c327b443b0..d4be79ea85 100644 --- a/lib/hipe/regalloc/Makefile +++ b/lib/hipe/regalloc/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. 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 @@ -112,8 +112,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/rtl/Makefile b/lib/hipe/rtl/Makefile index 30026831b7..426d1bd3ee 100644 --- a/lib/hipe/rtl/Makefile +++ b/lib/hipe/rtl/Makefile @@ -103,10 +103,10 @@ realclean: clean include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/rtl - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/rtl - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/rtl" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/rtl" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/sparc/Makefile b/lib/hipe/sparc/Makefile index e3acecdcb8..eb40117691 100644 --- a/lib/hipe/sparc/Makefile +++ b/lib/hipe/sparc/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -108,8 +108,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/tools/Makefile b/lib/hipe/tools/Makefile index f90d3c9f70..6761f11b01 100644 --- a/lib/hipe/tools/Makefile +++ b/lib/hipe/tools/Makefile @@ -96,8 +96,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/tools/hipe_tool.erl b/lib/hipe/tools/hipe_tool.erl index 190a68ee56..efcf073efa 100644 --- a/lib/hipe/tools/hipe_tool.erl +++ b/lib/hipe/tools/hipe_tool.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2010. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 diff --git a/lib/hipe/util/Makefile b/lib/hipe/util/Makefile index 7d1a7c01c7..a5ee232057 100644 --- a/lib/hipe/util/Makefile +++ b/lib/hipe/util/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. 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 @@ -103,10 +103,10 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/util - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/util - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/util" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/util" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/hipe/vsn.mk b/lib/hipe/vsn.mk index 347a0336cd..e296997466 100644 --- a/lib/hipe/vsn.mk +++ b/lib/hipe/vsn.mk @@ -1 +1 @@ -HIPE_VSN = 3.9 +HIPE_VSN = 3.9.1 diff --git a/lib/hipe/x86/Makefile b/lib/hipe/x86/Makefile index aed757246f..3602949944 100644 --- a/lib/hipe/x86/Makefile +++ b/lib/hipe/x86/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2010. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. 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 @@ -116,8 +116,8 @@ $(DOCS)/%.html:%.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/ic/c_src/Makefile.in b/lib/ic/c_src/Makefile.in index 5e034c47c6..d8b8c85a8e 100644 --- a/lib/ic/c_src/Makefile.in +++ b/lib/ic/c_src/Makefile.in @@ -140,12 +140,12 @@ $(OBJDIR)/%.o: %.c include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/c_src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_DATA) ic.c ic_tmo.c $(RELSYSDIR)/c_src - $(INSTALL_DATA) $(IDL_FILES) $(H_FILES) $(RELSYSDIR)/include - $(INSTALL_DATA) $(LIBRARY) $(RELSYSDIR)/priv/lib + $(INSTALL_DIR) "$(RELSYSDIR)/c_src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_DATA) ic.c ic_tmo.c "$(RELSYSDIR)/c_src" + $(INSTALL_DATA) $(IDL_FILES) $(H_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(LIBRARY) "$(RELSYSDIR)/priv/lib" release_docs_spec: diff --git a/lib/ic/doc/src/Makefile b/lib/ic/doc/src/Makefile index 208f16e441..0ee242b234 100644 --- a/lib/ic/doc/src/Makefile +++ b/lib/ic/doc/src/Makefile @@ -220,12 +220,12 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/doc/html - (/bin/cp -rf $(HTMLDIR) $(RELSYSDIR)/doc) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + (/bin/cp -rf $(HTMLDIR) "$(RELSYSDIR)/doc") + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/ic/doc/src/ic_clib.xml b/lib/ic/doc/src/ic_clib.xml index b557c4b5f6..ebeaabae91 100644 --- a/lib/ic/doc/src/ic_clib.xml +++ b/lib/ic/doc/src/ic_clib.xml @@ -4,7 +4,7 @@ <cref> <header> <copyright> - <year>2003</year><year>2009</year> + <year>2003</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -41,7 +41,7 @@ </section> <funcs> <func> - <name><ret>CORBA_Environment*</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name> + <name><ret>CORBA_Environment *</ret><nametext>CORBA_Environment_alloc(int inbufsz, int outbufsz)</nametext></name> <fsummary>Allocate environment data.</fsummary> <desc> <p>This function is used to allocate and initiate the @@ -79,14 +79,14 @@ </desc> </func> <func> - <name><ret>CORBA_char*</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name> + <name><ret>CORBA_char *</ret><nametext>CORBA_string_alloc(CORBA_unsigned_long len)</nametext></name> <fsummary>Allocate a string.</fsummary> <desc> <p>Allocates a (simple) CORBA character string of length <c>len + 1</c>.</p> </desc> </func> <func> - <name><ret>CORBA_wchar*</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name> + <name><ret>CORBA_wchar *</ret><nametext>CORBA_wstring_alloc(CORBA_unsigned_long len)</nametext></name> <fsummary>Allocate a wide string.</fsummary> <desc> <p>Allocates a CORBA wide string of length <c>len + 1</c>.</p> @@ -101,7 +101,7 @@ </section> <funcs> <func> - <name><ret>CORBA_char*</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name> + <name><ret>CORBA_char *</ret><nametext>CORBA_exception_id(CORBA_Environment *env)</nametext></name> <fsummary>Get exception identity.</fsummary> <desc> <p>Returns the exception identity if an exception is set, otherwise @@ -109,7 +109,7 @@ </desc> </func> <func> - <name><ret>void*</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name> + <name><ret>void *</ret><nametext>CORBA_exception_value(CORBA_Environment *env)</nametext></name> <fsummary>Get exception value.</fsummary> <desc> <p>Returns the exception value, if an exception is set, otherwise @@ -160,7 +160,7 @@ </desc> </func> <func> - <name><ret>oe_map_t*</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name> + <name><ret>oe_map_t *</ret><nametext>oe_merge_maps(oe_map_t *maps, int size)</nametext></name> <fsummary>Merge an array of server maps to one single map.</fsummary> <desc> <p>Merge an array of server maps to one single map.</p> diff --git a/lib/ic/doc/src/notes.xml b/lib/ic/doc/src/notes.xml index 6329bf1fb5..eac932fc06 100644 --- a/lib/ic/doc/src/notes.xml +++ b/lib/ic/doc/src/notes.xml @@ -30,7 +30,23 @@ <file>notes.xml</file> </header> - <section><title>IC 4.2.29</title> + <section><title>IC 4.2.30</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Add generation of Erlang callback functions to generated + Erlang source code to avoid compiler warnings.</p> + <p> + Own Id: OTP-9998</p> + </item> + </list> + </section> + +</section> + +<section><title>IC 4.2.29</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/ic/examples/pre_post_condition/Makefile b/lib/ic/examples/pre_post_condition/Makefile index 8f85babb1a..8ae37394ed 100644 --- a/lib/ic/examples/pre_post_condition/Makefile +++ b/lib/ic/examples/pre_post_condition/Makefile @@ -124,8 +124,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/pre_post_condition - $(INSTALL_DATA) $(ERL_FILES) $(IDL_FILES) $(TXT_FILES) $(RELSYSDIR)/examples/pre_post_condition + $(INSTALL_DIR) "$(RELSYSDIR)/examples/pre_post_condition" + $(INSTALL_DATA) $(ERL_FILES) $(IDL_FILES) $(TXT_FILES) "$(RELSYSDIR)/examples/pre_post_condition" release_docs_spec: diff --git a/lib/ic/java_src/com/ericsson/otp/ic/Makefile b/lib/ic/java_src/com/ericsson/otp/ic/Makefile index f730749ccb..1b14d4430f 100644 --- a/lib/ic/java_src/com/ericsson/otp/ic/Makefile +++ b/lib/ic/java_src/com/ericsson/otp/ic/Makefile @@ -109,10 +109,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/java_src/com/ericsson/otp/ic - $(INSTALL_DATA) $(JAVA_FILES) $(RELSYSDIR)/java_src/com/ericsson/otp/ic - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/java_src/com/ericsson/otp/ic" + $(INSTALL_DATA) $(JAVA_FILES) "$(RELSYSDIR)/java_src/com/ericsson/otp/ic" + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/ic/src/Makefile b/lib/ic/src/Makefile index 5dac304e32..c830ebec95 100644 --- a/lib/ic/src/Makefile +++ b/lib/ic/src/Makefile @@ -196,23 +196,23 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/examples/c-client - $(INSTALL_DATA) $(CCL_EX_FILES) $(RELSYSDIR)/examples/c-client - $(INSTALL_DIR) $(RELSYSDIR)/examples/c-server - $(INSTALL_DATA) $(CSRV_EX_FILES) $(RELSYSDIR)/examples/c-server - $(INSTALL_DIR) $(RELSYSDIR)/examples/erl-plain - $(INSTALL_DATA) $(EPL_EX_FILES) $(RELSYSDIR)/examples/erl-plain - $(INSTALL_DIR) $(RELSYSDIR)/examples/erl-genserv - $(INSTALL_DATA) $(ESRV_EX_FILES) $(RELSYSDIR)/examples/erl-genserv - $(INSTALL_DIR) $(RELSYSDIR)/examples/java-client-server - $(INSTALL_DATA) $(JAVA_EX_FILES) $(RELSYSDIR)/examples/java-client-server - $(INSTALL_DIR) $(RELSYSDIR)/examples/all-against-all - $(INSTALL_DATA) $(MIXED_EX_FILES) $(RELSYSDIR)/examples/all-against-all + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/c-client" + $(INSTALL_DATA) $(CCL_EX_FILES) "$(RELSYSDIR)/examples/c-client" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/c-server" + $(INSTALL_DATA) $(CSRV_EX_FILES) "$(RELSYSDIR)/examples/c-server" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/erl-plain" + $(INSTALL_DATA) $(EPL_EX_FILES) "$(RELSYSDIR)/examples/erl-plain" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/erl-genserv" + $(INSTALL_DATA) $(ESRV_EX_FILES) "$(RELSYSDIR)/examples/erl-genserv" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/java-client-server" + $(INSTALL_DATA) $(JAVA_EX_FILES) "$(RELSYSDIR)/examples/java-client-server" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/all-against-all" + $(INSTALL_DATA) $(MIXED_EX_FILES) "$(RELSYSDIR)/examples/all-against-all" release_docs_spec: diff --git a/lib/ic/src/ic.erl b/lib/ic/src/ic.erl index 50fad921c4..bc7ec8ba91 100644 --- a/lib/ic/src/ic.erl +++ b/lib/ic/src/ic.erl @@ -250,7 +250,7 @@ make_erl_options(Opts) -> Optimize = Opts#options.optimize, PreProc = lists:flatten( - lists:map(fun(D) -> io_lib:format("-I~s ", [ic_util:to_list(D)]) end, + lists:map(fun(D) -> io_lib:format("-I\"~s\" ", [ic_util:to_list(D)]) end, Includes1)++ lists:map( fun ({Name, Value}) -> diff --git a/lib/ic/src/ic_noc.erl b/lib/ic/src/ic_noc.erl index d43d550a52..fcb413ba2e 100644 --- a/lib/ic/src/ic_noc.erl +++ b/lib/ic/src/ic_noc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -289,6 +289,9 @@ emit_serv_std(G, N, X) -> emit(Fd, "terminate(Reason, State) ->\n"), emit(Fd, " ~p:~p(Reason, State).\n", [Impl, terminate]), + nl(Fd), + emit(Fd, "code_change(_OldVsn, State, _Extra) ->\n"), + emit(Fd, " {ok, State}.\n"), nl(Fd), nl(Fd) end, Fd. @@ -304,13 +307,13 @@ gen_end_of_call(G, _N, _X) -> Fd = ic_genobj:stubfiled(G), nl(Fd), nl(Fd), ic_codegen:mcomment_light(Fd, ["Standard gen_server call handle"]), - emit(Fd, "handle_call(stop, From, State) ->\n"), + emit(Fd, "handle_call(stop, _From, State) ->\n"), emit(Fd, " {stop, normal, ok, State}"), case get_opt(G, serv_last_call) of exception -> emit(Fd, ";\n"), nl(Fd), - emit(Fd, "handle_call(Req, From, State) ->\n"), + emit(Fd, "handle_call(_Req, _From, State) ->\n"), emit(Fd, " {reply, ~p, State}.\n",[getCallErr()]); exit -> emit(Fd, ".\n"), @@ -335,7 +338,7 @@ gen_end_of_cast(G, _N, _X) -> exception -> emit(Fd, ";\n"), nl(Fd), - emit(Fd, "handle_cast(Req, State) ->\n"), + emit(Fd, "handle_cast(_Req, State) ->\n"), emit(Fd, " {reply, ~p, State}.\n",[getCastErr()]); exit -> emit(Fd, ".\n"), @@ -353,12 +356,13 @@ emit_skel_footer(G, N, X) -> Fd = ic_genobj:stubfiled(G), nl(Fd), nl(Fd), ic_codegen:mcomment_light(Fd, ["Standard gen_server handles"]), - emit(Fd, "handle_info(X, State) ->\n"), case use_impl_handle_info(G, N, X) of true -> + emit(Fd, "handle_info(X, State) ->\n"), emit(Fd, " ~p:handle_info(X, State).\n\n", [list_to_atom(ic_genobj:impl(G))]); false -> + emit(Fd, "handle_info(_X, State) ->\n"), emit(Fd, " {reply, ~p, State}.\n\n",[getInfoErr()]) end end, @@ -402,9 +406,8 @@ get_if_gen(G, N, X) -> [io_lib:format("Standard Operation: ~p", [Name])]), - emit(Fd, "handle_call({~s, ~p, []}, From, State) ->~n", + emit(Fd, "handle_call({_~s, ~p, []}, _From, State) ->~n", [mk_name(G, "Ref"), Name]), - emit(Fd, " {reply, ~p, State};~n", [IFC_TKS]), nl(Fd), ok; @@ -479,8 +482,8 @@ gen_head_special(G, N, X) when is_record(X, interface) -> nl(Fd), ic_codegen:comment(Fd, "gen server export stuff"), emit(Fd, "-behaviour(gen_server).\n"), - ic_codegen:export(Fd, [{init, 1}, {terminate, 2}, {handle_call, 3}, - {handle_cast, 2}, {handle_info, 2}]), + ic_codegen:export(Fd, [{init, 1}, {terminate, 2}, {code_change, 3}, + {handle_call, 3}, {handle_cast, 2}, {handle_info, 2}]), nl(Fd), nl(Fd), ic_codegen:mcomment(Fd, ["Object interface functions."]), nl(Fd), nl(Fd), nl(Fd) diff --git a/lib/ic/src/ic_pp.erl b/lib/ic/src/ic_pp.erl index 8b53473caa..50eeeab48c 100644 --- a/lib/ic/src/ic_pp.erl +++ b/lib/ic/src/ic_pp.erl @@ -1894,23 +1894,37 @@ include_dir(Flags) when is_list(Flags)-> include_dir(_Flags) -> []. -include_dir(Flags,IncDir) -> +include_dir(Flags,IncDirs) -> case string:str(Flags,"-I") of 0 -> - lists:reverse(IncDir); + lists:reverse(IncDirs); X -> - Rem2 = string:sub_string(Flags, X+2), - Rem = string:strip(Rem2, left), - Y = string:str(Rem," "), - case string:str(Rem," ") of - 0 -> - lists:reverse([string:sub_string(Rem, Y+1)|IncDir]); - Y -> - include_dir(string:sub_string(Rem, Y+1), - [string:sub_string(Rem,1,Y-1)|IncDir]) - end + {NewDir, RemainingFlags} = + gobble_inc_dir(string:sub_string(Flags, X+2),nq,[]), + include_dir(RemainingFlags, [NewDir|IncDirs]) end. +% nq = not-quoted, q = quoted. +% Possible strange scenarios: +% /usr/test\ ing/ +% "/usr/test ing/" +% /usr/test\"ing/ +% "/usr/test\"ing/" +gobble_inc_dir([],nq,Acc) -> + % Only accept nq here, if we end up here in q mode the user has missed a " + {lists:reverse(Acc),[]}; +gobble_inc_dir([$\\,$"|R],Q,Acc) -> + gobble_inc_dir(R,Q,[$"|Acc]); +gobble_inc_dir([$"|R],nq,Acc) -> + gobble_inc_dir(R,q,Acc); +gobble_inc_dir([$"|R],q,Acc) -> + gobble_inc_dir(R,nq,Acc); +gobble_inc_dir([$\\,$ |R],nq,Acc) -> + gobble_inc_dir(R,nq,[$ |Acc]); +gobble_inc_dir([$ |R],nq,Acc) -> + {lists:reverse(Acc),R}; +gobble_inc_dir([C|R],Q,Acc) -> + gobble_inc_dir(R,Q,[C|Acc]). %%=============================================================== @@ -1954,7 +1968,6 @@ find_inc_file2(FileName, [D|Rem]) -> _ -> D++"/" end, - case catch file:read_file_info(Dir++FileName) of {ok, _} -> {ok, Dir++FileName}; diff --git a/lib/ic/test/Makefile b/lib/ic/test/Makefile index 1d90a1bc17..65003d703b 100644 --- a/lib/ic/test/Makefile +++ b/lib/ic/test/Makefile @@ -239,39 +239,39 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/ic_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/ic_register_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/ic_pragma_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/ic_pp_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/ic_be_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/c_client_erl_server_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/c_client_erl_server_proto_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/erl_client_c_server_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/erl_client_c_server_proto_SUITE_data - $(INSTALL_DIR) $(RELSYSDIR)/java_client_erl_server_SUITE_data + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_register_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_pragma_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_pp_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/ic_be_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/c_client_erl_server_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/c_client_erl_server_proto_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/erl_client_c_server_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/erl_client_c_server_proto_SUITE_data" + $(INSTALL_DIR) "$(RELSYSDIR)/java_client_erl_server_SUITE_data" $(INSTALL_DATA) $(IDL_FILES) ic.cover $(TEST_SPEC_FILE) $(ERL_FILES) \ - $(RELSYSDIR) - $(INSTALL_DATA) $(COMPILER_TEST_FILES) $(RELSYSDIR)/ic_SUITE_data + "$(RELSYSDIR)" + $(INSTALL_DATA) $(COMPILER_TEST_FILES) "$(RELSYSDIR)/ic_SUITE_data" $(INSTALL_DATA) $(COMPILER_TEST_FILES2) \ - $(RELSYSDIR)/ic_register_SUITE_data + "$(RELSYSDIR)/ic_register_SUITE_data" $(INSTALL_DATA) $(COMPILER_TEST_FILES3) \ - $(RELSYSDIR)/ic_pragma_SUITE_data + "$(RELSYSDIR)/ic_pragma_SUITE_data" $(INSTALL_DATA) $(COMPILER_TEST_FILES4) \ - $(RELSYSDIR)/ic_be_SUITE_data + "$(RELSYSDIR)/ic_be_SUITE_data" $(INSTALL_DATA) $(PREPROCESSOR_TEST_FILES) \ - $(RELSYSDIR)/ic_pp_SUITE_data + "$(RELSYSDIR)/ic_pp_SUITE_data" $(INSTALL_DATA) $(C_CLIENT_ERL_SERVER_TEST_FILES) \ - $(RELSYSDIR)/c_client_erl_server_SUITE_data + "$(RELSYSDIR)/c_client_erl_server_SUITE_data" $(INSTALL_DATA) $(C_CLIENT_ERL_SERVER_PROTO_TEST_FILES) \ - $(RELSYSDIR)/c_client_erl_server_proto_SUITE_data + "$(RELSYSDIR)/c_client_erl_server_proto_SUITE_data" $(INSTALL_DATA) $(C_CLIENT_ERL_SERVER_PROTO_TMO_TEST_FILES) \ - $(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data + "$(RELSYSDIR)/c_client_erl_server_proto_tmo_SUITE_data" $(INSTALL_DATA) $(ERL_CLIENT_C_SERVER_TEST_FILES) \ - $(RELSYSDIR)/erl_client_c_server_SUITE_data + "$(RELSYSDIR)/erl_client_c_server_SUITE_data" $(INSTALL_DATA) $(ERL_CLIENT_C_SERVER_PROTO_TEST_FILES) \ - $(RELSYSDIR)/erl_client_c_server_proto_SUITE_data - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) + "$(RELSYSDIR)/erl_client_c_server_proto_SUITE_data" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) $(JAVA_CLIENT_ERL_SERVER_TEST_FILES) \ - $(RELSYSDIR)/java_client_erl_server_SUITE_data + "$(RELSYSDIR)/java_client_erl_server_SUITE_data" diff --git a/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src b/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src index d5277eb256..f3e6fdb67e 100644 --- a/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src +++ b/lib/ic/test/c_client_erl_server_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2011. All Rights Reserved. +# Copyright Ericsson AB 2001-2012. 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 @@ -122,24 +122,33 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.idl +c_erl_test.built_c: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" c_erl_test.idl + echo done > c_erl_test.built_c -$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.idl +c_erl_test.built_erl: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" c_erl_test.idl + echo done > c_erl_test.built_erl -.c@obj@: +.c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< -.erl.@EMULATOR@: +.erl.@EMULATOR@: $(ERLC) -I $(IC_INCLUDE_PATH) $< diff --git a/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src b/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src index 8bc1a907a7..0818be01a6 100644 --- a/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src +++ b/lib/ic/test/c_client_erl_server_proto_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2011. All Rights Reserved. +# Copyright Ericsson AB 2003-2012. 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 @@ -122,25 +122,34 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.idl +c_erl_test.built_c: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" \ "+{user_protocol,my}" c_erl_test.idl + echo done > c_erl_test.built_c -$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.idl +c_erl_test.built_erl: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" c_erl_test.idl + echo done > c_erl_test.built_erl -.c@obj@: +.c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< -.erl.@EMULATOR@: +.erl.@EMULATOR@: $(ERLC) -I $(IC_INCLUDE_PATH) $< diff --git a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src index 2585341791..07b353bc0a 100644 --- a/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src +++ b/lib/ic/test/c_client_erl_server_proto_tmo_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. 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 @@ -122,25 +122,33 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.idl - $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" \ - "+{user_protocol,my}" "+{c_timeout,{5000,5000}}" c_erl_test.idl +c_erl_test.built_c: c_erl_test.idl + $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_client}" c_erl_test.idl + echo done > c_erl_test.built_c -$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.idl +c_erl_test.built_erl: c_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" c_erl_test.idl + echo done > c_erl_test.built_erl -.c@obj@: +.c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< -.erl.@EMULATOR@: +.erl.@EMULATOR@: $(ERLC) -I $(IC_INCLUDE_PATH) $< diff --git a/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src b/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src index 50cf9d4445..e190295d70 100644 --- a/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src +++ b/lib/ic/test/erl_client_c_server_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2012. 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 @@ -122,25 +122,34 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): erl_c_test.idl +c_erl_test.built_c: erl_c_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_server}" \ "+{scoped_op_calls,true}" erl_c_test.idl + echo done > c_erl_test.built_c # If we have scoped operation calls for C, we must have that for # Erlang as well, if we use the m_i.erl file for calling the server. -$(GEN_ERL_FILES) $(GEN_HRL_FILES): erl_c_test.idl +c_erl_test.built_erl: erl_c_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" \ "+{scoped_op_calls,true}" "+{timeout,true}" erl_c_test.idl + echo done > c_erl_test.built_erl .c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< diff --git a/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src b/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src index 6c7701ca50..86917950c9 100644 --- a/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src +++ b/lib/ic/test/erl_client_c_server_proto_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. 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 @@ -122,25 +122,34 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(PGMS) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): c_erl_test.built_erl +$(GEN_C_FILES) $(GEN_H_FILES): c_erl_test.built_c +$(OBJS): $(GEN_C_FILES) $(GEN_H_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c -del /F /Q $(OBJS) $(GEN_C_FILES) $(GEN_H_FILES) $(PGMS) \ - $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) + $(EBINS) $(GEN_ERL_FILES) $(GEN_HRL_FILES) \ + c_erl_test.built_erl c_erl_test.built_c $(PGMS): $(OBJS) $(LD) $(LDFLAGS) -o $@ $(OBJS) $(LIBS) -$(GEN_C_FILES) $(GEN_H_FILES): erl_c_test.idl +c_erl_test.built_c: erl_c_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,c_server}" \ "+{scoped_op_calls,true}" erl_c_test.idl + echo done > c_erl_test.built_c # If we have scoped operation calls for C, we must have that for # Erlang as well, if we use the m_i.erl file for calling the server. -$(GEN_ERL_FILES) $(GEN_HRL_FILES): erl_c_test.idl +c_erl_test.built_erl: erl_c_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" \ "+{scoped_op_calls,true}" "+{timeout,true}" erl_c_test.idl + echo done > c_erl_test.built_erl .c@obj@: $(CC) -c -o $*@obj@ $(CFLAGS) $< diff --git a/lib/ic/test/java_client_erl_server_SUITE.erl b/lib/ic/test/java_client_erl_server_SUITE.erl index 407c3d2d44..d4523dc2ad 100644 --- a/lib/ic/test/java_client_erl_server_SUITE.erl +++ b/lib/ic/test/java_client_erl_server_SUITE.erl @@ -249,7 +249,7 @@ marshal_any_2(Config) when is_list(Config) -> java(Java, Dir, ClassAndArgs) -> - cmd(Java++" -classpath "++classpath(Dir)++" "++ClassAndArgs). + cmd(Java++" -classpath \""++classpath(Dir)++"\" "++ClassAndArgs). java(Java, Dir, Class, Args) -> java(Java, Dir, Class++" "++to_string(Args)). diff --git a/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src b/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src index 5e190fe1a5..c106261efd 100644 --- a/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src +++ b/lib/ic/test/java_client_erl_server_SUITE_data/Makefile.src @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2011. All Rights Reserved. +# Copyright Ericsson AB 2003-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -69,20 +69,29 @@ EBINS = $(ERL_FILES:.erl=.@EMULATOR@) all: $(CLASS_FILES) $(EBINS) +$(GEN_ERL_FILES) $(GEN_HRL_FILES): java_erl_test.built_erl +$(GEN_JAVA_FILES): java_erl_test.built_java +$(CLASS_FILES): $(GEN_JAVA_FILES) +$(EBINS): $(GEN_ERL_FILES) $(GEN_HRL_FILES) + clean: -rm -f $(GEN_JAVA_FILES) $(CLASS_FILES) \ - $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS) + $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS) \ + java_erl_test.built_erl java_erl_test.built_java -del /F /Q $(GEN_JAVA_FILES) $(CLASS_FILES) \ - $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS) + $(GEN_ERL_FILES) $(GEN_HRL_FILES) $(EBINS) \ + java_erl_test.built_erl java_erl_test.built_java -$(GEN_JAVA_FILES) : java_erl_test.idl +java_erl_test.built_java: java_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,java}" java_erl_test.idl + echo done > java_erl_test.built_java $(CLASS_FILES) : $(JAVA_FILES) $(JAVAC) -classpath $(CLASSPATH) $(JAVA_FILES) -$(GEN_ERL_FILES) $(GEN_HRL_FILES): java_erl_test.idl +java_erl_test.built_erl: java_erl_test.idl $(ERLC) -I $(IC_INCLUDE_PATH) "+{be,erl_genserv}" java_erl_test.idl + echo done > java_erl_test.built_erl .erl.@EMULATOR@: $(ERLC) -I $(IC_INCLUDE_PATH) $< diff --git a/lib/ic/vsn.mk b/lib/ic/vsn.mk index e21fc8fbb2..fc1475974e 100644 --- a/lib/ic/vsn.mk +++ b/lib/ic/vsn.mk @@ -1 +1 @@ -IC_VSN = 4.2.29 +IC_VSN = 4.2.30 diff --git a/lib/inets/doc/src/Makefile b/lib/inets/doc/src/Makefile index 53d505b102..1a8e1c7ca8 100644 --- a/lib/inets/doc/src/Makefile +++ b/lib/inets/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2011. All Rights Reserved. +# Copyright Ericsson AB 1997-2012. 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,6 +48,7 @@ XML_REF3_FILES = \ inets.xml \ ftp.xml \ tftp.xml \ + http_uri.xml\ httpc.xml\ httpd.xml \ httpd_conf.xml \ @@ -139,13 +140,13 @@ clean_man: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(HTMLDIR)/* $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/inets/doc/src/book.xml b/lib/inets/doc/src/book.xml index 7da0abd98f..51cbb2d963 100644 --- a/lib/inets/doc/src/book.xml +++ b/lib/inets/doc/src/book.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE book SYSTEM "book.dtd"> <book xmlns:xi="http://www.w3.org/2001/XInclude"> <header titlestyle="normal"> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/fascicules.xml b/lib/inets/doc/src/fascicules.xml index 101e745722..ea3b988882 100644 --- a/lib/inets/doc/src/fascicules.xml +++ b/lib/inets/doc/src/fascicules.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE fascicules SYSTEM "fascicules.dtd"> <fascicules> diff --git a/lib/inets/doc/src/ftp_client.xml b/lib/inets/doc/src/ftp_client.xml index 7f62a453a6..b44674d997 100644 --- a/lib/inets/doc/src/ftp_client.xml +++ b/lib/inets/doc/src/ftp_client.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/http_uri.xml b/lib/inets/doc/src/http_uri.xml new file mode 100644 index 0000000000..bd31ae42d2 --- /dev/null +++ b/lib/inets/doc/src/http_uri.xml @@ -0,0 +1,160 @@ +<?xml version="1.0" encoding="iso-8859-1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2012</year><year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>http_uri</title> + <prepared></prepared> + <responsible></responsible> + <docno></docno> + <date></date> + <rev></rev> + </header> + + <module>http_uri</module> + <modulesummary>URI utility module</modulesummary> + + <description> + <p>This module provides utility functions for working with URIs, + according to RFC 3986. </p> + + </description> + + <section> + <title>COMMON DATA TYPES </title> + <p>Type definitions that are used more than once in + this module:</p> + <code type="none"><![CDATA[ +boolean() = true | false +string() = list of ASCII characters + ]]></code> + + </section> + + <section> + <title>URI DATA TYPES </title> + <p>Type definitions that are related to URI:</p> + <p>For more information about URI, see RFC 3986. </p> + + <code type="none"><![CDATA[ +uri() = string() - Syntax according to the URI definition in rfc 3986, ex: "http://www.erlang.org/" +user_info() = string() +scheme() = atom() - Example: http, https +host() = string() +port() = pos_integer() +path() = string() - Representing a file path or directory path +query() = string() + ]]></code> + + <marker id="scheme_defaults"></marker> + </section> + + <funcs> + <func> + <name>scheme_defaults() -> SchemeDefaults</name> + <fsummary>A list of scheme and their default ports</fsummary> + <type> + <v>SchemeDefaults = [{scheme(), default_scheme_port_number()}] </v> + <v>default_scheme_port_number() = pos_integer()</v> + </type> + <desc> + <p>This function provides a list of the scheme and their default + port numbers currently supported (by default) by this utility. </p> + + <marker id="parse"></marker> + </desc> + </func> + + <func> + <name>parse(URI) -> {ok, Result} | {error, Reason}</name> + <name>parse(URI, Options) -> {ok, Result} | {error, Reason}</name> + <fsummary>Parse an URI</fsummary> + <type> + <v>URI = uri() </v> + <v>Options = [Option] </v> + <v>Option = {ipv6_host_with_brackets, boolean()} | + {scheme_defaults, scheme_defaults()}]</v> + <v>Result = {Scheme, UserInfo, Host, Port, Path, Query}</v> + <v>UserInfo = user_info()</v> + <v>Host = host()</v> + <v>Port = pos_integer()</v> + <v>Path = path()</v> + <v>Query = query()</v> + <v>Reason = term() </v> + </type> + <desc> + <p>This function is used to parse an URI. If no scheme defaults + are provided, the value of + <seealso marker="#scheme_defaults">scheme_defaults</seealso> + function will be used. </p> + + <p>Note that when parsing an URI with an unknown scheme (that is, + a scheme not found in the scheme defaults) a port number must be + provided or else the parsing will fail. </p> + + <marker id="encode"></marker> + </desc> + </func> + + <func> + <name>encode(URI) -> HexEncodedURI</name> + + <fsummary>Hex encode an URI</fsummary> + <type> + <v>URI = uri()</v> + <v>HexEncodedURI = string() - Hex encoded uri</v> + </type> + + <desc> + <p>Hex encode an URI. </p> + + <marker id="decode"></marker> + </desc> + </func> + + <func> + <name>decode(HexEncodedURI) -> URI</name> + + <fsummary>Decode a hex encoded URI</fsummary> + <type> + <v>HexEncodedURI = string() - A possibly hex encoded uri</v> + <v>URI = uri()</v> + </type> + + <desc> + <p>Decode a possibly hex encoded URI. </p> + + </desc> + </func> + + </funcs> + +<!-- + <section> + <title>SEE ALSO</title> + <p>RFC 2616, <seealso marker="inets">inets(3)</seealso>, + <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso>, + <seealso marker="ssl:ssl">ssl(3)</seealso> + </p> + </section> +--> + +</erlref> diff --git a/lib/inets/doc/src/httpc.xml b/lib/inets/doc/src/httpc.xml index 48a2089605..14ce3cbe7f 100644 --- a/lib/inets/doc/src/httpc.xml +++ b/lib/inets/doc/src/httpc.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2004</year><year>2011</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -480,66 +480,98 @@ apply(Module, Function, [ReplyInfo | Args]) <d>ex: "134.138" or "[FEDC:BA98" (all IP-addresses starting with 134.138 or FEDC:BA98), "66.35.250.150" or "[2010:836B:4179::836B:4179]" (a complete IP-address).</d> <v>MaxSessions = integer() </v> <d>Default is <c>2</c>. - Maximum number of persistent connections to a host.</d> + Maximum number of persistent connections to a host.</d> <v>MaxKeepAlive = integer() </v> - <d>Default is <c>5</c>. - Maximum number of outstanding requests on the same connection to - a host.</d> - <v>KeepAliveTimeout = integer() </v> - <d>Default is <c>120000</c> (= 2 min). - If a persistent connection is idle longer than the - <c>keep_alive_timeout</c> the client will close the connection. - The server may also have such a time out but you should - not count on it!</d> + <d>Default is <c>5</c>. + Maximum number of outstanding requests on the same connection to + a host.</d> + <v>KeepAliveTimeout = integer() </v> + <d>Default is <c>120000</c> (= 2 min). + If a persistent connection is idle longer than the + <c>keep_alive_timeout</c> in milliseconds, + the client will close the connection. + The server may also have such a time out but you should + not count on it!</d> <v>MaxPipeline = integer() </v> - <d>Default is <c>2</c>. - Maximum number of outstanding requests on a pipelined connection to a host.</d> - <v>PipelineTimeout = integer() </v> - <d>Default is <c>0</c>, - which will result in pipelining not being used. - If a persistent connection is idle longer than the - <c>pipeline_timeout</c> the client will close the connection. </d> + <d>Default is <c>2</c>. + Maximum number of outstanding requests on a pipelined connection + to a host.</d> + <v>PipelineTimeout = integer() </v> + <d>Default is <c>0</c>, + which will result in pipelining not being used. + If a persistent connection is idle longer than the + <c>pipeline_timeout</c> in milliseconds, + the client will close the connection. </d> <v>CookieMode = enabled | disabled | verify </v> <d>Default is <c>disabled</c>. - If Cookies are enabled all valid cookies will automatically be - saved in the client manager's cookie database. - If the option <c>verify</c> is used the function <c>store_cookies/2</c> - has to be called for the cookies to be saved.</d> - <v>IpFamily = inet | inet6 | inet6fb4 </v> - <d>By default <c>inet</c>. - When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6. - It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>. - The option is here to provide a workaround for buggy ipv6 stacks to ensure that - ipv4 will always work.</d> + If Cookies are enabled all valid cookies will automatically be + saved in the client manager's cookie database. + If the option <c>verify</c> is used the function <c>store_cookies/2</c> + has to be called for the cookies to be saved.</d> + <v>IpFamily = inet | inet6 | inet6fb4 </v> + <d>By default <c>inet</c>. + When it is set to <c>inet6fb4</c> you can use both ipv4 and ipv6. + It first tries <c>inet6</c> and if that does not works falls back to <c>inet</c>. + The option is here to provide a workaround for buggy ipv6 stacks to ensure that + ipv4 will always work.</d> <v>IpAddress = ip_address() </v> - <d>If the host has several network interfaces, this option specifies which one to use. - See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> + <d>If the host has several network interfaces, this option specifies which one to use. + See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> <v>Port = integer() </v> - <d>Specify which local port number to use. - See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> - <v>VerboseMode = false | verbose | debug | trace </v> - <d>Default is <c>false</c>. - This option is used to switch on (or off) - different levels of erlang trace on the client. - It is a debug feature.</d> + <d>Specify which local port number to use. + See <seealso marker="kernel:gen_tcp#connect">gen_tcp:connect/3,4</seealso> for more info. </d> + <v>VerboseMode = false | verbose | debug | trace </v> + <d>Default is <c>false</c>. + 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() | pid() (when started <c>stand_alone</c>)</v> </type> <desc> - <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 - with or without pipeline depending on configuration - and current circumstances. The HTTP/1.1 specification does not - provide a guideline for how many requests would be - ideal to be sent on a persistent connection, - this very much depends on the - application. Note that a very long queue of requests may cause a - user perceived delay as earlier requests may take a long time - to complete. The HTTP/1.1 specification does suggest a - limit of 2 persistent connections per server, which is the - default value of the <c>max_sessions</c> option. </p> - </note> + <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 + with or without pipeline depending on configuration + and current circumstances. The HTTP/1.1 specification does not + provide a guideline for how many requests would be + ideal to be sent on a persistent connection, + this very much depends on the + application. Note that a very long queue of requests may cause a + user perceived delay as earlier requests may take a long time + to complete. The HTTP/1.1 specification does suggest a + limit of 2 persistent connections per server, which is the + default value of the <c>max_sessions</c> option. </p> + </note> + + <marker id="get_options"></marker> + </desc> + </func> + + <func> + <name>get_options(OptionItems) -> {ok, Values} | {error, Reason}</name> + <name>get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason}</name> + <fsummary>Gets the currently used options.</fsummary> + <type> + <v>OptionItems = all | [option_item()]</v> + <v>option_item() = proxy | + max_sessions | + keep_alive_timeout | + max_keep_alive_length | + pipeline_timeout | + max_pipeline_length | + cookies | + ipfamily | + ip | + port | + socket_opts | + verbose</v> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + <v>Values = [{option_item(), term()}]</v> + <v>Reason = term() </v> + </type> + <desc> + <p>Retrieves the options currently used by the client.</p> <marker id="stream_next"></marker> </desc> @@ -619,6 +651,8 @@ apply(Module, Function, [ReplyInfo | Args]) <p>Resets (clears) the cookie database for the specified <c>Profile</c>. If no profile is specified the default profile will be used. </p> + + <marker id="which_cookies"></marker> </desc> </func> @@ -638,6 +672,42 @@ apply(Module, Function, [ReplyInfo | Args]) <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> + + <marker id="which_sessions"></marker> + </desc> + </func> + + <func> + <name>which_sessions() -> session_info()</name> + <name>which_sessions(Profile) -> session_info()</name> + <fsummary>Produces a slightly processed dump of the sessions database.</fsummary> + <type> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + <v>session_info() = {GoodSessions, BadSessions, NonSessions}</v> + <v>GoodSessions = session()</v> + <v>BadSessions = tuple()</v> + <v>NonSessions = term()</v> + </type> + <desc> + <p>This function produces a slightly processed dump of the session + database. It is intended for debugging. + If no profile is specified the default profile will be used. </p> + + <marker id="info"></marker> + </desc> + </func> + + <func> + <name>info() -> list()</name> + <name>info(Profile) -> list()</name> + <fsummary>Produces a list of miscelleneous info</fsummary> + <type> + <v>Profile = profile() | pid() (when started <c>stand_alone</c>)</v> + </type> + <desc> + <p>This function produces a list of miscelleneous info. + It is intended for debugging. + If no profile is specified the default profile will be used. </p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/httpd.xml b/lib/inets/doc/src/httpd.xml index f88099a82e..7e21229fcf 100644 --- a/lib/inets/doc/src/httpd.xml +++ b/lib/inets/doc/src/httpd.xml @@ -55,14 +55,14 @@ <section> <title>ERLANG HTTP SERVER SERVICE START/STOP </title> <p>A web server can be configured to start when starting the inets - application or started dynamically in runtime by calling the - Inets application API <c>inets:start(httpd, ServiceConfig)</c>, or - <c>inets:start(httpd, ServiceConfig, How)</c>, - see <seealso marker="inets">inets(3)</seealso> Below follows a - description of the available configuration options, also called - properties.</p> - - <marker id="file_prop"></marker> + application or started dynamically in runtime by calling the + Inets application API <c>inets:start(httpd, ServiceConfig)</c>, or + <c>inets:start(httpd, ServiceConfig, How)</c>, + see <seealso marker="inets">inets(3)</seealso> Below follows a + description of the available configuration options, also called + properties.</p> + + <marker id="props_file"></marker> <p><em>File properties</em></p> <p>When the web server is started @@ -76,21 +76,25 @@ list.</p> <taglist> + <marker id="prop_proplist_file"></marker> <tag>{proplist_file, path()}</tag> <item> - If this property is defined inets will expect to find - all other properties defined in this file. Note that the - file must include all properties listed under mandatory - properties. </item> - <tag>{file, path()}</tag> + <p>If this property is defined inets will expect to find + all other properties defined in this file. Note that the + file must include all properties listed under mandatory + properties. </p> + </item> - <item> If this property is defined - inets will expect to find all other properties defined in this - file, that uses Apache like syntax. Note that the file must - include all properties listed under mandatory properties. The - Apache like syntax is the property, written as one word where - each new word begins with a capital, followed by a white-space - followed by the value followed by a new line. Ex: + <marker id="prop_file"></marker> + <tag>{file, path()}</tag> + <item> + <p>If this property is defined inets will expect to find all + other properties defined in this file, that uses Apache like + syntax. Note that the file must include all properties listed + under mandatory properties. The Apache like syntax is the property, + written as one word where each new word begins with a capital, + followed by a white-space followed by the value followed by a + new line. Ex: </p> <code> {server_root, "/urs/local/www"} -> ServerRoot /usr/local/www @@ -114,40 +118,51 @@ <p>The properties proplist_file and file are mutually exclusive.</p> </note> - <marker id="mand_prop"></marker> + <marker id="props_mand"></marker> <p><em>Mandatory properties</em></p> <taglist> + <marker id="prop_port"></marker> <tag>{port, integer()} </tag> <item> - The port that the HTTP server shall listen on. - If zero is specified as port, an arbitrary available port - will be picked and you can use the httpd:info/2 function to find - out which port was picked. </item> + <p>The port that the HTTP server shall listen on. + If zero is specified as port, an arbitrary available port + will be picked and you can use the httpd:info/2 function to find + out which port was picked. </p> + </item> + + <marker id="prop_server_name"></marker> <tag>{server_name, string()} </tag> <item> - The name of your server, normally a fully qualified domain - name. + <p>The name of your server, normally a fully qualified domain name. </p> </item> + + <marker id="prop_server_root"></marker> <tag>{server_root, path()} </tag> <item> - Defines the servers home directory where log files etc can + <p>Defines the servers home directory where log files etc can be stored. Relative paths specified in other properties refer - to this directory.</item> + to this directory. </p> + </item> + + <marker id="prop_doc_root"></marker> <tag>{document_root, path()}</tag> <item> Defines the top directory for the documents that - are available on the HTTP server.</item> + are available on the HTTP server. + </item> </taglist> - <marker id="comm_prop"></marker> + <marker id="props_comm"></marker> <p><em>Communication properties</em> </p> <taglist> + <marker id="prop_bind_address"></marker> <tag>{bind_address, ip_address() | hostname() | any} </tag> <item> - Defaults to <c>any</c>. Note that <c>any</c> is denoted <em>*</em> - in the apache like configuration file. + <p>Defaults to <c>any</c>. Note that <c>any</c> is denoted <em>*</em> + in the apache like configuration file. </p> </item> + <marker id="prop_socket_type"></marker> <tag>{socket_type, ip_comm | ssl | essl}</tag> <item> <p>When using ssl, there are currently only one alternative. @@ -156,6 +171,7 @@ <p>Defaults to <c>ip_comm</c>. </p> </item> + <marker id="prop_ipfamily"></marker> <tag>{ipfamily, inet | inet6 | inet6fb4}</tag> <item> <p>Defaults to <c>inet6fb4. </c> </p> @@ -165,74 +181,93 @@ </taglist> + <marker id="props_api_modules"></marker> <p><em>Erlang Web server API modules</em> </p> <taglist> + <marker id="prop_modules"></marker> <tag>{modules, [atom()]} </tag> <item> - Defines which modules the HTTP server will use to handle + <p>Defines which modules the HTTP server will use to handle requests. Defaults to: <c>[mod_alias, mod_auth, mod_esi, mod_actions, mod_cgi, mod_dir, mod_get, mod_head, mod_log, mod_disk_log] </c> Note that some mod-modules are dependent on others, so the order can not be entirely arbitrary. See the <seealso marker="http_server"> Inets Web server Modules in the - Users guide</seealso> for more information. + Users guide</seealso> for more information. </p> </item> - </taglist> + </taglist> - <marker id="limit_prop"></marker> + <marker id="props_limit"></marker> <p><em>Limit properties</em> </p> <taglist> + <marker id="prop_disable_chunked_encoding"></marker> <tag>{disable_chunked_transfer_encoding_send, boolean()}</tag> <item> - This property allows you to disable chunked + <p>This property allows you to disable chunked transfer-encoding when sending a response to a HTTP/1.1 - client, by default this is false.</item> + client, by default this is false. </p> + </item> + <marker id="prop_keep_alive"></marker> <tag>{keep_alive, boolean()}</tag> <item> - Instructs the server whether or not to use persistent + <p>Instructs the server whether or not to use persistent connections when the client claims to be HTTP/1.1 - compliant, default is true.</item> + compliant, default is true. </p> + </item> + <marker id="prop_keep_alive_timeout"></marker> <tag>{keep_alive_timeout, integer()}</tag> <item> - The number of seconds the server will wait for a + <p>The number of seconds the server will wait for a subsequent request from the client before closing the - connection. Default is 150.</item> + connection. Default is 150. </p> + </item> + <marker id="prop_max_body_size"></marker> <tag>{max_body_size, integer()}</tag> <item> - Limits the size of the message body of HTTP request. - By the default there is no limit.</item> + <p>Limits the size of the message body of HTTP request. + By the default there is no limit. </p> + </item> + <marker id="prop_max_clients"></marker> <tag>{max_clients, integer()}</tag> <item> - Limits the number of simultaneous requests that can be - supported. Defaults to 150. </item> + <p>Limits the number of simultaneous requests that can be + supported. Defaults to 150. </p> + </item> + <marker id="prop_max_header_size"></marker> <tag>{max_header_size, integer()}</tag> <item> - Limits the size of the message header of HTTP request. - Defaults to 10240. + <p>Limits the size of the message header of HTTP request. + Defaults to 10240. </p> </item> + <marker id="prop_max_uri"></marker> <tag>{max_uri, integer()}</tag> <item> - Limits the size of the HTTP request URI. By - default there is no limit.</item> + <p>Limits the size of the HTTP request URI. By + default there is no limit. </p> + </item> + <marker id="prop_max_keep_alive_req"></marker> <tag>{max_keep_alive_requests, integer()}</tag> - <item> The number of request that a client can do on one + <item> + <p>The number of request that a client can do on one connection. When the server has responded to the number of requests defined by max_keep_alive_requests the server close the connection. The server will close it even if there are queued - request. Defaults to no limit.</item> + request. Defaults to no limit. </p> + </item> </taglist> - <marker id="admin_prop"></marker> + <marker id="props_admin"></marker> <p><em>Administrative properties</em></p> <taglist> + <marker id="prop_mime_types"></marker> <tag>{mime_types, [{MimeType, Extension}] | path()}</tag> <item> <p>Where MimeType = string() and Extension = string(). @@ -250,19 +285,43 @@ text/plain asc txt <p>Defaults to [{"html","text/html"},{"htm","text/html"}]</p> </item> + <marker id="prop_mime_type"></marker> <tag>{mime_type, string()}</tag> - <item> - When the server is asked to provide a document type which + <p>When the server is asked to provide a document type which cannot be determined by the MIME Type Settings, the server will - use this default type. </item> + use this default type. </p> + </item> + <marker id="prop_server_admin"></marker> <tag>{server_admin, string()}</tag> <item> - ServerAdmin defines the email-address of the server + <p>ServerAdmin defines the email-address of the server administrator, to be included in any error messages returned by - the server.</item> + the server. </p> + </item> + <marker id="prop_server_tokens"></marker> + <tag>{server_tokens, prod|major|minor|minimal|os|full|{private, string()}}</tag> + <item> + <p>ServerTokens defines how the value of the server header + should look. </p> + <p>Example: Assuming the version of inets is 5.8.1, + here is what the server header string could look like for + the different values of server-tokens: </p> + <pre> +prod "inets" +major "inets/5" +minor "inets/5.8" +minimal "inets/5.8.1" +os "inets/5.8.1 (unix)" +full "inets/5.8.1 (unix/linux) OTP/R15B" +{private, "foo/bar"} "foo/bar" + </pre> + <p>By default, the value is as before, which is <c>minimal</c>. </p> + </item> + + <marker id="prop_log_format"></marker> <tag>{log_format, common | combined}</tag> <item> <p>Defines if access logs should be written according to the common @@ -307,8 +366,9 @@ bytes <p>This affects the access logs written by mod_log and mod_disk_log. </p> - </item> - + </item> + + <marker id="prop_elog_format"></marker> <tag>{error_log_format, pretty | compact}</tag> <item> <p>Defaults to pretty. If the error log is meant to be read @@ -330,63 +390,77 @@ bytes </taglist> - <marker id="ssl_prop"></marker> + <marker id="props_ssl"></marker> <p><em>ssl properties</em></p> <taglist> + <marker id="prop_ssl_ca_cert_file"></marker> <tag>{ssl_ca_certificate_file, path()}</tag> <item> - Used as cacertfile option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> </item> + <p>Used as cacertfile option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> + </item> + <marker id="prop_ssl_cert_file"></marker> <tag>{ssl_certificate_file, path()}</tag> <item> - Used as certfile option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> + <p>Used as certfile option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> </item> + <marker id="prop_ssl_ciphers"></marker> <tag>{ssl_ciphers, list()}</tag> <item> - Used as ciphers option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> + <p>Used as ciphers option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> </item> + <marker id="prop_ssl_verify_client"></marker> <tag>{ssl_verify_client, integer()}</tag> <item> - Used as verify option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> </item> + <p>Used as verify option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> + </item> + <marker id="prop_ssl_verify_depth"></marker> <tag>{ssl_verify_depth, integer()}</tag> <item> - Used as depth option in ssl:listen/2 see - <seealso marker="ssl:ssl">ssl(3)</seealso> </item> + <p>Used as depth option in ssl:listen/2 see + <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> + </item> + <marker id="prop_ssl_passwd_callback_funct"></marker> <tag>{ssl_password_callback_function, atom()}</tag> <item> - Used together with ssl_password_callback_module + <p>Used together with ssl_password_callback_module to retrieve a value to use as password option to ssl:listen/2 - see <seealso marker="ssl:ssl">ssl(3)</seealso> + see <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> </item> + <marker id="prop_ssl_passwd_callback_args"></marker> <tag>{ssl_password_callback_arguments, list()}</tag> <item> - Used together with ssl_password_callback_function to supply a + <p>Used together with ssl_password_callback_function to supply a list of arguments to the callback function. If not specified - the callback function will be assumed to have arity 0. </item> + the callback function will be assumed to have arity 0. </p> + </item> + <marker id="prop_ssl_passwd_callback_mod"></marker> <tag>{ssl_password_callback_module, atom()}</tag> <item> - Used together with ssl_password_callback_function + <p>Used together with ssl_password_callback_function to retrieve a value to use as password option to ssl:listen/2 - see <seealso marker="ssl:ssl">ssl(3)</seealso></item> + see <seealso marker="ssl:ssl">ssl(3)</seealso>. </p> + </item> </taglist> - <marker id="alias_prop"></marker> + <marker id="props_alias"></marker> <p><em>URL aliasing properties - requires mod_alias</em></p> <taglist> + <marker id="prop_alias"></marker> <tag>{alias, {Alias, RealName}}</tag> - - <item> Where Alias = string() and RealName = string(). + <item> + <p>Where Alias = string() and RealName = string(). The Alias property allows documents to be stored in the local file system instead of the document_root location. URLs with a path that begins with url-path is mapped to local files that begins with @@ -395,11 +469,13 @@ bytes <code>{alias, {"/image", "/ftp/pub/image"}</code> and an access to http://your.server.org/image/foo.gif would refer to - the file /ftp/pub/image/foo.gif.</item> + the file /ftp/pub/image/foo.gif. </p> + </item> - <tag>{re_write, {Re, Replacement}}</tag> - - <item> Where Re = string() and Replacement = string(). + <marker id="prop_re_write"></marker> + <tag>{re_write, {Re, Replacement}}</tag> + <item> + <p>Where Re = string() and Replacement = string(). The ReWrite property allows documents to be stored in the local file system instead of the document_root location. URLs are rewritten by re:replace/3 to produce a path in the local filesystem. @@ -419,13 +495,13 @@ bytes Beware of trailing space in Replacement that will be used. If you must have a space in Re use e.g the character encoding - <code>\040</code> see <seealso marker="stdlib:re">re(3)</seealso>. + <code>\040</code> see <seealso marker="stdlib:re">re(3)</seealso>. </p> </item> - <tag>{directory_index, [string()]}</tag> - + <marker id="prop_dir_idx"></marker> + <tag>{directory_index, [string()]}</tag> <item> - DirectoryIndex specifies a list of resources to look for + <p>DirectoryIndex specifies a list of resources to look for if a client requests a directory using a / at the end of the directory name. file depicts the name of a file in the directory. Several files may be given, in which case the server @@ -436,70 +512,79 @@ bytes and access to http://your.server.org/docs/ would return http://your.server.org/docs/index.html or http://your.server.org/docs/welcome.html if index.html do not - exist. + exist. </p> </item> </taglist> - <marker id="cgi_prop"></marker> + <marker id="props_cgi"></marker> <p><em>CGI properties - requires mod_cgi</em></p> <taglist> + <marker id="prop_script_alias"></marker> <tag>{script_alias, {Alias, RealName}}</tag> - <item> Where Alias = string() and RealName = string(). + <item> + <p>Where Alias = string() and RealName = string(). Has the same behavior as the Alias property, except that it also marks the target directory as containing CGI scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - <code> {script_alias, {"/cgi-bin/", "/web/cgi-bin/"}</code> + <code>{script_alias, {"/cgi-bin/", "/web/cgi-bin/"}</code> and an access to http://your.server.org/cgi-bin/foo would cause - the server to run the script /web/cgi-bin/foo. + the server to run the script /web/cgi-bin/foo. </p> </item> + <marker id="prop_script_re_write"></marker> <tag>{script_re_write, {Re, Replacement}}</tag> - <item> Where Re = string() and Replacement = string(). + <item> + <p>Where Re = string() and Replacement = string(). Has the same behavior as the ReWrite property, except that it also marks the target directory as containing CGI scripts. URLs with a path beginning with url-path are mapped to scripts beginning with directory-filename, for example: - <code> {script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}</code> + <code>{script_re_write, {"^/cgi-bin/(\\d+)/", "/web/\\1/cgi-bin/"}</code> and an access to http://your.server.org/cgi-bin/17/foo would cause - the server to run the script /web/17/cgi-bin/foo. + the server to run the script /web/17/cgi-bin/foo. </p> </item> + <marker id="prop_script_nocache"></marker> <tag>{script_nocache, boolean()}</tag> - <item> - If ScriptNoCache is set to true the HTTP server will by + <p>If ScriptNoCache is set to true the HTTP server will by default add the header fields necessary to prevent proxies from caching the page. Generally this is something you want. Defaults - to false.</item> + to false. </p> + </item> + <marker id="prop_script_timeout"></marker> <tag>{script_timeout, integer()}</tag> - <item> - The time in seconds the web server will wait between each + <p>The time in seconds the web server will wait between each chunk of data from the script. If the CGI-script not delivers any data before the timeout the connection to the client will be - closed. Defaults to 15. </item> + closed. Defaults to 15. </p> + </item> + <marker id="prop_action"></marker> <tag>{action, {MimeType, CgiScript}} - requires mod_action</tag> - - <item>Where MimeType = string() and CgiScript = string(). + <item> + <p>Where MimeType = string() and CgiScript = string(). Action adds an action, which will activate a cgi-script whenever a file of a certain mime-type is requested. It propagates the URL and file path of the requested document using the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - <code> {action, {"text/plain", "/cgi-bin/log_and_deliver_text"} - </code> + + <code>{action, {"text/plain", "/cgi-bin/log_and_deliver_text"}</code> + </p> </item> + <marker id="prop_script"></marker> <tag>{script, {Method, CgiScript}} - requires mod_action</tag> - - <item>Where Method = string() and CgiScript = string(). + <item> + <p>Where Method = string() and CgiScript = string(). Script adds an action, which will activate a cgi-script whenever a file is requested using a certain HTTP method. The method is either GET or POST as defined in RFC 1945. It @@ -507,18 +592,19 @@ bytes the standard CGI PATH_INFO and PATH_TRANSLATED environment variables. - <code> {script, {"PUT", "/cgi-bin/put"} - </code> + <code>{script, {"PUT", "/cgi-bin/put"}</code> + </p> </item> </taglist> - <marker id="esi_prop"></marker> + <marker id="props_esi"></marker> <p><em>ESI properties - requires mod_esi</em></p> <taglist> - <tag>{erl_script_alias, {URLPath, [AllowedModule]}}</tag> - - <item>Where URLPath = string() and AllowedModule = atom(). + <marker id="prop_esi_alias"></marker> + <tag>{erl_script_alias, {URLPath, [AllowedModule]}}</tag> + <item> + <p>Where URLPath = string() and AllowedModule = atom(). erl_script_alias marks all URLs matching url-path as erl scheme scripts. A matching URL is mapped into a specific module and function. For example: @@ -531,140 +617,151 @@ bytes would refer to httpd_example:yahoo/3 or, if that did not exist, httpd_example:yahoo/2 and http://your.server.org/cgi-bin/example/other:yahoo would - not be allowed to execute. + not be allowed to execute. </p> </item> + <marker id="prop_esi_nocache"></marker> <tag>{erl_script_nocache, boolean()}</tag> - <item> - If erl_script_nocache is set to true the server will add + <p>If erl_script_nocache is set to true the server will add http header fields that prevents proxies from caching the page. This is generally a good idea for dynamic content, since - the content often vary between each request. Defaults to false. + the content often vary between each request. + Defaults to false. </p> </item> + <marker id="prop_esi_timeout"></marker> <tag>{erl_script_timeout, integer()}</tag> - <item> - If erl_script_timeout sets the time in seconds the server will + <p>If erl_script_timeout sets the time in seconds the server will wait between each chunk of data to be delivered through mod_esi:deliver/2. Defaults to 15. This is only relevant - for scripts that uses the erl scheme. + for scripts that uses the erl scheme. </p> </item> + <marker id="prop_esi_timeout"></marker> <tag>{eval_script_alias, {URLPath, [AllowedModule]}}</tag> - - <item>Where URLPath = string() and AllowedModule = atom(). + <item> + <p>Where URLPath = string() and AllowedModule = atom(). Same as erl_script_alias but for scripts - using the eval scheme. Note that this is only supported - for backwards compatibility. The eval scheme is deprecated.</item> + using the eval scheme. Note that this is only supported + for backwards compatibility. The eval scheme is deprecated. </p> + </item> </taglist> - <marker id="log_prop"></marker> + <marker id="props_log"></marker> <p><em>Log properties - requires mod_log</em></p> <taglist> + <marker id="prop_elog"></marker> <tag>{error_log, path()}</tag> - <item> - Defines the filename of the error log file to be used to log + <p>Defines the filename of the error log file to be used to log server errors. If the filename does not begin with a slash (/) - it is assumed to be relative to the server_root</item> + it is assumed to be relative to the server_root. </p> + </item> + <marker id="prop_slog"></marker> <tag>{security_log, path()}</tag> - <item> - Defines the filename of the access log file to be used to - log security events. If the filename does not begin with a slash - (/) it is assumed to be relative to the server_root. + <p>Defines the filename of the access log file to be used to + log security events. If the filename does not begin with a slash + (/) it is assumed to be relative to the server_root. </p> </item> + <marker id="prop_tlog"></marker> <tag>{transfer_log, path()}</tag> - <item> - Defines the filename of the access log file to be used to + <p>Defines the filename of the access log file to be used to log incoming requests. If the filename does not begin with a - slash (/) it is assumed to be relative to the server_root. + slash (/) it is assumed to be relative to the server_root. </p> </item> </taglist> - <marker id="dlog_prop"></marker> + <marker id="props_dlog"></marker> <p><em>Disk Log properties - requires mod_disk_log</em></p> <taglist> + <marker id="prop_dlog_format"></marker> <tag>{disk_log_format, internal | external}</tag> - <item> - Defines the file-format of the log files see disk_log for + <p>Defines the file-format of the log files see disk_log for more information. If the internal file-format is used, the logfile will be repaired after a crash. When a log file is repaired data might get lost. When the external file-format is used httpd will not start if the log file is broken. Defaults to - external. + external. </p> </item> + <marker id="prop_edlog"></marker> <tag>{error_disk_log, internal | external}</tag> - <item> - Defines the filename of the (disk_log(3)) error log file + <p>Defines the filename of the (disk_log(3)) error log file to be used to log server errors. If the filename does not begin - with a slash (/) it is assumed to be relative to the server_root. + with a slash (/) it is assumed to be relative to the server_root. </p> </item> + <marker id="prop_edlog_size"></marker> <tag>{error_disk_log_size, {MaxBytes, MaxFiles}}</tag> - - <item>Where MaxBytes = integer() and MaxFiles = integer(). + <item> + <p>Where MaxBytes = integer() and MaxFiles = integer(). Defines the properties of the (disk_log(3)) error log file. The disk_log(3) error log file is of type wrap log and max-bytes will be written to each file and max-files will be - used before the first file is truncated and reused. </item> + used before the first file is truncated and reused. </p> + </item> + <marker id="prop_sdlog"></marker> <tag>{security_disk_log, path()}</tag> - <item> - Defines the filename of the (disk_log(3)) access log file + <p>Defines the filename of the (disk_log(3)) access log file which logs incoming security events i.e authenticated requests. If the filename does not begin with a slash (/) it - is assumed to be relative to the server_root. + is assumed to be relative to the server_root. </p> </item> + <marker id="prop_sdlog_size"></marker> <tag>{security_disk_log_size, {MaxBytes, MaxFiles}}</tag> - - <item>Where MaxBytes = integer() and MaxFiles = integer(). + <item> + <p>Where MaxBytes = integer() and MaxFiles = integer(). Defines the properties of the disk_log(3) access log file. The disk_log(3) access log file is of type wrap log and max-bytes will be written to each file and max-files will be - used before the first file is truncated and reused.</item> + used before the first file is truncated and reused. </p> + </item> - <tag>{transfer_disk_log, path()}</tag> - + <marker id="prop_tdlog"></marker> + <tag>{transfer_disk_log, path()}</tag> <item> - Defines the filename of the (disk_log(3)) access log file + <p>Defines the filename of the (disk_log(3)) access log file which logs incoming requests. If the filename does not begin with a slash (/) it is assumed to be relative to the - server_root. + server_root. </p> </item> + <marker id="prop_tdlog_size"></marker> <tag>{transfer_disk_log_size, {MaxBytes, MaxFiles}}</tag> - - <item>Where MaxBytes = integer() and MaxFiles = integer(). + <item> + <p>Where MaxBytes = integer() and MaxFiles = integer(). Defines the properties of the disk_log(3) access log file. The disk_log(3) access log file is of type wrap log and max-bytes will be written to each file and max-files will be - used before the first file is truncated and reused.</item> + used before the first file is truncated and reused. </p> + </item> </taglist> - <marker id="auth_prop"></marker> + <marker id="props_auth"></marker> <p><em>Authentication properties - requires mod_auth</em></p> + <marker id="prop_dri"></marker> <p><em>{directory, {path(), [{property(), term()}]}}</em></p> - <marker id="dir_prop"></marker> + <marker id="props_dir"></marker> <p>Here follows the valid properties for directories </p> <taglist> + <marker id="prop_allow_from"></marker> <tag>{allow_from, all | [RegxpHostString]}</tag> - <item> - Defines a set of hosts which should be granted access to a + <p>Defines a set of hosts which should be granted access to a given directory. For example: @@ -672,34 +769,36 @@ bytes <code>{allow_from, ["123.34.56.11", "150.100.23"] </code> The host 123.34.56.11 and all machines on the 150.100.23 - subnet are allowed access.</item> + subnet are allowed access. </p> + </item> + <marker id="prop_deny_from"></marker> <tag>{deny_from, all | [RegxpHostString]}</tag> - <item> - Defines a set of hosts + <p>Defines a set of hosts which should be denied access to a given directory. For example: <code>{deny_from, ["123.34.56.11", "150.100.23"] </code> The host 123.34.56.11 and all machines on the 150.100.23 - subnet are not allowed access.</item> + subnet are not allowed access. </p> + </item> + <marker id="prop_auth_type"></marker> <tag>{auth_type, plain | dets | mnesia}</tag> - <item> - Sets the type of authentication database that is used for the + <p>Sets the type of authentication database that is used for the directory.The key difference between the different methods is that dynamic data can be saved when Mnesia and Dets is used. This property is called AuthDbType in the Apache like - configuration files. + configuration files. </p> </item> + <marker id="prop_auth_user_file"></marker> <tag>{auth_user_file, path()}</tag> - <item> - Sets the name of a file which contains the list of users and + <p>Sets the name of a file which contains the list of users and passwords for user authentication. filename can be either absolute or relative to the <c>server_root</c>. If using the plain storage method, this file is a plain text file, where @@ -717,12 +816,13 @@ bytes storage method. For security reasons, make sure that the <c>auth_user_file</c> is stored outside the document tree of the Web server. If it is placed in the directory which it protects, - clients will be able to download it. + clients will be able to download it. </p> </item> + <marker id="prop_auth_group_file"></marker> <tag>{auth_group_file, path()}</tag> - - <item> Sets the name of a file which contains the list of user + <item> + <p>Sets the name of a file which contains the list of user groups for user authentication. Filename can be either absolute or relative to the <c>server_root</c>. If you use the plain storage method, the group file is a plain text file, where @@ -738,93 +838,109 @@ bytes For security reasons, make sure that the <c>auth_group_file</c> is stored outside the document tree of the Web server. If it is placed in the directory which it protects, clients will be - able to download it.</item> + able to download it. </p> + </item> + <marker id="prop_auth_name"></marker> <tag>{auth_name, string()}</tag> - <item> - Sets the name of the authorization realm (auth-domain) for + <p>Sets the name of the authorization realm (auth-domain) for a directory. This string informs the client about which user - name and password to use. </item> + name and password to use. </p> + </item> + <marker id="prop_auth_access_passwd"></marker> <tag>{auth_access_password, string()}</tag> - - <item> If set to other than "NoPassword" the password is required + <item> + <p>If set to other than "NoPassword" the password is required for all API calls. If the password is set to "DummyPassword" the password must be changed before any other API calls. To secure the authenticating data the password must be changed after the web server is started since it otherwise is written in clear - text in the configuration file.</item> + text in the configuration file. </p> + </item> + <marker id="prop_req_user"></marker> <tag>{require_user, [string()]}</tag> <item> - Defines users which should be granted access to a given - directory using a secret password. + <p>Defines users which should be granted access to a given + directory using a secret password. </p> </item> + <marker id="prop_req_grp"></marker> <tag>{require_group, [string()]}</tag> <item> - Defines users which should be granted access to a given - directory using a secret password. + <p>Defines users which should be granted access to a given + directory using a secret password. </p> </item> </taglist> - <marker id="htaccess_prop"></marker> + <marker id="props_htaccess"></marker> <p><em>Htaccess authentication properties - requires mod_htaccess</em></p> <taglist> + <marker id="prop_access_files"></marker> <tag>{access_files, [path()]}</tag> - - <item> Specify which filenames that are used for + <item> + <p>Specify which filenames that are used for access-files. When a request comes every directory in the path to the requested asset will be searched after files with the names specified by this parameter. If such a file is found the file will be parsed and the restrictions specified in it will - be applied to the request. + be applied to the request. </p> </item> </taglist> - <marker id="sec_prop"></marker> + <marker id="props_sec"></marker> <p><em>Security properties - requires mod_security </em></p> + <marker id="prop_sec_dir"></marker> <p><em>{security_directory, {path(), [{property(), term()}]}</em></p> - <marker id="sdir_prop"></marker> - <p> Here follows the valid properties for security directories</p> + <marker id="props_sdir"></marker> + <p>Here follows the valid properties for security directories</p> <taglist> - <tag>{data_file, path()}</tag> - + <marker id="prop_data_file"></marker> + <tag>{data_file, path()}</tag> <item> - Name of the security data file. The filename can either + <p>Name of the security data file. The filename can either absolute or relative to the server_root. This file is used to - store persistent data for the mod_security module. </item> - - <tag>{max_retries, integer()}</tag> + store persistent data for the mod_security module. </p> + </item> - <item> Specifies the maximum number of tries to authenticate a + <marker id="prop_max_retries"></marker> + <tag>{max_retries, integer()}</tag> + <item> + <p>Specifies the maximum number of tries to authenticate a user has before the user is blocked out. If a user successfully authenticates when the user has been blocked, the user will receive a 403 (Forbidden) response from the server. If the user makes a failed attempt while blocked the server will return 401 (Unauthorized), for security - reasons. Defaults to 3 may also be set to infinity.</item> + reasons. + Defaults to 3 may also be set to infinity. </p> + </item> + <marker id="prop_block_time"></marker> <tag>{block_time, integer()}</tag> - - <item> Specifies the number of minutes a user is blocked. After + <item> + <p>Specifies the number of minutes a user is blocked. After this amount of time, he automatically regains access. - Defaults to 60</item> + Defaults to 60. </p> + </item> + <marker id="prop_fail_exp_time"></marker> <tag>{fail_expire_time, integer()}</tag> - <item> - Specifies the number of minutes a failed user authentication + <p>Specifies the number of minutes a failed user authentication is remembered. If a user authenticates after this amount of time, his previous failed authentications are - forgotten. Defaults to 30</item> + forgotten. + Defaults to 30. </p> + </item> + <marker id="prop_auth_timeout"></marker> <tag>{auth_timeout, integer()}</tag> - <item> Specifies the number of seconds a successful user authentication is remembered. After this time has passed, the @@ -835,6 +951,7 @@ bytes <funcs> <func> + <marker id="info1"></marker> <name>info(Pid) -></name> <name>info(Pid, Properties) -> [{Option, Value}]</name> <fsummary>Fetches information about the HTTP server</fsummary> @@ -858,6 +975,7 @@ bytes </func> <func> + <marker id="info2"></marker> <name>info(Address, Port) -> </name> <name>info(Address, Port, Properties) -> [{Option, Value}] </name> <fsummary>Fetches information about the HTTP server</fsummary> @@ -883,6 +1001,7 @@ bytes </func> <func> + <marker id="reload_config"></marker> <name>reload_config(Config, Mode) -> ok | {error, Reason}</name> <fsummary>Reloads the HTTP server configuration without restarting the server.</fsummary> @@ -1003,6 +1122,7 @@ bytes </section> <funcs> <func> + <marker id="module_do"></marker> <name>Module:do(ModData)-> {proceed, OldData} | {proceed, NewData} | {break, NewData} | done</name> <fsummary>Called for each request to the Web server.</fsummary> <type> @@ -1046,7 +1166,9 @@ bytes closing the connection.</p> </desc> </func> + <func> + <marker id="module_load"></marker> <name>Module:load(Line, AccIn)-> eof | ok | {ok, AccOut} | {ok, AccOut, {Option, Value}} | {ok, AccOut, [{Option, Value}]} | {error, Reason} </name> <fsummary>Load is used to convert a line in a Apache like config file to a <c>{Option, Value}</c> tuple.</fsummary> @@ -1068,7 +1190,9 @@ bytes </p> </desc> </func> + <func> + <marker id="module_store"></marker> <name>Module:store({Option, Value}, Config)-> {ok, {Option, NewValue}} | {error, Reason} </name> <fsummary></fsummary> <type> @@ -1092,6 +1216,7 @@ bytes </func> <func> + <marker id="module_remove"></marker> <name>Module:remove(ConfigDB) -> ok | {error, Reason} </name> <fsummary>Callback function that is called when the Web server is closed.</fsummary> <type> @@ -1112,6 +1237,7 @@ bytes </section> <funcs> <func> + <marker id="parse_query"></marker> <name>parse_query(QueryString) -> [{Key,Value}]</name> <fsummary>Parse incoming data to <c>erl </c>and <c>eval </c>scripts.</fsummary> <type> @@ -1120,7 +1246,6 @@ bytes <v>Value = string()</v> </type> <desc> - <marker id="parse_query"></marker> <p><c>parse_query/1</c> parses incoming data to <c>erl</c> and <c>eval</c> scripts (See <seealso marker="mod_esi">mod_esi(3)</seealso>) as defined in the standard URL format, that is '+' becomes 'space' and decoding of diff --git a/lib/inets/doc/src/httpd_conf.xml b/lib/inets/doc/src/httpd_conf.xml index a1ad76a8ae..fc34f14ec3 100644 --- a/lib/inets/doc/src/httpd_conf.xml +++ b/lib/inets/doc/src/httpd_conf.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,11 +33,14 @@ Web server API programmer.</modulesummary> <description> <p>This module provides the Erlang Webserver API programmer with - utility functions for adding run-time configuration directives.</p> + utility functions for adding run-time configuration directives.</p> + + <marker id="check_enum"></marker> </description> + <funcs> <func> - <name>check_enum(EnumString,ValidEnumStrings) -> Result</name> + <name>check_enum(EnumString, ValidEnumStrings) -> Result</name> <fsummary>Check if string is a valid enumeration.</fsummary> <type> <v>EnumString = string()</v> @@ -47,10 +50,13 @@ <desc> <marker id="check_enum"></marker> <p><c>check_enum/2</c> checks if <c>EnumString</c> is a valid - enumeration of <c>ValidEnumStrings</c> in which case it is - returned as an atom.</p> + enumeration of <c>ValidEnumStrings</c> in which case it is + returned as an atom.</p> + + <marker id="clean"></marker> </desc> </func> + <func> <name>clean(String) -> Stripped</name> <fsummary>Remove leading and/or trailing white spaces.</fsummary> @@ -60,9 +66,12 @@ <desc> <marker id="clean"></marker> <p><c>clean/1</c> removes leading and/or trailing white spaces - from <c>String</c>.</p> + from <c>String</c>.</p> + + <marker id="custom_clean"></marker> </desc> </func> + <func> <name>custom_clean(String,Before,After) -> Stripped</name> <fsummary>Remove leading and/or trailing white spaces and custom characters.</fsummary> @@ -73,11 +82,14 @@ <desc> <marker id="custom_clean"></marker> <p><c>custom_clean/3</c> removes leading and/or trailing white - spaces and custom characters from <c>String</c>. <c>Before</c> - and <c>After</c> are regular expressions, as defined in - <c>regexp(3)</c>, describing the custom characters.</p> + spaces and custom characters from <c>String</c>. <c>Before</c> + and <c>After</c> are regular expressions, as defined in + <c>regexp(3)</c>, describing the custom characters.</p> + + <marker id="is_directory"></marker> </desc> </func> + <func> <name>is_directory(FilePath) -> Result</name> <fsummary>Check if a file path is a directory.</fsummary> @@ -91,13 +103,16 @@ <desc> <marker id="is_directory"></marker> <p><c>is_directory/1</c> checks if <c>FilePath</c> is a - directory in which case it is returned. Please read - <c>file(3)</c> for a description of <c>enoent</c>, - <c>eaccess</c> and <c>enotdir</c>. The definition of - the file info record can be found by including <c>file.hrl</c> - from the kernel application, see file(3).</p> + directory in which case it is returned. Please read + <c>file(3)</c> for a description of <c>enoent</c>, + <c>eaccess</c> and <c>enotdir</c>. The definition of + the file info record can be found by including <c>file.hrl</c> + from the kernel application, see file(3).</p> + + <marker id="is_file"></marker> </desc> </func> + <func> <name>is_file(FilePath) -> Result</name> <fsummary>Check if a file path is a regular file.</fsummary> @@ -111,13 +126,16 @@ <desc> <marker id="is_file"></marker> <p><c>is_file/1</c> checks if <c>FilePath</c> is a regular - file in which case it is returned. Read <c>file(3)</c> for a - description of <c>enoent</c>, <c>eaccess</c> and - <c>enotdir</c>. The definition of the file info record can be - found by including <c>file.hrl</c> from the kernel application, - see file(3).</p> + file in which case it is returned. Read <c>file(3)</c> for a + description of <c>enoent</c>, <c>eaccess</c> and + <c>enotdir</c>. The definition of the file info record can be + found by including <c>file.hrl</c> from the kernel application, + see file(3).</p> + + <marker id="make_integer"></marker> </desc> </func> + <func> <name>make_integer(String) -> Result</name> <fsummary>Return an integer representation of a string.</fsummary> diff --git a/lib/inets/doc/src/httpd_socket.xml b/lib/inets/doc/src/httpd_socket.xml index fba1a58d3a..58cd2ec575 100644 --- a/lib/inets/doc/src/httpd_socket.xml +++ b/lib/inets/doc/src/httpd_socket.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,10 +33,13 @@ Web server API programmer.</modulesummary> <description> <p>This module provides the Erlang Web server API module programmer - with utility functions for generic sockets communication. The - appropriate communication mechanism is transparently used, that - is <c>ip_comm</c> or <c>ssl</c>.</p> + with utility functions for generic sockets communication. The + appropriate communication mechanism is transparently used, that + is <c>ip_comm</c> or <c>ssl</c>.</p> + + <marker id="deliver"></marker> </description> + <funcs> <func> <name>deliver(SocketType, Socket, Data) -> Result</name> @@ -50,11 +53,14 @@ <desc> <marker id="deliver"></marker> <p><c>deliver/3</c> sends the <c>Binary</c> over the - <c>Socket</c> using the specified <c>SocketType</c>. Socket - and SocketType should be the socket and the socket_type form - the mod record as defined in httpd.hrl</p> + <c>Socket</c> using the specified <c>SocketType</c>. Socket + and SocketType should be the socket and the socket_type form + the mod record as defined in httpd.hrl</p> + + <marker id="peername"></marker> </desc> </func> + <func> <name>peername(SocketType,Socket) -> {Port,IPAddress}</name> <fsummary>Return the port and IP-address of the remote socket.</fsummary> @@ -67,9 +73,12 @@ <desc> <marker id="peername"></marker> <p><c>peername/3</c> returns the <c>Port</c> and - <c>IPAddress</c> of the remote <c>Socket</c>. </p> + <c>IPAddress</c> of the remote <c>Socket</c>. </p> + + <marker id="resolve"></marker> </desc> </func> + <func> <name>resolve() -> HostName</name> <fsummary>Return the official name of the current host.</fsummary> @@ -79,7 +88,7 @@ <desc> <marker id="resolve"></marker> <p><c>resolve/0</c> returns the official <c>HostName</c> of - the current host. </p> + the current host. </p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml index 6ac2b13c72..9f290084d2 100644 --- a/lib/inets/doc/src/httpd_util.xml +++ b/lib/inets/doc/src/httpd_util.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/inets_services.xml b/lib/inets/doc/src/inets_services.xml index c274d67f19..e282050b12 100644 --- a/lib/inets/doc/src/inets_services.xml +++ b/lib/inets/doc/src/inets_services.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/mod_alias.xml b/lib/inets/doc/src/mod_alias.xml index c783b99b23..265a1b8e76 100644 --- a/lib/inets/doc/src/mod_alias.xml +++ b/lib/inets/doc/src/mod_alias.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,8 +32,11 @@ <modulesummary>URL aliasing.</modulesummary> <description> <p>Erlang Webserver Server internal API for handling of things - such as interaction data exported by the mod_alias module.</p> + such as interaction data exported by the mod_alias module.</p> + + <marker id="default_index"></marker> </description> + <funcs> <func> <name>default_index(ConfigDB, Path) -> NewPath</name> @@ -45,17 +48,20 @@ <desc> <marker id="default_index"></marker> <p>If <c>Path</c> is a directory, <c>default_index/2</c>, it starts - searching for resources or files that are specified in the config - directive DirectoryIndex. - If an appropriate resource or file is found, it is appended to - the end of <c>Path</c> and then returned. <c>Path</c> is - returned unaltered, if no appropriate - file is found, or if <c>Path</c> is not a directory. - <c>config_db()</c> is the server config file in ETS table format - as described in - <seealso marker="http_server">Inets Users Guide.</seealso>.</p> + searching for resources or files that are specified in the config + directive DirectoryIndex. + If an appropriate resource or file is found, it is appended to + the end of <c>Path</c> and then returned. <c>Path</c> is + returned unaltered, if no appropriate + file is found, or if <c>Path</c> is not a directory. + <c>config_db()</c> is the server config file in ETS table format + as described in + <seealso marker="http_server">Inets Users Guide.</seealso>.</p> + + <marker id="path"></marker> </desc> </func> + <func> <name>path(PathData, ConfigDB, RequestURI) -> Path</name> <fsummary>Return the actual file path to a URL.</fsummary> @@ -67,15 +73,19 @@ <desc> <marker id="path"></marker> <p><c>path/3</c> returns the actual file <c>Path</c> in the - <c>RequestURI</c> (See RFC 1945). If the interaction data - <c>{real_name,{Path,AfterPath}}</c> has been exported by - mod_alias; - <c>Path</c> is returned. If no interaction data has been - exported, ServerRoot is used to - generate a file <c>Path</c>. <c>config_db()</c> and - <c>interaction_data()</c> are as defined in <seealso marker="http_server">Inets Users Guide</seealso>.</p> + <c>RequestURI</c> (See RFC 1945). If the interaction data + <c>{real_name,{Path,AfterPath}}</c> has been exported by + mod_alias; + <c>Path</c> is returned. If no interaction data has been + exported, ServerRoot is used to + generate a file <c>Path</c>. <c>config_db()</c> and + <c>interaction_data()</c> are as defined in + <seealso marker="http_server">Inets Users Guide</seealso>.</p> + + <marker id="real_name"></marker> </desc> </func> + <func> <name>real_name(ConfigDB, RequestURI, Aliases) -> Ret</name> <fsummary>Expand a request uri using Alias config directives.</fsummary> @@ -89,18 +99,24 @@ <desc> <marker id="real_name"></marker> <p><c>real_name/3</c> traverses <c>Aliases</c>, typically - extracted from <c>ConfigDB</c>, and matches each - <c>FakeName</c> with <c>RequestURI</c>. If a match is found - <c>FakeName</c> is replaced with <c>RealName</c> in the - match. The resulting path is split into two parts, that - is <c>ShortPath</c> and <c>AfterPath</c> as defined in <seealso marker="httpd_util#split_path">httpd_util:split_path/1</seealso>. - <c>Path</c> is generated from <c>ShortPath</c>, that is - the result from <seealso marker="#default_index">default_index/2</seealso> with - <c>ShortPath</c> as an argument. - <c>config_db()</c> is the server config file in ETS table - format as described in <seealso marker="http_server">Inets User Guide.</seealso>. </p> + extracted from <c>ConfigDB</c>, and matches each + <c>FakeName</c> with <c>RequestURI</c>. If a match is found + <c>FakeName</c> is replaced with <c>RealName</c> in the + match. The resulting path is split into two parts, that + is <c>ShortPath</c> and <c>AfterPath</c> as defined in + <seealso marker="httpd_util#split_path">httpd_util:split_path/1</seealso>. + <c>Path</c> is generated from <c>ShortPath</c>, that is + the result from + <seealso marker="#default_index">default_index/2</seealso> with + <c>ShortPath</c> as an argument. + <c>config_db()</c> is the server config file in ETS table + format as described in + <seealso marker="http_server">Inets User Guide.</seealso>. </p> + + <marker id="real_script_name"></marker> </desc> </func> + <func> <name>real_script_name(ConfigDB,RequestURI,ScriptAliases) -> Ret</name> <fsummary>Expand a request uri using ScriptAlias config directives.</fsummary> @@ -114,15 +130,15 @@ <desc> <marker id="real_script_name"></marker> <p><c>real_name/3</c> traverses <c>ScriptAliases</c>, - typically extracted from <c>ConfigDB</c>, and matches each - <c>FakeName</c> with <c>RequestURI</c>. If a match is found - <c>FakeName</c> is replaced with <c>RealName</c> in the - match. If the resulting match is not an executable script - <c>not_a_script</c> is returned. If it is a script the - resulting script path is in two parts, that is - <c>ShortPath</c> and <c>AfterPath</c> as defined in <seealso marker="httpd_util#split_script_path">httpd_util:split_script_path/1</seealso>. - <c>config_db()</c> is the server config file in ETS table - format as described in <seealso marker="http_server">Inets Users Guide.</seealso>.</p> + typically extracted from <c>ConfigDB</c>, and matches each + <c>FakeName</c> with <c>RequestURI</c>. If a match is found + <c>FakeName</c> is replaced with <c>RealName</c> in the + match. If the resulting match is not an executable script + <c>not_a_script</c> is returned. If it is a script the + resulting script path is in two parts, that is + <c>ShortPath</c> and <c>AfterPath</c> as defined in <seealso marker="httpd_util#split_script_path">httpd_util:split_script_path/1</seealso>. + <c>config_db()</c> is the server config file in ETS table + format as described in <seealso marker="http_server">Inets Users Guide.</seealso>.</p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/mod_auth.xml b/lib/inets/doc/src/mod_auth.xml index 2134ebeeae..a176242a72 100644 --- a/lib/inets/doc/src/mod_auth.xml +++ b/lib/inets/doc/src/mod_auth.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1997</year><year>2011</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -32,8 +32,11 @@ <modulesummary>User authentication using text files, dets or mnesia database.</modulesummary> <description> <p>This module provides for basic user authentication using - textual files, dets databases as well as mnesia databases. </p> + textual files, dets databases as well as mnesia databases. </p> + + <marker id="add_user"></marker> </description> + <funcs> <func> <name>add_user(UserName, Options) -> true| {error, Reason}</name> @@ -55,12 +58,17 @@ <desc> <marker id="user_api"></marker> <marker id="add_user"></marker> - <p><c>add_user/2, add_user/5</c> and <c>add_user/6</c> adds a user to the user - database. If the operation is successful, this function returns - <c>true</c>. If an error occurs, <c>{error,Reason}</c> is returned. When <c>add_user/2</c> - is called the Password, UserData Port and Dir options is mandatory.</p> + <p><c>add_user/2, add_user/5</c> and <c>add_user/6</c> adds a + user to the user + database. If the operation is successful, this function returns + <c>true</c>. If an error occurs, <c>{error,Reason}</c> is returned. + When <c>add_user/2</c> is called the Password, + UserData Port and Dir options is mandatory.</p> + + <marker id="delete_user"></marker> </desc> </func> + <func> <name>delete_user(UserName,Options) -> true | {error, Reason}</name> <name>delete_user(UserName, Port, Dir) -> true | {error, Reason}</name> @@ -79,13 +87,16 @@ <desc> <marker id="delete_user"></marker> <p><c>delete_user/2, delete_user/3</c> and <c>delete_user/4</c> - deletes a user - from the user database. If the operation is successful, this - function returns <c>true</c>. If an error occurs, - <c>{error,Reason}</c> is returned. When <c>delete_user/2</c> is - called the Port and Dir options are mandatory.</p> + deletes a user from the user database. + If the operation is successful, this function returns <c>true</c>. + If an error occurs, <c>{error,Reason}</c> is returned. + When <c>delete_user/2</c> is called the Port and Dir options + are mandatory.</p> + + <marker id="get_user"></marker> </desc> </func> + <func> <name>get_user(UserName,Options) -> {ok, #httpd_user} |{error, Reason}</name> <name>get_user(UserName, Port, Dir) -> {ok, #httpd_user} | {error, Reason}</name> @@ -104,12 +115,15 @@ <desc> <marker id="get_user"></marker> <p><c>get_user/2, get_user/3</c> and <c>get_user/4</c> returns a - <c>httpd_user</c> record containing the userdata for a - specific user. If the user cannot be found, <c>{error, Reason}</c> - is returned. When <c>get_user/2</c> is called the Port and Dir - options are mandatory.</p> + <c>httpd_user</c> record containing the userdata for a + specific user. If the user cannot be found, <c>{error, Reason}</c> + is returned. When <c>get_user/2</c> is called the Port and Dir + options are mandatory.</p> + + <marker id="list_users"></marker> </desc> </func> + <func> <name>list_users(Options) -> {ok, Users} | {error, Reason}</name> <name>list_users(Port, Dir) -> {ok, Users} | {error, Reason}</name> @@ -127,12 +141,16 @@ </type> <desc> <marker id="list_users"></marker> - <p><c>list_users/1, list_users/2</c> and <c>list_users/3</c> returns a list - of users in the user database for a specific <c>Port/Dir</c>. - When <c>list_users/1</c> is called the Port and Dir - options are mandatory.</p> + <p><c>list_users/1, list_users/2</c> and <c>list_users/3</c> + returns a list + of users in the user database for a specific <c>Port/Dir</c>. + When <c>list_users/1</c> is called the Port and Dir + options are mandatory.</p> + + <marker id="add_group_member"></marker> </desc> </func> + <func> <name>add_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name> <name>add_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name> @@ -151,13 +169,18 @@ </type> <desc> <marker id="add_group_member"></marker> - <p><c>add_group_member/3, add_group_member/4</c> and <c>add_group_member/5</c> - adds a user to a group. If the group does not exist, it - is created and the user is added to the group. Upon successful - operation, this function returns <c>true</c>. When <c>add_group_members/3</c> - is called the Port and Dir options are mandatory.</p> + <p><c>add_group_member/3, add_group_member/4</c> and + <c>add_group_member/5</c> + adds a user to a group. If the group does not exist, it + is created and the user is added to the group. Upon successful + operation, this function returns <c>true</c>. + When <c>add_group_members/3</c> + is called the Port and Dir options are mandatory.</p> + + <marker id="delete_group_member"></marker> </desc> </func> + <func> <name>delete_group_member(GroupName, UserName, Options) -> true | {error, Reason}</name> <name>delete_group_member(GroupName, UserName, Port, Dir) -> true | {error, Reason}</name> @@ -176,13 +199,17 @@ </type> <desc> <marker id="delete_group_member"></marker> - <p><c>delete_group_member/3, delete_group_member/4</c> and <c>delete_group_member/5</c> deletes a user from a group. - If the group or the user does not exist, - this function returns an error, otherwise it returns <c>true</c>. - When <c>delete_group_member/3</c> is called the Port and Dir options - are mandatory.</p> + <p><c>delete_group_member/3, delete_group_member/4</c> and + <c>delete_group_member/5</c> deletes a user from a group. + If the group or the user does not exist, + this function returns an error, otherwise it returns <c>true</c>. + When <c>delete_group_member/3</c> is called the Port and Dir options + are mandatory.</p> + + <marker id="list_group_members"></marker> </desc> </func> + <func> <name>list_group_members(GroupName, Options) -> {ok, Users} | {error, Reason}</name> <name>list_group_members(GroupName, Port, Dir) -> {ok, Users} | {error, Reason}</name> @@ -201,13 +228,17 @@ </type> <desc> <marker id="list_group_members"></marker> - <p><c>list_group_members/2, list_group_members/3</c> and <c>list_group_members/4</c> - lists the members of a specified group. If the group does not - exist or there is an error, <c>{error, Reason}</c> is returned. - When <c>list_group_members/2</c> is called the Port and Dir options - are mandatory.</p> + <p><c>list_group_members/2, list_group_members/3</c> and + <c>list_group_members/4</c> + lists the members of a specified group. If the group does not + exist or there is an error, <c>{error, Reason}</c> is returned. + When <c>list_group_members/2</c> is called the Port and Dir options + are mandatory.</p> + + <marker id="list_groups"></marker> </desc> </func> + <func> <name>list_groups(Options) -> {ok, Groups} | {error, Reason}</name> <name>list_groups(Port, Dir) -> {ok, Groups} | {error, Reason}</name> @@ -225,12 +256,16 @@ </type> <desc> <marker id="list_groups"></marker> - <p><c>list_groups/1, list_groups/2</c> and <c>list_groups/3</c> lists all - the groups available. If there is an error, <c>{error, Reason}</c> - is returned. When <c>list_groups/1</c> is called the Port and Dir options - are mandatory.</p> + <p><c>list_groups/1, list_groups/2</c> and <c>list_groups/3</c> + lists all the groups available. + If there is an error, <c>{error, Reason}</c> is returned. + When <c>list_groups/1</c> is called the Port and Dir options + are mandatory.</p> + + <marker id="delete_group"></marker> </desc> </func> + <func> <name>delete_group(GroupName, Options) -> true | {error,Reason} <name>delete_group(GroupName, Port, Dir) -> true | {error, Reason}</name> <name>delete_group(GroupName, Address, Port, Dir) -> true | {error, Reason}</name> @@ -247,12 +282,16 @@ </type> <desc> <marker id="delete_group"></marker> - <p><c>delete_group/2, delete_group/3</c> and <c>delete_group/4</c> deletes the - group specified and returns <c>true</c>. If there is an error, - <c>{error, Reason}</c> is returned. When <c>delete_group/2</c> is called the - Port and Dir options are mandatory.</p> + <p><c>delete_group/2, delete_group/3</c> and <c>delete_group/4</c> + deletes the group specified and returns <c>true</c>. + If there is an error, <c>{error, Reason}</c> is returned. + When <c>delete_group/2</c> is called the + Port and Dir options are mandatory.</p> + + <marker id="update_password"></marker> </desc> </func> + <func> <name>update_password(Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name> <name>update_password(Address,Port, Dir, OldPassword, NewPassword, NewPassword) -> ok | {error, Reason}</name> @@ -268,10 +307,12 @@ </type> <desc> <marker id="update_password"></marker> - <p><c>update_password/5</c> and <c>update_password/6</c> Updates the AuthAccessPassword - for the specified directory. If NewPassword is equal to "NoPassword" no password is requires to - change authorisation data. If NewPassword is equal to "DummyPassword" no changes can be done - without changing the password first.</p> + <p><c>update_password/5</c> and <c>update_password/6</c> + Updates the AuthAccessPassword for the specified directory. + If NewPassword is equal to "NoPassword" no password is requires to + change authorisation data. + If NewPassword is equal to "DummyPassword" no changes can be done + without changing the password first.</p> </desc> </func> </funcs> diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index cfc58b8ddb..3aae1ff70a 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -13,12 +13,12 @@ compliance with the License. You should have received a copy of the Erlang Public License along with this software. If not, it can be retrieved online at http://www.erlang.org/. - + Software distributed under the License is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the specific language governing rights and limitations under the License. - + </legalnotice> <title>Inets Release Notes</title> @@ -32,7 +32,174 @@ <file>notes.xml</file> </header> + + <section> + <title>Inets 5.9.1</title> + + <section> + <title>Improvements and New Features</title> + <!-- + <p>-</p> + --> + + <list> + <item> + <p>Better handling of errorI(s) during update of the session + database. </p> + <p>Also added and updated some debugging functions + <seealso marker="httpc#which_sessions">which_sessions/10,1</seealso> + and + <seealso marker="httpc#info">info/0</seealso>. </p> + <p>Own Id: OTP-10093</p> + <p>Aux Id: Seq 12062</p> + </item> + + <item> + <p>Removed R14B compatible version of (inets-service and + tftp) behaviour definition. </p> + <p>Own Id: OTP-10095</p> + </item> + + <item> + <p>[httpc] Documentation of KeepAlive and Pipeline timeout + options have been improved. </p> + <p>Own Id: OTP-10114</p> + </item> + </list> + + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <!-- + <p>-</p> + --> + + <list> + <item> + <p>[httpc] Cancel request does not work due to incorrect + handler table creation (wrong keypos). </p> + <p>Vyacheslav Vorobyov</p> + <p>Own Id: OTP-10092</p> + </item> + + </list> + </section> + + <section> + <title>Incompatibilities</title> + <p>-</p> + + <!-- + <list> + <item> + <p>[httpc|httpd] The old ssl implementation (based on OpenSSL), + has been deprecated. The config option that specified usage of + this version of the ssl app, <c>ossl</c>, has been removed. </p> + <p>Own Id: OTP-9522</p> + </item> + + </list> + --> + + </section> + + </section> <!-- 5.9.1 --> + + + <section> + <title>Inets 5.9</title> + + <section> + <title>Improvements and New Features</title> + <!-- + <p>-</p> + --> + + <list> + <item> + <p>[httpd] Make the server header configurable with new config + option + <seealso marker="httpd#prop_server_tokens">server_tokens</seealso>. + The value of the server header, which was previously hard-coded + (at compile time), is now possible to manipulate through the means + of the + <seealso marker="httpd#prop_server_tokens">server_tokens</seealso> + config option. </p> + <p>Own Id: OTP-9805</p> + </item> + + <item> + <p>Improve inets support for inets as an included application. </p> + <p><c>inets_app</c> calls <c>supervisor:start_link/3</c> directly + rather than calling the root supervisor function + <c>inets_sup:start_link/0</c>. + This precludes using included_applications to start inets without + having a wrapper function. </p> + <p>Jay Nelson</p> + <p>Own Id: OTP-9960</p> + </item> + + <item> + <p>[httpc] Add function for retrieving current options, + <seealso marker="httpc#get_options">get_options/1,2</seealso>. </p> + <p>Own Id: OTP-9979</p> + </item> + + <item> + <p>Utility module + <seealso marker="http_uri">http_uri</seealso> + now officially supported. </p> + <p>Also, the + <seealso marker="http_uri#parse">parse</seealso> + function has been extended with more + scheme support and a way to provide your own scheme info. </p> + <p>Own Id: OTP-9983</p> + <p>Aux Id: Seq 12022</p> + </item> + + </list> + + </section> + + <section><title>Fixed Bugs and Malfunctions</title> + <p>-</p> + +<!-- + <list> + <item> + <p>[httpd] Fix logging of content length in mod_log. </p> + <p>Garrett Smith</p> + <p>Own Id: OTP-9715</p> + </item> + + </list> +--> + + </section> + +<!-- + <section> + <title>Incompatibilities</title> + <p>-</p> + + <list> + <item> + <p>[httpc|httpd] The old ssl implementation (based on OpenSSL), + has been deprecated. The config option that specified usage of + this version of the ssl app, <c>ossl</c>, has been removed. </p> + <p>Own Id: OTP-9522</p> + </item> + + </list> + + </section> +--> + + </section> <!-- 5.9 --> + + <section><title>Inets 5.8.1</title> <section><title>Improvements and New Features</title> <p>-</p> @@ -195,31 +362,6 @@ </section> - <section> - <title>Incompatibilities</title> -<!-- - <p>-</p> ---> - - <list> - <item> - <p>[httpc] Deprecated interface module <c>http</c> has been removed. - It has (long) been replaced by http client interface module - <seealso marker="httpc#">httpc</seealso>. </p> - <p>Own Id: OTP-9359</p> - </item> - - <item> - <p>[httpc|httpd] The old ssl implementation (based on OpenSSL), - has been deprecated. The config option that specified usage of - this version of the ssl app, <c>ossl</c>, has been removed. </p> - <p>Own Id: OTP-9522</p> - </item> - - </list> - - </section> - <section><title>Fixed Bugs and Malfunctions</title> <!-- <p>-</p> @@ -242,6 +384,24 @@ </list> </section> +<!-- + <section> + <title>Incompatibilities</title> + <p>-</p> + + <list> + <item> + <p>[httpc] Deprecated interface module <c>http</c> has been removed. + It has (long) been replaced by http client interface module + <seealso marker="httpc#">httpc</seealso>. </p> + <p>Own Id: OTP-9359</p> + </item> + + </list> + + </section> +--> + </section> <!-- 5.7.2 --> @@ -417,7 +577,7 @@ <p><c>ossl</c> will work for as long as the ssl application supports it. </p> <p>See the httpd - <seealso marker="httpd#comm_prop">socket_type</seealso> + <seealso marker="httpd#props_comm">socket_type</seealso> communication property or the httpc <seealso marker="httpc#request2">request/4,5</seealso> function for more info. </p> @@ -436,7 +596,7 @@ <list> <item> <p>[httpd] Wrong - <seealso marker="httpd#sec_prop">security property</seealso> + <seealso marker="httpd#props_sec">security property</seealso> names used in documentation. </p> <p><c>security_data_file</c> used instead of <c>data_file</c>. </p> <p><c>security_max_retries</c> used instead of <c>max_retries</c>. </p> @@ -620,7 +780,7 @@ the <c>essl</c> tag instead. </p> <p>See the <c>http_option</c> option in the <seealso marker="httpc#request2">request/4,5</seealso> or - the <seealso marker="httpd#comm_prop">socket-type</seealso> + the <seealso marker="httpd#props_comm">socket-type</seealso> section of the Communication properties chapter for more info, </p> <p>Own Id: OTP-7907</p> </item> @@ -637,9 +797,9 @@ <p>[httpd] - Improved mod_alias. Now able to do better URL rewrites. </p> <p>See - <seealso marker="httpd#alias_prop">URL aliasing properties</seealso> + <seealso marker="httpd#props_alias">URL aliasing properties</seealso> and the - <seealso marker="httpd#cgi_prop">CGI properties</seealso> + <seealso marker="httpd#props_cgi">CGI properties</seealso> section(s) for more info, </p> <p>Own Id: OTP-8573</p> </item> @@ -1225,7 +1385,7 @@ <p>Default is <c>inet6fb4</c> which emulates the behaviour of the previous version. </p> <p>See the - <seealso marker="httpd#comm_prop">Communication properties</seealso> + <seealso marker="httpd#props_comm">Communication properties</seealso> section for more info. </p> <p>Own Id: OTP-8069</p> <p>Aux Id: seq11086</p> diff --git a/lib/inets/doc/src/notes_history.xml b/lib/inets/doc/src/notes_history.xml index 151bec375e..bd59c1ba47 100644 --- a/lib/inets/doc/src/notes_history.xml +++ b/lib/inets/doc/src/notes_history.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>2004</year><year>2011</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/part.xml b/lib/inets/doc/src/part.xml index 36955df6b3..3b6734a9b8 100644 --- a/lib/inets/doc/src/part.xml +++ b/lib/inets/doc/src/part.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/part_notes.xml b/lib/inets/doc/src/part_notes.xml index 21f464318b..81b0dedbfa 100644 --- a/lib/inets/doc/src/part_notes.xml +++ b/lib/inets/doc/src/part_notes.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>2002</year><year>2009</year> + <year>2002</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/part_notes_history.xml b/lib/inets/doc/src/part_notes_history.xml index 3c1e6f5232..f714a6d2e3 100644 --- a/lib/inets/doc/src/part_notes_history.xml +++ b/lib/inets/doc/src/part_notes_history.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE part SYSTEM "part.dtd"> <part> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/inets/doc/src/ref_man.xml b/lib/inets/doc/src/ref_man.xml index 45d5dfcd0e..e44829827c 100644 --- a/lib/inets/doc/src/ref_man.xml +++ b/lib/inets/doc/src/ref_man.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE application SYSTEM "application.dtd"> <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1997</year><year>2010</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -30,8 +30,8 @@ </header> <description> <p>Inets is a container for Internet clients and - servers. Currently a FTP client, a HTTP client and server, and - a tftp client and server has been incorporated in Inets.</p> + servers. Currently a FTP client, a HTTP client and server, and + a tftp client and server has been incorporated in Inets.</p> </description> <xi:include href="inets.xml"/> <xi:include href="ftp.xml"/> @@ -45,6 +45,7 @@ <xi:include href="mod_auth.xml"/> <xi:include href="mod_esi.xml"/> <xi:include href="mod_security.xml"/> + <xi:include href="http_uri.xml"/> </application> diff --git a/lib/inets/doc/src/tftp.xml b/lib/inets/doc/src/tftp.xml index 96d6ae0dd5..0b3e93a153 100644 --- a/lib/inets/doc/src/tftp.xml +++ b/lib/inets/doc/src/tftp.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2006</year><year>2009</year> + <year>2006</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -218,6 +218,8 @@ <c>5</c> times when the timeout expires.</p> </item> </taglist> + + <marker id="start1"></marker> </section> <funcs> @@ -231,11 +233,14 @@ </type> <desc> <p>Starts a daemon process which listens for udp packets on a - port. When it receives a request for read or write it spawns - a temporary server process which handles the actual transfer - of the (virtual) file.</p> + port. When it receives a request for read or write it spawns + a temporary server process which handles the actual transfer + of the (virtual) file.</p> + + <marker id="read_file"></marker> </desc> </func> + <func> <name>read_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name> <fsummary>Read a (virtual) file from a TFTP server</fsummary> @@ -248,23 +253,26 @@ </type> <desc> <p>Reads a (virtual) file <c>RemoteFilename</c> from a TFTP - server.</p> - <p>If <c>LocalFilename</c> is the atom <c>binary</c>, - <c>tftp_binary</c> is used as callback module. It concatenates - all transferred blocks and returns them as one single binary - in <c>LastCallbackState</c>.</p> - <p>If <c>LocalFilename</c> is a string and there are no - registered callback modules, <c>tftp_file</c> is used as - callback module. It writes each transferred block to the file - named <c>LocalFilename</c> and returns the number of - transferred bytes in <c>LastCallbackState</c>.</p> - <p>If <c>LocalFilename</c> is a string and there are registered - callback modules, <c>LocalFilename</c> is tested against - the regexps of these and the callback module corresponding to - the first match is used, or an error tuple is returned if no - matching regexp is found.</p> + server.</p> + <p>If <c>LocalFilename</c> is the atom <c>binary</c>, + <c>tftp_binary</c> is used as callback module. It concatenates + all transferred blocks and returns them as one single binary + in <c>LastCallbackState</c>.</p> + <p>If <c>LocalFilename</c> is a string and there are no + registered callback modules, <c>tftp_file</c> is used as + callback module. It writes each transferred block to the file + named <c>LocalFilename</c> and returns the number of + transferred bytes in <c>LastCallbackState</c>.</p> + <p>If <c>LocalFilename</c> is a string and there are registered + callback modules, <c>LocalFilename</c> is tested against + the regexps of these and the callback module corresponding to + the first match is used, or an error tuple is returned if no + matching regexp is found.</p> </desc> + + <marker id="write_file"></marker> </func> + <func> <name>write_file(RemoteFilename, LocalFilename, Options) -> {ok, LastCallbackState} | {error, Reason}</name> <fsummary>Write a (virtual) file to a TFTP server</fsummary> @@ -288,10 +296,12 @@ block by block and returns the number of transferred bytes in <c>LastCallbackState</c>.</p> <p>If <c>LocalFilename</c> is a string and there are registered - callback modules, <c>LocalFilename</c> is tested against - the regexps of these and the callback module corresponding to - the first match is used, or an error tuple is returned if no - matching regexp is found.</p> + callback modules, <c>LocalFilename</c> is tested against + the regexps of these and the callback module corresponding to + the first match is used, or an error tuple is returned if no + matching regexp is found.</p> + + <marker id="info_daemons"></marker> </desc> </func> @@ -304,8 +314,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Returns info about all TFTP daemon processes. - </p> + <p>Returns info about all TFTP daemon processes. </p> + + <marker id="info_servers"></marker> </desc> </func> @@ -318,8 +329,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Returns info about all TFTP server processes. - </p> + <p>Returns info about all TFTP server processes. </p> + + <marker id="info_pid"></marker> </desc> </func> @@ -332,6 +344,8 @@ </type> <desc> <p>Returns info about a TFTP daemon, server or client process.</p> + + <marker id="change_config_daemons"></marker> </desc> </func> @@ -346,8 +360,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Changes config for all TFTP daemon processes - </p> + <p>Changes config for all TFTP daemon processes. </p> + + <marker id="change_config_servers"></marker> </desc> </func> @@ -362,8 +377,9 @@ <v>Reason = term()</v> </type> <desc> - <p>Changes config for all TFTP server processes - </p> + <p>Changes config for all TFTP server processes. </p> + + <marker id="change_config_pid"></marker> </desc> </func> @@ -378,8 +394,11 @@ </type> <desc> <p>Changes config for a TFTP daemon, server or client process</p> + + <marker id="start2"></marker> </desc> </func> + <func> <name>start() -> ok | {error, Reason}</name> <fsummary>Start the Inets application</fsummary> @@ -442,8 +461,9 @@ by the already ongoing connection on the server side. By not setting up yet another connection, in parallel with the ongoing one, the server will - consumer lesser resources. - </p> + consumer lesser resources. </p> + + <marker id="prepare"></marker> </section> <funcs> @@ -468,17 +488,20 @@ <v>Text = string()</v> </type> <desc> - <p>Prepares to open a file on the client side.</p> - <p>No new options may be added, but the ones that are present in - <c>SuggestedOptions</c> may be omitted or replaced with new - values in <c>AcceptedOptions</c>.</p> - <p>Will be followed by a call to <c>open/4</c> before any - read/write access is performed. <c>AcceptedOptions</c> is - sent to the server which replies with those options that it - accepts. These will be forwarded to <c>open/4</c> as - <c>SuggestedOptions</c>.</p> + <p>Prepares to open a file on the client side.</p> + <p>No new options may be added, but the ones that are present in + <c>SuggestedOptions</c> may be omitted or replaced with new + values in <c>AcceptedOptions</c>.</p> + <p>Will be followed by a call to <c>open/4</c> before any + read/write access is performed. <c>AcceptedOptions</c> is + sent to the server which replies with those options that it + accepts. These will be forwarded to <c>open/4</c> as + <c>SuggestedOptions</c>.</p> + + <marker id="open"></marker> </desc> </func> + <func> <name>open(Peer, Access, Filename, Mode, SuggestedOptions, State) -> {ok, AcceptedOptions, NewState} | {error, {Code, Text}}</name> <fsummary>Open a file for read or write access</fsummary> @@ -503,14 +526,17 @@ <desc> <p>Opens a file for read or write access.</p> <p>On the client side where the <c>open/5</c> call has been - preceded by a call to <c>prepare/5</c>, all options must be - accepted or rejected.</p> - <p>On the server side, where there is no preceding - <c>prepare/5</c> call, no new options may be added, but - the ones that are present in <c>SuggestedOptions</c> may be - omitted or replaced with new values in <c>AcceptedOptions</c>.</p> + preceded by a call to <c>prepare/5</c>, all options must be + accepted or rejected.</p> + <p>On the server side, where there is no preceding + <c>prepare/5</c> call, no new options may be added, but + the ones that are present in <c>SuggestedOptions</c> may be + omitted or replaced with new values in <c>AcceptedOptions</c>.</p> + + <marker id="read"></marker> </desc> </func> + <func> <name>read(State) -> {more, Bin, NewState} | {last, Bin, FileSize} | {error, {Code, Text}}</name> <fsummary>Read a chunk from the file</fsummary> @@ -526,15 +552,18 @@ <desc> <p>Read a chunk from the file.</p> <p>The callback function is expected to close - the file when the last file chunk is - encountered. When an error is encountered - the callback function is expected to clean - up after the aborted file transfer, such as - closing open file descriptors etc. In both - cases there will be no more calls to any of - the callback functions.</p> + the file when the last file chunk is + encountered. When an error is encountered + the callback function is expected to clean + up after the aborted file transfer, such as + closing open file descriptors etc. In both + cases there will be no more calls to any of + the callback functions.</p> + + <marker id="write"></marker> </desc> </func> + <func> <name>write(Bin, State) -> {more, NewState} | {last, FileSize} | {error, {Code, Text}}</name> <fsummary>Write a chunk to the file</fsummary> @@ -550,15 +579,18 @@ <desc> <p>Write a chunk to the file.</p> <p>The callback function is expected to close - the file when the last file chunk is - encountered. When an error is encountered - the callback function is expected to clean - up after the aborted file transfer, such as - closing open file descriptors etc. In both - cases there will be no more calls to any of - the callback functions.</p> + the file when the last file chunk is + encountered. When an error is encountered + the callback function is expected to clean + up after the aborted file transfer, such as + closing open file descriptors etc. In both + cases there will be no more calls to any of + the callback functions.</p> + + <marker id="abort"></marker> </desc> </func> + <func> <name>abort(Code, Text, State) -> ok</name> <fsummary>Abort the file transfer</fsummary> @@ -572,14 +604,14 @@ <desc> <p>Invoked when the file transfer is aborted.</p> <p>The callback function is expected to clean - up its used resources after the aborted file - transfer, such as closing open file - descriptors etc. The function will not be - invoked if any of the other callback - functions returns an error, as it is - expected that they already have cleaned up - the necessary resources. It will however be - invoked if the functions fails (crashes).</p> + up its used resources after the aborted file + transfer, such as closing open file + descriptors etc. The function will not be + invoked if any of the other callback + functions returns an error, as it is + expected that they already have cleaned up + the necessary resources. It will however be + invoked if the functions fails (crashes).</p> </desc> </func> </funcs> @@ -589,7 +621,9 @@ <title>LOGGER FUNCTIONS</title> <p>A <c>tftp_logger</c> callback module should be implemented as a - <c>tftp_logger</c> behavior and export the functions listed below.</p> + <c>tftp_logger</c> behavior and export the functions listed below.</p> + + <marker id="error_msg"></marker> </section> <funcs> @@ -602,7 +636,10 @@ <v>Reason = term()</v> </type> <desc> - <p>Log an error message. See <c>error_logger:error_msg/2 for details.</c> </p> + <p>Log an error message. + See <c>error_logger:error_msg/2 for details.</c> </p> + + <marker id="warning_msg"></marker> </desc> </func> @@ -615,7 +652,10 @@ <v>Reason = term()</v> </type> <desc> - <p>Log a warning message. See <c>error_logger:warning_msg/2 for details.</c> </p> + <p>Log a warning message. + See <c>error_logger:warning_msg/2 for details.</c> </p> + + <marker id="info_msg"></marker> </desc> </func> @@ -628,7 +668,8 @@ <v>Reason = term()</v> </type> <desc> - <p>Log an info message. See <c>error_logger:info_msg/2 for details.</c> </p> + <p>Log an info message. + See <c>error_logger:info_msg/2 for details.</c> </p> </desc> </func> </funcs> diff --git a/lib/inets/examples/httpd_load_test/Makefile b/lib/inets/examples/httpd_load_test/Makefile index 1cc61ad8ae..de1b2a289f 100644 --- a/lib/inets/examples/httpd_load_test/Makefile +++ b/lib/inets/examples/httpd_load_test/Makefile @@ -34,7 +34,7 @@ VSN=$(INETS_VSN) # Release directory specification # ---------------------------------------------------- RELSYSDIR = $(RELEASE_PATH)/lib/inets-$(VSN) -EXAMPLE_RELSYSDIR = $(RELSYSDIR)/examples +EXAMPLE_RELSYSDIR = "$(RELSYSDIR)/examples" HDLT_RELSYSDIR = $(EXAMPLE_RELSYSDIR)/httpd_load_test diff --git a/lib/inets/examples/server_root/Makefile b/lib/inets/examples/server_root/Makefile index d7a3231068..2230a7eafc 100644 --- a/lib/inets/examples/server_root/Makefile +++ b/lib/inets/examples/server_root/Makefile @@ -174,36 +174,36 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/auth - $(INSTALL_DATA) $(AUTH_FILES) $(RELSYSDIR)/examples/server_root/auth - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/cgi-bin - $(INSTALL_SCRIPT) $(CGI_FILES) $(RELSYSDIR)/examples/server_root/cgi-bin - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/conf - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/examples/server_root/conf - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/open + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/auth" + $(INSTALL_DATA) $(AUTH_FILES) "$(RELSYSDIR)/examples/server_root/auth" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/cgi-bin" + $(INSTALL_SCRIPT) $(CGI_FILES) "$(RELSYSDIR)/examples/server_root/cgi-bin" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/conf" + $(INSTALL_DATA) $(CONF_FILES) "$(RELSYSDIR)/examples/server_root/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/htdocs/open" $(INSTALL_DATA) $(OPEN_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/open - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open + "$(RELSYSDIR)/examples/server_root/htdocs/open" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/htdocs/mnesia_open" $(INSTALL_DATA) $(MNESIA_OPEN_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_open - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs/misc + "$(RELSYSDIR)/examples/server_root/htdocs/mnesia_open" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/htdocs/misc" $(INSTALL_DATA) $(MISC_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/misc + "$(RELSYSDIR)/examples/server_root/htdocs/misc" $(INSTALL_DIR) \ - $(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret + "$(RELSYSDIR)/examples/server_root/htdocs/secret/top_secret" $(INSTALL_DIR) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret + "$(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret/top_secret" $(INSTALL_DATA) $(SECRET_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/secret + "$(RELSYSDIR)/examples/server_root/htdocs/secret" $(INSTALL_DATA) $(MNESIA_SECRET_FILES) \ - $(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/htdocs - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/examples/server_root/htdocs - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/icons - $(INSTALL_DATA) $(ICON_FILES) $(RELSYSDIR)/examples/server_root/icons - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/ssl - $(INSTALL_DATA) $(SSL_FILES) $(RELSYSDIR)/examples/server_root/ssl - $(INSTALL_DIR) $(RELSYSDIR)/examples/server_root/logs + "$(RELSYSDIR)/examples/server_root/htdocs/mnesia_secret" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/htdocs" + $(INSTALL_DATA) $(HTDOCS_FILES) "$(RELSYSDIR)/examples/server_root/htdocs" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/icons" + $(INSTALL_DATA) $(ICON_FILES) "$(RELSYSDIR)/examples/server_root/icons" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/ssl" + $(INSTALL_DATA) $(SSL_FILES) "$(RELSYSDIR)/examples/server_root/ssl" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/server_root/logs" release_docs_spec: diff --git a/lib/inets/priv/Makefile b/lib/inets/priv/Makefile index 85da409e55..4dc521f618 100644 --- a/lib/inets/priv/Makefile +++ b/lib/inets/priv/Makefile @@ -57,8 +57,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(EXECUTABLES) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_SCRIPT) $(EXECUTABLES) "$(RELSYSDIR)/priv/bin" release_docs_spec: diff --git a/lib/inets/src/ftp/Makefile b/lib/inets/src/ftp/Makefile index 19b93870df..426286f254 100644 --- a/lib/inets/src/ftp/Makefile +++ b/lib/inets/src/ftp/Makefile @@ -88,11 +88,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/ftp - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/ftp - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/ftp" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/ftp" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 560ee55271..fe25c23316 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/inets/src/http_client/Makefile b/lib/inets/src/http_client/Makefile index d490e59929..850c790bd1 100644 --- a/lib/inets/src/http_client/Makefile +++ b/lib/inets/src/http_client/Makefile @@ -89,11 +89,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/http_client - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_client - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/http_client" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_client" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index ae87ceed93..b6e7708353 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -31,12 +31,15 @@ -export([ request/1, request/2, request/4, request/5, cancel_request/1, cancel_request/2, - set_option/2, set_option/3, + set_option/2, set_option/3, set_options/1, set_options/2, + get_option/1, get_option/2, + get_options/1, get_options/2, store_cookies/2, store_cookies/3, cookie_header/1, cookie_header/2, cookie_header/3, which_cookies/0, which_cookies/1, reset_cookies/0, reset_cookies/1, + which_sessions/0, which_sessions/1, stream_next/1, default_profile/0, profile_name/1, profile_name/2, @@ -156,7 +159,7 @@ request(Method, {http_options, HTTPOptions}, {options, Options}, {profile, Profile}]), - case http_uri:parse(Url, Options) of + case uri_parse(Url, Options) of {error, Reason} -> {error, Reason}; {ok, ParsedUrl} -> @@ -177,7 +180,7 @@ request(Method, {http_options, HTTPOptions}, {options, Options}, {profile, Profile}]), - case http_uri:parse(Url, Options) of + case uri_parse(Url, Options) of {error, Reason} -> {error, Reason}; {ok, ParsedUrl} -> @@ -230,7 +233,7 @@ cancel_request(RequestId, Profile) set_options(Options) -> set_options(Options, default_profile()). set_options(Options, Profile) when is_atom(Profile) orelse is_pid(Profile) -> - ?hcrt("set cookies", [{options, Options}, {profile, Profile}]), + ?hcrt("set options", [{options, Options}, {profile, Profile}]), case validate_options(Options) of {ok, Opts} -> try @@ -253,6 +256,59 @@ set_option(Key, Value, Profile) -> %%-------------------------------------------------------------------------- +%% get_options(OptionItems) -> {ok, Values} | {error, Reason} +%% get_options(OptionItems, Profile) -> {ok, Values} | {error, Reason} +%% OptionItems - all | [option_item()] +%% option_item() - proxy | pipeline_timeout | max_pipeline_length | +%% keep_alive_timeout | max_keep_alive_length | +%% max_sessions | verbose | +%% cookies | ipfamily | ip | port | socket_opts +%% Profile - atom() +%% Values - [{option_item(), term()}] +%% Reason - term() +%% Description: Retrieves the current options. +%%------------------------------------------------------------------------- + +get_options() -> + record_info(fields, options). + +get_options(Options) -> + get_options(Options, default_profile()). + +get_options(all = _Options, Profile) -> + get_options(get_options(), Profile); +get_options(Options, Profile) + when (is_list(Options) andalso + (is_atom(Profile) orelse is_pid(Profile))) -> + ?hcrt("get options", [{options, Options}, {profile, Profile}]), + case Options -- get_options() of + [] -> + try + begin + {ok, httpc_manager:get_options(Options, + profile_name(Profile))} + end + catch + exit:{noproc, _} -> + {error, inets_not_started} + end; + InvalidGetOptions -> + {error, {invalid_options, InvalidGetOptions}} + end. + +get_option(Key) -> + get_option(Key, default_profile()). + +get_option(Key, Profile) -> + case get_options([Key], Profile) of + {ok, [{Key, Value}]} -> + {ok, Value}; + Error -> + Error + end. + + +%%-------------------------------------------------------------------------- %% store_cookies(SetCookieHeaders, Url [, Profile]) -> ok | {error, reason} %% %% @@ -274,7 +330,7 @@ store_cookies(SetCookieHeaders, Url, Profile) %% Since the Address part is not actually used %% by the manager when storing cookies, we dont %% care about ipv6-host-with-brackets. - {ok, {_, _, Host, Port, Path, _}} = http_uri:parse(Url), + {ok, {_, _, Host, Port, Path, _}} = uri_parse(Url), Address = {Host, Port}, ProfileName = profile_name(Profile), Cookies = httpc_cookie:cookies(SetCookieHeaders, Path, Host), @@ -319,8 +375,6 @@ cookie_header(Url, Opts, Profile) {error, {not_started, Profile}} end. - - %%-------------------------------------------------------------------------- %% which_cookies() -> [cookie()] @@ -344,10 +398,32 @@ which_cookies(Profile) -> %%-------------------------------------------------------------------------- +%% which_sessions() -> {GoodSession, BadSessions, NonSessions} +%% which_sessions(Profile) -> {GoodSession, BadSessions, NonSessions} +%% +%% Description: Debug function, dumping the sessions database, sorted +%% into three groups (Good-, Bad- and Non-sessions). +%%------------------------------------------------------------------------- +which_sessions() -> + which_sessions(default_profile()). + +which_sessions(Profile) -> + ?hcrt("which sessions", [{profile, Profile}]), + try + begin + httpc_manager:which_sessions(profile_name(Profile)) + end + catch + exit:{noproc, _} -> + {[], [], []} + end. + + +%%-------------------------------------------------------------------------- %% info() -> list() %% info(Profile) -> list() %% -%% Description: Debug function, retreive info about the profile +%% Description: Debug function, retrieve info about the profile %%------------------------------------------------------------------------- info() -> info(default_profile()). @@ -1144,6 +1220,22 @@ validate_headers(RequestHeaders, _, _) -> RequestHeaders. +%%-------------------------------------------------------------------------- +%% These functions is just simple wrappers to parse specifically HTTP URIs +%%-------------------------------------------------------------------------- + +scheme_defaults() -> + [{http, 80}, {https, 443}]. + +uri_parse(URI) -> + http_uri:parse(URI, [{scheme_defaults, scheme_defaults()}]). + +uri_parse(URI, Opts) -> + http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]). + + +%%-------------------------------------------------------------------------- + child_name2info(undefined) -> {error, no_such_service}; child_name2info(httpc_manager) -> diff --git a/lib/inets/src/http_client/httpc_handler.erl b/lib/inets/src/http_client/httpc_handler.erl index bfe9b14ef6..6fe05dec80 100644 --- a/lib/inets/src/http_client/httpc_handler.erl +++ b/lib/inets/src/http_client/httpc_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 @@ -851,14 +851,17 @@ connect(SocketType, ToAddress, case IpFamily of inet6fb4 -> Opts3 = [inet6 | Opts2], - case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of - {error, _Reason} = Error -> + case http_transport:connect(SocketType, + ToAddress, Opts3, Timeout) of + {error, Reason6} -> Opts4 = [inet | Opts2], case http_transport:connect(SocketType, ToAddress, Opts4, Timeout) of - {error, _} -> - %% Reply with the "original" error - Error; + {error, Reason4} -> + {error, {failed_connect, + [{to_address, ToAddress}, + {inet6, Opts3, Reason6}, + {inet, Opts4, Reason4}]}}; OK -> OK end; @@ -867,7 +870,13 @@ connect(SocketType, ToAddress, end; _ -> Opts3 = [IpFamily | Opts2], - http_transport:connect(SocketType, ToAddress, Opts3, Timeout) + case http_transport:connect(SocketType, ToAddress, Opts3, Timeout) of + {error, Reason} -> + {error, {failed_connect, [{to_address, ToAddress}, + {IpFamily, Opts3, Reason}]}}; + Else -> + Else + end end. connect_and_send_first_request(Address, Request, #state{options = Options} = State) -> @@ -1704,7 +1713,32 @@ update_session(ProfileName, #session{id = SessionId} = Session, Pos, Value) -> catch error:undef -> % This could happen during code upgrade Session2 = erlang:setelement(Pos, Session, Value), - insert_session(Session2, ProfileName) + insert_session(Session2, ProfileName); + T:E -> + error_logger:error_msg("Failed updating session: " + "~n ProfileName: ~p" + "~n SessionId: ~p" + "~n Pos: ~p" + "~n Value: ~p" + "~nwhen" + "~n Session (db) info: ~p" + "~n Session (db): ~p" + "~n Session (record): ~p" + "~n T: ~p" + "~n E: ~p", + [ProfileName, SessionId, Pos, Value, + (catch httpc_manager:which_session_info(ProfileName)), + Session, + (catch httpc_manager:lookup_session(ProfileName, SessionId)), + T, E]), + exit({failed_updating_session, + [{profile, ProfileName}, + {session_id, SessionId}, + {pos, Pos}, + {value, Value}, + {etype, T}, + {error, E}, + {stacktrace, erlang:get_stacktrace()}]}) end. diff --git a/lib/inets/src/http_client/httpc_manager.erl b/lib/inets/src/http_client/httpc_manager.erl index 453081da21..3612b331e7 100644 --- a/lib/inets/src/http_client/httpc_manager.erl +++ b/lib/inets/src/http_client/httpc_manager.erl @@ -34,9 +34,13 @@ retry_request/2, redirect_request/2, insert_session/2, + lookup_session/2, update_session/4, delete_session/2, + which_sessions/1, + which_session_info/1, set_options/2, + get_options/2, store_cookies/3, which_cookies/1, which_cookies/2, which_cookies/3, reset_cookies/1, @@ -58,17 +62,9 @@ options = #options{} }). --record(handler_info, - { - id, % Id of the request: request_id() - starter, % Pid of the handler starter process (temp): pid() - handler, % Pid of the handler process: pid() - from, % From for the request: from() - state % State of the handler: initiating | started | operational | canceled - }). - -define(DELAY, 500). + %%==================================================================== %% Internal Application API %%==================================================================== @@ -194,13 +190,28 @@ insert_session(Session, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: lookup_session(SessionId, ProfileName) -> _ +%% SessionId - term() +%% ProfileName - atom() +%% +%% Description: Looks up a session record in the httpc manager +%% table <ProfileName>__session_db. +%%-------------------------------------------------------------------- + +lookup_session(SessionId, ProfileName) -> + SessionDbName = session_db_name(ProfileName), + ?hcrt("lookup session", [{session_id, SessionId}, {profile, ProfileName}]), + ets:lookup(SessionDbName, SessionId). + + +%%-------------------------------------------------------------------- %% Function: update_session(ProfileName, SessionId, Pos, Value) -> _ %% Session - #session{} %% ProfileName - atom() %% %% Description: Update, only one field (Pos) of the session record %% identified by the SessionId, the session information -%% of the httpc manager table <ProfileName>_session_db. +%% of the httpc manager table <ProfileName>__session_db. %% Intended to be called by the httpc request handler process. %%-------------------------------------------------------------------- @@ -215,12 +226,12 @@ update_session(ProfileName, SessionId, Pos, Value) -> %%-------------------------------------------------------------------- -%% Function: delete_session(SessionId, ProfileName) -> _ +%% Function: delete_session(SessionId, ProfileName) -> void() %% SessionId - {{Host, Port}, HandlerPid} %% ProfileName - atom() %% %% Description: Deletes session information from the httpc manager -%% table httpc_manager_session_db_<Profile>. Intended to be called by +%% table <ProfileName>__session_db. Intended to be called by %% the httpc request handler process. %%-------------------------------------------------------------------- @@ -231,6 +242,57 @@ delete_session(SessionId, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: which sessions(ProfileName) -> SessionsInfo +%% ProfileName - atom() +%% SessionsInfo - {GoodSessions, BadSessions, NonSessions} +%% GoodSessions - [#session{}] +%% BadSessions - [tuple()] +%% NonSessions - [term()] +%% +%% Description: Produces a list of all sessions in the session db. +%% Used for debugging and since that is the intent, there is some +%% checking and transforming done, which produces the results. +%%-------------------------------------------------------------------- + +which_sessions(ProfileName) -> + ?hcrt("which_sessions", [{profile, ProfileName}]), + SessionDbName = session_db_name(ProfileName), + which_sessions2(SessionDbName). + +which_sessions2(SessionDbName) -> + Sessions = which_sessions_order(ets:tab2list(SessionDbName)), + GoodSessions = [GoodSession || {good_session, GoodSession} <- Sessions], + BadSessions = [BadSession || {bad_session, BadSession} <- Sessions], + NonSessions = [NonSession || {non_session, NonSession} <- Sessions], + {lists:keysort(#session.id, GoodSessions), + lists:keysort(#session.id, BadSessions), + lists:sort(NonSessions)}. + +which_sessions_order([]) -> + []; +which_sessions_order([Session|Sessions]) when is_record(Session, session) -> + [{good_session, Session} | which_sessions_order(Sessions)]; +which_sessions_order([BadSession|Sessions]) + when is_tuple(BadSession) andalso + (element(1, BadSession) =:= session) -> + [{bad_session, BadSession} | which_sessions_order(Sessions)]; +which_sessions_order([NonSession|Sessions]) -> + [{non_session, NonSession} | which_sessions_order(Sessions)]. + + +%%-------------------------------------------------------------------- +%% Function: which session_info(ProfileName) -> list() +%% +%% Description: Produces a ets table info list of the sessions table +%%-------------------------------------------------------------------- + +which_session_info(ProfileName) -> + SessionDbName = session_db_name(ProfileName), + ?hcrt("which_session_info", [{profile, ProfileName}]), + ets:info(SessionDbName). + + +%%-------------------------------------------------------------------- %% Function: set_options(Options, ProfileName) -> ok %% %% Options = [Option] @@ -250,6 +312,21 @@ set_options(Options, ProfileName) -> %%-------------------------------------------------------------------- +%% Function: get_options(OptionItems, ProfileName) -> Values +%% +%% OptionItems = [OptionItem] +%% OptionItem = Any or all fields of the current #options{} record +%% Values = [{OptionItem, Value}] +%% Value = term() +%% +%% Description: Gets the specified options used by the client. +%%-------------------------------------------------------------------- + +get_options(Options, ProfileName) -> + call(ProfileName, {get_options, Options}). + + +%%-------------------------------------------------------------------- %% Function: store_cookies(Cookies, Address, ProfileName) -> ok %% %% Cookies = [Cookie] @@ -363,8 +440,7 @@ do_init(ProfileName, CookiesDir) -> %% Create handler db ?hcrt("create handler/request db", []), HandlerDbName = handler_db_name(ProfileName), - ets:new(HandlerDbName, - [protected, set, named_table, {keypos, #handler_info.id}]), + ets:new(HandlerDbName, [protected, set, named_table, {keypos, 1}]), %% Cookie DB ?hcrt("create cookie db", []), @@ -398,9 +474,10 @@ handle_call({request, Request}, _, State) -> {stop, Error, httpc_response:error(Request, Error), State} end; -handle_call({cancel_request, RequestId}, From, State) -> +handle_call({cancel_request, RequestId}, From, + #state{handler_db = HandlerDb} = State) -> ?hcri("cancel_request", [{request_id, RequestId}]), - case ets:lookup(State#state.handler_db, RequestId) of + case ets:lookup(HandlerDb, RequestId) of [] -> %% The request has allready compleated make sure %% it is deliverd to the client process queue so @@ -412,9 +489,9 @@ handle_call({cancel_request, RequestId}, From, State) -> {noreply, State}; [{_, Pid, _}] -> httpc_handler:cancel(RequestId, Pid, From), - {noreply, State#state{cancel = - [{RequestId, Pid, From} | - State#state.cancel]}} + {noreply, + State#state{cancel = + [{RequestId, Pid, From} | State#state.cancel]}} end; handle_call(reset_cookies, _, #state{cookie_db = CookieDb} = State) -> @@ -430,7 +507,7 @@ handle_call(which_cookies, _, #state{cookie_db = CookieDb} = State) -> handle_call({which_cookies, Url, Options}, _, #state{cookie_db = CookieDb} = State) -> ?hcrv("which cookies", [{url, Url}, {options, Options}]), - case http_uri:parse(Url, Options) of + case uri_parse(Url, Options) of {ok, {Scheme, _, Host, Port, Path, _}} -> CookieHeaders = httpc_cookie:header(CookieDb, Scheme, {Host, Port}, Path), @@ -439,6 +516,12 @@ handle_call({which_cookies, Url, Options}, _, {reply, ERROR, State} end; +handle_call({get_options, OptionItems}, _, #state{options = Options} = State) -> + ?hcrv("get options", [{option_items, OptionItems}]), + Reply = [{OptionItem, get_option(OptionItem, Options)} || + OptionItem <- OptionItems], + {reply, Reply, State}; + handle_call(info, _, State) -> ?hcrv("info", []), Info = get_manager_info(State), @@ -623,7 +706,7 @@ code_change(_, code_change(_, State, _) -> {ok, State}. -%% This function is to catch everything that calls through the cracks... +%% This function is used to catch everything that falls through the cracks... update_session_table(SessionDB, Transform) -> ets:safe_fixtable(SessionDB, true), update_session_table(SessionDB, ets:first(SessionDB), Transform), @@ -651,40 +734,56 @@ update_session_table(SessionDB, Key, Transform) -> %%-------------------------------------------------------------------- get_manager_info(#state{handler_db = HDB, - cookie_db = CDB} = _State) -> + session_db = SDB, + cookie_db = CDB, + options = Options} = _State) -> HandlerInfo = get_handler_info(HDB), + SessionInfo = which_sessions2(SDB), + OptionsInfo = + [{Item, get_option(Item, Options)} || + Item <- record_info(fields, options)], CookieInfo = httpc_cookie:which_cookies(CDB), - [{handlers, HandlerInfo}, {cookies, CookieInfo}]. + [{handlers, HandlerInfo}, + {sessions, SessionInfo}, + {options, OptionsInfo}, + {cookies, CookieInfo}]. + +sort_handlers(Unsorted) -> + sort_handlers2(lists:keysort(1, Unsorted)). + +sort_handlers2([]) -> + []; +sort_handlers2([{HandlerPid, RequestId}|L]) -> + {Handler, Rest} = sort_handlers2(HandlerPid, [RequestId], L), + [Handler | sort_handlers2(Rest)]. + +sort_handlers2(HandlerPid, Reqs, []) -> + {{HandlerPid, lists:sort(Reqs)}, []}; +sort_handlers2(HandlerPid, Reqs, [{HandlerPid, ReqId}|Rest]) -> + sort_handlers2(HandlerPid, [ReqId|Reqs], Rest); +sort_handlers2(HandlerPid1, Reqs, [{HandlerPid2, _}|_] = Rest) + when HandlerPid1 =/= HandlerPid2 -> + {{HandlerPid1, lists:sort(Reqs)}, Rest}. get_handler_info(Tab) -> - Pattern = #handler_info{handler = '$1', - state = '$2', - _ = '_'}, - Handlers1 = [{Pid, State} || [Pid, State] <- ets:match(Tab, Pattern)], - F = fun({Pid, State} = Elem, Acc) when State =/= canceled -> - case lists:keymember(Pid, 1, Acc) of - true -> - Acc; - false -> - [Elem | Acc] - end; - (_, Acc) -> - Acc - end, - Handlers2 = lists:foldl(F, [], Handlers1), - Handlers3 = [{Pid, State, - case (catch httpc_handler:info(Pid)) of - {'EXIT', _} -> + Pattern = {'$2', '$1', '_'}, + Handlers1 = [{Pid, Id} || [Pid, Id] <- ets:match(Tab, Pattern)], + Handlers2 = sort_handlers(Handlers1), + Handlers3 = [{Pid, Reqs, + try + begin + httpc_handler:info(Pid) + end + catch + _:_ -> %% Why would this crash? %% Only if the process has died, but we don't %% know about it? - []; - Else -> - Else - end} || - {Pid, State} <- Handlers2], + [] + end} || {Pid, Reqs} <- Handlers2], Handlers3. + handle_request(#request{settings = #http_options{version = "HTTP/0.9"}} = Request, State) -> @@ -736,19 +835,21 @@ handle_request(Request, State = #state{options = Options}) -> {reply, {ok, NewRequest#request.id}, State}. -start_handler(Request, State) -> +start_handler(#request{id = Id, + from = From} = Request, + #state{profile_name = ProfileName, + handler_db = HandlerDb, + options = Options}) -> {ok, Pid} = case is_inets_manager() of true -> httpc_handler_sup:start_child([whereis(httpc_handler_sup), - Request, State#state.options, - State#state.profile_name]); + Request, Options, ProfileName]); false -> - httpc_handler:start_link(self(), Request, State#state.options, - State#state.profile_name) + httpc_handler:start_link(self(), Request, Options, ProfileName) end, - ets:insert(State#state.handler_db, {Request#request.id, - Pid, Request#request.from}), + HandlerInfo = {Id, Pid, From}, + ets:insert(HandlerDb, HandlerInfo), erlang:monitor(process, Pid). @@ -805,12 +906,14 @@ select_session(Candidates, Max) -> {ok, HandlerPid} end. -pipeline_or_keep_alive(Request, HandlerPid, State) -> +pipeline_or_keep_alive(#request{id = Id, + from = From} = Request, + HandlerPid, + #state{handler_db = HandlerDb} = State) -> case (catch httpc_handler:send(Request, HandlerPid)) of ok -> - ets:insert(State#state.handler_db, {Request#request.id, - HandlerPid, - Request#request.from}); + HandlerInfo = {Id, HandlerPid, From}, + ets:insert(HandlerDb, HandlerInfo); _ -> % timeout pipelining failed start_handler(Request, State) end. @@ -872,6 +975,19 @@ make_db_name(ProfileName, Post) -> list_to_atom(atom_to_list(ProfileName) ++ Post). +%%-------------------------------------------------------------------------- +%% These functions is just simple wrappers to parse specifically HTTP URIs +%%-------------------------------------------------------------------------- + +scheme_defaults() -> + [{http, 80}, {https, 443}]. + +uri_parse(URI, Opts) -> + http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]). + + +%%-------------------------------------------------------------------------- + call(ProfileName, Msg) -> Timeout = infinity, @@ -883,6 +999,31 @@ cast(ProfileName, Msg) -> gen_server:cast(ProfileName, Msg). +get_option(proxy, #options{proxy = Proxy}) -> + Proxy; +get_option(pipeline_timeout, #options{pipeline_timeout = Timeout}) -> + Timeout; +get_option(max_pipeline_length, #options{max_pipeline_length = Length}) -> + Length; +get_option(keep_alive_timeout, #options{keep_alive_timeout = Timeout}) -> + Timeout; +get_option(max_keep_alive_length, #options{max_keep_alive_length = Length}) -> + Length; +get_option(max_sessions, #options{max_sessions = MaxSessions}) -> + MaxSessions; +get_option(cookies, #options{cookies = Cookies}) -> + Cookies; +get_option(verbose, #options{verbose = Verbose}) -> + Verbose; +get_option(ipfamily, #options{ipfamily = IpFamily}) -> + IpFamily; +get_option(ip, #options{ip = IP}) -> + IP; +get_option(port, #options{port = Port}) -> + Port; +get_option(socket_opts, #options{socket_opts = SocketOpts}) -> + SocketOpts. + get_proxy(Opts, #options{proxy = Default}) -> proplists:get_value(proxy, Opts, Default). diff --git a/lib/inets/src/http_client/httpc_response.erl b/lib/inets/src/http_client/httpc_response.erl index 919115a23a..04976447cc 100644 --- a/lib/inets/src/http_client/httpc_response.erl +++ b/lib/inets/src/http_client/httpc_response.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -342,7 +342,7 @@ redirect(Response = {StatusLine, Headers, Body}, Request) -> RedirUrl -> UrlParseOpts = [{ipv6_host_with_brackets, Request#request.ipv6_host_with_brackets}], - case http_uri:parse(RedirUrl, UrlParseOpts) of + case uri_parse(RedirUrl, UrlParseOpts) of {error, no_scheme} when (Request#request.settings)#http_options.relaxed -> NewLocation = fix_relative_uri(Request, RedirUrl), @@ -437,3 +437,17 @@ format_response({StatusLine, Headers, Body}) -> end, {{StatusLine, http_response:header_list(Headers), NewBody}, Data}. +%%-------------------------------------------------------------------------- +%% These functions is just simple wrappers to parse specifically HTTP URIs +%%-------------------------------------------------------------------------- + +scheme_defaults() -> + [{http, 80}, {https, 443}]. + +uri_parse(URI, Opts) -> + http_uri:parse(URI, [{scheme_defaults, scheme_defaults()} | Opts]). + + +%%-------------------------------------------------------------------------- + + diff --git a/lib/inets/src/http_lib/Makefile b/lib/inets/src/http_lib/Makefile index aaf3cfb995..3ec6ce7436 100644 --- a/lib/inets/src/http_lib/Makefile +++ b/lib/inets/src/http_lib/Makefile @@ -87,11 +87,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/http_lib - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_lib - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/http_lib" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_lib" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_lib/http_uri.erl b/lib/inets/src/http_lib/http_uri.erl index 32c6305a79..5962001c3a 100644 --- a/lib/inets/src/http_lib/http_uri.erl +++ b/lib/inets/src/http_lib/http_uri.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -17,39 +17,95 @@ %% %CopyrightEnd% %% %% -%% RFC 3986 +%% This is from chapter 3, Syntax Components, of RFC 3986: +%% +%% The generic URI syntax consists of a hierarchical sequence of +%% components referred to as the scheme, authority, path, query, and +%% fragment. +%% +%% URI = scheme ":" hier-part [ "?" query ] [ "#" fragment ] +%% +%% hier-part = "//" authority path-abempty +%% / path-absolute +%% / path-rootless +%% / path-empty +%% +%% The scheme and path components are required, though the path may be +%% empty (no characters). When authority is present, the path must +%% either be empty or begin with a slash ("/") character. When +%% authority is not present, the path cannot begin with two slash +%% characters ("//"). These restrictions result in five different ABNF +%% rules for a path (Section 3.3), only one of which will match any +%% given URI reference. +%% +%% The following are two example URIs and their component parts: +%% +%% foo://example.com:8042/over/there?name=ferret#nose +%% \_/ \______________/\_________/ \_________/ \__/ +%% | | | | | +%% scheme authority path query fragment +%% | _____________________|__ +%% / \ / \ +%% urn:example:animal:ferret:nose +%% +%% scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) +%% authority = [ userinfo "@" ] host [ ":" port ] +%% userinfo = *( unreserved / pct-encoded / sub-delims / ":" ) +%% %% -module(http_uri). -export([parse/1, parse/2, + scheme_defaults/0, encode/1, decode/1]). +-export_type([scheme/0, default_scheme_port_number/0]). + %%%========================================================================= %%% API %%%========================================================================= + +-type scheme() :: atom(). +-type default_scheme_port_number() :: pos_integer(). + +-spec scheme_defaults() -> + [{scheme(), default_scheme_port_number()}]. + +scheme_defaults() -> + [{http, 80}, + {https, 443}, + {ftp, 21}, + {ssh, 22}, + {sftp, 22}, + {tftp, 69}]. + parse(AbsURI) -> parse(AbsURI, []). parse(AbsURI, Opts) -> - case parse_scheme(AbsURI) of + case parse_scheme(AbsURI, Opts) of {error, Reason} -> {error, Reason}; - {Scheme, Rest} -> - case (catch parse_uri_rest(Scheme, Rest, Opts)) of - {UserInfo, Host, Port, Path, Query} -> + {Scheme, DefaultPort, Rest} -> + case (catch parse_uri_rest(Scheme, DefaultPort, Rest, Opts)) of + {ok, {UserInfo, Host, Port, Path, Query}} -> {ok, {Scheme, UserInfo, Host, Port, Path, Query}}; + {error, Reason} -> + {error, {Reason, Scheme, AbsURI}}; _ -> - {error, {malformed_url, AbsURI}} + {error, {malformed_url, Scheme, AbsURI}} end end. +reserved() -> + sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?, + $#, $[, $], $<, $>, $\", ${, $}, $|, + $\\, $', $^, $%, $ ]). + encode(URI) -> - Reserved = sets:from_list([$;, $:, $@, $&, $=, $+, $,, $/, $?, - $#, $[, $], $<, $>, $\", ${, $}, $|, - $\\, $', $^, $%, $ ]), - %% lists:append(lists:map(fun(Char) -> uri_encode(Char, Reserved) end, URI)). + Reserved = reserved(), lists:append([uri_encode(Char, Reserved) || Char <- URI]). decode(String) -> @@ -67,20 +123,31 @@ do_decode([]) -> %%% Internal functions %%%======================================================================== -parse_scheme(AbsURI) -> +which_scheme_defaults(Opts) -> + Key = scheme_defaults, + case lists:keysearch(Key, 1, Opts) of + {value, {Key, SchemeDefaults}} -> + SchemeDefaults; + false -> + scheme_defaults() + end. + +parse_scheme(AbsURI, Opts) -> case split_uri(AbsURI, ":", {error, no_scheme}, 1, 1) of {error, no_scheme} -> {error, no_scheme}; - {StrScheme, Rest} -> - case list_to_atom(http_util:to_lower(StrScheme)) of - Scheme when (Scheme =:= http) orelse (Scheme =:= https) -> - {Scheme, Rest}; - Scheme -> - {error, {not_supported_scheme, Scheme}} + {SchemeStr, Rest} -> + Scheme = list_to_atom(http_util:to_lower(SchemeStr)), + SchemeDefaults = which_scheme_defaults(Opts), + case lists:keysearch(Scheme, 1, SchemeDefaults) of + {value, {Scheme, DefaultPort}} -> + {Scheme, DefaultPort, Rest}; + false -> + {Scheme, no_default_port, Rest} end end. -parse_uri_rest(Scheme, "//" ++ URIPart, Opts) -> +parse_uri_rest(Scheme, DefaultPort, "//" ++ URIPart, Opts) -> {Authority, PathQuery} = case split_uri(URIPart, "/", URIPart, 1, 0) of Split = {_, _} -> @@ -93,26 +160,25 @@ parse_uri_rest(Scheme, "//" ++ URIPart, Opts) -> {URIPart,""} end end, - {UserInfo, HostPort} = split_uri(Authority, "@", {"", Authority}, 1, 1), - {Host, Port} = parse_host_port(Scheme, HostPort, Opts), + {Host, Port} = parse_host_port(Scheme, DefaultPort, HostPort, Opts), {Path, Query} = parse_path_query(PathQuery), - {UserInfo, Host, Port, Path, Query}. + {ok, {UserInfo, Host, Port, Path, Query}}. parse_path_query(PathQuery) -> {Path, Query} = split_uri(PathQuery, "\\?", {PathQuery, ""}, 1, 0), {path(Path), Query}. -parse_host_port(Scheme,"[" ++ HostPort, Opts) -> %ipv6 - DefaultPort = default_port(Scheme), +%% In this version of the function, we no longer need +%% the Scheme argument, but just in case... +parse_host_port(_Scheme, DefaultPort, "[" ++ HostPort, Opts) -> %ipv6 {Host, ColonPort} = split_uri(HostPort, "\\]", {HostPort, ""}, 1, 1), Host2 = maybe_ipv6_host_with_brackets(Host, Opts), {_, Port} = split_uri(ColonPort, ":", {"", DefaultPort}, 0, 1), {Host2, int_port(Port)}; -parse_host_port(Scheme, HostPort, _Opts) -> - DefaultPort = default_port(Scheme), +parse_host_port(_Scheme, DefaultPort, HostPort, _Opts) -> {Host, Port} = split_uri(HostPort, ":", {HostPort, DefaultPort}, 1, 1), {Host, int_port(Port)}. @@ -133,15 +199,14 @@ maybe_ipv6_host_with_brackets(Host, Opts) -> Host end. -default_port(http) -> - 80; -default_port(https) -> - 443. int_port(Port) when is_integer(Port) -> Port; int_port(Port) when is_list(Port) -> - list_to_integer(Port). + list_to_integer(Port); +%% This is the case where no port was found and there was no default port +int_port(no_default_port) -> + throw({error, no_default_port}). path("") -> "/"; diff --git a/lib/inets/src/http_server/Makefile b/lib/inets/src/http_server/Makefile index 55cc68dede..67555d5f1c 100644 --- a/lib/inets/src/http_server/Makefile +++ b/lib/inets/src/http_server/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2005-2010. All Rights Reserved. +# Copyright Ericsson AB 2005-2012. 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 @@ -88,6 +88,8 @@ ERL_FILES = $(MODULES:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' + # ---------------------------------------------------- # FLAGS @@ -121,11 +123,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/http_server - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/http_server - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/http_server" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/http_server" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/http_server/httpd_conf.erl b/lib/inets/src/http_server/httpd_conf.erl index 7646300409..b575d7331b 100644 --- a/lib/inets/src/http_server/httpd_conf.erl +++ b/lib/inets/src/http_server/httpd_conf.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -210,12 +210,32 @@ load("MaxBodySize " ++ MaxBodySize, []) -> {ok, Integer} -> {ok, [], {max_body_size,Integer}}; {error, _} -> - {error, ?NICE(clean(MaxBodySize)++ + {error, ?NICE(clean(MaxBodySize) ++ " is an invalid number of MaxBodySize")} end; load("ServerName " ++ ServerName, []) -> - {ok,[],{server_name,clean(ServerName)}}; + {ok,[], {server_name, clean(ServerName)}}; + +load("ServerTokens " ++ ServerTokens, []) -> + %% These are the valid *plain* server tokens: + %% sprod, major, minor, minimum, os, full + %% It can also be a "private" server token: private:<any string> + case string:tokens(ServerTokens, [$:]) of + ["private", Private] -> + {ok,[], {server_tokens, clean(Private)}}; + [TokStr] -> + Tok = list_to_atom(clean(TokStr)), + case lists:member(Tok, [prod, major, minor, minimum, os, full]) of + true -> + {ok,[], {server_tokens, Tok}}; + false -> + {error, ?NICE(clean(ServerTokens) ++ + " is an invalid ServerTokens")} + end; + _ -> + {error, ?NICE(clean(ServerTokens) ++ " is an invalid ServerTokens")} + end; load("SocketType " ++ SocketType, []) -> %% ssl is the same as HTTP_DEFAULT_SSL_KIND @@ -475,7 +495,7 @@ validate_properties(Properties) -> validate_properties2(Properties) -> case proplists:get_value(bind_address, Properties) of undefined -> - case proplists:get_value(sock_type, Properties, ip_comm) of + case proplists:get_value(sock_type, Properties, ip_comm) of ip_comm -> case proplists:get_value(ipfamily, Properties) of undefined -> @@ -537,6 +557,20 @@ validate_config_params([{server_name, Value} | Rest]) validate_config_params([{server_name, Value} | _]) -> throw({server_name, Value}); +validate_config_params([{server_tokens, Value} | Rest]) + when is_atom(Value) -> + case lists:member(Value, plain_server_tokens()) of + true -> + validate_config_params(Rest); + false -> + throw({server_tokens, Value}) + end; +validate_config_params([{server_tokens, {private, Value}} | Rest]) + when is_list(Value) -> + validate_config_params(Rest); +validate_config_params([{server_tokens, Value} | _]) -> + throw({server_tokens, Value}); + validate_config_params([{socket_type, Value} | Rest]) when (Value =:= ip_comm) orelse (Value =:= ssl) orelse @@ -737,9 +771,73 @@ store({log_format, LogFormat}, _ConfigList) store({log_format, LogFormat}, _ConfigList) when (LogFormat =:= compact) orelse (LogFormat =:= pretty) -> {ok, {log_format, LogFormat}}; +store({server_tokens, ServerTokens} = Entry, _ConfigList) -> + Server = server(ServerTokens), + {ok, [Entry, {server, Server}]}; store(ConfigListEntry, _ConfigList) -> {ok, ConfigListEntry}. + +%% The SERVER_SOFTWARE macro has the following structure: +%% <product>/<version> +%% Example: "inets/1.2.3" +%% So, with this example (on a linux machine, with OTP R15B), +%% this will result in: +%% prod: "inets" +%% major: "inets/1" +%% minor: "inets/1.2" +%% minimal: "inets/1.2.3" +%% os: "inets/1.2.3 (unix) +%% full: "inets/1.2.3 (unix/linux) OTP/R15B" +%% Note that the format of SERVER_SOFTWARE is that of 'minimal'. +%% Also, there will always be atleast two digits in a version: +%% Not just 1 but 1.0 +%% +%% We have already checked that the value is valid, +%% so there is no need to check enything here. +%% +server(prod = _ServerTokens) -> + [Prod|_Version] = string:tokens(?SERVER_SOFTWARE, [$/]), + Prod; +server(major = _ServerTokens) -> + [Prod|Version] = string:tokens(?SERVER_SOFTWARE, [$/]), + [Major|_] = string:tokens(Version, [$.]), + Prod ++ "/" ++ Major; +server(minor = _ServerTokens) -> + [Prod|Version] = string:tokens(?SERVER_SOFTWARE, [$/]), + [Major,Minor|_] = string:tokens(Version, [$.]), + Prod ++ "/" ++ Major ++ "." ++ Minor; +server(minimal = _ServerTokens) -> + %% This is the default + ?SERVER_SOFTWARE; +server(os = _ServerTokens) -> + OS = os_info(partial), + lists:flatten(io_lib:format("~s ~s", [?SERVER_SOFTWARE, OS])); +server(full = _ServerTokens) -> + OTPRelease = otp_release(), + OS = os_info(full), + lists:flatten( + io_lib:format("~s ~s OTP/~s", [?SERVER_SOFTWARE, OS, OTPRelease])); +server({private, Server} = _ServerTokens) when is_list(Server) -> + %% The user provide its own + Server; +server(_) -> + ?SERVER_SOFTWARE. + +os_info(Info) -> + case os:type() of + {OsFamily, _OsName} when Info =:= partial -> + lists:flatten(io_lib:format("(~w)", [OsFamily])); + {OsFamily, OsName} -> + lists:flatten(io_lib:format("(~w/~w)", [OsFamily, OsName])); + OsFamily -> + lists:flatten(io_lib:format("(~w)", [OsFamily])) + end. + +otp_release() -> + erlang:system_info(otp_release). + + %% Phase 3: Remove remove_all(ConfigDB) -> Modules = httpd_util:lookup(ConfigDB,modules,[]), @@ -1159,6 +1257,10 @@ ssl_ca_certificate_file(ConfigDB) -> [{cacertfile, File}] end. +plain_server_tokens() -> + [prod, major, minor, minimum, os, full]. + error_report(Where,M,F,Error) -> error_logger:error_report([{?MODULE, Where}, {apply, {M, F, []}}, Error]). + diff --git a/lib/inets/src/http_server/httpd_response.erl b/lib/inets/src/http_server/httpd_response.erl index dd7223876e..2dedb088e4 100644 --- a/lib/inets/src/http_server/httpd_response.erl +++ b/lib/inets/src/http_server/httpd_response.erl @@ -144,10 +144,14 @@ send_response(ModData, Header, Body) -> end end. -send_header(#mod{socket_type = Type, socket = Sock, - http_version = Ver, connection = Conn} = _ModData, +send_header(#mod{socket_type = Type, + socket = Sock, + http_version = Ver, + connection = Conn, + config_db = ConfigDb} = _ModData, StatusCode, KeyValueTupleHeaders) -> - Headers = create_header(lists:map(fun transform/1, KeyValueTupleHeaders)), + Headers = create_header(ConfigDb, + lists:map(fun transform/1, KeyValueTupleHeaders)), NewVer = case {Ver, StatusCode} of {[], _} -> %% May be implicit! @@ -275,13 +279,20 @@ cache_headers(#mod{config_db = Db}) -> [] end. -create_header(KeyValueTupleHeaders) -> - NewHeaders = add_default_headers([{"date", httpd_util:rfc1123_date()}, - {"content-type", "text/html"}, - {"server", ?SERVER_SOFTWARE}], - KeyValueTupleHeaders), +create_header(ConfigDb, KeyValueTupleHeaders) -> + Date = httpd_util:rfc1123_date(), + ContentType = "text/html", + Server = server(ConfigDb), + NewHeaders = add_default_headers([{"date", Date}, + {"content-type", ContentType}, + {"server", Server}], + KeyValueTupleHeaders), lists:map(fun fix_header/1, NewHeaders). + +server(ConfigDb) -> + httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE). + fix_header({Key0, Value}) -> %% make sure first letter is capital Words1 = string:tokens(Key0, "-"), diff --git a/lib/inets/src/http_server/httpd_script_env.erl b/lib/inets/src/http_server/httpd_script_env.erl index d3115150b0..ac79a8cc63 100644 --- a/lib/inets/src/http_server/httpd_script_env.erl +++ b/lib/inets/src/http_server/httpd_script_env.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -50,29 +50,44 @@ create_env(ScriptType, ModData, ScriptElements) -> %%%======================================================================== %%% Internal functions %%%======================================================================== + +which_server(#mod{config_db = ConfigDb}) -> + httpd_util:lookup(ConfigDb, server, ?SERVER_SOFTWARE). + +which_port(#mod{config_db = ConfigDb}) -> + httpd_util:lookup(ConfigDb, port, 80). + +which_peername(#mod{init_data = #init_data{peername = {_, RemoteAddr}}}) -> + RemoteAddr. + +which_resolve(#mod{init_data = #init_data{resolve = Resolve}}) -> + Resolve. + +which_method(#mod{method = Method}) -> + Method. + +which_request_uri(#mod{request_uri = RUri}) -> + RUri. + create_basic_elements(esi, ModData) -> - {_, RemoteAddr} = (ModData#mod.init_data)#init_data.peername, - [{server_software, ?SERVER_SOFTWARE}, - {server_name, (ModData#mod.init_data)#init_data.resolve}, - {gateway_interface,?GATEWAY_INTERFACE}, - {server_protocol, ?SERVER_PROTOCOL}, - {server_port, httpd_util:lookup(ModData#mod.config_db,port,80)}, - {request_method, ModData#mod.method}, - {remote_addr, RemoteAddr}, - {script_name, ModData#mod.request_uri}]; + [{server_software, which_server(ModData)}, + {server_name, which_resolve(ModData)}, + {gateway_interface, ?GATEWAY_INTERFACE}, + {server_protocol, ?SERVER_PROTOCOL}, + {server_port, which_port(ModData)}, + {request_method, which_method(ModData)}, + {remote_addr, which_peername(ModData)}, + {script_name, which_request_uri(ModData)}]; create_basic_elements(cgi, ModData) -> - {_, RemoteAddr} = (ModData#mod.init_data)#init_data.peername, - [{"SERVER_SOFTWARE",?SERVER_SOFTWARE}, - {"SERVER_NAME", (ModData#mod.init_data)#init_data.resolve}, - {"GATEWAY_INTERFACE",?GATEWAY_INTERFACE}, - {"SERVER_PROTOCOL",?SERVER_PROTOCOL}, - {"SERVER_PORT", - integer_to_list(httpd_util:lookup( - ModData#mod.config_db, port, 80))}, - {"REQUEST_METHOD", ModData#mod.method}, - {"REMOTE_ADDR", RemoteAddr}, - {"SCRIPT_NAME", ModData#mod.request_uri}]. + [{"SERVER_SOFTWARE", which_server(ModData)}, + {"SERVER_NAME", which_resolve(ModData)}, + {"GATEWAY_INTERFACE", ?GATEWAY_INTERFACE}, + {"SERVER_PROTOCOL", ?SERVER_PROTOCOL}, + {"SERVER_PORT", integer_to_list(which_port(ModData))}, + {"REQUEST_METHOD", which_method(ModData)}, + {"REMOTE_ADDR", which_peername(ModData)}, + {"SCRIPT_NAME", which_request_uri(ModData)}]. create_http_header_elements(ScriptType, Headers) -> create_http_header_elements(ScriptType, Headers, []). @@ -80,7 +95,7 @@ create_http_header_elements(ScriptType, Headers) -> create_http_header_elements(_, [], Acc) -> Acc; create_http_header_elements(ScriptType, [{Name, [Value | _] = Values } | - Headers], Acc) + Headers], Acc) when is_list(Value) -> NewName = lists:map(fun(X) -> if X == $- -> $_; true -> X end end, Name), Element = http_env_element(ScriptType, NewName, multi_value(Values)), diff --git a/lib/inets/src/http_server/httpd_sup.erl b/lib/inets/src/http_server/httpd_sup.erl index 264dc9f006..8f3e8f9500 100644 --- a/lib/inets/src/http_server/httpd_sup.erl +++ b/lib/inets/src/http_server/httpd_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -162,17 +162,30 @@ httpd_config([Value| _] = Config) when is_tuple(Value) -> httpd_child_spec([Value| _] = Config, AcceptTimeout, Debug) when is_tuple(Value) -> + ?hdrt("httpd_child_spec - entry", [{accept_timeout, AcceptTimeout}, + {debug, Debug}]), Address = proplists:get_value(bind_address, Config, any), Port = proplists:get_value(port, Config, 80), httpd_child_spec(Config, AcceptTimeout, Debug, Address, Port); -httpd_child_spec(ConfigFile, AcceptTimeout, Debug) -> +%% In this case the AcceptTimeout and Debug will only have default values... +httpd_child_spec(ConfigFile, AcceptTimeoutDef, DebugDef) -> + ?hdrt("httpd_child_spec - entry", [{config_file, ConfigFile}, + {accept_timeout_def, AcceptTimeoutDef}, + {debug_def, DebugDef}]), case httpd_conf:load(ConfigFile) of {ok, ConfigList} -> + ?hdrt("httpd_child_spec - loaded", [{config_list, ConfigList}]), case (catch httpd_conf:validate_properties(ConfigList)) of {ok, Config} -> + ?hdrt("httpd_child_spec - validated", [{config, Config}]), Address = proplists:get_value(bind_address, Config, any), Port = proplists:get_value(port, Config, 80), + AcceptTimeout = + proplists:get_value(accept_timeout, Config, + AcceptTimeoutDef), + Debug = + proplists:get_value(debug, Config, DebugDef), httpd_child_spec([{file, ConfigFile} | Config], AcceptTimeout, Debug, Address, Port); Error -> @@ -183,7 +196,7 @@ httpd_child_spec(ConfigFile, AcceptTimeout, Debug) -> end. httpd_child_spec(Config, AcceptTimeout, Debug, Addr, Port) -> - case Port == 0 orelse proplists:is_defined(fd, Config) of + case (Port =:= 0) orelse proplists:is_defined(fd, Config) of true -> httpd_child_spec_listen(Config, AcceptTimeout, Debug, Addr, Port); false -> diff --git a/lib/inets/src/http_server/mod_get.erl b/lib/inets/src/http_server/mod_get.erl index 5cb30e3d97..758985f330 100644 --- a/lib/inets/src/http_server/mod_get.erl +++ b/lib/inets/src/http_server/mod_get.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -18,9 +18,16 @@ %% %% -module(mod_get). + -export([do/1]). + -include("httpd.hrl"). -include("httpd_internal.hrl"). +-include("inets_internal.hrl"). + +-define(VMODULE,"GET"). + + %% do do(Info) -> @@ -84,15 +91,16 @@ send_response(_Socket, _SocketType, Path, Info)-> file:close(FileDescriptor), {proceed,[{response,{already_sent,200, FileInfo#file_info.size}}, - {mime_type,MimeType}|Info#mod.data]}; + {mime_type,MimeType} | Info#mod.data]}; {error, Reason} -> + ?hdrt("send_response -> failed open file", + [{path, Path}, {reason, Reason}]), Status = httpd_file:handle_error(Reason, "open", Info, Path), - {proceed, - [{status, Status}| Info#mod.data]} + {proceed, [{status, Status} | Info#mod.data]} end. %% send - + send(#mod{socket = Socket, socket_type = SocketType} = Info, StatusCode, Headers, FileDescriptor) -> ?DEBUG("send -> send header",[]), diff --git a/lib/inets/src/inets_app/Makefile b/lib/inets/src/inets_app/Makefile index d99e33b4ea..b8d923edd8 100644 --- a/lib/inets/src/inets_app/Makefile +++ b/lib/inets/src/inets_app/Makefile @@ -46,7 +46,8 @@ MODULES = \ inets_service \ inets_app \ inets_sup \ - inets_regexp + inets_regexp \ + inets_trace INTERNAL_HRL_FILES = inets_internal.hrl EXTERNAL_HRL_FILES = ../../include/httpd.hrl \ @@ -110,13 +111,13 @@ $(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/inets_app - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/inets_app - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/inets_app" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/inets_app" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/inets_app/inets.app.src b/lib/inets/src/inets_app/inets.app.src index 1db7ed2c30..4aea2ef3d7 100644 --- a/lib/inets/src/inets_app/inets.app.src +++ b/lib/inets/src/inets_app/inets.app.src @@ -18,14 +18,15 @@ %% {application,inets, - [{description,"INETS CXC 138 49"}, - {vsn,"%VSN%"}, + [{description, "INETS CXC 138 49"}, + {vsn, "%VSN%"}, {modules,[ inets, inets_sup, inets_app, inets_service, inets_regexp, + inets_trace, %% FTP ftp, diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index e80cb2a23b..2adb2a0fc8 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,62 +18,92 @@ {"%VSN%", [ - {"5.8", + {"5.9", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, [httpc_handler]} + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_manager, soft, soft_purge, soft_purge, []} ] }, - {"5.7.2", + {"5.8.1", [ - {restart_application, inets} - ] - }, - {"5.7.1", - [ - {restart_application, inets} + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + + {load_module, http_uri, soft_purge, soft_purge, []}, + {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, + + {load_module, httpc, soft_purge, soft_purge, + [http_uri, httpc_manager]}, + + {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, + {update, inets_sup, soft, soft_purge, soft_purge, []}, + + {load_module, httpd_conf, soft_purge, soft_purge, []}, + {load_module, httpd_response, soft_purge, soft_purge, []}, + {load_module, httpd_script_env, soft_purge, soft_purge, []}, + + {load_module, inets, soft_purge, soft_purge, [inets_trace]}, + {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpd_sup, soft, soft_purge, soft_purge, []}, + {add_module, inets_trace} ] }, - {"5.7", + {"5.8", [ {restart_application, inets} ] }, - {"5.6", + {"5.7.2", [ {restart_application, inets} ] - } + } ], [ - {"5.8", + {"5.9", [ - {load_module, ftp, soft_purge, soft_purge, []}, - {update, httpc_handler, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, []}, - {update, httpc_manager, {advanced, upgrade_from_pre_5_8_1}, - soft_purge, soft_purge, [httpc_handler]} + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + {load_module, httpc, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpc_manager, soft, soft_purge, soft_purge, []} ] }, - {"5.7.2", + {"5.8.1", [ - {restart_application, inets} - ] - }, - {"5.7.1", - [ - {restart_application, inets} + {load_module, tftp, soft_purge, soft_purge, [inets_service]}, + {load_module, inets_service, soft_purge, soft_purge, []}, + + {load_module, http_uri, soft_purge, soft_purge, []}, + {load_module, httpc_response, soft_purge, soft_purge, [http_uri]}, + + {load_module, httpc, soft_purge, soft_purge, + [http_uri, httpc_manager]}, + + {load_module, inets_app, soft_purge, soft_purge, [inets_sup]}, + {update, inets_sup, soft, soft_purge, soft_purge, []}, + + {load_module, httpd_conf, soft_purge, soft_purge, []}, + {load_module, httpd_response, soft_purge, soft_purge, []}, + {load_module, httpd_script_env, soft_purge, soft_purge, []}, + + {load_module, inets, soft_purge, soft_purge, []}, + {update, httpc_manager, soft, soft_purge, soft_purge, [http_uri]}, + {update, httpc_handler, soft, soft_purge, soft_purge, [httpc_manager]}, + {update, httpd_sup, soft, soft_purge, soft_purge, []}, + {remove, {inets_trace, soft_purge, brutal_purge}} ] }, - {"5.7", + {"5.8", [ {restart_application, inets} ] }, - {"5.6", + {"5.7.2", [ {restart_application, inets} ] diff --git a/lib/inets/src/inets_app/inets.erl b/lib/inets/src/inets_app/inets.erl index 054468e445..f33e0abe27 100644 --- a/lib/inets/src/inets_app/inets.erl +++ b/lib/inets/src/inets_app/inets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,7 +28,7 @@ stop/0, stop/2, services/0, services_info/0, service_names/0]). --export([enable_trace/2, enable_trace/3, disable_trace/0, set_trace/1, +-export([enable_trace/2, enable_trace/3, disable_trace/0, set_trace/1, report_event/4]). -export([versions/0, print_version_info/0, print_version_info/1]). @@ -409,47 +409,8 @@ service_names() -> %% Severity withing Limit) will be written to stdout using io:format. %% %%----------------------------------------------------------------- -enable_trace(Level, Dest) -> - enable_trace(Level, Dest, all). - -enable_trace(Level, Dest, Service) -> - case valid_trace_service(Service) of - true -> - enable_trace2(Level, Dest, Service); - false -> - {error, {invalid_service, Service}} - end. - -enable_trace2(Level, File, Service) - when is_list(File) -> - case file:open(File, [write]) of - {ok, Fd} -> - HandleSpec = {fun handle_trace/2, {Service, Fd}}, - do_enable_trace(Level, process, HandleSpec); - Err -> - Err - end; -enable_trace2(Level, Port, _) when is_integer(Port) -> - do_enable_trace(Level, port, dbg:trace_port(ip, Port)); -enable_trace2(Level, io, Service) -> - HandleSpec = {fun handle_trace/2, {Service, standard_io}}, - do_enable_trace(Level, process, HandleSpec); -enable_trace2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) -> - do_enable_trace(Level, process, HandleSpec). - -do_enable_trace(Level, Type, HandleSpec) -> - case dbg:tracer(Type, HandleSpec) of - {ok, _} -> - set_trace(Level), - ok; - Error -> - Error - end. - -valid_trace_service(all) -> - true; -valid_trace_service(Service) -> - lists:member(Service, [httpc, httpd, ftpc, tftp]). +enable_trace(Level, Dest) -> inets_trace:enable(Level, Dest). +enable_trace(Level, Dest, Service) -> inets_trace:enable(Level, Dest, Service). %%----------------------------------------------------------------- @@ -458,12 +419,7 @@ valid_trace_service(Service) -> %% Description: %% This function is used to stop tracing. %%----------------------------------------------------------------- -disable_trace() -> - %% This is to make handle_trace/2 close the output file (if the - %% event gets there before dbg closes) - inets:report_event(100, "stop trace", stop_trace, [stop_trace]), - dbg:stop(). - +disable_trace() -> inets_trace:disable(). %%----------------------------------------------------------------- @@ -476,60 +432,7 @@ disable_trace() -> %% This function is used to change the trace level when tracing has %% already been started. %%----------------------------------------------------------------- -set_trace(Level) -> - set_trace(Level, all). - -set_trace(Level, Service) -> - Pat = make_pattern(?MODULE, Service, Level), - change_pattern(Pat). - -make_pattern(Mod, Service, Level) - when is_atom(Mod) andalso is_atom(Service) -> - case Level of - min -> - {Mod, Service, []}; - max -> - Head = ['$1', '_', '_', '_'], - Body = [], - Cond = [], - {Mod, Service, [{Head, Cond, Body}]}; - DetailLevel when is_integer(DetailLevel) -> - Head = ['$1', '_', '_', '_'], - Body = [], - Cond = [{ '=<', '$1', DetailLevel}], - {Mod, Service, [{Head, Cond, Body}]}; - _ -> - exit({bad_level, Level}) - end. - -change_pattern({Mod, Service, Pattern}) - when is_atom(Mod) andalso is_atom(Service) -> - MFA = {Mod, report_event, 4}, - case Pattern of - [] -> - try - error_to_exit(ctp, dbg:ctp(MFA)), - error_to_exit(p, dbg:p(all, clear)) - catch - exit:{Where, Reason} -> - {error, {Where, Reason}} - end; - List when is_list(List) -> - try - error_to_exit(ctp, dbg:ctp(MFA)), - error_to_exit(tp, dbg:tp(MFA, Pattern)), - error_to_exit(p, dbg:p(all, [call, timestamp])) - catch - exit:{Where, Reason} -> - {error, {Where, Reason}} - end - end, - ok. - -error_to_exit(_Where, {ok, _} = OK) -> - OK; -error_to_exit(Where, {error, Reason}) -> - exit({Where, Reason}). +set_trace(Level) -> inets_trace:set_level(Level). %%----------------------------------------------------------------- @@ -546,164 +449,8 @@ error_to_exit(Where, {error, Reason}) -> %% put trace on this function. %%----------------------------------------------------------------- -report_event(Severity, Label, Service, Content) - when (is_integer(Severity) andalso - (Severity >= 0) andalso (100 >= Severity)) andalso - is_list(Label) andalso - is_atom(Service) andalso - is_list(Content) -> - hopefully_traced. - - -%% ---------------------------------------------------------------------- -%% handle_trace(Event, Fd) -> Verbosity -%% -%% Parameters: -%% Event -> The trace event (only megaco 'trace_ts' events are printed) -%% Fd -> standard_io | file_descriptor() | trace_port() -%% -%% Description: -%% This function is used to "receive" and print the trace events. -%% Events are printed if: -%% - Verbosity is max -%% - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40) -%% Events are not printed if: -%% - Verbosity is min -%% - Severity is > Verbosity -%%----------------------------------------------------------------- - -handle_trace(_, closed_file = Fd) -> - Fd; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - {_, standard_io} = Fd) -> - (catch io:format(standard_io, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - Fd; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - standard_io = Fd) -> - (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - Fd; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - {_Service, Fd}) -> - (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - (catch file:close(Fd)), - closed_file; -handle_trace({trace_ts, _Who, call, - {?MODULE, report_event, - [_Sev, "stop trace", stop_trace, [stop_trace]]}, - Timestamp}, - Fd) -> - (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), - (catch file:close(Fd)), - closed_file; -handle_trace({trace_ts, Who, call, - {?MODULE, report_event, - [Sev, Label, Service, Content]}, Timestamp}, - Fd) -> - (catch print_inets_trace(Fd, Sev, Timestamp, Who, - Label, Service, Content)), - Fd; -handle_trace(Event, Fd) -> - (catch print_trace(Fd, Event)), - Fd. - - -print_inets_trace({Service, Fd}, - Sev, Timestamp, Who, Label, Service, Content) -> - do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content); -print_inets_trace({ServiceA, Fd}, - Sev, Timestamp, Who, Label, ServiceB, Content) - when (ServiceA =:= all) -> - do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content); -print_inets_trace({ServiceA, _Fd}, - _Sev, _Timestamp, _Who, _Label, ServiceB, _Content) - when ServiceA =/= ServiceB -> - ok; -print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> - do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content). - -do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> - Ts = format_timestamp(Timestamp), - io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s " - "~n Content: ~p" - "~n", - [Service, Sev, Who, Ts, Label, Content]). - -print_trace({_, Fd}, Event) -> - do_print_trace(Fd, Event); -print_trace(Fd, Event) -> - do_print_trace(Fd, Event). - -do_print_trace(Fd, {trace, Who, What, Where}) -> - io:format(Fd, "[trace]" - "~n Who: ~p" - "~n What: ~p" - "~n Where: ~p" - "~n", [Who, What, Where]); - -do_print_trace(Fd, {trace, Who, What, Where, Extra}) -> - io:format(Fd, "[trace]" - "~n Who: ~p" - "~n What: ~p" - "~n Where: ~p" - "~n Extra: ~p" - "~n", [Who, What, Where, Extra]); - -do_print_trace(Fd, {trace_ts, Who, What, Where, When}) -> - Ts = format_timestamp(When), - io:format(Fd, "[trace ~s]" - "~n Who: ~p" - "~n What: ~p" - "~n Where: ~p" - "~n", [Ts, Who, What, Where]); - -do_print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) -> - Ts = format_timestamp(When), - io:format(Fd, "[trace ~s]" - "~n Who: ~p" - "~n What: ~p" - "~n Where: ~p" - "~n Extra: ~p" - "~n", [Ts, Who, What, Where, Extra]); - -do_print_trace(Fd, {seq_trace, What, Where}) -> - io:format(Fd, "[seq trace]" - "~n What: ~p" - "~n Where: ~p" - "~n", [What, Where]); - -do_print_trace(Fd, {seq_trace, What, Where, When}) -> - Ts = format_timestamp(When), - io:format(Fd, "[seq trace ~s]" - "~n What: ~p" - "~n Where: ~p" - "~n", [Ts, What, Where]); - -do_print_trace(Fd, {drop, Num}) -> - io:format(Fd, "[drop trace] ~p~n", [Num]); - -do_print_trace(Fd, Trace) -> - io:format(Fd, "[trace] " - "~n ~p" - "~n", [Trace]). - - -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). +report_event(Severity, Label, Service, Content) -> + inets_trace:report_event(Severity, Label, Service, Content). %%-------------------------------------------------------------------- diff --git a/lib/inets/src/inets_app/inets.mk b/lib/inets/src/inets_app/inets.mk index 194b4ca2b1..adef32dc19 100644 --- a/lib/inets/src/inets_app/inets.mk +++ b/lib/inets/src/inets_app/inets.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2010-2011. All Rights Reserved. +# Copyright Ericsson AB 2010-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -33,16 +33,10 @@ ifeq ($(WARN_UNUSED_WARS), true) ERL_COMPILE_FLAGS += +warn_unused_vars endif -ifeq ($(shell erl -noshell -eval 'io:format("~4s", [erlang:system_info(otp_release)])' -s init stop), R14B) -INETS_ERL_COMPILE_FLAGS += -D'OTP-R14B-COMPILER' -endif - INETS_APP_VSN_COMPILE_FLAGS = \ +'{parse_transform,sys_pre_attributes}' \ +'{attribute,insert,app_vsn,$(APP_VSN)}' -INETS_FLAGS = -D'SERVER_SOFTWARE="$(APPLICATION)/$(VSN)"' - INETS_ERL_COMPILE_FLAGS += \ -pa $(ERL_TOP)/lib/inets/ebin \ $(INETS_APP_VSN_COMPILE_FLAGS) diff --git a/lib/inets/src/inets_app/inets_app.erl b/lib/inets/src/inets_app/inets_app.erl index cae79a6767..ee3359c4c3 100644 --- a/lib/inets/src/inets_app/inets_app.erl +++ b/lib/inets/src/inets_app/inets_app.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2009. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -24,7 +24,7 @@ -export([start/2, stop/1]). start(_Type, _State) -> - supervisor:start_link({local, inets_sup}, inets_sup, []). + inets_sup:start_link(). stop(_State) -> ok. diff --git a/lib/inets/src/inets_app/inets_internal.hrl b/lib/inets/src/inets_app/inets_internal.hrl index 55c3669e4a..e56af3b59d 100644 --- a/lib/inets/src/inets_app/inets_internal.hrl +++ b/lib/inets/src/inets_app/inets_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. 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 @@ -24,8 +24,8 @@ %% Various trace macros -define(report(Severity, Label, Service, Content), - inets:report_event(Severity, Label, Service, - [{module, ?MODULE}, {line, ?LINE} | Content])). + inets_trace:report_event(Severity, Label, Service, + [{module, ?MODULE}, {line, ?LINE} | Content])). -define(report_important(Label, Service, Content), ?report(20, Label, Service, Content)). -define(report_verbose(Label, Service, Content), diff --git a/lib/inets/src/inets_app/inets_service.erl b/lib/inets/src/inets_app/inets_service.erl index a057a51e2c..d17fdfe13e 100644 --- a/lib/inets/src/inets_app/inets_service.erl +++ b/lib/inets/src/inets_app/inets_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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,20 +20,6 @@ -module(inets_service). --ifdef('OTP-R14B-COMPILER'). - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{start_standalone, 1}, - {start_service, 1}, - {stop_service, 1}, - {services, 0}, - {service_info, 1}]; -behaviour_info(_) -> - undefined. - --else. %% Starts service stand-alone %% start_standalone(Config) -> % {ok, Pid} | {error, Reason} @@ -83,4 +69,3 @@ behaviour_info(_) -> -callback service_info(Service :: term()) -> {ok, [{Property :: term(), Value :: term()}]} | {error, Reason :: term()}. --endif. diff --git a/lib/inets/src/inets_app/inets_sup.erl b/lib/inets/src/inets_app/inets_sup.erl index 20d5ef343e..66a0cdf785 100644 --- a/lib/inets/src/inets_app/inets_sup.erl +++ b/lib/inets/src/inets_app/inets_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,9 +25,19 @@ -behaviour(supervisor). +%% External API +-export([start_link/0]). + +%% Supervisor callbacks -export([init/1]). %%%========================================================================= +%%% External functions +%%%========================================================================= +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%%%========================================================================= %%% Supervisor callback %%%========================================================================= init([]) -> diff --git a/lib/inets/src/inets_app/inets_trace.erl b/lib/inets/src/inets_app/inets_trace.erl new file mode 100644 index 0000000000..8911f65897 --- /dev/null +++ b/lib/inets/src/inets_app/inets_trace.erl @@ -0,0 +1,357 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2011-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +%% +%%---------------------------------------------------------------------- +%% Purpose: Module for debug trace functions of the inets application +%%---------------------------------------------------------------------- + +-module(inets_trace). + +%% API +-export([enable/2, enable/3, + disable/0, + set_level/1, set_level/2, + report_event/4]). + + +%%==================================================================== +%% API +%%==================================================================== + +%%----------------------------------------------------------------- +%% enable(Level, Destination) -> void() +%% enable(Level, Destination, Service) -> void() +%% +%% Parameters: +%% Level -> max | min | integer() +%% Destination -> File | Port | io | HandlerSpec +%% Service -> httpc | httpd | ftpc | tftp | all +%% File -> string() +%% Port -> integer() +%% Verbosity -> true | false +%% HandlerSpec = {function(), Data} +%% Data = term() +%% +%% Description: +%% This function is used to start tracing at level Level and send +%% the result either to the file File, the port Port or to a +%% trace handler. +%% Note that it starts a tracer server. +%% When Destination is the atom io (or the tuple {io, Verbosity}), +%% all (printable) inets trace events (trace_ts events which has +%% Severity withing Limit) will be written to stdout using io:format. +%% +%%----------------------------------------------------------------- +enable(Level, Dest) -> + enable(Level, Dest, all). + +enable(Level, Dest, Service) -> + case valid_trace_service(Service) of + true -> + enable2(Level, Dest, Service); + false -> + {error, {invalid_service, Service}} + end. + +enable2(Level, File, Service) + when is_list(File) -> + case file:open(File, [write]) of + {ok, Fd} -> + HandleSpec = {fun handle_trace/2, {Service, Fd}}, + do_enable(Level, process, HandleSpec); + Err -> + Err + end; +enable2(Level, Port, _) when is_integer(Port) -> + do_enable(Level, port, dbg:trace_port(ip, Port)); +enable2(Level, io, Service) -> + HandleSpec = {fun handle_trace/2, {Service, standard_io}}, + do_enable(Level, process, HandleSpec); +enable2(Level, {Fun, _Data} = HandleSpec, _) when is_function(Fun) -> + do_enable(Level, process, HandleSpec). + +do_enable(Level, Type, HandleSpec) -> + case dbg:tracer(Type, HandleSpec) of + {ok, _} -> + set_level(Level), + ok; + Error -> + Error + end. + +valid_trace_service(all) -> + true; +valid_trace_service(Service) -> + lists:member(Service, [httpc, httpd, ftpc, tftp]). + + +%%----------------------------------------------------------------- +%% disable() -> void() +%% +%% Description: +%% This function is used to stop tracing. +%%----------------------------------------------------------------- + +disable() -> + %% This is to make handle_trace/2 close the output file (if the + %% event gets there before dbg closes) + inets_trace:report_event(100, "stop trace", stop_trace, [stop_trace]), + dbg:stop(). + + + +%%----------------------------------------------------------------- +%% set_level(Level) -> void() +%% +%% Parameters: +%% Level -> max | min | integer() +%% +%% Description: +%% This function is used to change the trace level when tracing has +%% already been started. +%%----------------------------------------------------------------- +set_level(Level) -> + set_level(Level, all). + +set_level(Level, Service) -> + Pat = make_pattern(?MODULE, Service, Level), + change_pattern(Pat). + +make_pattern(Mod, Service, Level) + when is_atom(Mod) andalso is_atom(Service) -> + case Level of + min -> + {Mod, Service, []}; + max -> + Head = ['$1', '_', '_', '_'], + Body = [], + Cond = [], + {Mod, Service, [{Head, Cond, Body}]}; + DetailLevel when is_integer(DetailLevel) -> + Head = ['$1', '_', '_', '_'], + Body = [], + Cond = [{ '=<', '$1', DetailLevel}], + {Mod, Service, [{Head, Cond, Body}]}; + _ -> + exit({bad_level, Level}) + end. + +change_pattern({Mod, Service, Pattern}) + when is_atom(Mod) andalso is_atom(Service) -> + MFA = {Mod, report_event, 4}, + case Pattern of + [] -> + try + error_to_exit(ctp, dbg:ctp(MFA)), + error_to_exit(p, dbg:p(all, clear)) + catch + exit:{Where, Reason} -> + {error, {Where, Reason}} + end; + List when is_list(List) -> + try + error_to_exit(ctp, dbg:ctp(MFA)), + error_to_exit(tp, dbg:tp(MFA, Pattern)), + error_to_exit(p, dbg:p(all, [call, timestamp])) + catch + exit:{Where, Reason} -> + {error, {Where, Reason}} + end + end. + +error_to_exit(_Where, {ok, _} = OK) -> + OK; +error_to_exit(Where, {error, Reason}) -> + exit({Where, Reason}). + + +%%----------------------------------------------------------------- +%% report_event(Severity, Label, Service, Content) +%% +%% Parameters: +%% Severity -> 0 =< integer() =< 100 +%% Label -> string() +%% Service -> httpd | httpc | ftp | tftp +%% Content -> [{tag, term()}] +%% +%% Description: +%% This function is used to generate trace events, that is, +%% put trace on this function. +%%----------------------------------------------------------------- + +report_event(Severity, Label, Service, Content) + when (is_integer(Severity) andalso + (Severity >= 0) andalso (100 >= Severity)) andalso + is_list(Label) andalso + is_atom(Service) andalso + is_list(Content) -> + hopefully_traced. + + +%% ---------------------------------------------------------------------- +%% handle_trace(Event, Fd) -> Verbosity +%% +%% Parameters: +%% Event -> The trace event +%% Fd -> standard_io | file_descriptor() | trace_port() +%% +%% Description: +%% This function is used to "receive" and pretty print the trace events. +%% Events are printed if: +%% - Verbosity is max +%% - Severity is =< Verbosity (e.g. Severity = 30, and Verbosity = 40) +%% Events are not printed if: +%% - Verbosity is min +%% - Severity is > Verbosity +%%----------------------------------------------------------------- + +handle_trace(_, closed_file = Fd) -> + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + {_, standard_io} = Fd) -> + (catch io:format(standard_io, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + standard_io = Fd) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + Fd; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + {_Service, Fd}) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + (catch file:close(Fd)), + closed_file; +handle_trace({trace_ts, _Who, call, + {?MODULE, report_event, + [_Sev, "stop trace", stop_trace, [stop_trace]]}, + Timestamp}, + Fd) -> + (catch io:format(Fd, "stop trace at ~s~n", [format_timestamp(Timestamp)])), + (catch file:close(Fd)), + closed_file; +handle_trace({trace_ts, Who, call, + {?MODULE, report_event, + [Sev, Label, Service, Content]}, Timestamp}, + Fd) -> + (catch print_inets_trace(Fd, Sev, Timestamp, Who, + Label, Service, Content)), + Fd; +handle_trace(Event, Fd) -> + (catch print_trace(Fd, Event)), + Fd. + + +print_inets_trace({Service, Fd}, + Sev, Timestamp, Who, Label, Service, Content) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content); +print_inets_trace({ServiceA, Fd}, + Sev, Timestamp, Who, Label, ServiceB, Content) + when (ServiceA =:= all) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, ServiceB, Content); +print_inets_trace({ServiceA, _Fd}, + _Sev, _Timestamp, _Who, _Label, ServiceB, _Content) + when ServiceA =/= ServiceB -> + ok; +print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> + do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content). + +do_print_inets_trace(Fd, Sev, Timestamp, Who, Label, Service, Content) -> + Ts = format_timestamp(Timestamp), + io:format(Fd, "[inets ~w trace ~w ~w ~s] ~s " + "~n Content: ~p" + "~n", + [Service, Sev, Who, Ts, Label, Content]). + +print_trace({_, Fd}, Event) -> + do_print_trace(Fd, Event); +print_trace(Fd, Event) -> + do_print_trace(Fd, Event). + +do_print_trace(Fd, {trace, Who, What, Where}) -> + io:format(Fd, "[trace]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n", [Who, What, Where]); + +do_print_trace(Fd, {trace, Who, What, Where, Extra}) -> + io:format(Fd, "[trace]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n Extra: ~p" + "~n", [Who, What, Where, Extra]); + +do_print_trace(Fd, {trace_ts, Who, What, Where, When}) -> + Ts = format_timestamp(When), + io:format(Fd, "[trace ~s]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n", [Ts, Who, What, Where]); + +do_print_trace(Fd, {trace_ts, Who, What, Where, Extra, When}) -> + Ts = format_timestamp(When), + io:format(Fd, "[trace ~s]" + "~n Who: ~p" + "~n What: ~p" + "~n Where: ~p" + "~n Extra: ~p" + "~n", [Ts, Who, What, Where, Extra]); + +do_print_trace(Fd, {seq_trace, What, Where}) -> + io:format(Fd, "[seq trace]" + "~n What: ~p" + "~n Where: ~p" + "~n", [What, Where]); + +do_print_trace(Fd, {seq_trace, What, Where, When}) -> + Ts = format_timestamp(When), + io:format(Fd, "[seq trace ~s]" + "~n What: ~p" + "~n Where: ~p" + "~n", [Ts, What, Where]); + +do_print_trace(Fd, {drop, Num}) -> + io:format(Fd, "[drop trace] ~p~n", [Num]); + +do_print_trace(Fd, Trace) -> + io:format(Fd, "[trace] " + "~n ~p" + "~n", [Trace]). + + +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/src/tftp/Makefile b/lib/inets/src/tftp/Makefile index 759b70c8e4..a9df8d0319 100644 --- a/lib/inets/src/tftp/Makefile +++ b/lib/inets/src/tftp/Makefile @@ -39,8 +39,10 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # ---------------------------------------------------- # Target Specs # ---------------------------------------------------- +BEHAVIOUR_MODULES= \ + tftp + MODULES = \ - tftp \ tftp_binary \ tftp_engine \ tftp_file \ @@ -50,10 +52,13 @@ MODULES = \ HRL_FILES = tftp.hrl -ERL_FILES = $(MODULES:%=%.erl) +ERL_FILES= \ + $(MODULES:%=%.erl) \ + $(BEHAVIOUR_MODULES:%=%.erl) TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) # ---------------------------------------------------- # FLAGS @@ -72,10 +77,12 @@ ERL_COMPILE_FLAGS += \ # Targets # ---------------------------------------------------- +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) + debug opt: $(TARGET_FILES) clean: - rm -f $(TARGET_FILES) + rm -f $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) rm -f core docs: @@ -86,11 +93,11 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/tftp - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src/tftp - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/tftp" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src/tftp" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(BEHAVIOUR_TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inets/src/tftp/tftp.erl b/lib/inets/src/tftp/tftp.erl index 0d7ae1a89e..1621add246 100644 --- a/lib/inets/src/tftp/tftp.erl +++ b/lib/inets/src/tftp/tftp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -224,20 +224,6 @@ service_info/1 ]). --ifdef('OTP-R14B-COMPILER'). - --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{prepare, 6}, - {open, 6}, - {read, 1}, - {write, 2}, - {abort, 3}]; -behaviour_info(_) -> - undefined. - --else. -type peer() :: {PeerType :: inet | inet6, PeerHost :: inet:ip_address(), @@ -280,8 +266,6 @@ behaviour_info(_) -> -callback abort(Code :: error_code(), string(), State :: term()) -> 'ok'. --endif. - -include("tftp.hrl"). diff --git a/lib/inets/test/Makefile b/lib/inets/test/Makefile index 87ca60e4b3..7befa82272 100644 --- a/lib/inets/test/Makefile +++ b/lib/inets/test/Makefile @@ -234,7 +234,7 @@ endif # Release directory specification # ---------------------------------------------------- -RELTESTSYSDIR = $(RELEASE_PATH)/inets_test +RELTESTSYSDIR = "$(RELEASE_PATH)/inets_test" RELTESTSYSALLDATADIR = $(RELTESTSYSDIR)/all_SUITE_data RELTESTSYSBINDIR = $(RELTESTSYSALLDATADIR)/bin @@ -284,9 +284,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/test - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/test - $(INSTALL_DATA) $(INETS_FILES) $(RELSYSDIR)/test + $(INSTALL_DIR) "$(RELSYSDIR)/test" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/test" + $(INSTALL_DATA) $(INETS_FILES) "$(RELSYSDIR)/test" @for d in $(DATADIRS); do \ echo "installing data dir $$d"; \ if test -f $$d/TAR.exclude; then \ @@ -299,9 +299,9 @@ release_spec: opt find $$d -name 'erl_crash.dump' >> $$d/TAR.exclude2; \ find $$d -name 'core' >> $$d/TAR.exclude2; \ find $$d -name '.cmake.state' >> $$d/TAR.exclude2; \ - tar cfX - $$d/TAR.exclude2 $$d | (cd $(RELSYSDIR)/test; tar xf -); \ + tar cfX - $$d/TAR.exclude2 $$d | (cd "$(RELSYSDIR)/test"; tar xf -); \ else \ - tar cf - $$d | (cd $(RELSYSDIR)/test; tar xf -); \ + tar cf - $$d | (cd "$(RELSYSDIR)/test"; tar xf -); \ fi; \ done @@ -330,8 +330,8 @@ info: @echo "INETS_SPECS = $(INETS_SPECS)" @echo "INETS_FILES = $(INETS_FILES)" @echo "" - @echo "RELEASE_PATH = $(RELEASE_PATH)" - @echo "RELSYSDIR = $(RELSYSDIR)" + @echo "RELEASE_PATH = "$(RELEASE_PATH)"" + @echo "RELSYSDIR = "$(RELSYSDIR)"" @echo "RELTESTSYSDIR = $(RELTESTSYSDIR)" @echo "RELTESTSYSALLDATADIR = $(RELTESTSYSALLDATADIR)" @echo "RELTESTSYSBINDIR = $(RELTESTSYSBINDIR)" diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 881266b70a..1cdd96f0b0 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -146,6 +146,20 @@ groups() -> ]. +init_per_group(ipv6 = _GroupName, Config) -> + case inets_test_lib:has_ipv6_support() of + {ok, _} -> + Config; + _ -> + {skip, "Host does not support IPv6"} + end; +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + %%-------------------------------------------------------------------- %% Function: init_per_suite(Config) -> Config %% Config - [tuple()] @@ -156,6 +170,9 @@ groups() -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + + ?PRINT_SYSTEM_INFO([]), + PrivDir = ?config(priv_dir, Config), DataDir = ?config(data_dir, Config), ServerRoot = filename:join(PrivDir, "server_root"), @@ -179,10 +196,11 @@ init_per_suite(Config) -> {ok, FileInfo} = file:read_file_info(Cgi), ok = file:write_file_info(Cgi, FileInfo#file_info{mode = 8#00755}), - [{server_root, ServerRoot}, - {doc_root, DocRoot}, - {local_port, ?IP_PORT}, - {local_ssl_port, ?SSL_PORT} | Config]. + [{has_ipv6_support, inets_test_lib:has_ipv6_support()}, + {server_root, ServerRoot}, + {doc_root, DocRoot}, + {local_port, ?IP_PORT}, + {local_ssl_port, ?SSL_PORT} | Config]. %%-------------------------------------------------------------------- @@ -198,6 +216,7 @@ end_per_suite(Config) -> application:stop(ssl), ok. + %%-------------------------------------------------------------------- %% Function: init_per_testcase(Case, Config) -> Config %% Case - atom() @@ -229,12 +248,12 @@ init_per_testcase(initial_server_connect = Case, Config) -> "~n ~p", [Case, App, ActualError]), SkipString = "Could not start " ++ atom_to_list(App), - {skip, SkipString}; + skip(SkipString); _:X -> SkipString = lists:flatten( io_lib:format("Failed starting apps: ~p", [X])), - {skip, SkipString} + skip(SkipString) end; init_per_testcase(Case, Config) -> @@ -244,6 +263,7 @@ init_per_testcase(Case, Timeout, Config) -> io:format(user, "~n~n*** INIT ~w:~w[~w] ***" "~n~n", [?MODULE, Case, Timeout]), + PrivDir = ?config(priv_dir, Config), application:stop(inets), Dog = test_server:timetrap(inets_test_lib:minutes(Timeout)), @@ -337,7 +357,8 @@ init_per_testcase(Case, Timeout, Config) -> inets:start(httpc, [{profile, Profile}, {data_dir, PrivDir}], stand_alone), - httpc:set_options([{ipfamily, inet6}], ProfilePid), + ok = httpc:set_options([{ipfamily, inet6}], + ProfilePid), tsp("httpc profile pid: ~p", [ProfilePid]), [{watchdog, Dog}, {profile, ProfilePid}| TmpConfig] catch @@ -346,27 +367,67 @@ init_per_testcase(Case, Timeout, Config) -> "~n ~p", [Case, App, ActualError]), SkipString = "Could not start " ++ atom_to_list(App), - {skip, SkipString}; + skip(SkipString); _:X -> SkipString = lists:flatten( io_lib:format("Failed starting apps: ~p", [X])), - {skip, SkipString} + skip(SkipString) end; _ -> + %% Try inet6fb4 on windows... + %% No need? Since it is set above? + + %% tsp("init_per_testcase -> allways try IPv6 on windows"), + %% ?RUN_ON_WINDOWS( + %% fun() -> + %% tsp("init_per_testcase:set_options_fun -> " + %% "set-option ipfamily to inet6fb4"), + %% Res = httpc:set_options([{ipfamily, inet6fb4}]), + %% tsp("init_per_testcase:set_options_fun -> " + %% "~n Res: ~p", [Res]), + %% Res + %% end), + TmpConfig2 = lists:keydelete(local_server, 1, TmpConfig), %% Will start inets + tsp("init_per_testcase -> try start server"), Server = start_http_server(PrivDir, IpConfFile), [{watchdog, Dog}, {local_server, Server} | TmpConfig2] end, + %% <IPv6> + %% Set default ipfamily to the same as the main server has by default + %% This makes the client try w/ ipv6 before falling back to ipv4, + %% as that is what the server is configured to do. + %% Note that this is required for the tests to run on *BSD w/ ipv6 enabled + %% as well as on Windows. The Linux behaviour of allowing ipv4 connects + %% to ipv6 sockets is not required or even encouraged. + + tsp("init_per_testcase -> Options before ipfamily set: ~n~p", + [httpc:get_options(all)]), + ok = httpc:set_options([{ipfamily, inet6fb4}]), + tsp("init_per_testcase -> Options after ipfamily set: ~n~p", + [httpc:get_options(all)]), + + %% Note that the IPv6 test case(s) *must* use inet6, + %% so this value will be overwritten (see "ipv6_" below). + %% </IPv6> + %% This will fail for the ipv6_ - cases (but that is ok) ProxyExceptions = ["localhost", ?IPV6_LOCAL_HOST], - httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ProxyExceptions}}]), + tsp("init_per_testcase -> Options before proxy set: ~n~p", + [httpc:get_options(all)]), + ok = httpc:set_options([{proxy, {{?PROXY, ?PROXY_PORT}, ProxyExceptions}}]), + tsp("init_per_testcase -> Options after proxy set: ~n~p", + [httpc:get_options(all)]), inets:enable_trace(max, io, httpc), %% inets:enable_trace(max, io, all), %% snmp:set_trace([gen_tcp]), + tsp("init_per_testcase(~w) -> done when" + "~n NewConfig: ~p" + "~n~n", [Case, NewConfig]), NewConfig. @@ -405,6 +466,7 @@ end_per_testcase(http_save_to_file = Case, Config) -> end_per_testcase(Case, Config) -> io:format(user, "~n~n*** END ~w:~w ***~n~n", [?MODULE, Case]), + dbg:stop(), % ? case atom_to_list(Case) of "ipv6_" ++ _Rest -> tsp("end_per_testcase(~w) -> stop ssl", [Case]), @@ -449,29 +511,39 @@ http_options(doc) -> http_options(suite) -> []; http_options(Config) when is_list(Config) -> - {skip, "Not supported by httpd"}. + skip("Not supported by httpd"). http_head(doc) -> ["Test http head request against local server."]; http_head(suite) -> []; http_head(Config) when is_list(Config) -> - case ?config(local_server, Config) of - ok -> - Port = ?config(local_port, Config), - URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", - case httpc:request(head, {URL, []}, [], []) of - {ok, {{_,200,_}, [_ | _], []}} -> - ok; - {ok, WrongReply} -> - tsf({wrong_reply, WrongReply}); - Error -> - tsf({failed, Error}) - end; - _ -> - {skip, "Failed to start local http-server"} - end. + tsp("http_head -> entry with" + "~n Config: ~p", [Config]), + Method = head, + Port = ?config(local_port, Config), + URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", + Request = {URL, []}, + HttpOpts = [], + Opts = [], + VerifyResult = + fun({ok, {{_,200,_}, [_ | _], []}}) -> + ok; + ({ok, UnexpectedReply}) -> + tsp("http_head:verify_fun -> Unexpected Reply: " + "~n ~p", [UnexpectedReply]), + tsf({unexpected_reply, UnexpectedReply}); + ({error, Reason} = Error) -> + tsp("http_head:verify_fun -> Error reply: " + "~n Reason: ~p", [Reason]), + tsf({bad_reply, Error}) + end, + simple_request_and_verify(Config, + Method, Request, HttpOpts, Opts, VerifyResult). + + %%------------------------------------------------------------------------- + http_get(doc) -> ["Test http get request against local server"]; http_get(suite) -> @@ -488,7 +560,8 @@ http_get(Config) when is_list(Config) -> Request = {URL, []}, Timeout = timer:seconds(1), ConnTimeout = Timeout + timer:seconds(1), - HttpOptions1 = [{timeout, Timeout}, {connect_timeout, ConnTimeout}], + HttpOptions1 = [{timeout, Timeout}, + {connect_timeout, ConnTimeout}], Options1 = [], Body = case httpc:request(Method, Request, HttpOptions1, Options1) of @@ -516,14 +589,15 @@ http_get(Config) when is_list(Config) -> tsf({bad_reply, Error2}) end; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- + http_post(doc) -> - ["Test http post request against local server. We do in this case" - " only care about the client side of the the post. The server" - " script will not actually use the post data."]; + ["Test http post request against local server. We do in this case " + "only care about the client side of the the post. The server " + "script will not actually use the post data."]; http_post(suite) -> []; http_post(Config) when is_list(Config) -> @@ -551,7 +625,7 @@ http_post(Config) when is_list(Config) -> httpc:request(post, {URL, [{"expect","100-continue"}], "text/plain", "foobar"}, [], []); _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- @@ -597,7 +671,7 @@ http_post_streaming(Config) when is_list(Config) -> "text/plain", {BodyFun, 10}}, [], []); _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -621,7 +695,7 @@ http_emulate_lower_versions(Config) when is_list(Config) -> httpc:request(get, {URL, []}, [{version, "HTTP/1.1"}], []), inets_test_lib:check_body(Body2); _-> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -682,121 +756,163 @@ http_inets_pipe(Config) when is_list(Config) -> URL = ?URL_START ++ integer_to_list(Port) ++ "/dummy.html", test_pipeline(URL); _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. test_pipeline(URL) -> - p("test_pipeline -> entry with" - "~n URL: ~p", [URL]), - - httpc:set_options([{pipeline_timeout, 50000}]), - - p("test_pipeline -> issue (async) request 1"), - {ok, RequestId1} = + p("test_pipeline -> entry with" + "~n URL: ~p", [URL]), + + httpc:set_options([{pipeline_timeout, 50000}]), + + p("test_pipeline -> issue (async) request 1" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdA1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - test_server:format("RequestId1: ~p~n", [RequestId1]), - p("test_pipeline -> RequestId1: ~p", [RequestId1]), - - %% Make sure pipeline is initiated - p("test_pipeline -> sleep some", []), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request 2"), - {ok, RequestId2} = + tsp("RequestIdA1: ~p", [RequestIdA1]), + p("test_pipeline -> RequestIdA1: ~p" + "~n when profile info: ~p", [RequestIdA1, httpc:info()]), + + %% Make sure pipeline is initiated + p("test_pipeline -> sleep some", []), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request A2, A3 and A4" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdA2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId2: ~p", [RequestId2]), - p("test_pipeline -> RequestId2: ~p", [RequestId2]), - - p("test_pipeline -> issue (sync) request 3"), - {ok, {{_,200,_}, [_ | _], [_ | _]}} = + {ok, RequestIdA3} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdA4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestIdAs => A2: ~p, A3: ~p and A4: ~p", + [RequestIdA2, RequestIdA3, RequestIdA4]), + p("test_pipeline -> RequestIds => A2: ~p, A3: ~p and A4: ~p" + "~n when profile info: ~p", + [RequestIdA2, RequestIdA3, RequestIdA4, httpc:info()]), + + p("test_pipeline -> issue (sync) request 3"), + {ok, {{_,200,_}, [_ | _], [_ | _]}} = httpc:request(get, {URL, []}, [], []), + + p("test_pipeline -> expect reply for (async) request A1, A2, A3 and A4" + "~n when profile info: ~p", [httpc:info()]), + pipeline_await_async_reply([{RequestIdA1, a1, 200}, + {RequestIdA2, a2, 200}, + {RequestIdA3, a3, 200}, + {RequestIdA4, a4, 200}], ?MINS(1)), - p("test_pipeline -> expect reply for (async) request 1 or 2"), - receive - {http, {RequestId1, {{_, 200, _}, _, _}}} -> - p("test_pipeline -> received reply for (async) request 1 - now wait for 2"), - receive - {http, {RequestId2, {{_, 200, _}, _, _}}} -> - p("test_pipeline -> received reply for (async) request 2"), - ok; - {http, Msg1} -> - tsf(Msg1) - end; - {http, {RequestId2, {{_, 200, _}, _, _}}} -> - io:format("test_pipeline -> received reply for (async) request 2 - now wait for 1"), - receive - {http, {RequestId1, {{_, 200, _}, _, _}}} -> - io:format("test_pipeline -> received reply for (async) request 1"), - ok; - {http, Msg2} -> - tsf(Msg2) - end; - {http, Msg3} -> - tsf(Msg3) - after 60000 -> - receive Any1 -> - tsp("received crap after timeout: ~n ~p", [Any1]), - tsf({error, {timeout, Any1}}) - end - end, - - p("test_pipeline -> sleep some"), - test_server:sleep(4000), - - p("test_pipeline -> issue (async) request 4"), - {ok, RequestId3} = + p("test_pipeline -> sleep some" + "~n when profile info: ~p", [httpc:info()]), + test_server:sleep(4000), + + p("test_pipeline -> issue (async) request B1, B2, B3 and B4" + "~n when profile info: ~p", [httpc:info()]), + {ok, RequestIdB1} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId3: ~p", [RequestId3]), - p("test_pipeline -> RequestId3: ~p", [RequestId3]), - - p("test_pipeline -> issue (async) request 5"), - {ok, RequestId4} = + {ok, RequestIdB2} = httpc:request(get, {URL, []}, [], [{sync, false}]), - tsp("RequestId4: ~p~n", [RequestId4]), - p("test_pipeline -> RequestId4: ~p", [RequestId4]), - - p("test_pipeline -> cancel (async) request 4"), - ok = httpc:cancel_request(RequestId3), - - p("test_pipeline -> expect *no* reply for cancelled (async) request 4 (for 3 secs)"), - receive - {http, {RequestId3, _}} -> - tsf(http_cancel_request_failed) - after 3000 -> - ok - end, - - p("test_pipeline -> expect reply for (async) request 4"), - Body = - receive - {http, {RequestId4, {{_, 200, _}, _, BinBody4}}} = Res -> - p("test_pipeline -> received reply for (async) request 5"), - tsp("Receive : ~p", [Res]), - BinBody4; - {http, Msg4} -> - tsf(Msg4) - after 60000 -> - receive Any2 -> - tsp("received crap after timeout: ~n ~p", [Any2]), - tsf({error, {timeout, Any2}}) - end - end, + {ok, RequestIdB3} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + {ok, RequestIdB4} = + httpc:request(get, {URL, []}, [], [{sync, false}]), + tsp("RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p", + [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4]), + p("test_pipeline -> RequestIdBs => B1: ~p, B2: ~p, B3: ~p and B4: ~p" + "~n when profile info: ~p", + [RequestIdB1, RequestIdB2, RequestIdB3, RequestIdB4, httpc:info()]), + + p("test_pipeline -> cancel (async) request B2" + "~n when profile info: ~p", [httpc:info()]), + ok = httpc:cancel_request(RequestIdB2), + + p("test_pipeline -> " + "expect *no* reply for cancelled (async) request B2 (for 3 secs)" + "~n when profile info: ~p", [httpc:info()]), + receive + {http, {RequestIdB2, _}} -> + tsf(http_cancel_request_failed) + after 3000 -> + ok + end, + + p("test_pipeline -> expect reply for (async) request B1, B3 and B4" + "~n when profile info: ~p", [httpc:info()]), + Bodies = pipeline_await_async_reply([{RequestIdB1, b1, 200}, + {RequestIdB3, b3, 200}, + {RequestIdB4, b4, 200}], ?MINS(1)), + [{b1, Body}|_] = Bodies, - p("test_pipeline -> check reply for (async) request 5"), + p("test_pipeline -> check reply for (async) request B1" + "~n when profile info: ~p", [httpc:info()]), inets_test_lib:check_body(binary_to_list(Body)), - - p("test_pipeline -> ensure no unexpected incomming"), + + p("test_pipeline -> ensure no unexpected incomming" + "~n when profile info: ~p", [httpc:info()]), receive {http, Any} -> tsf({unexpected_message, Any}) after 500 -> ok end, - - p("test_pipeline -> done"), + + p("test_pipeline -> done" + "~n when profile info: ~p", [httpc:info()]), ok. +pipeline_await_async_reply(ReqIds, Timeout) -> + pipeline_await_async_reply(ReqIds, Timeout, []). + +pipeline_await_async_reply([], _, Acc) -> + lists:keysort(1, Acc); +pipeline_await_async_reply(ReqIds, Timeout, Acc) when Timeout > 0 -> + T1 = inets_test_lib:timestamp(), + p("pipeline_await_async_reply -> await replies" + "~n ReqIds: ~p" + "~n Timeout: ~p", [ReqIds, Timeout]), + receive + {http, {RequestId, {{_, Status, _}, _, Body}}} -> + p("pipeline_await_async_reply -> received reply for" + "~n RequestId: ~p" + "~n Status: ~p", [RequestId, Status]), + case lists:keysearch(RequestId, 1, ReqIds) of + {value, {RequestId, N, Status}} -> + p("pipeline_await_async_reply -> " + "found expected request ~w", [N]), + ReqIds2 = lists:keydelete(RequestId, 1, ReqIds), + NewTimeout = Timeout - (inets_test_lib:timestamp()-T1), + pipeline_await_async_reply(ReqIds2, NewTimeout, + [{N, Body} | Acc]); + {value, {RequestId, N, WrongStatus}} -> + p("pipeline_await_async_reply -> " + "found request ~w with wrong status", [N]), + tsf({reply_with_unexpected_status, + {RequestId, N, WrongStatus}}); + false -> + tsf({unexpected_reply, {RequestId, Status}}) + end; + {http, Msg} -> + tsf({unexpected_reply, Msg}) + after Timeout -> + receive + Any -> + tsp("pipeline_await_async_reply -> " + "received unknown data after timeout: " + "~n ~p", [Any]), + tsf({timeout, {unknown, Any}}) + end + end; +pipeline_await_async_reply(ReqIds, _, Acc) -> + tsp("pipeline_await_async_reply -> " + "timeout: " + "~n ~p" + "~nwhen" + "~n ~p", [ReqIds, Acc]), + tsf({timeout, ReqIds, Acc}). + + + %%------------------------------------------------------------------------- http_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; @@ -818,7 +934,7 @@ http_trace(Config) when is_list(Config) -> tsf({failed, Error}) end; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- http_async(doc) -> @@ -853,7 +969,7 @@ http_async(Config) when is_list(Config) -> ok end; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- @@ -874,7 +990,7 @@ http_save_to_file(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), Bin == Body; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -904,7 +1020,7 @@ http_save_to_file_async(Config) when is_list(Config) -> {ok, {{_,200,_}, [_ | _], Body}} = httpc:request(URL), Bin == Body; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- http_headers(doc) -> @@ -961,7 +1077,7 @@ http_headers(Config) when is_list(Config) -> ]}, [], []), ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. %%------------------------------------------------------------------------- @@ -1096,9 +1212,9 @@ ssl_head(SslTag, Config) -> {ok, {{_,200, _}, [_ | _], []}} = httpc:request(head, {URL, []}, [{ssl, SSLConfig}], []); {ok, _} -> - {skip, "local http-server not started"}; + skip("local http-server not started"); _ -> - {skip, "SSL not started"} + skip("SSL not started") end. @@ -1136,13 +1252,24 @@ ssl_get(SslTag, Config) when is_list(Config) -> "~n URL: ~p" "~n SslTag: ~p" "~n SSLOptions: ~p", [URL, SslTag, SSLOptions]), - {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} = - httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []), - inets_test_lib:check_body(Body); + case httpc:request(get, {URL, []}, [{ssl, SSLConfig}], []) of + {ok, {{_,200, _}, [_ | _], Body = [_ | _]}} -> + inets_test_lib:check_body(Body), + ok; + {ok, {StatusLine, Headers, _Body}} -> + tsp("ssl_get -> unexpected result: " + "~n StatusLine: ~p" + "~n Headers: ~p", [StatusLine, Headers]), + tsf({unexpected_response, StatusLine, Headers}); + {error, Reason} -> + tsp("ssl_get -> request failed: " + "~n Reason: ~p", [Reason]), + tsf({request_failed, Reason}) + end; {ok, _} -> - {skip, "local http-server not started"}; + skip("local http-server not started"); _ -> - {skip, "SSL not started"} + skip("SSL not started") end. @@ -1191,9 +1318,9 @@ ssl_trace(SslTag, Config) when is_list(Config) -> tsf({failed, Error}) end; {ok, _} -> - {skip, "local http-server not started"}; + skip("local http-server not started"); _ -> - {skip, "SSL not started"} + skip("SSL not started") end. @@ -1208,8 +1335,8 @@ http_redirect(Config) when is_list(Config) -> "~n Config: ~p", [Config]), case ?config(local_server, Config) of ok -> - tsp("http_redirect -> set ipfamily option to inet"), - ok = httpc:set_options([{ipfamily, inet}]), + %% tsp("http_redirect -> set ipfamily option to inet"), + %% ok = httpc:set_options([{ipfamily, inet}]), tsp("http_redirect -> start dummy server inet"), {DummyServerPid, Port} = dummy_server(ipv4), @@ -1312,7 +1439,7 @@ http_redirect(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -1449,7 +1576,7 @@ proxy_options(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1470,7 +1597,7 @@ proxy_head(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1489,7 +1616,7 @@ proxy_get(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. %%------------------------------------------------------------------------- @@ -1530,12 +1657,13 @@ proxy_emulate_lower_versions(Config) when is_list(Config) -> end; Reason -> - {skip, Reason} + skip(Reason) end. pelv_get(Version) -> httpc:request(get, {?PROXY_URL, []}, [{version, Version}], []). + %%------------------------------------------------------------------------- proxy_trace(doc) -> ["Perform a TRACE request that goes through a proxy."]; @@ -1544,8 +1672,8 @@ proxy_trace(suite) -> proxy_trace(Config) when is_list(Config) -> %%{ok, {{_,200,_}, [_ | _], "TRACE " ++ _}} = %% httpc:request(trace, {?PROXY_URL, []}, [], []), - {skip, "HTTP TRACE is no longer allowed on the ?PROXY_URL server due " - "to security reasons"}. + skip("HTTP TRACE is no longer allowed on the ?PROXY_URL server due " + "to security reasons"). %%------------------------------------------------------------------------- @@ -1568,7 +1696,7 @@ proxy_post(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1593,7 +1721,7 @@ proxy_put(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1618,7 +1746,7 @@ proxy_delete(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1652,9 +1780,10 @@ proxy_headers(Config) when is_list(Config) -> ]}, [], []), ok; Reason -> - {skip, Reason} + skip(Reason) end. + %%------------------------------------------------------------------------- proxy_auth(doc) -> ["Test the code for sending of proxy authorization."]; @@ -1674,7 +1803,7 @@ proxy_auth(Config) when is_list(Config) -> tsf({unexpected_result, Unexpected}) end; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1718,7 +1847,7 @@ proxy_page_does_not_exist(Config) when is_list(Config) -> httpc:request(get, {URL, []}, [], []), ok; Reason -> - {skip, Reason} + skip(Reason) end. @@ -1737,6 +1866,7 @@ proxy_https_not_supported(Config) when is_list(Config) -> tsf({unexpected_reason, Result}) end. + %%------------------------------------------------------------------------- http_stream(doc) -> @@ -1863,7 +1993,7 @@ proxy_stream(Config) when is_list(Config) -> Body == binary_to_list(StreamedBody); Reason -> - {skip, Reason} + skip(Reason) end. @@ -1886,7 +2016,7 @@ parse_url(Config) when is_list(Config) -> http_uri:parse("http://[2010:836B:4179::836B:4179]/foobar.html", [{foo, false}]), {error, - {malformed_url,"http://2010:836B:4179::836B:4179/foobar.html"}} = + {malformed_url, _, "http://2010:836B:4179::836B:4179/foobar.html"}} = http_uri:parse("http://2010:836B:4179::836B:4179/foobar.html"), %% ipv4 @@ -1902,8 +2032,8 @@ parse_url(Config) when is_list(Config) -> http_uri:parse("http://nisse:foobar@localhost:8888/foobar.html"), %% Scheme error - {error,no_scheme} = http_uri:parse("localhost/foobar.html"), - {error,{not_supported_scheme,localhost}} = + {error, no_scheme} = http_uri:parse("localhost/foobar.html"), + {error, {malformed_url, _, _}} = http_uri:parse("localhost:8888/foobar.html"), %% Query @@ -1966,7 +2096,7 @@ ipv6_essl(Config) when is_list(Config) -> ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) -> %% Check if we are a IPv6 host - tsp("ipv6 -> verify ipv6 support", []), + tsp("ipv6 -> verify ipv6 support"), case inets_test_lib:has_ipv6_support(Config) of {ok, Addr} -> tsp("ipv6 -> ipv6 supported: ~p", [Addr]), @@ -1997,8 +2127,8 @@ ipv6(SocketType, Scheme, HTTPOptions, Extra, Config) -> end, ok; _ -> - tsp("ipv6 -> ipv6 not supported", []), - {skip, "Host does not support IPv6"} + tsp("ipv6 -> ipv6 not supported"), + skip("Host does not support IPv6") end. @@ -2355,7 +2485,7 @@ options(Config) when is_list(Config) -> = httpc:request(get, {URL, []}, [{timeout, infinity}], [{full_result, false}]); _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -2468,7 +2598,7 @@ proxy_not_modified_otp_6821(Config) when is_list(Config) -> undefined -> provocate_not_modified_bug(?PROXY_URL); Reason -> - {skip, Reason} + skip(Reason) end. @@ -2870,7 +3000,7 @@ otp_8106_pid(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -2890,7 +3020,7 @@ otp_8106_fun(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -2910,7 +3040,7 @@ otp_8106_mfa(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -3059,7 +3189,7 @@ otp_8352(Config) when is_list(Config) -> ok; _ -> - {skip, "Failed to start local http-server"} + skip("Failed to start local http-server") end. @@ -3254,7 +3384,11 @@ create_config(FileName, ComType, Port, PrivDir, ServerRoot, DocRoot, " mod_include mod_dir mod_get mod_head" " mod_log mod_disk_log mod_trace", + %% BindAddress = "*|inet", % Force the use of IPv4 + BindAddress = "*", % This corresponds to using IpFamily inet6fb4 + HttpConfig = [ + cline(["BindAddress ", BindAddress]), cline(["Port ", integer_to_list(Port)]), cline(["ServerName ", "httpc_test"]), cline(["SocketType ", atom_to_list(ComType)]), @@ -3813,6 +3947,34 @@ pick_header(Headers, Name) -> Val end. + +%% ------------------------------------------------------------------------- + +simple_request_and_verify(Config, + Method, Request, HttpOpts, Opts, VerifyResult) + when (is_list(Config) andalso + is_atom(Method) andalso + is_list(HttpOpts) andalso + is_list(Opts) andalso + is_function(VerifyResult, 1)) -> + tsp("request_and_verify -> entry with" + "~n Method: ~p" + "~n Request: ~p" + "~n HttpOpts: ~p" + "~n Opts: ~p", [Method, Request, HttpOpts, Opts]), + case ?config(local_server, Config) of + ok -> + tsp("request_and_verify -> local-server running"), + Result = (catch httpc:request(Method, Request, HttpOpts, Opts)), + VerifyResult(Result); + _ -> + tsp("request_and_verify -> local-server *not* running - skip"), + hard_skip("Local http-server not running") + end. + + + + not_implemented_yet() -> exit(not_implemented_yet). @@ -3823,9 +3985,9 @@ p(F, A) -> io:format("~p ~w:" ++ F ++ "~n", [self(), ?MODULE | A]). tsp(F) -> - inets_test_lib:tsp(F). + inets_test_lib:tsp("[~w]" ++ F, [?MODULE]). tsp(F, A) -> - inets_test_lib:tsp(F, A). + inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]). tsf(Reason) -> test_server:fail(Reason). @@ -3864,6 +4026,8 @@ dummy_ssl_server_hang_loop(_) -> ok end. +hard_skip(Reason) -> + throw(skip(Reason)). skip(Reason) -> {skip, Reason}. diff --git a/lib/inets/test/httpd_SUITE.erl b/lib/inets/test/httpd_SUITE.erl index a4bb8f7159..41e4188e5f 100644 --- a/lib/inets/test/httpd_SUITE.erl +++ b/lib/inets/test/httpd_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -224,7 +224,8 @@ all() -> ]. groups() -> - [{ip, [], + [ + {ip, [], [ip_mod_alias, ip_mod_actions, ip_mod_security, ip_mod_auth, ip_mod_auth_api, ip_mod_auth_mnesia_api, ip_mod_htaccess, ip_mod_cgi, ip_mod_esi, ip_mod_get, @@ -293,6 +294,14 @@ groups() -> [ticket_5775, ticket_5865, ticket_5913, ticket_6003, ticket_7304]}]. + +init_per_group(ipv6 = _GroupName, Config) -> + case inets_test_lib:has_ipv6_support() of + {ok, _} -> + Config; + _ -> + {skip, "Host does not support IPv6"} + end; init_per_group(_GroupName, Config) -> Config. @@ -314,6 +323,8 @@ init_per_suite(Config) -> "~n Config: ~p" "~n", [Config]), + ?PRINT_SYSTEM_INFO([]), + PrivDir = ?config(priv_dir, Config), SuiteTopDir = filename:join(PrivDir, ?MODULE), case file:make_dir(SuiteTopDir) of @@ -325,10 +336,11 @@ init_per_suite(Config) -> throw({error, {failed_creating_suite_top_dir, Error}}) end, - [{suite_top_dir, SuiteTopDir}, - {node, node()}, - {host, inets_test_lib:hostname()}, - {address, getaddr()} | Config]. + [{has_ipv6_support, inets_test_lib:has_ipv6_support()}, + {suite_top_dir, SuiteTopDir}, + {node, node()}, + {host, inets_test_lib:hostname()}, + {address, getaddr()} | Config]. %%-------------------------------------------------------------------- @@ -363,10 +375,9 @@ init_per_testcase(Case, Config) -> init_per_testcase2(Case, Config) -> - io:format(user, "~w:init_per_testcase2(~w) -> entry with" - "~n Config: ~p" - "~n", [?MODULE, Case, Config]), - + tsp("init_per_testcase2 -> entry with" + "~n Config: ~p", [Config]), + IpNormal = integer_to_list(?IP_PORT) ++ ".conf", IpHtaccess = integer_to_list(?IP_PORT) ++ "htaccess.conf", SslNormal = integer_to_list(?SSL_PORT) ++ ".conf", @@ -375,39 +386,33 @@ init_per_testcase2(Case, Config) -> DataDir = ?config(data_dir, Config), SuiteTopDir = ?config(suite_top_dir, Config), - io:format(user, "~w:init_per_testcase2(~w) -> " - "~n SuiteDir: ~p" - "~n DataDir: ~p" - "~n", [?MODULE, Case, SuiteTopDir, DataDir]), + tsp("init_per_testcase2 -> " + "~n SuiteDir: ~p" + "~n DataDir: ~p", [SuiteTopDir, DataDir]), TcTopDir = filename:join(SuiteTopDir, Case), ?line ok = file:make_dir(TcTopDir), - io:format(user, "~w:init_per_testcase2(~w) -> " - "~n TcTopDir: ~p" - "~n", [?MODULE, Case, TcTopDir]), + tsp("init_per_testcase2 -> " + "~n TcTopDir: ~p", [TcTopDir]), DataSrc = filename:join([DataDir, "server_root"]), ServerRoot = filename:join([TcTopDir, "server_root"]), - io:format(user, "~w:init_per_testcase2(~w) -> " - "~n DataSrc: ~p" - "~n ServerRoot: ~p" - "~n", [?MODULE, Case, DataSrc, ServerRoot]), + tsp("init_per_testcase2 -> " + "~n DataSrc: ~p" + "~n ServerRoot: ~p", [DataSrc, ServerRoot]), ok = file:make_dir(ServerRoot), ok = file:make_dir(filename:join([TcTopDir, "logs"])), NewConfig = [{tc_top_dir, TcTopDir}, {server_root, ServerRoot} | Config], - io:format(user, "~w:init_per_testcase2(~w) -> " - "copy DataSrc to ServerRoot~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> copy DataSrc to ServerRoot"), inets_test_lib:copy_dirs(DataSrc, ServerRoot), - io:format(user, "~w:init_per_testcase2(~w) -> fix cgi~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> fix cgi"), EnvCGI = filename:join([ServerRoot, "cgi-bin", "printenv.sh"]), {ok, FileInfo} = file:read_file_info(EnvCGI), ok = file:write_file_info(EnvCGI, @@ -427,16 +432,14 @@ init_per_testcase2(Case, Config) -> FileInfo1#file_info{mode = 8#00755}), %% To be used by IP test cases - io:format(user, "~w:init_per_testcase2(~w) -> ip testcase setups~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> ip testcase setups"), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], normal_access, IpNormal), create_config([{port, ?IP_PORT}, {sock_type, ip_comm} | NewConfig], mod_htaccess, IpHtaccess), %% To be used by SSL test cases - io:format(user, "~w:init_per_testcase2(~w) -> ssl testcase setups~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> ssl testcase setups"), SocketType = case atom_to_list(Case) of [X, $s, $s, $l | _] -> @@ -460,8 +463,7 @@ init_per_testcase2(Case, Config) -> %% when you run the whole test suite due to shortcomings %% of the test server. - io:format(user, "~w:init_per_testcase2(~w) -> " - "maybe generate IPv6 config file(s)", [?MODULE, Case]), + tsp("init_per_testcase2 -> maybe generate IPv6 config file(s)"), NewConfig2 = case atom_to_list(Case) of "ipv6_" ++ _ -> @@ -502,15 +504,15 @@ init_per_testcase2(Case, Config) -> NewConfig end, - io:format(user, "~w:init_per_testcase2(~w) -> done~n", - [?MODULE, Case]), + tsp("init_per_testcase2 -> done when" + "~n NewConfig2: ~p", [NewConfig2]), NewConfig2. init_per_testcase3(Case, Config) -> - io:format(user, "~w:init_per_testcase3(~w) -> entry with" - "~n Config: ~p", [?MODULE, Case, Config]), + tsp("init_per_testcase3(~w) -> entry with" + "~n Config: ~p", [Case, Config]), %% %% Create a new fresh node to be used by the server in this test-case @@ -532,12 +534,10 @@ init_per_testcase3(Case, Config) -> %% Set trace level case lists:reverse(atom_to_list(Case)) of "tset_emit" ++ _Rest -> % test-cases ending with time_test - io:format(user, "~w:init_per_testcase3(~w) -> disabling trace", - [?MODULE, Case]), + tsp("init_per_testcase3(~w) -> disabling trace", [Case]), inets:disable_trace(); _ -> - io:format(user, "~w:init_per_testcase3(~w) -> enabling trace", - [?MODULE, Case]), + tsp("init_per_testcase3(~w) -> enabling trace", [Case]), %% TraceLevel = 70, TraceLevel = max, TraceDest = io, @@ -545,8 +545,7 @@ init_per_testcase3(Case, Config) -> end, %% Start initialization - io:format(user, "~w:init_per_testcase3(~w) -> start init", - [?MODULE, Case]), + tsp("init_per_testcase3(~w) -> start init", [Case]), Dog = test_server:timetrap(inets_test_lib:minutes(10)), @@ -627,26 +626,32 @@ init_per_testcase3(Case, Config) -> end end, - case CaseRest of - {skip, _} = Skip -> - Skip; - "mod_auth_" ++ _ -> - start_mnesia(?config(node, Config)), - [{watchdog, Dog} | NewConfig]; - "mod_htaccess" -> - ServerRoot = ?config(server_root, Config), - Path = filename:join([ServerRoot, "htdocs"]), - catch remove_htaccess(Path), - create_htaccess_data(Path, ?config(address, Config)), - [{watchdog, Dog} | NewConfig]; - "range" -> - ServerRoot = ?config(server_root, Config), - Path = filename:join([ServerRoot, "htdocs"]), - create_range_data(Path), - [{watchdog, Dog} | NewConfig]; - _ -> - [{watchdog, Dog} | NewConfig] - end. + InitRes = + case CaseRest of + {skip, _} = Skip -> + Skip; + "mod_auth_" ++ _ -> + start_mnesia(?config(node, Config)), + [{watchdog, Dog} | NewConfig]; + "mod_htaccess" -> + ServerRoot = ?config(server_root, Config), + Path = filename:join([ServerRoot, "htdocs"]), + catch remove_htaccess(Path), + create_htaccess_data(Path, ?config(address, Config)), + [{watchdog, Dog} | NewConfig]; + "range" -> + ServerRoot = ?config(server_root, Config), + Path = filename:join([ServerRoot, "htdocs"]), + create_range_data(Path), + [{watchdog, Dog} | NewConfig]; + _ -> + [{watchdog, Dog} | NewConfig] + end, + + tsp("init_per_testcase3(~w) -> done when" + "~n InitRes: ~p", [Case, InitRes]), + + InitRes. %%-------------------------------------------------------------------- @@ -664,16 +669,14 @@ end_per_testcase(Case, Config) -> ok. end_per_testcase2(Case, Config) -> - io:format(user, "~w:end_per_testcase2(~w) -> entry with" - "~n Config: ~p~n", - [?MODULE, Case, Config]), + tsp("end_per_testcase2(~w) -> entry with" + "~n Config: ~p", [Case, Config]), application:unset_env(inets, services), application:stop(inets), application:stop(ssl), application:stop(crypto), % used by the new ssl (essl test cases) cleanup_mnesia(), - io:format(user, "~w:end_per_testcase2(~w) -> done~n", - [?MODULE, Case]), + tsp("end_per_testcase2(~w) -> done", [Case]), ok. @@ -2354,32 +2357,33 @@ create_config(Config, Access, FileName) -> true -> [] end, - ModOrder = case Access of - mod_htaccess -> - "Modules mod_alias mod_htaccess mod_auth " - "mod_security " - "mod_responsecontrol mod_trace mod_esi " - "mod_actions mod_cgi mod_include mod_dir " - "mod_range mod_get " - "mod_head mod_log mod_disk_log"; - _ -> - "Modules mod_alias mod_auth mod_security " - "mod_responsecontrol mod_trace mod_esi " - "mod_actions mod_cgi mod_include mod_dir " + ModOrder = + case Access of + mod_htaccess -> + "Modules mod_alias mod_htaccess mod_auth " + "mod_security " + "mod_responsecontrol mod_trace mod_esi " + "mod_actions mod_cgi mod_include mod_dir " + "mod_range mod_get " + "mod_head mod_log mod_disk_log"; + _ -> + "Modules mod_alias mod_auth mod_security " + "mod_responsecontrol mod_trace mod_esi " + "mod_actions mod_cgi mod_include mod_dir " "mod_range mod_get " - "mod_head mod_log mod_disk_log" - end, + "mod_head mod_log mod_disk_log" + end, -%% The test suite currently does not handle an explicit BindAddress. -%% They assume any has been used, that is Addr is always set to undefined! + %% The test suite currently does not handle an explicit BindAddress. + %% They assume any has been used, that is Addr is always set to undefined! -%% {ok, Hostname} = inet:gethostname(), -%% {ok, Addr} = inet:getaddr(Hostname, inet6), -%% AddrStr = make_ipv6(Addr), -%% BindAddress = lists:flatten(io_lib:format("~s|inet6", [AddrStr])), + %% {ok, Hostname} = inet:gethostname(), + %% {ok, Addr} = inet:getaddr(Hostname, inet6), + %% AddrStr = make_ipv6(Addr), + %% BindAddress = lists:flatten(io_lib:format("~s|inet6", [AddrStr])), - %% BindAddress = "*|inet", - BindAddress = "*", + BindAddress = "*|inet", + %% BindAddress = "*", HttpConfig = [ cline(["Port ", integer_to_list(Port)]), @@ -2770,10 +2774,10 @@ create_ipv6_config(Config, FileName, Ipv6Address) -> ok = file:close(Fd). -%% tsp(F) -> -%% inets_test_lib:tsp(F). +tsp(F) -> + inets_test_lib:tsp("[~w]" ++ F, [?MODULE]). tsp(F, A) -> - inets_test_lib:tsp(F, A). + inets_test_lib:tsp("[~w]" ++ F, [?MODULE|A]). tsf(Reason) -> inets_test_lib:tsf(Reason). diff --git a/lib/inets/test/httpd_basic_SUITE.erl b/lib/inets/test/httpd_basic_SUITE.erl index 4cd38f2ec4..7a476ea14a 100644 --- a/lib/inets/test/httpd_basic_SUITE.erl +++ b/lib/inets/test/httpd_basic_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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,6 +20,8 @@ -module(httpd_basic_SUITE). -include_lib("common_test/include/ct.hrl"). +-include("inets_test_lib.hrl"). + %% Note: This directive should only be used in test suites. -compile(export_all). @@ -184,6 +186,15 @@ escaped_url_in_error_body(doc) -> escaped_url_in_error_body(suite) -> []; escaped_url_in_error_body(Config) when is_list(Config) -> + %% <CONDITIONAL-SKIP> + %% This skip is due to a problem on windows with long path's + %% If a path is too long file:open fails with, for example, eio. + %% Until that problem is fixed, we skip this case... + Skippable = [win32], + Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, + ?NON_PC_TC_MAYBE_SKIP(Config, Condition), + %% </CONDITIONAL-SKIP> + tsp("escaped_url_in_error_body -> entry"), HttpdConf = ?config(httpd_conf, Config), {ok, Pid} = inets:start(httpd, [{port, 0} | HttpdConf]), @@ -192,6 +203,7 @@ escaped_url_in_error_body(Config) when is_list(Config) -> _Address = proplists:get_value(bind_address, Info), %% Request 1 + tss(1000), tsp("escaped_url_in_error_body -> request 1"), URL1 = ?URL_START ++ integer_to_list(Port), %% Make sure the server is ok, by making a request for a valid page @@ -202,11 +214,12 @@ escaped_url_in_error_body(Config) when is_list(Config) -> {ok, {200, _}} -> %% Don't care about the the body, just that we get a ok response ok; - {ok, UnexpectedOK1} -> - tsf({unexpected_ok_1, UnexpectedOK1}) + {ok, {StatusCode1, Body1}} -> + tsf({unexpected_ok_1, StatusCode1, Body1}) end, %% Request 2 + tss(1000), tsp("escaped_url_in_error_body -> request 2"), %% Make sure the server is ok, by making a request for a valid page case httpc:request(get, {URL1 ++ "/dummy.html", []}, @@ -216,11 +229,12 @@ escaped_url_in_error_body(Config) when is_list(Config) -> {ok, {200, _}} -> %% Don't care about the the body, just that we get a ok response ok; - {ok, UnexpectedOK2} -> - tsf({unexpected_ok_2, UnexpectedOK2}) + {ok, {StatusCode2, Body2}} -> + tsf({unexpected_ok_2, StatusCode2, Body2}) end, %% Request 3 + tss(1000), tsp("escaped_url_in_error_body -> request 3"), %% Ask for a non-existing page(1) Path = "/<b>this_is_bold<b>", @@ -238,10 +252,11 @@ escaped_url_in_error_body(Config) when is_list(Config) -> tsf({unexpected_path_3, HTMLEncodedPath, BadPath3}) end; {ok, UnexpectedOK3} -> - tsf({unexpected_ok_1, UnexpectedOK3}) + tsf({unexpected_ok_3, UnexpectedOK3}) end, %% Request 4 + tss(1000), tsp("escaped_url_in_error_body -> request 4"), %% Ask for a non-existing page(2) case httpc:request(get, {URL2, []}, @@ -253,11 +268,12 @@ escaped_url_in_error_body(Config) when is_list(Config) -> HTMLEncodedPath -> ok; BadPath4 -> - tsf({unexpected_path_2, HTMLEncodedPath, BadPath4}) + tsf({unexpected_path_4, HTMLEncodedPath, BadPath4}) end; {ok, UnexpectedOK4} -> tsf({unexpected_ok_4, UnexpectedOK4}) end, + tss(1000), tsp("escaped_url_in_error_body -> stop inets"), inets:stop(httpd, Pid), tsp("escaped_url_in_error_body -> done"), @@ -277,7 +293,12 @@ tsp(F, A) -> inets_test_lib:tsp(F, A). tsf(Reason) -> - test_server:fail(Reason). + inets_test_lib:tsf(Reason). + +tss(Time) -> + inets_test_lib:tss(Time). + + skip(Reason) -> diff --git a/lib/inets/test/httpd_mod.erl b/lib/inets/test/httpd_mod.erl index 5016cdb9e6..387263ce58 100644 --- a/lib/inets/test/httpd_mod.erl +++ b/lib/inets/test/httpd_mod.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -39,14 +39,10 @@ %% Test cases starts here. %%------------------------------------------------------------------------- alias(Type, Port, Host, Node) -> -%% io:format(user, "~w:alias -> entry with" -%% "~n Type: ~p" -%% "~n Port: ~p" -%% "~n Host: ~p" -%% "~n Node: ~p" -%% "~n", [?MODULE, Type, Port, Host, Node]), - - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + %% This is very crude, but... + tsp("alias -> Has IPv6 support: ~p", [inets_test_lib:has_ipv6_support()]), + Opts = [], + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, "GET /pics/icon.sheet.gif " "HTTP/1.0\r\n\r\n", [{statuscode, 200}, @@ -55,7 +51,7 @@ alias(Type, Port, Host, Node) -> {header, "Date"}, {version, "HTTP/1.0"}]), - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, "GET / HTTP/1.0\r\n\r\n", [{statuscode, 200}, {header, "Content-Type","text/html"}, @@ -63,7 +59,7 @@ alias(Type, Port, Host, Node) -> {header, "Date"}, {version, "HTTP/1.0"}]), - ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, "GET /misc/ HTTP/1.0\r\n\r\n", [{statuscode, 200}, {header, "Content-Type","text/html"}, @@ -71,8 +67,8 @@ alias(Type, Port, Host, Node) -> {header, "Date"}, {version, "HTTP/1.0"}]), - %% Check redirection if trailing slash is missing. - ok = httpd_test_lib:verify_request(Type,Host,Port,Node, + %% Check redirection if trailing slash is missing. + ok = httpd_test_lib:verify_request(Type, Host, Port, Opts, Node, "GET /misc HTTP/1.0\r\n\r\n", [{statuscode, 301}, {header, "Location"}, @@ -86,19 +82,23 @@ actions(Type, Port, Host, Node) -> [{statuscode, 200}, {version, "HTTP/1.0"}]). + %%------------------------------------------------------------------------- security(ServerRoot, Type, Port, Host, Node) -> - %% io:format(user, "~w:security -> entry with" - %% "~n ServerRoot: ~p" - %% "~n Type: ~p" - %% "~n Port: ~p" - %% "~n Host: ~p" - %% "~n Node: ~p" - %% "~n", [?MODULE, ServerRoot, Type, Port, Host, Node]), - -%% io:format(user, "~w:security -> register~n", [?MODULE]), + tsp("security -> " + "entry with" + "~n ServerRoot: ~p" + "~n Type: ~p" + "~n Port: ~p" + "~n Host: ~p" + "~n Node: ~p", [ServerRoot, Type, Port, Host, Node]), + + tsp("security -> " + "register - receive security events"), global:register_name(mod_security_test, self()), % Receive events + tsp("security -> " + "sleep"), test_server:sleep(5000), OpenDir = filename:join([ServerRoot, "htdocs", "open"]), @@ -106,133 +106,240 @@ security(ServerRoot, Type, Port, Host, Node) -> %% Test blocking / unblocking of users. %% /open, require user one Aladdin -%% io:format(user, "~w:security -> remove user~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "remove all existing users"), remove_users(Node, ServerRoot, Host, Port, "open"), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for nonex user 'one' - expect 401"), auth_request(Type, Host, Port, Node, "/open/", "one", "onePassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "onePassword"}]}, Node, Port), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for nonex user 'two' - expect 401"), auth_request(Type,Host,Port,Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "two"}, {password, "twoPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for nonex user 'Alladin' - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "Aladdin", "AladdinPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "Aladdin"}, {password, "AladdinPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> add users~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "add user 'one'"), add_user(Node, ServerRoot, Port, "open", "one", "onePassword", []), + + tsp("security -> " + "blocking and unblocking of users - " + "add user 'two'"), add_user(Node, ServerRoot, Port, "open", "two", "twoPassword", []), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request 1 for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request 2 for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> await fail security event~n", [?MODULE]), + + tsp("security -> " + "blocking and unblocking of users - " + "await fail security event"), receive_security_event({event, auth_fail, Port, OpenDir, [{user, "one"}, {password, "WrongPassword"}]}, Node, Port), -%% io:format(user, "~w:security -> await block security event~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "await block security event (two failed attempts)"), receive_security_event({event, user_block, Port, OpenDir, [{user, "one"}]}, Node, Port), -%% io:format(user, "~w:security -> unregister~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "unregister - no more security events"), global:unregister_name(mod_security_test), % No more events. -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for user 'one' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "one", "WrongPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "auth request for user 'one' with correct password - expect 403"), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 403}]), %% User "one" should be blocked now.. - %% [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "list blocked users - 'one' should be the only one"), case list_blocked_users(Node, Port) of [{"one",_, Port, OpenDir,_}] -> ok; Blocked -> - %% io:format(user, "~w:security -> Blocked: ~p" - %% "~n", [?MODULE, Blocked]), + tsp(" *** unexpected blocked users ***" + "~n Blocked: ~p", [Blocked]), exit({unexpected_blocked, Blocked}) end, - -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), - [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node,Port,OpenDir), -%% io:format(user, "~w:security -> unblock user~n", [?MODULE]), + tsp("security -> " + "blocking and unblocking of users - " + "list users blocked for dir '~p' - " + "user 'one' should be the only one", [OpenDir]), + [{"one",_, Port, OpenDir,_}] = list_blocked_users(Node, Port, OpenDir), + + tsp("security -> " + "blocking and unblocking of users - " + "unblock user 'one' for dir '~p'", [OpenDir]), true = unblock_user(Node, "one", Port, OpenDir), - %% User "one" should not be blocked any more.. -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + %% User "one" should not be blocked any more. + + tsp("security -> " + "blocking and unblocking of users - " + "ensure user 'one' is no longer blocked"), [] = list_blocked_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), - [] = list_blocked_users(Node, Port, OpenDir), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + + tsp("security -> " + "blocking and unblocking of users - " + "auth request for user 'one' with correct password - expect 200"), auth_request(Type, Host, Port, Node,"/open/", "one", "onePassword", [{statuscode, 200}]), + + %% Test list_auth_users & auth_timeout -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), - ["one"] = list_auth_users(Node, Port, OpenDir), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "auth request for user 'two' with wrong password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "onePassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users for dir '~p' - expect user 'one'", [OpenDir]), ["one"] = list_auth_users(Node, Port, OpenDir), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "auth request for user 'two' with correct password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", [{statuscode, 401}]), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect user 'one'"), ["one"] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users for dir '~p' - expect user 'one'", [OpenDir]), ["one"] = list_auth_users(Node, Port, OpenDir), + %% Wait for successful auth to timeout. + tsp("security -> " + "list-auth-users and auth-timeout - " + "wait for successful auth to timeout"), test_server:sleep(?AUTH_TIMEOUT*1001), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users - expect none"), [] = list_auth_users(Node, Port), -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "list auth users for dir '~p'~n - expect none", [OpenDir]), [] = list_auth_users(Node, Port, OpenDir), + %% "two" is blocked. -%% io:format(user, "~w:security -> unblock user~n", [?MODULE]), + + tsp("security -> " + "list-auth-users and auth-timeout - " + "unblock user 'two' for dir '~p'", [OpenDir]), true = unblock_user(Node, "two", Port, OpenDir), + + %% Test explicit blocking. Block user 'two'. -%% io:format(user, "~w:security -> list blocked users~n", [?MODULE]), + + tsp("security -> " + "explicit blocking - list blocked users - should be none"), [] = list_blocked_users(Node,Port,OpenDir), -%% io:format(user, "~w:security -> block user~n", [?MODULE]), + + tsp("security -> " + "explicit blocking - " + "block user 'two' for dir '~p'", [OpenDir]), true = block_user(Node, "two", Port, OpenDir, 10), -%% io:format(user, "~w:security -> auth request~n", [?MODULE]), + + tsp("security -> " + "explicit blocking - " + "auth request for user 'two' with correct password - expect 401"), auth_request(Type, Host, Port, Node,"/open/", "two", "twoPassword", - [{statuscode, 401}]). + [{statuscode, 401}]), + tsp("security -> " + "done"). + %%------------------------------------------------------------------------- auth(Type, Port, Host, Node) -> + tsp("auth -> " + "entry with" + "~n Type: ~p" + "~n Port: ~p" + "~n Host: ~p" + "~n Node: ~p", [Type, Port, Host, Node]), + %% Authentication required! ok = httpd_test_lib:verify_request(Type,Host,Port,Node, "GET /open/ HTTP/1.0\r\n\r\n", @@ -917,13 +1024,11 @@ list_users(Node, Root, _Host, Port, Dir) -> receive_security_event(Event, Node, Port) -> - %% io:format(user, "~w:receive_security_event -> entry with" - %% "~n Event: ~p" - %% "~n Node: ~p" - %% "~n Port: ~p" - %% "~n", [?MODULE, Event, Node, Port]), + tsp("receive_security_event -> await ~w event", [element(2, Event)]), receive Event -> + tsp("receive_security_event -> " + "received expected ~w event", [element(2, Event)]), ok; {'EXIT', _, _} -> receive_security_event(Event, Node, Port) @@ -1031,10 +1136,17 @@ check_lists_members1(L1,L2) -> {error,{lists_not_equal,L1,L2}}. -%% tsp(F) -> -%% tsp(F, []). -%% tsp(F, A) -> -%% test_server:format("~p ~p:" ++ F ++ "~n", [self(), ?MODULE | A]). +%% p(F) -> +%% p(F, []). + +%% p(F, A) -> +%% io:format(user, "~w:" ++ F ++ "~n", [?MODULE|A]). + +tsp(F) -> + inets_test_lib:tsp(F). +tsp(F, A) -> + inets_test_lib:tsp(F, A). + tsf(Reason) -> test_server:fail(Reason). diff --git a/lib/inets/test/httpd_test_lib.erl b/lib/inets/test/httpd_test_lib.erl index 2f5867559a..4b33350cf2 100644 --- a/lib/inets/test/httpd_test_lib.erl +++ b/lib/inets/test/httpd_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -79,19 +79,19 @@ %%-------------------------------------------------------------------- %% API %%------------------------------------------------------------------ + 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, 30000). + 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, 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) -> + 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" @@ -101,7 +101,7 @@ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, "~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 + try 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), @@ -117,25 +117,37 @@ verify_request(SocketType, Host, Port, TranspOpts, Node, RequestStr, case request(State#state{request = RequestStr, socket = Socket}, TimeOut) of {error, Reason} -> - tsp("request failed: " + tsp("verify_request -> request failed: " "~n Reason: ~p", [Reason]), {error, Reason}; NewState -> - tsp("validate reply: " + tsp("verify_request -> validate reply: " "~n NewState: ~p", [NewState]), ValidateResult = validate(RequestStr, NewState, Options, Node, Port), - tsp("validation result: " + tsp("verify_request -> validation result: " "~n ~p", [ValidateResult]), inets_test_lib:close(SocketType, Socket), ValidateResult end; ConnectError -> - tsp("verify_request -> connect failed: " + tsp("verify_request -> connect error: " "~n ~p" "~n", [ConnectError]), - tsf({connect_failure, ConnectError}) + tsf({connect_error, ConnectError, + [SocketType, Host, Port, TranspOpts]}) + catch + T:E -> + tsp("verify_request -> connect failed: " + "~n E: ~p" + "~n T: ~p" + "~n", [E, T]), + tsf({connect_failure, + [{type, T}, + {error, E}, + {stacktrace, erlang:get_stacktrace()}, + {args, [SocketType, Host, Port, TranspOpts]}]}) end. request(#state{mfa = {Module, Function, Args}, diff --git a/lib/inets/test/inets_test_lib.erl b/lib/inets/test/inets_test_lib.erl index bbed35e1f8..0f8671b682 100644 --- a/lib/inets/test/inets_test_lib.erl +++ b/lib/inets/test/inets_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -31,42 +31,49 @@ 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([timestamp/0, formated_timestamp/0]). +-export([tsp/1, tsp/2, tsf/1, tss/1]). -export([check_body/1]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). --export([oscmd/1, has_ipv6_support/1]). +-export([oscmd/1, has_ipv6_support/0, has_ipv6_support/1, print_system_info/1]). +-export([run_on_os/2, run_on_windows/1]). -export([ensure_started/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() -> + 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. + 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; + has_ipv6_support(); + {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]), @@ -88,6 +95,49 @@ has_ipv6_support(Config) -> oscmd(Cmd) -> string:strip(os:cmd(Cmd), right, $\n). + +print_system_info([]) -> + do_print_system_info("System Info"); +print_system_info(Prefix) when is_list(Prefix) -> + NewPrefix = lists:flatten(io_lib:format("~s: System Info", [Prefix])), + do_print_system_info(NewPrefix). + +do_print_system_info(Prefix) -> + tsp("~s => " + "~n" + "~n OS Type: ~p" + "~n OS version: ~p" + "~n Sys Arch: ~p" + "~n CPU Topology: ~p" + "~n Num logical procs: ~p" + "~n SMP support: ~p" + "~n Num schedulers: ~p" + "~n Scheduler bindings: ~p" + "~n Wordsize: ~p" + "~n~n", [Prefix, + os:type(), os:version(), + erlang:system_info(system_architecture), + erlang:system_info(cpu_topology), + erlang:system_info(logical_processors), + erlang:system_info(smp_support), + erlang:system_info(schedulers), + erlang:system_info(scheduler_bindings), + erlang:system_info(wordsize)]), + ok. + + +run_on_windows(Fun) -> + run_on_os(windows, Fun). + +run_on_os(windows, Fun) -> + case os:type() of + {win32, _} -> + Fun(); + _ -> + ok + end. + + %% -- Misc node operation wrapper functions -- start_node(Name) -> @@ -156,6 +206,17 @@ do_ensure_started(App, Start) when is_function(Start) -> end. +ensure_loaded(App) -> + case application:load(App) of + ok -> + ok; + {error, {already_loaded,inets}} -> + ok; + Error -> + Error + end. + + %% ---------------------------------------------------------------- %% HTTPD starter functions @@ -165,8 +226,9 @@ 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"), + tsp("start_http_server(essl) -> try start crypto"), application:start(crypto), + tsp("start_http_server(essl) -> try start public_key"), application:start(public_key), do_start_http_server(Conf); start_http_server(Conf, SslTag) -> @@ -177,23 +239,32 @@ do_start_http_server(Conf) -> tsp("do_start_http_server -> entry with" "~n Conf: ~p" "~n", [Conf]), - application:load(inets), - case application:set_env(inets, services, [{httpd, Conf}]) of + tsp("do_start_http_server -> load inets"), + case ensure_loaded(inets) of ok -> - tsp("start_http_server -> httpd conf stored in inets app env"), - case application:start(inets) of + tsp("do_start_http_server -> inets loaded - now set_env for httpd"), + case application:set_env(inets, services, [{httpd, Conf}]) of ok -> - tsp("start_http_server -> inets started"), - ok; - Error1 -> - tsp("<ERROR> Failed starting application: " - "~n Error1: ~p", [Error1]), - Error1 + tsp("do_start_http_server -> " + "httpd conf stored in inets app env"), + case (catch application:start(inets)) of + ok -> + tsp("do_start_http_server -> inets started"), + ok; + Error1 -> + tsp("<ERROR> Failed starting application: " + "~n Error1: ~p", [Error1]), + tsf({failed_starting_inets, Error1}) + end; + Error2 -> + tsp("<ERROR> Failed set application env: " + "~n Error: ~p", [Error2]), + tsf({failed_set_env, Error2}) end; - Error2 -> - tsp("<ERROR> Failed set application env: " - "~n Error: ~p", [Error2]), - Error2 + {error, Reason} -> + tsp("do_start_http_server -> failed loading inets" + "~n Reason: ~p", [Reason]), + tsf({failed_loading_inets, Reason}) end. start_http_server_ssl(FileName) -> @@ -213,6 +284,7 @@ do_start_http_server_ssl(FileName) -> catch do_start_http_server(FileName). + %% ---------------------------------------------------------------------- %% print functions %% @@ -257,20 +329,49 @@ copy_files(FromDir, ToDir) -> copy_dirs(FromDirRoot, ToDirRoot) -> - {ok, Files} = file:list_dir(FromDirRoot), - lists:foreach( - fun(FileOrDir) -> - %% Check if it's a directory or a file - case filelib:is_dir(filename:join(FromDirRoot, FileOrDir)) of - true -> - FromDir = filename:join([FromDirRoot, FileOrDir]), - ToDir = filename:join([ToDirRoot, FileOrDir]), - ok = file:make_dir(ToDir), - copy_dirs(FromDir, ToDir); - false -> - copy_file(FileOrDir, FromDirRoot, ToDirRoot) - end - end, Files). + case file:list_dir(FromDirRoot) of + {ok, Files} -> + lists:foreach( + fun(FileOrDir) -> + %% Check if it's a directory or a file + case filelib:is_dir(filename:join(FromDirRoot, + FileOrDir)) of + true -> + FromDir = filename:join([FromDirRoot, FileOrDir]), + ToDir = filename:join([ToDirRoot, FileOrDir]), + case file:make_dir(ToDir) of + ok -> + copy_dirs(FromDir, ToDir); + {error, Reason} -> + tsp("<ERROR> Failed creating directory: " + "~n ToDir: ~p" + "~n Reason: ~p" + "~nwhen" + "~n ToDirRoot: ~p" + "~n ToDirRoot file info: ~p", + [ToDir, + Reason, + ToDirRoot, + file:read_file_info(ToDirRoot)]), + tsf({failed_copy_dir, ToDir, Reason}) + end; + false -> + copy_file(FileOrDir, FromDirRoot, ToDirRoot) + end + end, Files); + {error, Reason} -> + tsp("<ERROR> Failed get directory file list: " + "~n FromDirRoot: ~p" + "~n Reason: ~p" + "~nwhen" + "~n FromDirRoot file info: ~p", + [FromDirRoot, + Reason, + file:read_file_info(FromDirRoot)]), + tsf({failed_list_dir, FromDirRoot, Reason}) + end. + + del_dirs(Dir) -> case file:list_dir(Dir) of @@ -394,56 +495,73 @@ connect_byte(ip_comm, Host, Port, Opts0) -> connect(ip_comm, Host, Port, Opts). -connect(ssl, Host, Port, Opts) -> +%% This always falls back on IPV4, but tries IPV6 first. +connect(Proto, Host, Port, Opts0) -> + Opts = Opts0 -- [inet, inet6], + connect(Proto, Host, Port, Opts ++ [inet6], inet6). + +connect(ssl, Host, Port, Opts, Type) -> tsp("connect(ssl) -> entry with" "~n Host: ~p" "~n Port: ~p" - "~n Opts: ~p", [Host, Port, Opts]), + "~n Opts: ~p" + "~n Type: ~p", [Host, Port, Opts, Type]), ssl:start(), - %% Does not support ipv6 in old ssl + %% We ignore this option for ssl... + %% ...maybe we should really treat this in the same way as ip_comm... case ssl:connect(Host, Port, Opts) of {ok, Socket} -> {ok, Socket}; + {error, Reason} when Type =:= inet6 -> + tsp("connect(ssl) -> failed connecting with inet6: " + "~n Reason: ~p" + "~n trying inet", [Reason]), + connect(ssl, Host, Port, Opts -- [inet6], inet); {error, Reason} -> - {error, Reason}; + tsp("connect(ssl) -> failed connecting: " + "~n Reason: ~p", [Reason]), + {error, Reason}; Error -> Error end; -connect(ip_comm, Host, Port, Opts) -> +connect(ip_comm, Host, Port, Opts, Type) -> 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 + "~n Opts: ~p" + "~n Type: ~p", [Host, Port, Opts, Type]), + + case gen_tcp:connect(Host, Port, Opts, timer:seconds(10)) of {ok, Socket} -> tsp("connect success"), {ok, Socket}; - {error, nxdomain} -> - tsp("connect error nxdomain when opts: ~p", [Opts]), - connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); - {error, eafnosupport} -> - 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("connect error eafnosupport when opts: ~p", [Opts]), - connect(ip_comm, Host, Port, lists:delete(inet6, Opts)); - {error, {enfile,_}} -> - tsp("connect error enfile when opts: ~p", [Opts]), - {error, enfile}; + + {error, Reason} when ((Type =:= inet6) andalso + ((Reason =:= timeout) orelse + (Reason =:= nxdomain) orelse + (Reason =:= eafnosupport) orelse + (Reason =:= econnreset) orelse + (Reason =:= enetunreach) orelse + (Reason =:= econnrefused) orelse + (Reason =:= ehostunreach))) -> + tsp("connect(ip_comm) -> Connect error: " + "~n Reason: ~p" + "~n Type: ~p" + "~n Opts: ~p", [Reason, Type, Opts]), + connect(ip_comm, Host, Port, Opts -- [inet6], inet); + Error -> - tsp("Unexpected error: " + tsp("connect(ip_comm) -> Fatal connect error: " "~n Error: ~p" "~nwhen" "~n Host: ~p" "~n Port: ~p" "~n Opts: ~p" - "~n", [Error, Host, Port, Opts]), + "~n Type: ~p" + "~n", [Error, Host, Port, Opts, Type]), Error end. - + send(ssl, Socket, Data) -> ssl:send(Socket, Data); @@ -509,12 +627,18 @@ tsp(F) -> tsp(F, []). tsp(F, A) -> Timestamp = formated_timestamp(), - test_server:format("*** ~s ~p ~p ~w:" ++ F ++ "~n", - [Timestamp, node(), self(), ?MODULE | A]). + test_server:format("*** ~s ~p ~p " ++ F ++ "~n", + [Timestamp, node(), self() | A]). tsf(Reason) -> test_server:fail(Reason). +tss(Time) -> + test_server:sleep(Time). + +timestamp() -> + http_util:timestamp(). + formated_timestamp() -> format_timestamp( os:timestamp() ). diff --git a/lib/inets/test/inets_test_lib.hrl b/lib/inets/test/inets_test_lib.hrl index c578398c55..6a86b1b764 100644 --- a/lib/inets/test/inets_test_lib.hrl +++ b/lib/inets/test/inets_test_lib.hrl @@ -50,6 +50,11 @@ -define(OSCMD(Cmd), inets_test_lib:oscmd(Cmd)). +-define(PRINT_SYSTEM_INFO(P), inets_test_lib:print_system_info(P)). + +-define(RUN_ON_OS(OS, FUN), inets_test_lib:run_on_os(OS, FUN)). +-define(RUN_ON_WINDOWS(FUN), inets_test_lib:run_on_windows(FUN)). + %% - Test case macros - diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 77eb43a7ed..949eceea7f 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -18,7 +18,7 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 5.8.1 +INETS_VSN = 5.9.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/inviso/doc/src/Makefile b/lib/inviso/doc/src/Makefile index 1b184ed78b..f00b10f29c 100644 --- a/lib/inviso/doc/src/Makefile +++ b/lib/inviso/doc/src/Makefile @@ -106,14 +106,14 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/inviso/src/Makefile b/lib/inviso/src/Makefile index 1f2f8b1aff..292a2bec99 100644 --- a/lib/inviso/src/Makefile +++ b/lib/inviso/src/Makefile @@ -87,12 +87,12 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/inviso/test/Makefile b/lib/inviso/test/Makefile index c1df29d631..2650faa392 100644 --- a/lib/inviso/test/Makefile +++ b/lib/inviso/test/Makefile @@ -51,10 +51,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) inviso.spec inviso.cover $(ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) inviso.spec inviso.cover $(ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/inviso/test/inviso_tool_SUITE.erl b/lib/inviso/test/inviso_tool_SUITE.erl index f8a9cea47f..e14f32de44 100644 --- a/lib/inviso/test/inviso_tool_SUITE.erl +++ b/lib/inviso/test/inviso_tool_SUITE.erl @@ -1,7 +1,7 @@ %%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 1997-2011. All Rights Reserved.
+%% Copyright Ericsson AB 1997-2012. 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
diff --git a/lib/jinterface/doc/src/Makefile b/lib/jinterface/doc/src/Makefile index acd5307dee..858c97f187 100644 --- a/lib/jinterface/doc/src/Makefile +++ b/lib/jinterface/doc/src/Makefile @@ -160,17 +160,17 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html - $(INSTALL_DIR) $(RELSYSDIR)/doc/html/java/$(JAVA_PKG_PATH) - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - (/bin/cp -rf ../html $(RELSYSDIR)/doc) + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html/java/$(JAVA_PKG_PATH)" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + (/bin/cp -rf ../html "$(RELSYSDIR)/doc") # $(INSTALL_DATA) $(GIF_FILES) $(EXTRA_FILES) $(HTML_FILES) \ -# $(RELSYSDIR)/doc/html -# $(INSTALL_DATA) $(JAVA_EXTRA_FILES) $(RELSYSDIR)/doc/html/java -# $(INSTALL_DATA) $(TOP_HTML_FILES) $(RELSYSDIR)/doc +# "$(RELSYSDIR)/doc/html" +# $(INSTALL_DATA) $(JAVA_EXTRA_FILES) "$(RELSYSDIR)/doc/html/java" +# $(INSTALL_DATA) $(TOP_HTML_FILES) "$(RELSYSDIR)/doc" release_spec: diff --git a/lib/jinterface/doc/src/notes.xml b/lib/jinterface/doc/src/notes.xml index bf94077114..420e121389 100644 --- a/lib/jinterface/doc/src/notes.xml +++ b/lib/jinterface/doc/src/notes.xml @@ -30,6 +30,39 @@ </header> <p>This document describes the changes made to the Jinterface application.</p> +<section><title>Jinterface 1.5.6</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct spelling of registered (Thanks to Richard + Carlsson)</p> + <p> + Own Id: OTP-9925</p> + </item> + <item> + <p> + Java 1.5 has a bug where detecting codepoint offsets in + strings that are created by String.substring() gives + wrong results. The new implementation uses a different + method, avoinding the issue. (Thanks to Vlad Dumitrescu)</p> + <p> + Own Id: OTP-9927</p> + </item> + <item> + <p> + Improve error message when creating a too long + OtpErlangAtom. Also print the value that we tried to use + for the atom. (Thanks to Vlad Dumitrescu)</p> + <p> + Own Id: OTP-9928</p> + </item> + </list> + </section> + +</section> + <section><title>Jinterface 1.5.5</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile index 365798e68a..ec817aa39b 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/Makefile @@ -66,7 +66,7 @@ JARFLAGS=-cvf JAVA_OPTIONS = ifeq ($(TESTROOT),) -RELEASE_PATH=$(ERL_TOP)/release/$(TARGET) +RELEASE_PATH="$(ERL_TOP)/release/$(TARGET)" else RELEASE_PATH=$(TESTROOT) endif @@ -96,13 +96,13 @@ docs: # include $(ERL_TOP)/make/otp_release_targets.mk release release_docs release_tests release_html: - $(MAKE) $(MFLAGS) RELEASE_PATH=$(RELEASE_PATH) $(TARGET_MAKEFILE) $@_spec + $(MAKE) $(MFLAGS) RELEASE_PATH="$(RELEASE_PATH)" $(TARGET_MAKEFILE) $@_spec release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/java_src/com/ericsson/otp/erlang - $(INSTALL_DATA) $(JAVA_SRC) $(RELSYSDIR)/java_src/com/ericsson/otp/erlang - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/java_src/com/ericsson/otp/erlang" + $(INSTALL_DATA) $(JAVA_SRC) "$(RELSYSDIR)/java_src/com/ericsson/otp/erlang" + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(JAVA_DEST_ROOT)$(JARFILE) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java index 60eef7a126..ced4dbb8c2 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangAtom.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. + * Copyright Ericsson AB 2000-2012. 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 diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java index be8b7c5c12..6766b52ce5 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangString.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2011. All Rights Reserved. + * Copyright Ericsson AB 2000-2012. 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 diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java index b0b64e6953..0fd93b09f4 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpMbox.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2011. All Rights Reserved. + * Copyright Ericsson AB 2000-2012. 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 diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java index be7c8e5b1a..7ead0b9c54 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpNode.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2000-2009. All Rights Reserved. + * Copyright Ericsson AB 2000-2012. 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 diff --git a/lib/jinterface/test/Makefile b/lib/jinterface/test/Makefile index a85d0e7411..b70521c08d 100644 --- a/lib/jinterface/test/Makefile +++ b/lib/jinterface/test/Makefile @@ -80,6 +80,6 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(TEST_SPEC_FILE) $(COVER_FILE) $(ERL_FILES) $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(TEST_SPEC_FILE) $(COVER_FILE) $(ERL_FILES) "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) diff --git a/lib/jinterface/test/nc_SUITE_data/echo_server.java b/lib/jinterface/test/nc_SUITE_data/echo_server.java index 5ecb5b72a7..2e18e908d4 100644 --- a/lib/jinterface/test/nc_SUITE_data/echo_server.java +++ b/lib/jinterface/test/nc_SUITE_data/echo_server.java @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2004-2010. All Rights Reserved. + * Copyright Ericsson AB 2004-2012. 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 diff --git a/lib/jinterface/vsn.mk b/lib/jinterface/vsn.mk index 9d8229e9fa..15423e5658 100644 --- a/lib/jinterface/vsn.mk +++ b/lib/jinterface/vsn.mk @@ -1 +1 @@ -JINTERFACE_VSN = 1.5.5 +JINTERFACE_VSN = 1.5.6 diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index 214e994889..78e5f7bc26 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -155,18 +155,18 @@ $(SPECDIR)/specs_zlib_stub.xml: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man4 - $(INSTALL_DATA) $(MAN4_FILES) $(RELEASE_PATH)/man/man4 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/kernel/doc/src/app.xml b/lib/kernel/doc/src/app.xml index ff8a12fe97..1914844b37 100644 --- a/lib/kernel/doc/src/app.xml +++ b/lib/kernel/doc/src/app.xml @@ -4,7 +4,7 @@ <fileref> <header> <copyright> - <year>1997</year><year>2011</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -75,7 +75,7 @@ MaxT int() infinity Names [Name] [] Apps [App] [] Env [{Par,Val}] [] -Start {Module,StartArgs} undefined +Start {Module,StartArgs} [] Phases [{Phase,PhaseArgs}] undefined Module = Name = App = Par = Phase = atom() Val = StartArgs = PhaseArgs = term()</code> diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index 95b23e4aef..ec3274965a 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index 579b7f1f74..e327a4f907 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -123,7 +123,7 @@ <p>Completely closes the socket and all associations on it. The unsent data is flushed as in <c>eof/2</c>. The <c>close/1</c> call is blocking or otherwise depending of the value of - the <seealso marker="#option-linger">linger</seealso> socket + the <seealso marker="inet#option-linger">linger</seealso> socket <seealso marker="#options">option</seealso>. If <c>close</c> does not linger or linger timeout expires, the call returns and the data is flushed in the background.</p> @@ -309,8 +309,8 @@ <seealso marker="#option-active">passive</seealso> mode, with <anno>SockType</anno> <c>seqpacket</c>, and with reasonably large - <seealso marker="#option-sndbuf">kernel</seealso> and driver - <seealso marker="#option-buffer">buffers.</seealso></p> + <seealso marker="inet#option-sndbuf">kernel</seealso> and driver + <seealso marker="inet#option-buffer">buffers.</seealso></p> </desc> </func> <func> @@ -530,19 +530,8 @@ SCTP data interleaved with other inter-process messages.</p> </item> </list> - <marker id="option-buffer"></marker> </item> - <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> - and <c>recbuf</c> options which correspond to - the kernel socket buffers. It is recommended - to have <c>val(buffer) >= max(val(sndbuf),val(recbuf))</c>. - 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, integer()}</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 @@ -567,19 +556,8 @@ <c>{IP,Port}</c> of the socket can be re-used immediately: no waiting in the CLOSE_WAIT state is performed (may be required for high-throughput servers).</p> - <marker id="option-linger"></marker> - </item> - <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 - tuple is <c>false</c>, the 2nd one is ignored, which means that - <c>gen_sctp:close/1</c> returns immediately not waiting - for data to be flushed. Otherwise, the 2nd component is - the flushing time-out in seconds.</p> - <marker id="option-sndbuf"></marker> </item> - <tag><c>{sndbuf, integer()}</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 @@ -593,6 +571,15 @@ <c>val(sndbuf)</c>. Setting this option also adjusts the size of the driver buffer (see <c>buffer</c> above).</p> </item> + + <tag><c>{sctp_module, module()}</c></tag> + <item> <p> + Override which callback module is used. Defaults to + <c>inet_sctp</c> for IPv4 and <c>inet6_sctp</c> for IPv6. + </p> + </item> + + <tag><c>{sctp_rtoinfo, #sctp_rtoinfo{}}</c></tag> <item> <pre> #sctp_rtoinfo{ diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index 8a5d40bb16..11a0843c10 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2011</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -96,37 +96,47 @@ do_recv(Sock, Bs) -> can be either a hostname, or an IP address.</p> <p>The available options are:</p> <taglist> - <tag><c>list</c></tag> - <item> - <p>Received <c>Packet</c> is delivered as a list.</p> - </item> - <tag><c>binary</c></tag> - <item> - <p>Received <c>Packet</c> is delivered as a binary.</p> - </item> - <tag><c>{ip, ip_address()}</c></tag> + <tag><c>{ip, ip_address()}</c></tag> <item> <p>If the host has several network interfaces, this option specifies which one to use.</p> </item> - <tag><c>{port, Port}</c></tag> + + <tag><c>{ifaddr, ip_address()}</c></tag> <item> - <p>Specify which local port number to use.</p> - </item> + <p>Same as <c>{ip, ip_address()}</c>. If the host has several network interfaces, this option + specifies which one to use.</p> + </item> + <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 descriptor for it.</p> </item> - <tag><c>inet6</c></tag> + + <tag><c>inet</c></tag> <item> + <p>Set up the socket for IPv4.</p> + </item> + + <tag><c>inet6</c></tag> + <item> <p>Set up the socket for IPv6.</p> </item> - <tag><c>inet</c></tag> + + <tag><c>{port, Port}</c></tag> <item> - <p>Set up the socket for IPv4.</p> + <p>Specify which local port number to use.</p> </item> + + <tag><c>{tcp_module, module()}</c></tag> + <item> <p> + Override which callback module is used. Defaults to + <c>inet_tcp</c> for IPv4 and <c>inet6_tcp</c> for IPv6. + </p> + </item> + <tag><c>Opt</c></tag> <item> <p>See @@ -197,6 +207,13 @@ do_recv(Sock, Bs) -> <c>gen_tcp</c>, use this option to pass the file descriptor for it.</p> </item> + + <tag><c>{ifaddr, ip_address()}</c></tag> + <item> + <p>Same as <c>{ip, ip_address()}</c>. If the host has several network interfaces, this option + specifies which one to use.</p> + </item> + <tag><c>inet6</c></tag> <item> <p>Set up the socket for IPv6.</p> @@ -205,6 +222,14 @@ do_recv(Sock, Bs) -> <item> <p>Set up the socket for IPv4.</p> </item> + + <tag><c>{tcp_module, module()}</c></tag> + <item> <p> + Override which callback module is used. Defaults to + <c>inet_tcp</c> for IPv4 and <c>inet6_tcp</c> for IPv6. + </p> + </item> + <tag><c>Opt</c></tag> <item> <p>See @@ -235,7 +260,9 @@ do_recv(Sock, Bs) -> <p>Returns <c>{ok, <anno>Socket</anno>}</c> if a connection is established, or <c>{error, closed}</c> if <c><anno>ListenSocket</anno></c> is closed, or <c>{error, timeout}</c> if no connection is established - within the specified time. May also return a POSIX error + within the specified time, + or <c>{error, system_limit}</c> if all available ports in the + Erlang emulator are in use. May also return a POSIX error value if something else goes wrong, see inet(3) for possible error values.</p> <p>Packets can be sent to the returned socket <c><anno>Socket</anno></c> @@ -297,7 +324,7 @@ do_recv(Sock, Bs) -> <c><anno>Socket</anno></c>. The controlling process is the process which receives messages from the socket. If called by any other process than the current controlling process, - <c>{error, eperm}</c> is returned.</p> + <c>{error, not_owner}</c> is returned.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index daa9b7d887..726dc30546 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -72,6 +72,14 @@ <p>If the host has several network interfaces, this option specifies which one to use.</p> </item> + + <tag><c>{ifaddr, ip_address()}</c></tag> + <item> + <p>Same as <c>{ip, ip_address()}</c>. If the host has several network interfaces, this option + specifies which one to use.</p> + </item> + + <tag><c>{fd, integer() >= 0}</c></tag> <item> <p>If a socket has somehow been opened without using @@ -86,6 +94,51 @@ <item> <p>Set up the socket for IPv4.</p> </item> + + <tag><c>{udp_module, module()}</c></tag> + <item> <p> + Override which callback module is used. Defaults to + <c>inet_udp</c> for IPv4 and <c>inet6_udp</c> for IPv6. + </p> + </item> + + <tag><c>{multicast_if, Address}</c></tag> + <item> + <p>Set the local device for a multicast socket.</p> + </item> + + <tag><c>{multicast_loop, true | false}</c></tag> + <item> + <p> + When <c>true</c> sent multicast packets will be looped back to the local + sockets. + </p> + </item> + + <tag><c>{multicast_ttl, Integer}</c></tag> + <item> + <p> + The <c>multicast_ttl</c> option changes the time-to-live (TTL) for + outgoing multicast datagrams in order to control the scope of the + multicasts. + </p> + <p> + Datagrams with a TTL of 1 are not forwarded beyond the local + network. + <br />Default: 1 + </p> + </item> + + <tag><c>{add_membership, {MultiAddress, InterfaceAddress}}</c></tag> + <item> + <p>Join a multicast group. </p> + </item> + + <tag><c>{drop_membership, {MultiAddress, InterfaceAddress}}</c></tag> + <item> + <p>Leave multicast group.</p> + </item> + <tag><c>Opt</c></tag> <item> <p>See @@ -136,7 +189,9 @@ <desc> <p>Assigns a new controlling process <c><anno>Pid</anno></c> to <c><anno>Socket</anno></c>. The controlling process is the process which - receives messages from the socket.</p> + receives messages from the socket. If called by any other + process than the current controlling process, + <c>{error, not_owner}</c> is returned.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 1a05b4ba99..b727960d96 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1997</year><year>2011</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -149,7 +149,7 @@ fe80::204:acff:fe17:bf38 <fsummary>Return a descriptive string for an error reason</fsummary> <desc> <p>Returns a diagnostic error string. See the section below - for possible <c><anno>Posix</anno></c> values and the corresponding + for possible Posix values and the corresponding strings.</p> </desc> </func> @@ -445,10 +445,34 @@ fe80::204:acff:fe17:bf38 flow control; the other side will not be able send faster than the receiver can read.</p> </item> + + <tag><c>{bit8, clear | set | on | off}</c></tag> + <item> + <p> + Scans every byte in received data-packets and checks if the 8 bit + is set in any of them. Information is retrieved with + <c>inet:getopts/2</c>. + </p> + <p>Note that the <c>bit8</c> option is deprecated and will be removed in Erlang/OTP R16.</p> + </item> + <tag><c>{broadcast, Boolean}</c>(UDP sockets)</tag> <item> <p>Enable/disable permission to send broadcasts.</p> + <marker id="option-buffer"></marker> </item> + + <tag><c>{buffer, Size}</c></tag> + <item> + <p>Determines the size of the user-level software buffer used by + the driver. Not to be confused with <c>sndbuf</c> + and <c>recbuf</c> options which correspond to + the kernel socket buffers. It is recommended + to have <c>val(buffer) >= max(val(sndbuf),val(recbuf))</c>. + 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>{delay_send, Boolean}</c></tag> <item> <p>Normally, when an Erlang process sends to a socket, @@ -463,10 +487,19 @@ fe80::204:acff:fe17:bf38 real property of the socket. Needless to say it is an implementation specific option. Default is <c>false</c>.</p> </item> + + <tag><c>{deliver, port | term}</c></tag> + <item> <p> When <c>{active, true}</c> delivers data on the forms + <c>port</c> : <c>{S, {data, [H1,..Hsz | Data]}}</c> or + <c>term</c> : <c>{tcp, S, [H1..Hsz | Data]}</c>. + </p> + </item> + <tag><c>{dontroute, Boolean}</c></tag> <item> <p>Enable/disable routing bypass for outgoing messages.</p> </item> + <tag><c>{exit_on_close, Boolean}</c></tag> <item> <p>By default this option is set to <c>true</c>.</p> @@ -476,6 +509,7 @@ fe80::204:acff:fe17:bf38 <seealso marker="gen_tcp#shutdown/2">gen_tcp:shutdown/2</seealso> to shutdown the write side.</p> </item> + <tag><c>{header, Size}</c></tag> <item> <p>This option is only meaningful if the <c>binary</c> @@ -487,6 +521,15 @@ fe80::204:acff:fe17:bf38 example <c>Size == 2</c>, the data received will match <c>[Byte1,Byte2|Binary]</c>.</p> </item> + + <tag><c>{high_watermark, Size}</c></tag> + <item> <p> + Sender is forced busy if sent and enqueued data + reaches the highwater mark. + <br /> Default: 8192 kB. + </p> + </item> + <tag><c>{keepalive, Boolean}</c>(TCP/IP sockets)</tag> <item> <p>Enables/disables periodic transmission on a connected @@ -494,7 +537,43 @@ fe80::204:acff:fe17:bf38 the other end does not respond, the connection is considered broken and an error message will be sent to the controlling process. Default disabled.</p> + <marker id="option-linger"></marker> </item> + + <tag><c>{linger, {true|false, Seconds}}</c></tag> + <item> + <p>Determines the timeout in seconds for flushing unsent data in the + <c>close/1</c> socket call. If the 1st component of the value + tuple is <c>false</c>, the 2nd one is ignored, which means that + <c>close/1</c> returns immediately not waiting + for data to be flushed. Otherwise, the 2nd component is + the flushing time-out in seconds.</p> + </item> + + <tag><c>{low_watermark, Size}</c></tag> + <item> <p> + If the port has reached its <c>high_watermark</c> it will + force busy onto senders. When the port data queue reaches the + <c>low_watermark</c> callers are no longer forced busy. + <br /> Default: 4096 kB. + </p> + </item> + + <tag><c>{mode, Mode :: binary | list}</c></tag> + <item> + <p>Received <c>Packet</c> is delivered as defined by Mode.</p> + </item> + + <tag><c>list</c></tag> + <item> + <p>Received <c>Packet</c> is delivered as a list.</p> + </item> + + <tag><c>binary</c></tag> + <item> + <p>Received <c>Packet</c> is delivered as a binary.</p> + </item> + <tag><c>{nodelay, Boolean}</c>(TCP/IP sockets)</tag> <item> <p>If <c>Boolean == true</c>, the <c>TCP_NODELAY</c> option @@ -578,6 +657,16 @@ fe80::204:acff:fe17:bf38 indicated length are accepted and not considered invalid due to internal buffer limitations.</p> </item> + + <tag><c>{priority, Priority}</c></tag> + <item> <p>Set the protocol-defined priority for all packets to be sent + on this socket.</p> + </item> + + <tag><c>{raw, Protocol, OptionNum, ValueBin}</c></tag> + <item> <p>See below.</p> + </item> + <tag><c>{read_packets, Integer}</c>(UDP sockets)</tag> <item> <p>Sets the max number of UDP packets to read without @@ -589,7 +678,7 @@ fe80::204:acff:fe17:bf38 high the system can become unresponsive due to UDP packet flooding.</p> </item> - <tag><c>{recbuf, Integer}</c></tag> + <tag><c>{recbuf, Size}</c></tag> <item> <p>Gives the size of the receive buffer to use for the socket.</p> @@ -618,9 +707,10 @@ fe80::204:acff:fe17:bf38 returns <c>{error,timeout}</c>. The recommended setting is <c>true</c> which will automatically close the socket. Default is <c>false</c> due to backward compatibility.</p> + <marker id="option-sndbuf"></marker> </item> - <tag><c>{sndbuf, Integer}</c></tag> + <tag><c>{sndbuf, Size}</c></tag> <item> <p>Gives the size of the send buffer to use for the socket.</p> </item> @@ -639,6 +729,7 @@ fe80::204:acff:fe17:bf38 not implemented. Use with caution.</p> </item> </taglist> + <p>In addition to the options mentioned above, <em>raw</em> option specifications can be used. The raw options are specified as a tuple of arity four, beginning with the tag diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 63fdc223f4..faa8f86dbd 100644 --- a/lib/kernel/doc/src/kernel_app.xml +++ b/lib/kernel/doc/src/kernel_app.xml @@ -4,7 +4,7 @@ <appref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index 9121186631..94624bf512 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -30,6 +30,133 @@ </header> <p>This document describes the changes made to the Kernel application.</p> +<section><title>Kernel 2.15.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Driver output has been corrected so output of large + binaries (> 4 GiB) now does not silently fail or crash + the emulator, but either outputs the binary or fails the + call. This means that writing a binary > 4 Gib to file + now works but on e.g 64-bit Windows (that has + scatter/gather I/O buffer segment lengths of 32 bits) + fails. The behaviour may change in the future to always + write the binary, in parts if necessary.</p> + <p> + Own Id: OTP-9820 Aux Id: OTP-9795 </p> + </item> + <item> + <p> + erts: minor fix for unnecessary condition erts: change + SENDFILE_CHUNK_SIZE from signed to unsigned (Thanks to + jovi zhang)</p> + <p> + Own Id: OTP-9872</p> + </item> + <item> + <p> Two contracts in <c>gen_sctp</c> have been corrected. + </p> + <p> + Own Id: OTP-9874</p> + </item> + <item> + <p>If a process calls a module with an running on_load + handler, the process is supposed to be suspended. But if + the module with the on_load handler was loading used + <c>code:load_binary/3</c>, the call would instead fail + with an <c>undef</c> exception.</p> + <p> + Own Id: OTP-9875</p> + </item> + <item> + <p> + File name and error reason is now returned if creation of + a cookie fails. (Thanks to Magnus Henoch)</p> + <p> + Own Id: OTP-9954</p> + </item> + <item> + <p> + Fix port leak in <c>zlib</c> when passing invalid data to + <c>compress,uncompress,zip,unzip,gzip,gunzip</c>.</p> + <p> + Own Id: OTP-9981</p> + </item> + <item> + <p> + Various typographical errors corrected in documentation + for the global, error_logger, etop, lists, ets and + supervisor modules and in the c_portdriver and kernel_app + documentation. (Thanks to Ricardo Catalinas Jim�nez)</p> + <p> + Own Id: OTP-9987</p> + </item> + <item> + <p> Fix returned error from gen_tcp:accept/1,2 when + running out of ports + <p> + The {error, enfile} return value is badly misleading and + confusing for this case, since the Posix ENFILE errno + value has a well-defined meaning that has nothing to do + with Erlang ports. The fix changes the return value to + {error, system_limit}, which is consistent with e.g. + various file(3) functions. inet:format_error/1 has also + been updated to support system_limit in the same manner + as file:format_error/1. (Thanks to Per Hedeland)</p></p> + <p> + Own Id: OTP-9990</p> + </item> + <item> + <p><c>erts_debug:size/1</c> has been corrected to take + sharing in the environment of funs into account. For funs + it used to always give the same result as + <c>erts_debug:flat_size/1</c>.</p> + <p> + Own Id: OTP-9991</p> + </item> + <item> + <p> + In some cases when the process doing file:sendfile + crashes while sending the file the efile_drv code would + not clean up after itself correctly. This has now been + fixed.</p> + <p> + Own Id: OTP-9993</p> + </item> + <item> + <p> + On BSD based platforms file:sendfile would sometime go + into an infinite loop when sending big files. This has + now been fixed.</p> + <p> + Own Id: OTP-9994</p> + </item> + <item> + <p>While <c>disk_log</c> eagerly collects logged terms + for better performance, collecting too much data may + choke the system and cause huge binaries to be + written.</p> + <p>The problem was addressed in OTP-9764, but the + situation was not improved in all cases.</p> + <p>(Thanks to Richard Carlsson.)</p> + <p> + Own Id: OTP-9999 Aux Id: OTP-9764 </p> + </item> + <item> + <p> + The documentation of .app files incorrectly said that the + default value for the <c>mod</c> parameter is + <c>undefined</c>. This is now corrected to <c>[]</c>.</p> + <p> + Own Id: OTP-10002</p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 2.15</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index e94119845a..09c525b376 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -80,6 +80,10 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> Each environment variable is given as a single string on the format <c>"VarName=Value"</c>, where <c>VarName</c> is the name of the variable and <c>Value</c> its value.</p> + <p>If Unicode file name encoding is in effect (see the <seealso + marker="erts:erl#file_name_encoding">erl manual + page</seealso>), the strings may contain characters with + codepoints > 255.</p> </desc> </func> <func> @@ -93,6 +97,10 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> <p>Returns the <c>Value</c> of the environment variable <c>VarName</c>, or <c>false</c> if the environment variable is undefined.</p> + <p>If Unicode file name encoding is in effect (see the <seealso + marker="erts:erl#file_name_encoding">erl manual + page</seealso>), the strings (both <c>VarName</c> and + <c>Value</c>) may contain characters with codepoints > 255.</p> </desc> </func> <func> @@ -123,6 +131,13 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> <desc> <p>Sets a new <c>Value</c> for the environment variable <c>VarName</c>.</p> + <p>If Unicode filename encoding is in effect (see the <seealso + marker="erts:erl#file_name_encoding">erl manual + page</seealso>), the strings (both <c>VarName</c> and + <c>Value</c>) may contain characters with codepoints > 255.</p> + <p>On Unix platforms, the environment will be set using UTF-8 encoding + if Unicode file name translation is in effect. On Windows the + environment is set using wide character interfaces.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/packages.xml b/lib/kernel/doc/src/packages.xml index 80de2e05fc..81b8693baa 100644 --- a/lib/kernel/doc/src/packages.xml +++ b/lib/kernel/doc/src/packages.xml @@ -204,11 +204,5 @@ ok Explicitly declaring each use of a module makes for safe code.</p> </description> - <funcs> - <func> - <name>no functions exported</name> - <fsummary>x</fsummary> - </func> - </funcs> </erlref> diff --git a/lib/kernel/examples/Makefile b/lib/kernel/examples/Makefile index fb27f8d438..21dfd0ec0e 100644 --- a/lib/kernel/examples/Makefile +++ b/lib/kernel/examples/Makefile @@ -47,8 +47,8 @@ RELSYSDIR = $(RELEASE_PATH)/lib/kernel-$(KERNEL_VSN)/examples EXAMPLES = uds_dist release_spec: - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" tar cf - $(EXAMPLES) | \ - (cd $(RELSYSDIR); tar xf - ; chmod -R ug+w $(EXAMPLES) ) + (cd "$(RELSYSDIR)"; tar xf - ; chmod -R ug+w $(EXAMPLES) ) release_docs_spec: diff --git a/lib/kernel/examples/uds_dist/c_src/uds_drv.c b/lib/kernel/examples/uds_dist/c_src/uds_drv.c index 9327ab19dc..9ad6b85a0f 100644 --- a/lib/kernel/examples/uds_dist/c_src/uds_drv.c +++ b/lib/kernel/examples/uds_dist/c_src/uds_drv.c @@ -967,7 +967,7 @@ static void *my_malloc(size_t size) void *ptr; if ((ptr = driver_alloc(size)) == NULL) { - erl_exit(1,"Could not allocate %d bytes of memory",(int) size); + erl_exit(1,"Could not allocate %lu bytes of memory",(unsigned long) size); } return ptr; } @@ -977,7 +977,7 @@ static void *my_realloc(void *ptr, size_t size) void erl_exit(int, char *, ...); void *nptr; if ((nptr = driver_realloc(ptr, size)) == NULL) { - erl_exit(1,"Could not reallocate %d bytes of memory",(int) size); + erl_exit(1,"Could not reallocate %lu bytes of memory",(unsigned long) size); } return nptr; } diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index 54f21eb2b8..a39864d6b7 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -198,13 +198,13 @@ $(EBIN)/erl_epmd.beam: $(ESRC)/erl_epmd.erl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/kernel/src/auth.erl b/lib/kernel/src/auth.erl index 252c20ac4e..6ae786ebd9 100644 --- a/lib/kernel/src/auth.erl +++ b/lib/kernel/src/auth.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index b7fda69ce0..363072951e 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -63,7 +63,7 @@ which/1, where_is_file/1, where_is_file/2, - set_primary_archive/3, + set_primary_archive/4, clash/0]). -export_type([load_error_rsn/0, load_ret/0]). @@ -107,7 +107,7 @@ %% unstick_mod(Module) -> true %% is_sticky(Module) -> boolean() %% which(Module) -> Filename | loaded_ret_atoms() | non_existing -%% set_primary_archive((FileName, Bin, FileInfo) -> ok | {error, Reason} +%% set_primary_archive((FileName, ArchiveBin, FileInfo, ParserFun) -> ok | {error, Reason} %% clash() -> ok prints out number of clashes %%---------------------------------------------------------------------------- @@ -481,13 +481,16 @@ where_is_file(Path, File) when is_list(Path), is_list(File) -> -spec set_primary_archive(ArchiveFile :: file:filename(), ArchiveBin :: binary(), - FileInfo :: file:file_info()) + FileInfo :: file:file_info(), + ParserFun :: fun()) -> 'ok' | {'error', atom()}. -set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo) +set_primary_archive(ArchiveFile0, ArchiveBin, #file_info{} = FileInfo, + ParserFun) when is_list(ArchiveFile0), is_binary(ArchiveBin) -> ArchiveFile = filename:absname(ArchiveFile0), - case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo}) of + case call({set_primary_archive, ArchiveFile, ArchiveBin, FileInfo, + ParserFun}) of {ok, []} -> ok; {ok, _Mode, Ebins} -> diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 5d4f2eb70c..00ad923466 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -394,8 +394,8 @@ handle_call(stop,{_From,_Tag}, S) -> handle_call({is_cached,_File}, {_From,_Tag}, S=#state{cache=no_cache}) -> {reply, no, S}; -handle_call({set_primary_archive, File, ArchiveBin, FileInfo}, {_From,_Tag}, S=#state{mode=Mode}) -> - case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo) of +handle_call({set_primary_archive, File, ArchiveBin, FileInfo, ParserFun}, {_From,_Tag}, S=#state{mode=Mode}) -> + case erl_prim_loader:set_primary_archive(File, ArchiveBin, FileInfo, ParserFun) of {ok, Files} -> {reply, {ok, Mode, Files}, S}; {error, _Reason} = Error -> diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index fb9415d440..5b1efcd395 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -282,7 +282,8 @@ change_notify(Log, Pid, NewNotify) -> -spec change_header(Log, Header) -> 'ok' | {'error', Reason} when Log :: log(), - Header :: {head, dlog_head_opt()} | {head_func, mfa()}, + Header :: {head, dlog_head_opt()} + | {head_func, MFA :: {atom(), atom(), list()}}, Reason :: no_such_log | nonode | {read_only_mode, Log} | {blocked_log, Log} | {badarg, head}. change_header(Log, NewHead) -> @@ -336,7 +337,9 @@ format_error(Error) -> ok | {blocked, QueueLogRecords :: boolean()}} | {node, Node :: node()} | {distributed, Dist :: local | [node()]} - | {head, Head :: none | {head, term()} | mfa()} + | {head, Head :: none + | {head, term()} + | (MFA :: {atom(), atom(), list()})} | {no_written_items, NoWrittenItems ::non_neg_integer()} | {full, Full :: boolean} | {no_current_bytes, non_neg_integer()} @@ -1034,10 +1037,9 @@ sync_loop(From, S) -> -define(MAX_LOOK_AHEAD, 64*1024). %% Inlined. -log_loop(S, Pids, _Bins, _Sync, _Sz) when S#state.cache_error =/= ok -> +log_loop(#state{cache_error = CE}=S, Pids, _Bins, _Sync, _Sz) when CE =/= ok -> loop(cache_error(S, Pids)); -log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz) - when Sz > ?MAX_LOOK_AHEAD -> +log_loop(#state{}=S, Pids, Bins, Sync, Sz) when Sz > ?MAX_LOOK_AHEAD -> loop(log_end(S, Pids, Bins, Sync)); log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz) -> receive @@ -1046,8 +1048,7 @@ log_loop(#state{messages = []}=S, Pids, Bins, Sync, Sz) -> after 0 -> loop(log_end(S, Pids, Bins, Sync)) end; -log_loop(S, Pids, Bins, Sync, Sz) -> - [M | Ms] = S#state.messages, +log_loop(#state{messages = [M | Ms]}=S, Pids, Bins, Sync, Sz) -> S1 = S#state{messages = Ms}, log_loop(M, Pids, Bins, Sync, Sz, S1, get(log)). diff --git a/lib/kernel/src/disk_log.hrl b/lib/kernel/src/disk_log.hrl index 259967650f..242a25a7a6 100644 --- a/lib/kernel/src/disk_log.hrl +++ b/lib/kernel/src/disk_log.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -74,7 +74,7 @@ | {distributed, Nodes :: [node()]} | {notify, boolean()} | {head, Head :: dlog_head_opt()} - | {head_func, mfa()} + | {head_func, MFA :: {atom(), atom(), list()}} | {mode, Mode :: dlog_mode()}. -type dlog_options() :: [dlog_option()]. -type dlog_repair() :: 'truncate' | boolean(). diff --git a/lib/kernel/src/erts_debug.erl b/lib/kernel/src/erts_debug.erl index 7d6a5ade94..b8871e0d45 100644 --- a/lib/kernel/src/erts_debug.erl +++ b/lib/kernel/src/erts_debug.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2009. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -53,6 +53,11 @@ size(Tuple, Seen0, Sum0) when is_tuple(Tuple) -> Sum = Sum0 + 1 + tuple_size(Tuple), tuple_size(1, tuple_size(Tuple), Tuple, Seen, Sum) end; +size(Fun, Seen0, Sum) when is_function(Fun) -> + case remember_term(Fun, Seen0) of + seen -> {Sum,Seen0}; + Seen -> fun_size(Fun, Seen, Sum) + end; size(Term, Seen0, Sum) -> case erts_debug:flat_size(Term) of 0 -> {Sum,Seen0}; @@ -68,6 +73,21 @@ tuple_size(I, Sz, _, Seen, Sum) when I > Sz -> tuple_size(I, Sz, Tuple, Seen0, Sum0) -> {Sum,Seen} = size(element(I, Tuple), Seen0, Sum0), tuple_size(I+1, Sz, Tuple, Seen, Sum). + +fun_size(Fun, Seen, Sum) -> + case erlang:fun_info(Fun, type) of + {type,external} -> + {Sum + erts_debug:flat_size(Fun),Seen}; + {type,local} -> + Sz = erts_debug:flat_size(fun() -> ok end), + {env,Env} = erlang:fun_info(Fun, env), + fun_size_1(Env, Seen, Sum+Sz+length(Env)) + end. + +fun_size_1([H|T], Seen0, Sum0) -> + {Sum,Seen} = size(H, Seen0, Sum0), + fun_size_1(T, Seen, Sum); +fun_size_1([], Seen, Sum) -> {Sum,Seen}. remember_term(Term, Seen) -> case gb_trees:lookup(Term, Seen) of diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index 4028dd4f0b..cdb984c333 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -1258,7 +1258,7 @@ sendfile_fallback_int(File, Sock, Bytes, ChunkSize, BytesSent) when Bytes > BytesSent; Bytes == 0 -> Size = if Bytes == 0 -> ChunkSize; - (Bytes - BytesSent + ChunkSize) > 0 -> + (Bytes - BytesSent) < ChunkSize -> Bytes - BytesSent; true -> ChunkSize @@ -1416,7 +1416,11 @@ mode_list(_) -> %% Functions for communicating with the file server call(Command, Args) when is_list(Args) -> - gen_server:call(?FILE_SERVER, list_to_tuple([Command | Args]), infinity). + X = erlang:dt_spread_tag(true), + Y = gen_server:call(?FILE_SERVER, list_to_tuple([Command | Args]), + infinity), + erlang:dt_restore_tag(X), + Y. check_and_call(Command, Args) when is_list(Args) -> case check_args(Args) of diff --git a/lib/kernel/src/file_io_server.erl b/lib/kernel/src/file_io_server.erl index 14da9c1a55..0bff56cf46 100644 --- a/lib/kernel/src/file_io_server.erl +++ b/lib/kernel/src/file_io_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -57,9 +57,11 @@ start_link(Owner, FileName, ModeList) do_start(Spawn, Owner, FileName, ModeList) -> Self = self(), Ref = make_ref(), + Utag = erlang:dt_spread_tag(true), Pid = erlang:Spawn( fun() -> + erlang:dt_restore_tag(Utag), %% process_flag(trap_exit, true), case parse_options(ModeList) of {ReadMode, UnicodeMode, Opts} -> @@ -84,6 +86,7 @@ do_start(Spawn, Owner, FileName, ModeList) -> exit(Reason1) end end), + erlang:dt_restore_tag(Utag), Mref = erlang:monitor(process, Pid), receive {Ref, {error, _Reason} = Error} -> diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index d8954f0cf7..8fa963ec78 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -425,9 +425,10 @@ error_string(X) -> erlang:error(badarg, [X]). --spec controlling_process(Socket, Pid) -> ok when +-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: sctp_socket(), - Pid :: pid(). + Pid :: pid(), + Reason :: closed | not_owner | inet:posix(). controlling_process(S, Pid) when is_port(S), is_pid(Pid) -> inet:udp_controlling_process(S, Pid); diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl index 4d6c7f5f1d..e6dfdadb03 100644 --- a/lib/kernel/src/gen_tcp.erl +++ b/lib/kernel/src/gen_tcp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -31,7 +31,6 @@ -type option() :: {active, true | false | once} | - {bit8, clear | set | on | off} | {buffer, non_neg_integer()} | {delay_send, boolean()} | {deliver, port | term} | @@ -61,7 +60,6 @@ {tos, non_neg_integer()}. -type option_name() :: active | - bit8 | buffer | delay_send | deliver | @@ -175,7 +173,7 @@ try_connect([], _Port, _Opts, _Timer, _Mod, Err) -> Port :: inet:port_number(), Options :: [listen_option()], ListenSocket :: socket(), - Reason :: inet:posix(). + Reason :: system_limit | inet:posix(). listen(Port, Opts) -> Mod = mod(Opts, undefined), @@ -194,7 +192,7 @@ listen(Port, Opts) -> -spec accept(ListenSocket) -> {ok, Socket} | {error, Reason} when ListenSocket :: socket(), Socket :: socket(), - Reason :: closed | timeout | inet:posix(). + Reason :: closed | timeout | system_limit | inet:posix(). accept(S) -> case inet_db:lookup_socket(S) of @@ -208,7 +206,7 @@ accept(S) -> ListenSocket :: socket(), Timeout :: timeout(), Socket :: socket(), - Reason :: closed | timeout | inet:posix(). + Reason :: closed | timeout | system_limit | inet:posix(). accept(S, Time) when is_port(S) -> case inet_db:lookup_socket(S) of diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index 8688799ae9..914854c65c 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -185,9 +185,10 @@ connect(S, Address, Port) when is_port(S) -> Error end. --spec controlling_process(Socket, Pid) -> ok when +-spec controlling_process(Socket, Pid) -> ok | {error, Reason} when Socket :: socket(), - Pid :: pid(). + Pid :: pid(), + Reason :: closed | not_owner | inet:posix(). controlling_process(S, NewOwner) -> inet:udp_controlling_process(S, NewOwner). diff --git a/lib/kernel/src/global.erl b/lib/kernel/src/global.erl index 7da33859bf..36cb713ee1 100644 --- a/lib/kernel/src/global.erl +++ b/lib/kernel/src/global.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl index 255ae4e51b..218be964a0 100644 --- a/lib/kernel/src/heart.erl +++ b/lib/kernel/src/heart.erl @@ -18,6 +18,10 @@ %% -module(heart). +-compile(no_native). +% 'no_native' as part of a crude fix to make init:restart/0 work by clearing +% all hipe inter-module information (hipe_mfa_info's in hipe_bif0.c). + %%%-------------------------------------------------------------------- %%% This is a rewrite of pre_heart from BS.3. %%% diff --git a/lib/kernel/src/hipe_unified_loader.erl b/lib/kernel/src/hipe_unified_loader.erl index 8b3aa0286d..514c002d87 100644 --- a/lib/kernel/src/hipe_unified_loader.erl +++ b/lib/kernel/src/hipe_unified_loader.erl @@ -34,6 +34,13 @@ -module(hipe_unified_loader). +-compile(no_native). +% 'no_native' is a workaround to avoid "The code server called unloaded module" +% caused by Mod:module_info(exports) in patch_to_emu_step1() called by post_beam_load. +% Reproducable with hipelibs and asn1_SUITE. +% I think the real solution would be to let BIF erlang:load_module/2 redirect all +% hipe calls to the module and thereby remove post_beam_load. + -export([chunk_name/1, %% Only the code and code_server modules may call the entries below! load_native_code/2, diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 49f64a9236..0bb5444dbb 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -1218,11 +1218,13 @@ port_list(Name) -> %% utils %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --spec format_error(Posix) -> string() when - Posix :: posix(). +-spec format_error(Reason) -> string() when + Reason :: posix() | system_limit. format_error(exbadport) -> "invalid port state"; format_error(exbadseq) -> "bad command sequence"; +format_error(system_limit) -> + "a system limit was hit, probably not enough ports"; format_error(Tag) -> erl_posix_msg:message(Tag). @@ -1244,6 +1246,8 @@ udp_close(S) when is_port(S) -> %% Set controlling process for TCP socket. tcp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) -> case erlang:port_info(S, connected) of + {connected, NewOwner} -> + ok; {connected, Pid} when Pid =/= self() -> {error, not_owner}; undefined -> @@ -1295,6 +1299,8 @@ tcp_sync_input(S, Owner, Flag) -> %% Set controlling process for UDP or SCTP socket. udp_controlling_process(S, NewOwner) when is_port(S), is_pid(NewOwner) -> case erlang:port_info(S, connected) of + {connected, NewOwner} -> + ok; {connected, Pid} when Pid =/= self() -> {error, not_owner}; _ -> diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index e214ffa404..a3fc57a124 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -286,7 +286,7 @@ call(N,M,F,A) -> Reason :: term(), Timeout :: timeout(). -call(N,M,F,A,_Timeout) when node() =:= N -> %% Optimize local call +call(N,M,F,A,infinity) when node() =:= N -> %% Optimize local call local_call(M,F,A); call(N,M,F,A,infinity) -> do_call(N, {call,M,F,A,group_leader()}, infinity); diff --git a/lib/kernel/test/Makefile b/lib/kernel/test/Makefile index 5dcaad3f5e..7bb3e3a365 100644 --- a/lib/kernel/test/Makefile +++ b/lib/kernel/test/Makefile @@ -140,12 +140,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(APP_FILES) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(APP_FILES) "$(RELSYSDIR)" $(INSTALL_DATA) kernel.spec $(EMAKEFILE)\ - $(COVERFILE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(COVERFILE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/kernel/test/bif_SUITE.erl b/lib/kernel/test/bif_SUITE.erl index 6276270d20..a2826f34df 100644 --- a/lib/kernel/test/bif_SUITE.erl +++ b/lib/kernel/test/bif_SUITE.erl @@ -260,23 +260,15 @@ spawn_opt2(Config) when is_list(Config) -> ?line P1 = spawn_opt(fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - case heap_type() of - separate -> - [{fullsweep_after, 0},{min_heap_size, 1000}]; - shared -> - [] - end - ++ [link, {priority, max}]), + [{fullsweep_after, 0},{min_heap_size, 1000}, + link, {priority, max}]), ?line receive {P1, PV1} -> ?line Node = node(P1), ?line check_proc_vals(true, max, 0, 1000, PV1) end, ?line P2 = spawn_opt(fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - case heap_type() of - separate -> [{min_heap_size, 10}]; - shared -> [] - end), + [{min_heap_size, 10}]), ?line receive {P2, PV2} -> ?line Node = node(P2), @@ -295,13 +287,8 @@ spawn_opt3(Config) when is_list(Config) -> fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - case heap_type() of - separate -> - [{fullsweep_after,0}, {min_heap_size,1000}]; - shared -> - [] - end - ++ [link, {priority, max}]), + [{fullsweep_after,0}, {min_heap_size,1000}, + link, {priority, max}]), ?line receive {P1, PV1} -> ?line Node = node(P1), @@ -309,10 +296,7 @@ spawn_opt3(Config) when is_list(Config) -> end, ?line P2 = spawn_opt(Node, fun() -> Parent ! {self(), fetch_proc_vals(self())} end, - case heap_type() of - separate -> [{min_heap_size, 10}]; - shared -> [] - end), + [{min_heap_size, 10}]), ?line receive {P2, PV2} -> ?line Node = node(P2), @@ -333,13 +317,8 @@ spawn_opt4(Config) when is_list(Config) -> [fun() -> Parent ! {self(), fetch_proc_vals(self())} end], - case heap_type() of - separate -> - [{fullsweep_after,0}, {min_heap_size,1000}]; - shared -> - [] - end - ++ [link, {priority, max}]), + [{fullsweep_after,0}, {min_heap_size,1000}, + link, {priority, max}]), ?line receive {P1, PV1} -> ?line Node = node(P1), @@ -350,10 +329,7 @@ spawn_opt4(Config) when is_list(Config) -> [fun() -> Parent ! {self(), fetch_proc_vals(self())} end], - case heap_type() of - separate -> [{min_heap_size, 10}]; - shared -> [] - end), + [{min_heap_size, 10}]), ?line receive {P2, PV2} -> ?line Node = node(P2), @@ -374,13 +350,8 @@ spawn_opt5(Config) when is_list(Config) -> [fun() -> Parent ! {self(), fetch_proc_vals(self())} end], - case heap_type() of - separate -> - [{fullsweep_after,0}, {min_heap_size,1000}]; - shared -> - [] - end - ++ [link, {priority, max}]), + [{fullsweep_after,0}, {min_heap_size,1000}, + link, {priority, max}]), ?line receive {P1, PV1} -> ?line Node = node(P1), @@ -392,10 +363,7 @@ spawn_opt5(Config) when is_list(Config) -> [fun() -> Parent ! {self(), fetch_proc_vals(self())} end], - case heap_type() of - separate -> [{min_heap_size, 10}]; - shared -> [] - end), + [{min_heap_size, 10}]), ?line receive {P2, PV2} -> ?line Node = node(P2), @@ -532,34 +500,19 @@ spawn_failures(Config) when is_list(Config) -> check_proc_vals(Link, Priority, FullsweepAfter, MinHeapSize, {Ls, P, FA, HS}) -> ?line Link = lists:member(self(), Ls), ?line Priority = P, - ?line case heap_type() of - separate -> - ?line FullsweepAfter = FA, - ?line true = (HS >= MinHeapSize); - shared -> - ?line ok - end, + FullsweepAfter = FA, + true = (HS >= MinHeapSize), ?line ok. fetch_proc_vals(Pid) -> ?line PI = process_info(Pid), ?line {value,{links, Ls}} = lists:keysearch(links, 1, PI), ?line {value,{priority,P}} = lists:keysearch(priority, 1, PI), - ?line {FA, HS} - = case heap_type() of - separate -> - ?line {value, - {garbage_collection, - Gs}} = lists:keysearch(garbage_collection, 1, PI), - ?line {value, - {fullsweep_after, - Fa}} = lists:keysearch(fullsweep_after, 1, Gs), - ?line {value, - {heap_size,Hs}} = lists:keysearch(heap_size, 1, PI), - ?line {Fa, Hs}; - shared -> - {undefined, undefined} - end, + {value,{garbage_collection,Gs}} = + lists:keysearch(garbage_collection, 1, PI), + {value,{fullsweep_after,FA}} = + lists:keysearch(fullsweep_after, 1, Gs), + {value,{heap_size,HS}} = lists:keysearch(heap_size, 1, PI), ?line {Ls, P, FA, HS}. % This testcase should probably be moved somewhere else @@ -650,12 +603,3 @@ stop_node(Node) -> run_fun(Fun) -> Fun(). - -heap_type() -> - case catch erlang:system_info(heap_type) of - shared -> shared; - unified -> shared; - _ -> separate - end. - - diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 99b0cd2ffb..827208b048 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -501,7 +501,7 @@ sticky_dir(doc) -> ["Test that a module with the same name as a module in ", "a sticky directory cannot be loaded."]; sticky_dir(Config) when is_list(Config) -> MyDir=filename:dirname(code:which(?MODULE)), - ?line {ok, Node}=?t:start_node(sticky_dir, slave,[{args, "-pa "++MyDir}]), + ?line {ok, Node}=?t:start_node(sticky_dir, slave,[{args, "-pa \""++MyDir++"\""}]), File=filename:join([?config(data_dir, Config), "calendar"]), ?line Ret=rpc:call(Node, ?MODULE, sticky_compiler, [File]), case Ret of @@ -822,7 +822,7 @@ load_cached(Config) when is_list(Config) -> ?line WD = filename:dirname(code:which(?MODULE)), ?line {ok,Node} = ?t:start_node(code_cache_node, peer, [{args, - "-pa " ++ WD}, + "-pa \"" ++ WD ++ "\""}, {erl, [this]}]), CCTabCreated = fun(Tab) -> case ets:info(Tab, name) of @@ -907,7 +907,7 @@ add_and_rehash(Config) when is_list(Config) -> ?line WD = filename:dirname(code:which(?MODULE)), ?line {ok,Node} = ?t:start_node(code_cache_node, peer, [{args, - "-pa " ++ WD}, + "-pa \"" ++ WD ++ "\""}, {erl, [this]}]), CCTabCreated = fun(Tab) -> case ets:info(Tab, name) of @@ -1441,6 +1441,9 @@ filter_app("netconf",_) -> % Safe has the same kind of error in the .app file as ic filter_app("safe",_) -> false; +% Comte cannot be started in the "usual" way +filter_app("comte",_) -> + false; % OS_mon does not find it's port program when running cerl filter_app("os_mon",true) -> false; @@ -1547,7 +1550,8 @@ native_early_modules_1(Architecture) -> true -> ?line true = lists:all(fun code:is_module_native/1, [ets,file,filename,gb_sets,gb_trees, - hipe_unified_loader,lists,os,packages]), + %%hipe_unified_loader, no_native as workaround + lists,os,packages]), ok end. diff --git a/lib/kernel/test/disk_log_SUITE.erl b/lib/kernel/test/disk_log_SUITE.erl index ad987fe7a7..0c3f5c3514 100644 --- a/lib/kernel/test/disk_log_SUITE.erl +++ b/lib/kernel/test/disk_log_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -90,7 +90,7 @@ evil/1, - otp_6278/1]). + otp_6278/1, otp_10131/1]). -export([head_fun/1, hf/0, lserv/1, measure/0, init_m/1, xx/0, head_exit/0, slow_header/1]). @@ -124,7 +124,7 @@ [halt_int, wrap_int, halt_ext, wrap_ext, read_mode, head, notif, new_idx_vsn, reopen, block, unblock, open, close, error, chunk, truncate, many_users, info, change_size, - change_attribute, distribution, evil, otp_6278]). + change_attribute, distribution, evil, otp_6278, otp_10131]). %% The following two lists should be mutually exclusive. To skip a case %% on VxWorks altogether, use the kernel.spec.vxworks file instead. @@ -153,7 +153,7 @@ all() -> {group, open}, {group, close}, {group, error}, chunk, truncate, many_users, {group, info}, {group, change_size}, change_attribute, - {group, distribution}, evil, otp_6278]. + {group, distribution}, evil, otp_6278, otp_10131]. groups() -> [{halt_int, [], [halt_int_inf, {group, halt_int_sz}]}, @@ -4915,6 +4915,22 @@ otp_6278(Conf) when is_list(Conf) -> end, ?line error_logger:delete_report_handler(?MODULE). +otp_10131(suite) -> []; +otp_10131(doc) -> ["OTP-10131. head_func type."]; +otp_10131(Conf) when is_list(Conf) -> + Dir = ?privdir(Conf), + Log = otp_10131, + File = filename:join(Dir, lists:concat([Log, ".LOG"])), + HeadFunc = {?MODULE, head_fun, [{ok,"head"}]}, + {ok, Log} = disk_log:open([{name,Log},{file,File}, + {head_func, HeadFunc}]), + HeadFunc = info(Log, head, undef), + HeadFunc2 = {?MODULE, head_fun, [{ok,"head2"}]}, + ok = disk_log:change_header(Log, {head_func, HeadFunc2}), + HeadFunc2 = info(Log, head, undef), + ok = disk_log:close(Log), + ok. + mark(FileName, What) -> {ok,Fd} = file:open(FileName, [raw, binary, read, write]), {ok,_} = file:position(Fd, 4), diff --git a/lib/kernel/test/erl_prim_loader_SUITE.erl b/lib/kernel/test/erl_prim_loader_SUITE.erl index d0d52c5ea7..72239641e9 100644 --- a/lib/kernel/test/erl_prim_loader_SUITE.erl +++ b/lib/kernel/test/erl_prim_loader_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -426,7 +426,9 @@ primary_archive(Config) when is_list(Config) -> ExpectedEbins = [Archive, DictDir ++ "/ebin", DummyDir ++ "/ebin"], io:format("ExpectedEbins: ~p\n", [ExpectedEbins]), ?line {ok, FileInfo} = prim_file:read_file_info(Archive), - ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, [Archive, ArchiveBin, FileInfo]), + ?line {ok, Ebins} = rpc:call(Node, erl_prim_loader, set_primary_archive, + [Archive, ArchiveBin, FileInfo, + fun escript:parse_file/1]), ?line ExpectedEbins = lists:sort(Ebins), % assert ?line {ok, TopFiles2} = rpc:call(Node, erl_prim_loader, list_dir, [Archive]), @@ -435,7 +437,9 @@ primary_archive(Config) when is_list(Config) -> ?line ok = test_archive(Node, Archive, DictDir, BeamName), %% Cleanup - ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, [undefined, undefined, undefined]), + ?line {ok, []} = rpc:call(Node, erl_prim_loader, set_primary_archive, + [undefined, undefined, undefined, + fun escript:parse_file/1]), ?line stop_node(Node), ?line ok = file:delete(Archive), ok. diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index 53bcb1162d..be33ec2c06 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -74,7 +74,7 @@ init_per_suite/1,end_per_suite/1, init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). --export([normal/1,icky/1,very_icky/1,normalize/1]). +-export([normal/1,icky/1,very_icky/1,normalize/1,home_dir/1]). init_per_testcase(_Func, Config) -> @@ -88,7 +88,7 @@ end_per_testcase(_Func, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [normal, icky, very_icky, normalize]. + [normal, icky, very_icky, normalize, home_dir]. groups() -> []. @@ -105,6 +105,54 @@ init_per_group(_GroupName, Config) -> end_per_group(_GroupName, Config) -> Config. +home_dir(suite) -> + []; +home_dir(doc) -> + ["Check that Erlang can be started with unicode named home directory"]; +home_dir(Config) when is_list(Config) -> + try + Name=[960,945,964,961,953,954], + Priv = ?config(priv_dir, Config), + UniMode = file:native_name_encoding() =/= latin1, + if + not UniMode -> + throw(need_unicode_mode); + true -> + ok + end, + NewHome=filename:join(Priv,Name), + file:make_dir(NewHome), + {SaveOldName,SaveOldValue} = case os:type() of + {win32,nt} -> + HomePath=re:replace(filename:nativename(NewHome),"^[a-zA-Z]:","",[{return,list},unicode]), + Save = os:getenv("HOMEPATH"), + os:putenv("HOMEPATH",HomePath), + {"HOMEPATH",Save}; + {unix,_} -> + Save = os:getenv("HOME"), + os:putenv("HOME",NewHome), + {"HOME",Save}; + _ -> + rm_rf(prim_file,NewHome), + throw(unsupported_os) + end, + try + {ok,Node} = test_server:start_node(test_unicode_homedir,slave,[{args,"-setcookie "++atom_to_list(erlang:get_cookie())}]), + test_server:stop_node(Node), + ok + after + os:putenv(SaveOldName,SaveOldValue), + rm_rf(prim_file,NewHome) + end + catch + throw:need_unicode_mode -> + io:format("Sorry, can only run in unicode mode.~n"), + {skipped,"VM needs to be started in Unicode filename mode"}; + throw:unsupported_os -> + io:format("Sorry, can only run on Unix/Windows.~n"), + {skipped,"Runs only on Unix/Windows"} + end. + normalize(suite) -> []; normalize(doc) -> diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 3da4b07c05..1592399996 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -24,7 +24,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, - controlling_process/1, no_accept/1, close_with_pending_output/1, + controlling_process/1, controlling_process_self/1, + no_accept/1, close_with_pending_output/1, data_before_close/1, iter_max_socks/1, get_status/1, passive_sockets/1, accept_closed_by_other_process/1, init_per_testcase/2, end_per_testcase/2, @@ -39,7 +40,8 @@ accept_timeouts_in_order/1,accept_timeouts_in_order2/1, accept_timeouts_in_order3/1,accept_timeouts_mixed/1, killing_acceptor/1,killing_multi_acceptors/1,killing_multi_acceptors2/1, - several_accepts_in_one_go/1,active_once_closed/1, send_timeout/1, send_timeout_active/1, + several_accepts_in_one_go/1, accept_system_limit/1, + active_once_closed/1, send_timeout/1, send_timeout_active/1, otp_7731/1, zombie_sockets/1, otp_7816/1, otp_8102/1, otp_9389/1]). @@ -57,7 +59,7 @@ end_per_testcase(_Func, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [controlling_process, no_accept, + [controlling_process, controlling_process_self, no_accept, close_with_pending_output, data_before_close, iter_max_socks, passive_sockets, accept_closed_by_other_process, otp_3924, closed_socket, @@ -71,7 +73,7 @@ all() -> accept_timeouts_in_order, accept_timeouts_in_order2, accept_timeouts_in_order3, accept_timeouts_mixed, killing_acceptor, killing_multi_acceptors, - killing_multi_acceptors2, several_accepts_in_one_go, + killing_multi_acceptors2, several_accepts_in_one_go, accept_system_limit, active_once_closed, send_timeout, send_timeout_active, otp_7731, zombie_sockets, otp_7816, otp_8102, otp_9389]. @@ -306,6 +308,32 @@ not_owner(S) -> ok end. +controlling_process_self(doc) -> + ["Open a listen port and assign the controlling process to " + "it self, then exit and make sure the port is closed properly."]; +controlling_process_self(Config) when is_list(Config) -> + S = self(), + process_flag(trap_exit,true), + spawn_link(fun() -> + {ok,Sock} = gen_tcp:listen(0,[]), + S ! {socket, Sock}, + ok = gen_tcp:controlling_process(Sock,self()), + S ! done + end), + receive + done -> + receive + {socket,Sock} -> + process_flag(trap_exit,false), + %% Make sure the port is invalid after process crash + {error,einval} = inet:port(Sock) + end; + Msg when element(1,Msg) /= socket -> + process_flag(trap_exit,false), + exit({unknown_msg,Msg}) + end. + + no_accept(doc) -> ["Open a listen port and connect to it, then close the listen port ", "without doing any accept. The connected socket should receive ", @@ -1837,6 +1865,54 @@ wait_until_accepting(Proc,N) -> end. +accept_system_limit(suite) -> + []; +accept_system_limit(doc) -> + ["Check that accept returns {error, system_limit} " + "(and not {error, enfile}) when running out of ports"]; +accept_system_limit(Config) when is_list(Config) -> + ?line {ok, LS} = gen_tcp:listen(0, []), + ?line {ok, TcpPort} = inet:port(LS), + ?line Connector = spawn_link(fun () -> connector(TcpPort) end), + ?line ok = acceptor(LS, false, []), + ?line Connector ! stop, + ok. + +acceptor(LS, GotSL, A) -> + case gen_tcp:accept(LS, 1000) of + {ok, S} -> + acceptor(LS, GotSL, [S|A]); + {error, system_limit} -> + acceptor(LS, true, A); + {error, timeout} when GotSL -> + ok; + {error, timeout} -> + error + end. + +connector(TcpPort) -> + ManyPorts = open_ports([]), + ConnF = fun (Port) -> + case catch gen_tcp:connect({127,0,0,1}, TcpPort, []) of + {ok, Sock} -> + Sock; + _Error -> + port_close(Port) + end + end, + R = [ConnF(Port) || Port <- lists:sublist(ManyPorts, 10)], + receive stop -> R end. + +open_ports(L) -> + case catch open_port({spawn_driver, "ram_file_drv"}, []) of + Port when is_port(Port) -> + open_ports([Port|L]); + {'EXIT', {system_limit, _}} -> + {L1, L2} = lists:split(5, L), + [port_close(Port) || Port <- L1], + L2 + end. + active_once_closed(suite) -> []; @@ -1995,7 +2071,7 @@ send_timeout_active(Config) when is_list(Config) -> ?line {error,timeout} = Loop(fun() -> receive - {tcp, Sock, _Data} -> + {tcp, _Sock, _Data} -> inet:setopts(A, [{active, once}]), Res = gen_tcp:send(A,lists:duplicate(1000, $a)), %erlang:display(Res), @@ -2487,7 +2563,7 @@ otp_8102_do(LSocket, PortNum, {Bin,PType}) -> otp_9389(doc) -> ["Verify packet_size handles long HTTP header lines"]; otp_9389(suite) -> []; otp_9389(Config) when is_list(Config) -> - ?line {ok, LS} = gen_tcp:listen(0, []), + ?line {ok, LS} = gen_tcp:listen(0, [{active,false}]), ?line {ok, {_, PortNum}} = inet:sockname(LS), io:format("Listening on ~w with port number ~p\n", [LS, PortNum]), OrigLinkHdr = "/" ++ string:chars($S, 8192), diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index b39fadd65f..e3fa4642b7 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -608,7 +608,7 @@ boot2(Config) when is_list(Config) -> %% Absolute boot file name Boot = filename:join([code:root_dir(), "bin", "start_sasl"]), - Args = args() ++ " -boot " ++ Boot, + Args = args() ++ " -boot \"" ++ Boot++"\"", ?line {ok, Node} = start_node(init_test, Args), ?line stop_node(Node), @@ -618,7 +618,7 @@ boot2(Config) when is_list(Config) -> %% converted to backslashes. Win_boot = lists:map(fun($/) -> $\\; (C) -> C end, Boot), - Args2 = args() ++ " -boot " ++ Win_boot, + Args2 = args() ++ " -boot \"" ++ Win_boot ++ "\"", ?line {ok, Node2} = start_node(init_test, Args2), ?line stop_node(Node2); _ -> diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl index b2308dd321..96e45cc23f 100644 --- a/lib/kernel/test/interactive_shell_SUITE.erl +++ b/lib/kernel/test/interactive_shell_SUITE.erl @@ -251,7 +251,7 @@ rtnode(Commands,Nodename,ErlPrefix) -> ?line {skip, Reason2}; Tempdir -> ?line SPid = - start_runerl_node(RunErl,ErlPrefix++Erl, + start_runerl_node(RunErl,ErlPrefix++"\\\""++Erl++"\\\"", Tempdir,Nodename), ?line CPid = start_toerl_server(ToErl,Tempdir), ?line erase(getline_skipped), @@ -487,7 +487,7 @@ start_runerl_node(RunErl,Erl,Tempdir,Nodename) -> " -setcookie "++atom_to_list(erlang:get_cookie()) end, spawn(fun() -> - os:cmd(RunErl++" "++Tempdir++"/ "++Tempdir++" \""++ + os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++" \""++ Erl++XArg++"\"") end). @@ -518,7 +518,7 @@ try_to_erl(Command, N) -> end. toerl_server(Parent,ToErl,Tempdir) -> - Port = try_to_erl(ToErl++" "++Tempdir++"/ 2>/dev/null",8), + Port = try_to_erl("\""++ToErl++"\" "++Tempdir++"/ 2>/dev/null",8), case Port of P when is_port(P) -> Parent ! {self(),started}; diff --git a/lib/kernel/test/sendfile_SUITE.erl b/lib/kernel/test/sendfile_SUITE.erl index 6d0848ee05..4cf4c6489d 100644 --- a/lib/kernel/test/sendfile_SUITE.erl +++ b/lib/kernel/test/sendfile_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 @@ -26,7 +26,9 @@ all() -> [t_sendfile_small - ,t_sendfile_big + ,t_sendfile_big_all + ,t_sendfile_big_size + ,t_sendfile_many_small ,t_sendfile_partial ,t_sendfile_offset ,t_sendfile_sendafter @@ -38,20 +40,25 @@ all() -> ]. init_per_suite(Config) -> - Priv = ?config(priv_dir, Config), - SFilename = filename:join(Priv, "sendfile_small.html"), - {ok, DS} = file:open(SFilename,[write,raw]), - file:write(DS,"yo baby yo"), - file:sync(DS), - file:close(DS), - BFilename = filename:join(Priv, "sendfile_big.html"), - {ok, DB} = file:open(BFilename,[write,raw]), - [file:write(DB,[<<0:(10*8*1024*1024)>>]) || _I <- lists:seq(1,51)], - file:sync(DB), - file:close(DB), - [{small_file, SFilename}, - {file_opts,[raw,binary]}, - {big_file, BFilename}|Config]. + case {os:type(),os:version()} of + {{unix,sunos}, {5,8,_}} -> + {skip, "Solaris 8 not supported for now"}; + _ -> + Priv = ?config(priv_dir, Config), + SFilename = filename:join(Priv, "sendfile_small.html"), + {ok, DS} = file:open(SFilename,[write,raw]), + file:write(DS,"yo baby yo"), + file:sync(DS), + file:close(DS), + BFilename = filename:join(Priv, "sendfile_big.html"), + {ok, DB} = file:open(BFilename,[write,raw]), + [file:write(DB,[<<0:(10*8*1024*1024)>>]) || _I <- lists:seq(1,51)], + file:sync(DB), + file:close(DB), + [{small_file, SFilename}, + {file_opts,[raw,binary]}, + {big_file, BFilename}|Config] + end. end_per_suite(Config) -> file:delete(proplists:get_value(big_file, Config)). @@ -91,7 +98,34 @@ t_sendfile_small(Config) when is_list(Config) -> ok = sendfile_send(Send). -t_sendfile_big(Config) when is_list(Config) -> +t_sendfile_many_small(Config) when is_list(Config) -> + Filename = proplists:get_value(small_file, Config), + FileOpts = proplists:get_value(file_opts, Config, []), + + error_logger:add_report_handler(?MODULE,[self()]), + + Send = fun(Sock) -> + {Size,_} = sendfile_file_info(Filename), + N = 10000, + {ok,D} = file:open(Filename,[read|FileOpts]), + [begin + {ok,Size} = file:sendfile(D,Sock,0,0,[]) + end || _I <- lists:seq(1,N)], + file:close(D), + Size*N + end, + + ok = sendfile_send({127,0,0,1}, Send, 0), + + receive + {stolen,Reason} -> + exit(Reason) + after 200 -> + ok + end. + + +t_sendfile_big_all(Config) when is_list(Config) -> Filename = proplists:get_value(big_file, Config), Send = fun(Sock) -> @@ -103,6 +137,20 @@ t_sendfile_big(Config) when is_list(Config) -> ok = sendfile_send({127,0,0,1}, Send, 0). +t_sendfile_big_size(Config) -> + Filename = proplists:get_value(big_file, Config), + FileOpts = proplists:get_value(file_opts, Config, []), + + SendAll = fun(Sock) -> + {ok, #file_info{size = Size}} = + file:read_file_info(Filename), + {ok,D} = file:open(Filename,[read|FileOpts]), + {ok, Size} = file:sendfile(D, Sock,0,Size,[]), + Size + end, + + ok = sendfile_send({127,0,0,1}, SendAll, 0). + t_sendfile_partial(Config) -> Filename = proplists:get_value(small_file, Config), FileOpts = proplists:get_value(file_opts, Config, []), @@ -310,6 +358,10 @@ sendfile_server(ClientPid, Orig) -> -define(SENDFILE_TIMEOUT, 10000). sendfile_do_recv(Sock, Bs) -> + TimeoutMul = case os:type() of + {win32, _} -> 6; + _ -> 1 + end, receive stop when Bs /= 0,is_integer(Bs) -> gen_tcp:close(Sock), @@ -333,7 +385,7 @@ sendfile_do_recv(Sock, Bs) -> {tcp_closed, Sock} when is_integer(Bs) -> ct:log("Stopped due to close"), {ok, Bs} - after ?SENDFILE_TIMEOUT -> + after ?SENDFILE_TIMEOUT * TimeoutMul -> ct:log("Sendfile timeout"), timeout end. diff --git a/lib/kernel/test/zlib_SUITE.erl b/lib/kernel/test/zlib_SUITE.erl index 74bafe8935..bd237cb513 100644 --- a/lib/kernel/test/zlib_SUITE.erl +++ b/lib/kernel/test/zlib_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -73,6 +73,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [{group, api}, {group, examples}, {group, func}, smp, + otp_9981, otp_7359]. groups() -> @@ -964,6 +965,24 @@ otp_7359_def_inf(Data,{DefSize,InfSize}) -> ?line ok = zlib:close(ZInf), ok. +otp_9981(Config) when is_list(Config) -> + Ports = lists:sort(erlang:ports()), + Invalid = <<"My invalid data">>, + catch zlib:compress(invalid), + Ports = lists:sort(erlang:ports()), + catch zlib:uncompress(Invalid), + Ports = lists:sort(erlang:ports()), + catch zlib:zip(invalid), + Ports = lists:sort(erlang:ports()), + catch zlib:unzip(Invalid), + Ports = lists:sort(erlang:ports()), + catch zlib:gzip(invalid), + Ports = lists:sort(erlang:ports()), + catch zlib:gunzip(Invalid), + Ports = lists:sort(erlang:ports()), + ok. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% Helps with testing directly %%%%%%%%%%%%% diff --git a/lib/kernel/vsn.mk b/lib/kernel/vsn.mk index 76d3003ff4..c494f8a864 100644 --- a/lib/kernel/vsn.mk +++ b/lib/kernel/vsn.mk @@ -1 +1 @@ -KERNEL_VSN = 2.15.1 +KERNEL_VSN = 2.15.2 diff --git a/lib/megaco/Makefile b/lib/megaco/Makefile index 9dc84c122c..fc8f408b0a 100644 --- a/lib/megaco/Makefile +++ b/lib/megaco/Makefile @@ -49,11 +49,14 @@ VSN=$(MEGACO_VSN) DIR_NAME = megaco_src-$(VSN)$(PRE_VSN) +nullstring := +space := $(nullstring) # a space at the end + ifndef APP_RELEASE_DIR ifndef TESTROOT APP_RELEASE_DIR = /tmp else - APP_RELEASE_DIR = $(TESTROOT) + APP_RELEASE_DIR = $(subst $(space),\ ,$(TESTROOT)) endif endif @@ -151,7 +154,7 @@ version: # Application install (of a app built from source) targets # ---------------------------------------------------- app_install: - $(MAKE) TESTROOT=$(APP_INSTALL_DIR) release + $(MAKE) TESTROOT="$(APP_INSTALL_DIR)" release # ---------------------------------------------------- @@ -185,20 +188,20 @@ TAR.exclude2: Makefile TAR.exclude (cd ..; find megaco -name '.cmake.state' >> megaco/TAR.exclude2) $(APP_DIR): tar_exclude - mkdir -p $(APP_DIR); \ + mkdir -p "$(subst $(space),\ ,$@)"; \ (cd ..; tar cfX - megaco/TAR.exclude2 megaco) | \ - (cd $(APP_DIR); tar xf -); \ - mv $(APP_DIR)/megaco/* $(APP_DIR)/; \ - mkdir $(APP_DIR)/autoconf; \ - cp autoconf/config.guess $(APP_DIR)/autoconf/; \ - cp autoconf/config.sub $(APP_DIR)/autoconf/; \ - cp autoconf/install-sh $(APP_DIR)/autoconf/; \ - rmdir $(APP_DIR)/megaco + (cd "$(subst $(space),\ ,$@)"; tar xf -); \ + mv "$(subst $(space),\ ,$@)"/megaco/* "$(subst $(space),\ ,$@)"/; \ + mkdir $(subst $(space),\ ,$@)/autoconf; \ + cp autoconf/config.guess "$(subst $(space),\ ,$@)"/autoconf/; \ + cp autoconf/config.sub "$(subst $(space),\ ,$@)"/autoconf/; \ + cp autoconf/install-sh "$(subst $(space),\ ,$@)"/autoconf/; \ + rmdir "$(subst $(space),\ ,$@)"/megaco tar: $(APP_TAR_FILE) $(APP_TAR_FILE): $(APP_DIR) - (cd $(APP_RELEASE_DIR); gtar zcf $(APP_TAR_FILE) $(DIR_NAME)) + (cd "$(APP_RELEASE_DIR)"; gtar zcf "$(subst $(space),\ ,$@)" $(DIR_NAME)) dialyzer_plt: $(DIA_PLT) diff --git a/lib/megaco/doc/src/Makefile b/lib/megaco/doc/src/Makefile index 137f0315d8..493304523e 100644 --- a/lib/megaco/doc/src/Makefile +++ b/lib/megaco/doc/src/Makefile @@ -164,16 +164,16 @@ info: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELSYSDIR)/doc/standard - $(INSTALL_DATA) $(STANDARDS) $(RELSYSDIR)/doc/standard + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/standard" + $(INSTALL_DATA) $(STANDARDS) "$(RELSYSDIR)/doc/standard" release_spec: diff --git a/lib/megaco/doc/src/notes.xml b/lib/megaco/doc/src/notes.xml index d9c575885f..393064fbb5 100644 --- a/lib/megaco/doc/src/notes.xml +++ b/lib/megaco/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2000</year><year>2011</year> + <year>2000</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,6 +36,75 @@ section is the version number of Megaco.</p> + <section><title>Megaco 3.16.0.1</title> + + <p>Version 3.16.0.1 supports code replacement in runtime from/to + version 3.16, 3.15.1.1, 3.15.1 and 3.15.</p> + + <section> + <title>Improvements and new features</title> + +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>Fixed some faulty test cases. </p> +<!-- + <p>Own Id: OTP-9795</p> +--> + </item> + + <item> + <p>Removed use of deprecated system flag, + <c>scheduler_bind_type</c>, in the measurement tool + <c>mstone1</c>. </p> + <p>Own Id: OTP-9949</p> + </item> + + </list> + + </section> + + <section> + <title>Fixed bugs and malfunctions</title> + + <p>-</p> + + <!-- + <list type="bulleted"> + <item> + <p>Fixing miscellaneous things detected by dialyzer. </p> + <p>Own Id: OTP-9075</p> + </item> + + </list> + --> + + </section> + + <section> + <title>Incompatibilities</title> + <p>-</p> + +<!-- + <list type="bulleted"> + <item> + <p>Due to the change in the flex driver API, + we may no longer be able to build and/or use + the flex driver without reentrant support. </p> + <p>Own Id: OTP-9795</p> + </item> + + </list> +--> + + </section> + + </section> <!-- 3.16.0.1 --> + + <section><title>Megaco 3.16</title> <p>Version 3.16 supports code replacement in runtime from/to @@ -50,7 +119,7 @@ <list type="bulleted"> <item> - <p>Minor improvemnts to the emasurement tool mstone1. </p> + <p>Minor improvements to the measurement tool <c>mstone1</c>. </p> <p>Own Id: OTP-9604</p> </item> diff --git a/lib/megaco/examples/meas/Makefile.in b/lib/megaco/examples/meas/Makefile.in index 6af7ef6c65..607d81d440 100644 --- a/lib/megaco/examples/meas/Makefile.in +++ b/lib/megaco/examples/meas/Makefile.in @@ -131,12 +131,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(EXAMPLE_RELSYSDIR) - $(INSTALL_DIR) $(MEAS_RELSYSDIR) - $(INSTALL_DATA) $(MESSAGE_PACKAGES) $(MEAS_RELSYSDIR) - $(INSTALL_DATA) $(SCRIPT_SKELETONS) $(MEAS_RELSYSDIR) - $(INSTALL_DATA) $(TARGET_FILES) $(MEAS_RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(MEAS_RELSYSDIR) + $(INSTALL_DIR) "$(EXAMPLE_RELSYSDIR)" + $(INSTALL_DIR) "$(MEAS_RELSYSDIR)" + $(INSTALL_DATA) $(MESSAGE_PACKAGES) "$(MEAS_RELSYSDIR)" + $(INSTALL_DATA) $(SCRIPT_SKELETONS) "$(MEAS_RELSYSDIR)" + $(INSTALL_DATA) $(TARGET_FILES) "$(MEAS_RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) "$(MEAS_RELSYSDIR)" release_docs_spec: diff --git a/lib/megaco/examples/meas/meas.sh.skel.src b/lib/megaco/examples/meas/meas.sh.skel.src index c7bd6cdd2a..ecf463b9c6 100644 --- a/lib/megaco/examples/meas/meas.sh.skel.src +++ b/lib/megaco/examples/meas/meas.sh.skel.src @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2007-2011. All Rights Reserved. +# Copyright Ericsson AB 2007-2012. 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 @@ -32,6 +32,7 @@ STOP="-s init stop" ERL="erl \ -noshell \ + +sbt tnnps \ -pa $MEAS_HOME \ $MEAS_DEFAULT \ $STOP" diff --git a/lib/megaco/examples/meas/megaco_codec_mstone1.erl b/lib/megaco/examples/meas/megaco_codec_mstone1.erl index 9ab7822df8..2133cd633c 100644 --- a/lib/megaco/examples/meas/megaco_codec_mstone1.erl +++ b/lib/megaco/examples/meas/megaco_codec_mstone1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -191,7 +191,6 @@ mstone_init(MessagePackage, Factor, Codecs, DrvInclude) -> do_mstone(MessagePackage, Factor, Codecs, DrvInclude) -> io:format("~n", []), - ?LIB:set_default_sched_bind(), ?LIB:display_os_info(), ?LIB:display_system_info(), ?LIB:display_app_info(), diff --git a/lib/megaco/examples/meas/megaco_codec_mstone2.erl b/lib/megaco/examples/meas/megaco_codec_mstone2.erl index f3588f2e3d..54b889345f 100644 --- a/lib/megaco/examples/meas/megaco_codec_mstone2.erl +++ b/lib/megaco/examples/meas/megaco_codec_mstone2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2009. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ %% When the finished timer expires, it will stop respawing %% the worker processes, and instead just wait for them all %% to finish. -%% The test is finished by printing the statistics. +%% The test is finishes by printing the statistics. %% - A worker process for each codec combination. %% This process is spawned by the loader process. It receives %% at start a list of messages. It shall decode and then @@ -162,7 +162,6 @@ parse_message_package(BadMessagePackage) -> mstone_init(MessagePackage, DrvInclude) -> io:format("~n", []), - ?LIB:set_default_sched_bind(), ?LIB:display_os_info(), ?LIB:display_system_info(), ?LIB:display_app_info(), diff --git a/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl b/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl index 040af9826b..9af88d9f50 100644 --- a/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl +++ b/lib/megaco/examples/meas/megaco_codec_mstone_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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,7 +30,6 @@ -compile({no_auto_import,[error/1]}). -export([start_flex_scanner/0, stop_flex_scanner/1, expanded_messages/2, expanded_messages/3, - set_default_sched_bind/0, display_os_info/0, display_system_info/0, display_alloc_info/0, @@ -70,16 +69,6 @@ detect_version(Codec, Conf, Bin) -> %%---------------------------------------------------------------------- %% -%% S c h e d u l e r b i n d t y p e -%% -%%---------------------------------------------------------------------- - -set_default_sched_bind() -> - (catch erlang:system_flag(scheduler_bind_type, default_bind)). - - -%%---------------------------------------------------------------------- -%% %% D i s p l a y O s I n f o %% %%---------------------------------------------------------------------- @@ -111,7 +100,6 @@ display_system_info() -> OtpRel = otp_release(), SysVer = system_version(), SysHT = heap_type(), - SysGHSz = global_heaps_size(), SysSMP = smp_support(), SysNumSched = schedulers(), SysProcLimit = process_limit(), @@ -124,7 +112,6 @@ display_system_info() -> io:format("OTP release: ~s~n", [OtpRel]), io:format("System version: ~s~n", [SysVer]), io:format("Heap type: ~s~n", [SysHT]), - io:format("Global heap size: ~s~n", [SysGHSz]), io:format("Thread support: ~s~n", [SysThreads]), io:format("Thread pool size: ~s~n", [SysTPSz]), io:format("Process limit: ~s~n", [SysProcLimit]), @@ -148,9 +135,6 @@ system_version() -> heap_type() -> system_info(heap_type, any). -global_heaps_size() -> - system_info(global_heaps_size, any). - smp_support() -> system_info(smp_support, any). diff --git a/lib/megaco/examples/meas/mstone1.sh.skel.src b/lib/megaco/examples/meas/mstone1.sh.skel.src index 54a6c61a58..b1935acef6 100644 --- a/lib/megaco/examples/meas/mstone1.sh.skel.src +++ b/lib/megaco/examples/meas/mstone1.sh.skel.src @@ -3,7 +3,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2007-2011. All Rights Reserved. +# Copyright Ericsson AB 2007-2012. 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 @@ -55,6 +55,15 @@ Options: flex scanner will be used nd - only codec config(s) without drivers will be used od - only codec config(s) with drivers will be used + -sbt <bind-type> Set scheduler bind type. See erl man page for more info. + tnnps - Thread no node processor spread (default) + u - Unbound + ns - No spread + ts - Thread spread + ps - Processor spread + s - Spread + nnts - No node thread spread + nnps - No node processor spread -- everything after this is just passed on to erl. " @@ -67,6 +76,7 @@ MODULE=megaco_codec_mstone1 STARTF="start" FACTOR="" MSG_PACK=time_test +SBT="+sbt tnnps" while test $# != 0; do # echo "DBG: Value = $1" @@ -107,6 +117,17 @@ while test $# != 0; do exit 0 esac;; + -sbt) + case $2 in + tnnps|u|ns|ts|ps|s|nnts|nnps) + SBT="+sbt $2"; + shift ; shift ;; + *) + echo "unknown scheduler bind type: $2"; + echo "$usage" ; + exit 0 + esac;; + -f) if [ "x$SCHED" != "x" ]; then echo "option(s) -s and -f cannot both be given" ; @@ -177,6 +198,7 @@ if [ $TYPE = factor ]; then ERL="erl \ -noshell \ + $SBT \ $PHS \ $ATP \ $SMP_OPTS \ @@ -218,6 +240,7 @@ elif [ $TYPE = sched ]; then ERL="erl \ -noshell \ + $SBT \ $PHS \ $ATP \ $SMP_OPTS \ diff --git a/lib/megaco/examples/simple/Makefile b/lib/megaco/examples/simple/Makefile index f91d1d886f..261acbb479 100644 --- a/lib/megaco/examples/simple/Makefile +++ b/lib/megaco/examples/simple/Makefile @@ -139,9 +139,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/examples/simple - $(INSTALL_DATA) $(ERL_FILES) $(TARGET_FILES) $(RELSYSDIR)/examples/simple + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/simple" + $(INSTALL_DATA) $(ERL_FILES) $(TARGET_FILES) "$(RELSYSDIR)/examples/simple" release_docs_spec: diff --git a/lib/megaco/src/app/Makefile b/lib/megaco/src/app/Makefile index 01dfb9b860..a7b458845d 100644 --- a/lib/megaco/src/app/Makefile +++ b/lib/megaco/src/app/Makefile @@ -108,14 +108,14 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/app - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/app - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/app" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/app" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/megaco/src/app/megaco.appup.src b/lib/megaco/src/app/megaco.appup.src index 3c9740818a..7f89fa8bc2 100644 --- a/lib/megaco/src/app/megaco.appup.src +++ b/lib/megaco/src/app/megaco.appup.src @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -142,10 +142,17 @@ %% | %% v %% 3.16 +%% | +%% v +%% 3.16.0.1 %% %% {"%VSN%", [ + {"3.16", + [ + ] + }, {"3.15.1.1", [ {restart_application, megaco} @@ -163,6 +170,10 @@ } ], [ + {"3.16", + [ + ] + }, {"3.15.1.1", [ {restart_application, megaco} diff --git a/lib/megaco/src/binary/Makefile b/lib/megaco/src/binary/Makefile index d594f34f43..77b06c81d3 100644 --- a/lib/megaco/src/binary/Makefile +++ b/lib/megaco/src/binary/Makefile @@ -194,11 +194,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/binary - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(ASN1_FILES) $(RELSYSDIR)/src/binary + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/binary" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(ASN1_FILES) "$(RELSYSDIR)/src/binary" release_docs_spec: diff --git a/lib/megaco/src/engine/Makefile b/lib/megaco/src/engine/Makefile index 3943f4b957..ae2fadb3fc 100644 --- a/lib/megaco/src/engine/Makefile +++ b/lib/megaco/src/engine/Makefile @@ -42,9 +42,14 @@ RELSYSDIR = $(RELEASE_PATH)/lib/megaco-$(VSN) include modules.mk -ERL_FILES = $(MODULES:%=%.erl) +ERL_FILES = \ + $(MODULES:%=%.erl) \ + $(BEHAVIOUR_MODULES:%=%.erl) -TARGET_FILES = \ +BEHAVIOUR_TARGET_FILES= \ + $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) + +TARGET_FILES = \ $(MODULES:%=$(EBIN)/%.$(EMULATOR)) @@ -65,19 +70,23 @@ ERL_COMPILE_FLAGS += \ # ---------------------------------------------------- # Targets # ---------------------------------------------------- + +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) + debug: @${MAKE} TYPE=debug opt opt: $(TARGET_FILES) clean: - rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) + rm -f $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) rm -f errs core *~ docs: info: @echo "MODULES = $(MODULES)" + @echo "BEHAVIOUR_MODULES = $(BEHAVIOUR_MODULES)" @echo "" @@ -88,12 +97,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/engine - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/engine - $(INSTALL_DIR) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/engine" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/engine" + $(INSTALL_DIR) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/megaco/src/engine/modules.mk b/lib/megaco/src/engine/modules.mk index 4bc57cd63e..6e2f9246f0 100644 --- a/lib/megaco/src/engine/modules.mk +++ b/lib/megaco/src/engine/modules.mk @@ -23,7 +23,6 @@ BEHAVIOUR_MODULES = \ megaco_transport MODULES = \ - $(BEHAVIOUR_MODULES) \ megaco_config_misc \ megaco_config \ megaco_digit_map \ diff --git a/lib/megaco/src/flex/Makefile.in b/lib/megaco/src/flex/Makefile.in index 7d82644246..976ecaa4f7 100644 --- a/lib/megaco/src/flex/Makefile.in +++ b/lib/megaco/src/flex/Makefile.in @@ -271,16 +271,16 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/flex - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/flex - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/flex" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/flex" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" ifeq ($(ENABLE_MEGACO_FLEX_SCANNER),true) - $(INSTALL_DATA) $(FLEX_FILES) $(C_TARGETS) $(RELSYSDIR)/src/flex - $(INSTALL_PROGRAM) $(SOLIBS) $(RELSYSDIR)/priv/lib + $(INSTALL_DATA) $(FLEX_FILES) $(C_TARGETS) "$(RELSYSDIR)/src/flex" + $(INSTALL_PROGRAM) $(SOLIBS) "$(RELSYSDIR)/priv/lib" endif diff --git a/lib/megaco/src/tcp/Makefile b/lib/megaco/src/tcp/Makefile index 0bd4b7c4ee..efc76bb2c5 100644 --- a/lib/megaco/src/tcp/Makefile +++ b/lib/megaco/src/tcp/Makefile @@ -89,11 +89,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/tcp - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/tcp + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/tcp" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/tcp" release_docs_spec: diff --git a/lib/megaco/src/text/Makefile b/lib/megaco/src/text/Makefile index b2e8e762dd..2c7703cd33 100644 --- a/lib/megaco/src/text/Makefile +++ b/lib/megaco/src/text/Makefile @@ -130,11 +130,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(BEAM_TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/text - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_YRL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/text + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(BEAM_TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/text" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_YRL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/text" release_docs_spec: diff --git a/lib/megaco/src/udp/Makefile b/lib/megaco/src/udp/Makefile index 64b6478c2c..db9bb72d82 100644 --- a/lib/megaco/src/udp/Makefile +++ b/lib/megaco/src/udp/Makefile @@ -89,11 +89,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/udp - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src/udp + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/udp" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src/udp" release_docs_spec: diff --git a/lib/megaco/test/Makefile b/lib/megaco/test/Makefile index 88f6f06e73..b1bca10ca8 100644 --- a/lib/megaco/test/Makefile +++ b/lib/megaco/test/Makefile @@ -748,11 +748,11 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(RELTEST_FILES) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(RELTEST_FILES) "$(RELSYSDIR)" # $(INSTALL_DATA) $(TEST_SPEC_FILE) $(COVER_SPEC_FILE) \ # $(HRL_FILES) $(ERL_FILES) \ -# $(RELSYSDIR) +# "$(RELSYSDIR)" # - chmod -R u+w $(RELSYSDIR) + chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/megaco/test/megaco_mess_test.erl b/lib/megaco/test/megaco_mess_test.erl index 8bafab1aba..663ac8c329 100644 --- a/lib/megaco/test/megaco_mess_test.erl +++ b/lib/megaco/test/megaco_mess_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. 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 @@ -324,9 +324,6 @@ min(M) -> timer:minutes(M). %% Test server callbacks -% init_per_testcase(pending_ack = Case, Config) -> -% put(dbg,true), -% megaco_test_lib:init_per_testcase(Case, Config); init_per_testcase(otp_7189 = Case, Config) -> C = lists:keydelete(tc_timeout, 1, Config), megaco_test_lib:init_per_testcase(Case, [{tc_timeout, min(2)} |C]); @@ -337,9 +334,6 @@ init_per_testcase(Case, Config) -> C = lists:keydelete(tc_timeout, 1, Config), megaco_test_lib:init_per_testcase(Case, [{tc_timeout, min(1)} |C]). -% end_per_testcase(pending_ack = Case, Config) -> -% erase(dbg), -% megaco_test_lib:end_per_testcase(Case, Config); end_per_testcase(Case, Config) -> megaco_test_lib:end_per_testcase(Case, Config). @@ -434,6 +428,7 @@ connect(suite) -> connect(doc) -> []; connect(Config) when is_list(Config) -> + %% ?SKIP("Needs a re-write..."), ?ACQUIRE_NODES(1, Config), PrelMid = preliminary_mid, MgMid = ipv4_mid(4711), @@ -457,16 +452,25 @@ connect(Config) when is_list(Config) -> ?VERIFY(bad_send_mod, megaco:conn_info(PrelCH, send_mod)), SC = service_change_request(), case megaco:call(PrelCH, [SC], []) of - {error, - {send_message_failed, - {'EXIT', {undef, [{bad_send_mod, send_message, [sh, _]} | _]}}}} -> + {_Version, + {error, + {send_message_failed, + {'EXIT', {undef, [{bad_send_mod, send_message, [sh, _]} | _]}}}} + } -> + %% R14B and previous + ?LOG("expected send failure (1)", []), ok; %% As of R15, we also get some extra info (e.g. line numbers) - {error, - {send_message_failed, - {'EXIT', {undef, [{bad_send_mod, send_message, [sh, _], _} | _]}}}} -> + {_Version, + {error, + {send_message_failed, + {'EXIT', {undef, [{bad_send_mod, send_message, [sh, _], _} | _]}}}} + } -> + %% R15B and later + ?LOG("expected send failure (2)", []), ok; + Unexpected -> ?ERROR(Unexpected) end, diff --git a/lib/megaco/vsn.mk b/lib/megaco/vsn.mk index bb6f5f554a..11a951a23e 100644 --- a/lib/megaco/vsn.mk +++ b/lib/megaco/vsn.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2011. All Rights Reserved. +# Copyright Ericsson AB 1997-2012. 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 @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = megaco -MEGACO_VSN = 3.16 +MEGACO_VSN = 3.16.0.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(MEGACO_VSN)$(PRE_VSN)" diff --git a/lib/mnesia/doc/src/Makefile b/lib/mnesia/doc/src/Makefile index 1ac5760510..173e43ac1f 100644 --- a/lib/mnesia/doc/src/Makefile +++ b/lib/mnesia/doc/src/Makefile @@ -142,13 +142,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml index a300fcc12d..612ccb5ee8 100644 --- a/lib/mnesia/doc/src/notes.xml +++ b/lib/mnesia/doc/src/notes.xml @@ -38,7 +38,37 @@ thus constitutes one section in this document. The title of each section is the version number of Mnesia.</p> - <section><title>Mnesia 4.6</title> + <section><title>Mnesia 4.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Returns the same value for + mnesia_loader:disc_load_table/2 as + mnesia_loader:net_load_table/4 if a table copy can not be + found. (Thanks to Uwe Dauernheim)</p> + <p> + Own Id: OTP-10015</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Improved table lock algorithm.</p> + <p> + Own Id: OTP-9890</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.6</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/mnesia/examples/Makefile b/lib/mnesia/examples/Makefile index ff00ee76a5..5379761c64 100644 --- a/lib/mnesia/examples/Makefile +++ b/lib/mnesia/examples/Makefile @@ -78,9 +78,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/examples/bench + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/bench" (cd bench; $(INSTALL_DATA) \ Makefile \ README \ @@ -96,8 +96,8 @@ release_spec: opt bench.config5 \ bench.config6 \ bench.config7 \ - $(RELSYSDIR)/examples/bench) - (cd bench; $(INSTALL_SCRIPT) bench.sh $(RELSYSDIR)/examples/bench) + "$(RELSYSDIR)/examples/bench") + (cd bench; $(INSTALL_SCRIPT) bench.sh "$(RELSYSDIR)/examples/bench") release_docs_spec: diff --git a/lib/mnesia/include/Makefile b/lib/mnesia/include/Makefile index f9b7d72abe..20f62b5460 100644 --- a/lib/mnesia/include/Makefile +++ b/lib/mnesia/include/Makefile @@ -54,8 +54,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(INCLUDE_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(INCLUDE_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/mnesia/src/Makefile b/lib/mnesia/src/Makefile index 1c8ec54605..1432eabc37 100644 --- a/lib/mnesia/src/Makefile +++ b/lib/mnesia/src/Makefile @@ -132,10 +132,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl index e443b54016..4ba400fbbf 100644 --- a/lib/mnesia/src/mnesia_loader.erl +++ b/lib/mnesia/src/mnesia_loader.erl @@ -57,7 +57,7 @@ disc_load_table(Tab, Reason) -> do_get_disc_copy2(Tab, _Reason, Storage, _Type) when Storage == unknown -> verbose("Local table copy of ~p has recently been deleted, ignored.~n", [Tab]), - {loaded, ok}; %% ? + {not_loaded, storage_unknown}; do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_copies -> %% NOW we create the actual table Repair = mnesia_monitor:get_env(auto_repair), diff --git a/lib/mnesia/test/Makefile b/lib/mnesia/test/Makefile index ae4c9626c7..0451da697d 100644 --- a/lib/mnesia/test/Makefile +++ b/lib/mnesia/test/Makefile @@ -52,24 +52,29 @@ MODULES= \ mnesia_cost \ mnesia_dbn_meters -MnesiaExamplesDir := ../examples +DocExamplesDir := ../doc/src/ -ExampleModules = \ +DocExampleModules = \ company \ company_o \ - bup \ - mnesia_meter \ - mnesia_tpcb -ExamplesHrl = \ + bup + +DocExamplesHrl = \ company.hrl \ company_o.hrl -ERL_FILES= $(MODULES:%=%.erl) $(ExampleModules:%=$(MnesiaExamplesDir)/%.erl) +ExamplesDir := ../examples/ + +ExampleModules = \ + mnesia_meter \ + mnesia_tpcb -HRL_FILES= mnesia_test_lib.hrl $(ExamplesHrl:%=$(MnesiaExamplesDir)/%) +ERL_FILES= $(MODULES:%=%.erl) $(DocExampleModules:%=$(DocExamplesDir)/%.erl) $(ExampleModules:%=$(ExamplesDir)/%.erl) + +HRL_FILES= mnesia_test_lib.hrl $(DocExamplesHrl:%=$(DocExamplesDir)/%) TARGET_FILES= \ - $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(ExampleModules:%=$(EBIN)/%.$(EMULATOR)) + $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(DocExampleModules:%=$(EBIN)/%.$(EMULATOR)) $(ExampleModules:%=$(EBIN)/%.$(EMULATOR)) INSTALL_PROGS= $(TARGET_FILES) @@ -91,7 +96,10 @@ EBIN = . tests debug opt: $(TARGET_FILES) -$(EBIN)/%.beam: $(MnesiaExamplesDir)/%.erl +$(EBIN)/%.beam: $(DocExamplesDir)/%.erl + $(ERLC) -bbeam $(ERL_COMPILE_FLAGS) -o$(EBIN) $< + +$(EBIN)/%.beam: $(ExamplesDir)/%.erl $(ERLC) -bbeam $(ERL_COMPILE_FLAGS) -o$(EBIN) $< clean: @@ -108,11 +116,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) mnesia.spec mnesia.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_SCRIPT) mt $(INSTALL_PROGS) $(RELSYSDIR) -# chmod -R u+w $(RELSYSDIR) -# @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) mnesia.spec mnesia.cover $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_SCRIPT) mt $(INSTALL_PROGS) "$(RELSYSDIR)" +# chmod -R u+w "$(RELSYSDIR)" +# @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/mnesia/test/mnesia_consistency_test.erl b/lib/mnesia/test/mnesia_consistency_test.erl index f38e13f3a2..bb21723e27 100644 --- a/lib/mnesia/test/mnesia_consistency_test.erl +++ b/lib/mnesia/test/mnesia_consistency_test.erl @@ -100,7 +100,7 @@ groups() -> {group, updates_during_checkpoint_iteration}, {group, load_table_with_activated_checkpoint}, {group, - add_table_copy_to_table_with_activated_checkpoint}]}, + add_table_copy_to_table_checkpoint}]}, {updates_during_checkpoint_activation, [], [updates_during_checkpoint_activation_2_ram, updates_during_checkpoint_activation_2_disc, @@ -116,10 +116,10 @@ groups() -> [load_table_with_activated_checkpoint_ram, load_table_with_activated_checkpoint_disc, load_table_with_activated_checkpoint_disc_only]}, - {add_table_copy_to_table_with_activated_checkpoint, [], - [add_table_copy_to_table_with_activated_checkpoint_ram, - add_table_copy_to_table_with_activated_checkpoint_disc, - add_table_copy_to_table_with_activated_checkpoint_disc_only]}, + {add_table_copy_to_table_checkpoint, [], + [add_table_copy_to_table_checkpoint_ram, + add_table_copy_to_table_checkpoint_disc, + add_table_copy_to_table_checkpoint_disc_only]}, {backup_consistency, [], [{group, interupted_install_fallback}, {group, interupted_uninstall_fallback}, @@ -952,16 +952,16 @@ view(Source, Mod) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -add_table_copy_to_table_with_activated_checkpoint_ram(suite) -> []; -add_table_copy_to_table_with_activated_checkpoint_ram(Config) when is_list(Config) -> +add_table_copy_to_table_checkpoint_ram(suite) -> []; +add_table_copy_to_table_checkpoint_ram(Config) when is_list(Config) -> add_table_copy_to_table_with_activated_checkpoint(ram_copies, Config). -add_table_copy_to_table_with_activated_checkpoint_disc(suite) -> []; -add_table_copy_to_table_with_activated_checkpoint_disc(Config) when is_list(Config) -> +add_table_copy_to_table_checkpoint_disc(suite) -> []; +add_table_copy_to_table_checkpoint_disc(Config) when is_list(Config) -> add_table_copy_to_table_with_activated_checkpoint(disc_copies, Config). -add_table_copy_to_table_with_activated_checkpoint_disc_only(suite) -> []; -add_table_copy_to_table_with_activated_checkpoint_disc_only(Config) when is_list(Config) -> +add_table_copy_to_table_checkpoint_disc_only(suite) -> []; +add_table_copy_to_table_checkpoint_disc_only(Config) when is_list(Config) -> add_table_copy_to_table_with_activated_checkpoint(disc_only_copies, Config). add_table_copy_to_table_with_activated_checkpoint(Type,Config) -> diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk index 080548acac..da292cbb43 100644 --- a/lib/mnesia/vsn.mk +++ b/lib/mnesia/vsn.mk @@ -1 +1 @@ -MNESIA_VSN = 4.6 +MNESIA_VSN = 4.7 diff --git a/lib/observer/doc/src/Makefile b/lib/observer/doc/src/Makefile index cd9f9466ca..0f564d3299 100644 --- a/lib/observer/doc/src/Makefile +++ b/lib/observer/doc/src/Makefile @@ -133,16 +133,16 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/observer/doc/src/etop.xml b/lib/observer/doc/src/etop.xml index 4eb5603549..af6bb2442b 100644 --- a/lib/observer/doc/src/etop.xml +++ b/lib/observer/doc/src/etop.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2002</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/observer/src/Makefile b/lib/observer/src/Makefile index 7eb2144dee..7135a6abd5 100644 --- a/lib/observer/src/Makefile +++ b/lib/observer/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2002-2011. All Rights Reserved. +# Copyright Ericsson AB 2002-2012. 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 @@ -129,20 +129,20 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(EXECUTABLES) $(RELSYSDIR)/priv/bin - $(INSTALL_DIR) $(RELSYSDIR)/priv/crashdump_viewer - $(INSTALL_DATA) $(WEBTOOLFILES) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(GIF_FILES) $(RELSYSDIR)/priv/crashdump_viewer + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLE_FILES) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_SCRIPT) $(EXECUTABLES) "$(RELSYSDIR)/priv/bin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/crashdump_viewer" + $(INSTALL_DATA) $(WEBTOOLFILES) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(GIF_FILES) "$(RELSYSDIR)/priv/crashdump_viewer" release_docs_spec: diff --git a/lib/observer/src/crashdump_viewer_html.erl b/lib/observer/src/crashdump_viewer_html.erl index 24a80b1916..3151b83bfb 100644 --- a/lib/observer/src/crashdump_viewer_html.erl +++ b/lib/observer/src/crashdump_viewer_html.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -1394,7 +1394,7 @@ timers_table(Timer) -> td("ALIGN=right",Time)]). loaded_mods_table(#loaded_mod{mod=Mod,current_size=CS,old_size=OS}) -> - tr([td(href(["loaded_mod_details?mod=",Mod],Mod)), + tr([td(href(["loaded_mod_details?mod=",http_uri:encode(Mod)],Mod)), td("ALIGN=right",CS), td("ALIGN=right",OS)]). diff --git a/lib/observer/src/etop.erl b/lib/observer/src/etop.erl index 7ec0fedbb2..428757e5ce 100644 --- a/lib/observer/src/etop.erl +++ b/lib/observer/src/etop.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 diff --git a/lib/observer/src/etop_gui.erl b/lib/observer/src/etop_gui.erl index 9248d67344..f5cc0deb38 100644 --- a/lib/observer/src/etop_gui.erl +++ b/lib/observer/src/etop_gui.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2009. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 diff --git a/lib/observer/src/observer_app_wx.erl b/lib/observer/src/observer_app_wx.erl index 7eac2b8fab..380532e90c 100644 --- a/lib/observer/src/observer_app_wx.erl +++ b/lib/observer/src/observer_app_wx.erl @@ -147,11 +147,11 @@ setup_scrollbar({CW, CH}, AppWin, #app{dim={W0,H0}}) -> H = max(H0,CH), PPC = 20, if W0 =< CW, H0 =< CH -> - wxScrolledWindow:setScrollbars(AppWin, W, H, 1, 1); + wxScrolledWindow:setScrollbars(AppWin, W, H, 0, 0); H0 =< CH -> - wxScrolledWindow:setScrollbars(AppWin, PPC, H, W div PPC+1, 1); + wxScrolledWindow:setScrollbars(AppWin, PPC, H, W div PPC+1, 0); W0 =< CW -> - wxScrolledWindow:setScrollbars(AppWin, W, PPC, 1, H div PPC+1); + wxScrolledWindow:setScrollbars(AppWin, W, PPC, 0, H div PPC+1); true -> wxScrolledWindow:setScrollbars(AppWin, PPC, PPC, W div PPC+1, H div PPC+1) end; @@ -204,7 +204,7 @@ handle_event(#wx{id=?ID_PROC_MSG, event=#wxCommand{type=command_menu_selected}}, handle_event(#wx{id=?ID_PROC_KILL, event=#wxCommand{type=command_menu_selected}}, State = #state{panel=Panel, sel={#box{s1=#str{pid=Pid}},_}}) -> - case observer_lib:user_term(Panel, "Enter Exit Reason", "") of + case observer_lib:user_term(Panel, "Enter Exit Reason", "kill") of cancel -> ok; {ok, Term} -> exit(Pid, Term); {error, Error} -> observer_lib:display_info_dialog(Error) @@ -269,22 +269,21 @@ handle_cast(Event, _State) -> %%%%%%%%%% handle_info({active, Node}, State = #state{parent=Parent, current=Curr, appmon=Appmon}) -> create_menus(Parent, []), - {ok, Pid} = appmon_info:start_link(Node, self(), []), - case Appmon of - undefined -> ok; - Pid -> ok; - _ -> %% Deregister me as client (and stop appmon if last) - exit(Appmon, normal) - end, + Pid = try + Node = node(Appmon), + Appmon + catch _:_ -> + {ok, P} = appmon_info:start_link(Node, self(), []), + P + end, appmon_info:app_ctrl(Pid, Node, true, []), (Curr =/= undefined) andalso appmon_info:app(Pid, Curr, true, []), {noreply, State#state{appmon=Pid}}; - -handle_info(not_active, State = #state{appmon=AppMon, current=Prev}) -> +handle_info(not_active, State = #state{appmon=AppMon}) -> appmon_info:app_ctrl(AppMon, node(AppMon), false, []), - (Prev =/= undefined) andalso appmon_info:app(AppMon, Prev, false, []), - {noreply, State}; - + lists:member(node(AppMon), nodes()) andalso exit(AppMon, normal), + observer_wx:set_status(""), + {noreply, State#state{appmon=undefined}}; handle_info({delivery, Pid, app_ctrl, _, Apps0}, State = #state{appmon=Pid, apps_w=LBox, current=Curr0}) -> Apps = [atom_to_list(App) || {_, App, {_, _, _}} <- Apps0], @@ -341,6 +340,7 @@ handle_mouse_click(Node = {#box{s1=#str{pid=Pid}},_}, Type, right_down -> popup_menu(Panel); _ -> ok end, + observer_wx:set_status(io_lib:format("Pid: ~p", [Pid])), wxWindow:refresh(AppWin), State#state{sel=Node}; handle_mouse_click(_, _, State = #state{sel=undefined}) -> @@ -349,6 +349,7 @@ handle_mouse_click(_, right_down, State=#state{panel=Panel}) -> popup_menu(Panel), State; handle_mouse_click(_, _, State=#state{app_w=AppWin}) -> + observer_wx:set_status(""), wxWindow:refresh(AppWin), State#state{sel=undefined}. @@ -376,10 +377,11 @@ popup_menu(Panel) -> wxMenu:append(Menu, ?ID_TRACE_NAME, "Trace named process"), wxMenu:append(Menu, ?ID_TRACE_TREE_PIDS, "Trace process tree"), wxMenu:append(Menu, ?ID_TRACE_TREE_NAMES, "Trace named process tree"), + wxMenu:append(Menu, ?ID_PROC_MSG, "Send Msg"), + wxMenu:append(Menu, ?ID_PROC_KILL, "Kill process"), wxWindow:popupMenu(Panel, Menu), wxMenu:destroy(Menu). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% locate_node(X, _Y, [{Box=#box{x=BX}, _Chs}|_Rest]) when X < BX -> diff --git a/lib/observer/src/observer_perf_wx.erl b/lib/observer/src/observer_perf_wx.erl index 0de9785fb9..abf90ac612 100644 --- a/lib/observer/src/observer_perf_wx.erl +++ b/lib/observer/src/observer_perf_wx.erl @@ -123,7 +123,7 @@ init([Notebook, Parent]) -> }} catch _:Err -> io:format("~p crashed ~p: ~p~n",[?MODULE, Err, erlang:get_stacktrace()]), - {error, Err} + {stop, Err} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -278,7 +278,7 @@ collect_data(?IO_W, {N, Q}) -> end. mem_types() -> - [total, processes, system, atom, binary, code, ets]. + [total, processes, atom, binary, code, ets]. lmax([]) -> 0; lmax(List) -> diff --git a/lib/observer/src/observer_pro_wx.erl b/lib/observer/src/observer_pro_wx.erl index 7578215ff9..ee67664539 100644 --- a/lib/observer/src/observer_pro_wx.erl +++ b/lib/observer/src/observer_pro_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 @@ -191,13 +191,20 @@ dump_to_file(Parent, FileName, Holder) -> start_procinfo(undefined, _Frame, Opened) -> Opened; start_procinfo(Pid, Frame, Opened) -> - case lists:member(Pid, Opened) of - true -> - Opened; - false -> - observer_procinfo:start(Pid, Frame, self()), - [Pid | Opened] + %% This code doesn't work until we collect which windows have been + %% closed maybe it should moved to observer_wx.erl + %% and add a global menu which remembers windows. + %% case lists:keyfind(Pid, 1, Opened) of + %% false -> + case observer_procinfo:start(Pid, Frame, self()) of + {error, _} -> Opened; + PI -> [{Pid, PI} | Opened] end. + %%; + %% {_, PI} -> + %% wxFrame:raise(PI), + %% Opened + %% end. call(Holder, What) -> Ref = erlang:monitor(process, Holder), @@ -251,8 +258,7 @@ terminate(_Reason, #state{holder=Holder}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. - + {ok, State}. handle_call(Msg, _From, State) -> io:format("~p:~p: Unhandled call ~p~n",[?MODULE, ?LINE, Msg]), diff --git a/lib/observer/src/observer_procinfo.erl b/lib/observer/src/observer_procinfo.erl index ec08d3aff1..45218c177b 100644 --- a/lib/observer/src/observer_procinfo.erl +++ b/lib/observer/src/observer_procinfo.erl @@ -49,7 +49,8 @@ init([Pid, ParentFrame, Parent]) -> try Title=case observer_wx:try_rpc(node(Pid), erlang, process_info, [Pid, registered_name]) of [] -> io_lib:format("~p",[Pid]); - {registered_name, Registered} -> atom_to_list(Registered) + {registered_name, Registered} -> io_lib:format("~p (~p)",[Registered, Pid]); + undefined -> throw(process_undefined) end, Frame=wxFrame:new(ParentFrame, ?wxID_ANY, [atom_to_list(node(Pid)), $:, Title], [{style, ?wxDEFAULT_FRAME_STYLE}, {size, {850,600}}]), @@ -75,7 +76,10 @@ init([Pid, ParentFrame, Parent]) -> }} catch error:{badrpc, _} -> observer_wx:return_to_localnode(ParentFrame, node(Pid)), - {stop, badrpc, #state{parent=Parent, pid=Pid}} + {stop, badrpc}; + process_undefined -> + observer_lib:display_info_dialog("No such alive process"), + {stop, normal} end. init_panel(Notebook, Str, Pid, Fun) -> @@ -94,8 +98,11 @@ handle_event(#wx{event=#wxClose{type=close_window}}, State) -> handle_event(#wx{id=?wxID_CLOSE, event=#wxCommand{type=command_menu_selected}}, State) -> {stop, normal, State}; -handle_event(#wx{id=?REFRESH}, #state{pages=Pages}=State) -> - [(W#worker.callback)() || W <- Pages], +handle_event(#wx{id=?REFRESH}, #state{frame=Frame, pid=Pid, pages=Pages}=State) -> + try [(W#worker.callback)() || W <- Pages] + catch process_undefined -> + wxFrame:setTitle(Frame, io_lib:format("*DEAD* ~p",[Pid])) + end, {noreply, State}; handle_event(Event, _State) -> @@ -120,17 +127,15 @@ terminate(_Reason, #state{parent=Parent,pid=Pid,frame=Frame}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% init_process_page(Panel, Pid) -> Fields0 = process_info_fields(Pid), {FPanel, _, UpFields} = observer_lib:display_info(Panel, Fields0), - {FPanel, fun() -> case process_info_fields(Pid) of - Fields when is_list(Fields) -> - observer_lib:update_info(UpFields, Fields); - _ -> ok - end + {FPanel, fun() -> + Fields = process_info_fields(Pid), + observer_lib:update_info(UpFields, Fields) end}. init_text_page(Parent) -> @@ -162,7 +167,8 @@ init_message_page(Parent, Pid) -> false -> wxTextCtrl:writeText(Text, Messages) end; - _ -> ok + _ -> + throw(process_undefined) end end, Update(), @@ -178,7 +184,8 @@ init_dict_page(Parent, Pid) -> Last = wxTextCtrl:getLastPosition(Text), wxTextCtrl:remove(Text, 0, Last), wxTextCtrl:writeText(Text, Dict); - _ -> ok + _ -> + throw(process_undefined) end end, Update(), @@ -216,7 +223,8 @@ init_stack_page(Parent, Pid) -> wxListCtrl:setItem(LCtrl, Row, 1, FileLine), Row+1 end, 0, RawBt); - _ -> ok + _ -> + throw(process_undefined) end end, Resize = fun(#wx{event=#wxSize{size={W,_}}},Ev) -> @@ -266,7 +274,7 @@ process_info_fields(Pid) -> RawInfo when is_list(RawInfo) -> observer_lib:fill_info(Struct, RawInfo); _ -> - ok + throw(process_undefined) end. item_list() -> diff --git a/lib/observer/src/observer_sys_wx.erl b/lib/observer/src/observer_sys_wx.erl index 09602bbd9e..f00a666a35 100644 --- a/lib/observer/src/observer_sys_wx.erl +++ b/lib/observer/src/observer_sys_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 @@ -147,7 +147,7 @@ terminate(_Reason, _State) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. handle_call(Msg, _From, State) -> io:format("~p~p: Unhandled Call ~p~n",[?MODULE, ?LINE, Msg]), diff --git a/lib/observer/src/observer_trace_wx.erl b/lib/observer/src/observer_trace_wx.erl index d0b6a1e063..f2a1084f85 100644 --- a/lib/observer/src/observer_trace_wx.erl +++ b/lib/observer/src/observer_trace_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 @@ -489,7 +489,7 @@ terminate(_Reason, #state{nodes=_Nodes}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_add_patterns({Module, NewPs}, State=#state{tpatterns=TPs0, m_view=Mview, f_view=Fview}) -> diff --git a/lib/observer/src/observer_traceoptions_wx.erl b/lib/observer/src/observer_traceoptions_wx.erl index 6a634e06f0..e27f565abc 100644 --- a/lib/observer/src/observer_traceoptions_wx.erl +++ b/lib/observer/src/observer_traceoptions_wx.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 @@ -167,8 +167,10 @@ module_selector(Parent, Node) -> function_selector(Parent, Node, Module) -> Functions = observer_wx:try_rpc(Node, Module, module_info, [functions]), - Choices = lists:sort([{Name, Arity} || {Name, Arity} <- Functions, - not(erl_internal:guard_bif(Name, Arity))]), + Externals = observer_wx:try_rpc(Node, Module, module_info, [exports]), + + Choices = lists:usort([{Name, Arity} || {Name, Arity} <- Externals ++ Functions, + not(erl_internal:guard_bif(Name, Arity))]), ParsedChoices = parse_function_names(Choices), case check_selector(Parent, ParsedChoices) of [] -> [{Module, '_', '_'}]; diff --git a/lib/observer/src/observer_tv_table.erl b/lib/observer/src/observer_tv_table.erl index f432173f57..8fdcbf331c 100644 --- a/lib/observer/src/observer_tv_table.erl +++ b/lib/observer/src/observer_tv_table.erl @@ -24,6 +24,8 @@ -export([init/1, handle_info/2, terminate/2, code_change/3, handle_call/3, handle_event/2, handle_sync_event/3, handle_cast/2]). +-export([format/1]). + -include("observer_defs.hrl"). -import(observer_lib, [to_str/1]). @@ -265,7 +267,8 @@ handle_event(#wx{id=?ID_DELETE}, wxStatusBar:setStatusText(StatusBar, io_lib:format("Deleted object: ~s",[Str])), {noreply, State}; -handle_event(#wx{id=?wxID_CLOSE}, State) -> +handle_event(#wx{id=?wxID_CLOSE}, State = #state{frame=Frame}) -> + wxFrame:destroy(Frame), {stop, normal, State}; handle_event(Help = #wx{id=?wxID_HELP}, State) -> @@ -453,7 +456,7 @@ get_attr(Table, Item) -> Ref = erlang:monitor(process, Table), Table ! {get_attr, self(), Item}, receive - {'DOWN', Ref, _, _, _} -> ""; + {'DOWN', Ref, _, _, _} -> wx:null(); {Table, Res} -> erlang:demonitor(Ref), Res @@ -747,6 +750,13 @@ format(List) when is_list(List) -> format_list(List); format(Bin) when is_binary(Bin), byte_size(Bin) > 100 -> io_lib:format("<<#Bin:~w>>", [byte_size(Bin)]); +format(Bin) when is_binary(Bin) -> + try + true = printable_list(unicode:characters_to_list(Bin)), + io_lib:format("<<\"~ts\">>", [Bin]) + catch _:_ -> + io_lib:format("~w", [Bin]) + end; format(Float) when is_float(Float) -> io_lib:format("~.3g", [Float]); format(Term) -> diff --git a/lib/observer/src/observer_wx.erl b/lib/observer/src/observer_wx.erl index e2b256d768..e433bea8c2 100644 --- a/lib/observer/src/observer_wx.erl +++ b/lib/observer/src/observer_wx.erl @@ -20,7 +20,7 @@ -behaviour(wx_object). -export([start/0]). --export([create_menus/2, get_attrib/1, get_tracer/0, +-export([create_menus/2, get_attrib/1, get_tracer/0, set_status/1, create_txt_dialog/4, try_rpc/4, return_to_localnode/2]). -export([init/1, handle_event/2, handle_cast/2, terminate/2, code_change/3, @@ -58,7 +58,8 @@ perf_panel, active_tab, node, - nodes + nodes, + prev_node="" }). start() -> @@ -73,6 +74,9 @@ create_menus(Object, Menus) when is_list(Menus) -> get_attrib(What) -> wx_object:call(observer, {get_attrib, What}). +set_status(What) -> + wx_object:cast(observer, {status_bar, What}). + get_tracer() -> wx_object:call(observer, get_tracer). @@ -191,10 +195,13 @@ setup(#state{frame = Frame} = State) -> %%Callbacks handle_event(#wx{event=#wxNotebook{type=command_notebook_page_changing}}, #state{active_tab=Previous, node=Node} = State) -> - Pid = get_active_pid(State), - Previous ! not_active, - Pid ! {active, Node}, - {noreply, State#state{active_tab=Pid}}; + case get_active_pid(State) of + Previous -> {noreply, State}; + Pid -> + Previous ! not_active, + Pid ! {active, Node}, + {noreply, State#state{active_tab=Pid}} + end; handle_event(#wx{event = #wxClose{}}, State) -> {stop, normal, State}; @@ -258,20 +265,21 @@ handle_event(#wx{id = ?ID_CONNECT, event = #wxCommand{type = command_menu_select handle_event(#wx{id = ?ID_PING, event = #wxCommand{type = command_menu_selected}}, #state{frame = Frame} = State) -> UpdState = case create_connect_dialog(ping, State) of - cancel -> State; + cancel -> State; {value, Value} when is_list(Value) -> try Node = list_to_atom(Value), case net_adm:ping(Node) of pang -> create_txt_dialog(Frame, "Connect failed", "Pang", ?wxICON_EXCLAMATION), - State; + State#state{prev_node=Value}; pong -> - change_node_view(Node, State) + State1 = change_node_view(Node, State), + State1#state{prev_node=Value} end catch _:_ -> create_txt_dialog(Frame, "Connect failed", "Pang", ?wxICON_EXCLAMATION), - State + State#state{prev_node=Value} end end, {noreply, UpdState}; @@ -288,6 +296,10 @@ handle_event(Event, State) -> Pid ! Event, {noreply, State}. +handle_cast({status_bar, Msg}, State=#state{status_bar=SB}) -> + wxStatusBar:setStatusText(SB, Msg), + {noreply, State}; + handle_cast(_Cast, State) -> {noreply, State}. @@ -341,7 +353,7 @@ terminate(_Reason, #state{frame = Frame}) -> ok. code_change(_, _, State) -> - {stop, not_yet_implemented, State}. + {ok, State}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -401,7 +413,9 @@ connect2(NodeName, Opts, Cookie) -> end. change_node_view(Node, State) -> - get_active_pid(State) ! {active, Node}, + Tab = get_active_pid(State), + Tab ! not_active, + Tab ! {active, Node}, StatusText = ["Observer - " | atom_to_list(Node)], wxFrame:setTitle(State#state.frame, StatusText), wxStatusBar:setStatusText(State#state.status_bar, StatusText), @@ -439,8 +453,8 @@ pid2panel(Pid, #state{pro_panel=Pro, sys_panel=Sys, end. -create_connect_dialog(ping, #state{frame = Frame}) -> - Dialog = wxTextEntryDialog:new(Frame, "Connect to node"), +create_connect_dialog(ping, #state{frame = Frame, prev_node=Prev}) -> + Dialog = wxTextEntryDialog:new(Frame, "Connect to node", [{value, Prev}]), case wxDialog:showModal(Dialog) of ?wxID_OK -> Value = wxTextEntryDialog:getValue(Dialog), @@ -560,7 +574,16 @@ remove_menu_items([], _MB) -> ok. get_nodes() -> - Nodes = [node()| nodes()], + Nodes0 = case erlang:is_alive() of + false -> []; + true -> + case net_adm:names() of + {error, _} -> nodes(); + {ok, Names} -> + epmd_nodes(Names) ++ nodes() + end + end, + Nodes = lists:usort(Nodes0), {_, Menues} = lists:foldl(fun(Node, {Id, Acc}) when Id < ?LAST_NODES_MENU_ID -> {Id + 1, [#create_menu{id=Id + ?FIRST_NODES_MENU_ID, @@ -568,6 +591,10 @@ get_nodes() -> end, {1, []}, Nodes), {Nodes, lists:reverse(Menues)}. +epmd_nodes(Names) -> + [_, Host] = string:tokens(atom_to_list(node()),"@"), + [list_to_atom(Name ++ [$@|Host]) || {Name, _} <- Names]. + update_node_list(State = #state{menubar=MenuBar}) -> {Nodes, NodesMenuItems} = get_nodes(), NodeMenuId = wxMenuBar:findMenu(MenuBar, "Nodes"), diff --git a/lib/observer/test/Makefile b/lib/observer/test/Makefile index bf99f07081..9df0591da5 100644 --- a/lib/observer/test/Makefile +++ b/lib/observer/test/Makefile @@ -82,11 +82,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) observer.spec $(EMAKEFILE) \ $(COVERFILE) $(ERL_FILES) \ - $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl index 79ece7edf5..6f882d0be9 100644 --- a/lib/observer/test/crashdump_viewer_SUITE.erl +++ b/lib/observer/test/crashdump_viewer_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. 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 @@ -318,7 +318,7 @@ browse_file(Port,File) -> %% The page where a filename can be entered title(Port,"read_file_frame","Read File"), - + %% Load a file Url = "http://localhost:"++Port++"/cdv_erl/crashdump_viewer/read_file", Html = request_sync(post,{Url,[],[],"path="++File}), @@ -414,6 +414,10 @@ special(Port,File) -> _ -> ok end; + ".strangemodname" -> + AllMods = contents(Port,"loaded_modules"), + open_all_modules(Port,AllMods), + ok; %%! No longer needed - all atoms are shown on one page!! %% ".250atoms" -> %% Html1 = contents(Port,"atoms"), @@ -496,6 +500,26 @@ expand_binary_link(Html) -> expand_binary_link(T) end. +open_all_modules(Port,Modules) -> + case get_first_module(Modules) of + {Module,Rest} -> + ModuleDetails = contents(Port,"loaded_mod_details?mod=" ++ Module), + ModTitle = http_uri:decode(Module), + ModTitle = title(ModuleDetails), + open_all_modules(Port,Rest); + false -> + ok + end. + +get_first_module([]) -> + false; +get_first_module(Html) -> + case Html of + "<TD><A HREF=\"loaded_mod_details?mod=" ++ Rest -> + {string:sub_word(Rest,1,$"),Rest}; + [_H|T] -> + get_first_module(T) + end. %% next_link(Html) -> %% case Html of @@ -565,7 +589,7 @@ create_dumps(DataDir,[Rel|Rels],Acc) -> Fun = fun() -> do_create_dumps(DataDir,Rel) end, Pa = filename:dirname(code:which(?MODULE)), {SlAllocDumps,Dumps,DosDump} = - ?t:run_on_shielded_node(Fun, compat_rel(Rel) ++ "-pa " ++ Pa), + ?t:run_on_shielded_node(Fun, compat_rel(Rel) ++ "-pa \"" ++ Pa ++ "\""), create_dumps(DataDir,Rels,SlAllocDumps ++ Dumps ++ Acc ++ DosDump); create_dumps(_DataDir,[],Acc) -> Acc. @@ -590,7 +614,8 @@ do_create_dumps(DataDir,Rel) -> case Rel of current -> CD3 = dump_with_args(DataDir,Rel,"instr","+Mim true"), - {SlAllocDumps, [CD1,CD2,CD3], DosDump}; + CD4 = dump_with_strange_module_name(DataDir,Rel,"strangemodname"), + {SlAllocDumps, [CD1,CD2,CD3,CD4], DosDump}; _ -> {SlAllocDumps, [CD1,CD2], DosDump} end. @@ -600,7 +625,7 @@ do_create_dumps(DataDir,Rel) -> %% not connected node, and with monitors and links between nodes. full_dist_dump(DataDir,Rel) -> Opt = rel_opt(Rel), - Pz = "-pz " ++ filename:dirname(code:which(?MODULE)), + Pz = "-pz \"" ++ filename:dirname(code:which(?MODULE)) ++ "\"", PzOpt = [{args,Pz}], {ok,N1} = ?t:start_node(n1,peer,Opt ++ PzOpt), {ok,N2} = ?t:start_node(n2,peer,Opt ++ PzOpt), @@ -648,7 +673,22 @@ dump_with_args(DataDir,Rel,DumpName,Args) -> ?t:stop_node(n1), CD. +%% This dump is added to test OTP-10090 - regarding URL encoding of +%% module names in the module detail link. +dump_with_strange_module_name(DataDir,Rel,DumpName) -> + Opt = rel_opt(Rel), + {ok,N1} = ?t:start_node(n1,peer,Opt), + Mod = '<mod ule#with?strange%name>', + File = atom_to_list(Mod) ++ ".erl", + Forms = [{attribute,1,file,{File,1}}, + {attribute,1,module,Mod}, + {eof,4}], + {ok,Mod,Bin} = rpc:call(N1,compile,forms,[Forms,[binary]]), + {module,Mod} = rpc:call(N1,code,load_binary,[Mod,File,Bin]), + CD = dump(N1,DataDir,Rel,DumpName), + ?t:stop_node(n1), + CD. dump(Node,DataDir,Rel,DumpName) -> rpc:call(Node,erlang,halt,[DumpName]), @@ -711,6 +751,7 @@ rel_opt(Rel) -> r11b -> [{erl,[{release,"r11b_patched"}]}]; r12b -> [{erl,[{release,"r12b_patched"}]}]; r13b -> [{erl,[{release,"r13b_patched"}]}]; + r14b -> [{erl,[{release,"r14b_latest"}]}]; %naming convention changed current -> [] end. @@ -734,6 +775,6 @@ compat_rel(Rel) -> r11b -> "+R11 "; r12b -> "+R12 "; r13b -> "+R13 "; - r14b -> "+R13 "; + r14b -> "+R14 "; current -> "" end. diff --git a/lib/observer/test/etop_SUITE.erl b/lib/observer/test/etop_SUITE.erl index a277453620..06577f82cc 100644 --- a/lib/observer/test/etop_SUITE.erl +++ b/lib/observer/test/etop_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 diff --git a/lib/observer/vsn.mk b/lib/observer/vsn.mk index fa104ede01..4eb10ae4e8 100644 --- a/lib/observer/vsn.mk +++ b/lib/observer/vsn.mk @@ -1 +1 @@ -OBSERVER_VSN = 1.0 +OBSERVER_VSN = 1.1 diff --git a/lib/odbc/c_src/Makefile.in b/lib/odbc/c_src/Makefile.in index 3a96a53ef8..5b37b352fb 100644 --- a/lib/odbc/c_src/Makefile.in +++ b/lib/odbc/c_src/Makefile.in @@ -128,12 +128,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt ifdef EXE_TARGET - $(INSTALL_DIR) $(RELSYSDIR)/c_src - $(INSTALL_DATA) $(C_FILES) $(H_FILES) $(RELSYSDIR)/c_src - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_DIR) $(RELSYSDIR)/priv/obj - $(INSTALL_PROGRAM) $(EXE_TARGET) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/c_src" + $(INSTALL_DATA) $(C_FILES) $(H_FILES) "$(RELSYSDIR)/c_src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/obj" + $(INSTALL_PROGRAM) $(EXE_TARGET) "$(RELSYSDIR)/priv/bin" endif release_docs_spec: diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index ab2d7fe210..6d4460014f 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -176,7 +176,7 @@ static void encode_column_dyn(db_column column, int column_nr, static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, SQLSMALLINT decimal_digits, db_state *state); static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params, - int i, int j); + int i, int j, int num_param_values); /*------------- Erlang port communication functions ----------------------*/ @@ -212,6 +212,7 @@ static db_column * alloc_column_buffer(int n); static void free_column_buffer(db_column **columns, int n); static void free_params(param_array **params, int cols); static void clean_state(db_state *state); +static SQLLEN* alloc_strlen_indptr(int n, int val); /* ------------- Init/map/bind/retrive functions -------------------------*/ @@ -1157,7 +1158,7 @@ static db_result_msg encode_out_params(db_state *state, break; case SQL_C_BIT: ei_x_encode_atom(&dynamic_buffer(state), - ((Boolean*)values)[j]==TRUE?"true":"false"); + ((byte*)values)[j]==TRUE?"true":"false"); break; default: ei_x_encode_atom(&dynamic_buffer(state), "error"); @@ -1579,37 +1580,48 @@ static void encode_data_type(SQLSMALLINT sql_type, SQLINTEGER size, } static Boolean decode_params(db_state *state, byte *buffer, int *index, param_array **params, - int i, int j) + int i, int j, int num_param_values) { int erl_type, size; long bin_size, l64; long val; param_array* param; TIMESTAMP_STRUCT* ts; + char atomarray[MAXATOMLEN+1]; ei_get_type(buffer, index, &erl_type, &size); param = &(*params)[i]; + if(erl_type == ERL_ATOM_EXT) { + ei_decode_atom(buffer, index, atomarray); + if(strncmp(atomarray, "null", 4) == 0 ) { + param->offset += param->type.len; + + if(!param->type.strlen_or_indptr_array) + param->type.strlen_or_indptr_array = alloc_strlen_indptr(num_param_values, param->type.len); + + param->type.strlen_or_indptr_array[j] = SQL_NULL_DATA; + return TRUE; + } + } + switch (param->type.c) { case SQL_C_CHAR: if (binary_strings(state)) { ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size); param->offset += param->type.len; - param->type.strlen_or_indptr_array[j] = SQL_NTS; } else { if(erl_type != ERL_STRING_EXT) { return FALSE; } ei_decode_string(buffer, index, &(param->values.string[param->offset])); param->offset += param->type.len; - param->type.strlen_or_indptr_array[j] = SQL_NTS; } break; case SQL_C_WCHAR: ei_decode_binary(buffer, index, &(param->values.string[param->offset]), &bin_size); param->offset += param->type.len; - param->type.strlen_or_indptr_array[j] = SQL_NTS; break; case SQL_C_TYPE_TIMESTAMP: ts = (TIMESTAMP_STRUCT*) param->values.string; @@ -1661,9 +1673,13 @@ static Boolean decode_params(db_state *state, byte *buffer, int *index, param_ar if((erl_type != ERL_ATOM_EXT)) { return FALSE; } - ei_decode_boolean(buffer, index, &(param->values.bool[j])); + if (strncmp((char*)atomarray,"true",4) == 0) + param->values.bool[j] = TRUE; + else if (strncmp((char*)atomarray,"false",5) == 0) + param->values.bool[j] = FALSE; + else + return -1; break; - default: return FALSE; } @@ -2014,6 +2030,18 @@ static void clean_state(db_state *state) nr_of_columns(state) = 0; } +/* Allocates and fill with default value StrLen_or_IndPtr array */ +static SQLLEN* alloc_strlen_indptr(int n, int val) +{ + int i; + SQLLEN* arr = (SQLLEN*)safe_malloc(n * sizeof(SQLLEN)); + + for( i=0; i < n; ++i ) + arr[i] = val; + + return arr; +} + /* ------------- Init/map/bind/retrive functions ------------------------*/ /* Prepare the state for a connection */ @@ -2118,7 +2146,7 @@ static void init_param_column(param_array *params, byte *buffer, int *index, (double *)safe_malloc(num_param_values * params->type.len); } else if(params->type.c == SQL_C_CHAR) { params->type.strlen_or_indptr_array - = (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER)); + = alloc_strlen_indptr(num_param_values, SQL_NTS); params->values.string = (byte *)safe_malloc(num_param_values * sizeof(byte)* params->type.len); @@ -2136,8 +2164,8 @@ static void init_param_column(param_array *params, byte *buffer, int *index, params->type.len = length+1; params->type.c = SQL_C_CHAR; params->type.col_size = (SQLUINTEGER)length; - params->type.strlen_or_indptr_array = - (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER)); + params->type.strlen_or_indptr_array + = alloc_strlen_indptr(num_param_values, SQL_NTS); params->values.string = (byte *)safe_malloc(num_param_values * sizeof(byte)* params->type.len); @@ -2159,8 +2187,8 @@ static void init_param_column(param_array *params, byte *buffer, int *index, params->type.len = (length+1)*sizeof(SQLWCHAR); params->type.c = SQL_C_WCHAR; params->type.col_size = (SQLUINTEGER)length; - params->type.strlen_or_indptr_array = - (SQLLEN*)safe_malloc(num_param_values * sizeof(SQLINTEGER)); + params->type.strlen_or_indptr_array + = alloc_strlen_indptr(num_param_values, SQL_NTS); params->values.string = (byte *)safe_malloc(num_param_values * sizeof(byte) * params->type.len); @@ -2201,10 +2229,10 @@ static void init_param_column(param_array *params, byte *buffer, int *index, case USER_BOOLEAN: params->type.sql = SQL_BIT; params->type.c = SQL_C_BIT; - params->type.len = sizeof(Boolean); + params->type.len = sizeof(byte); params->type.col_size = params->type.len; params->values.bool = - (Boolean *)safe_malloc(num_param_values * params->type.len); + (byte *)safe_malloc(num_param_values * params->type.len); break; } params->offset = 0; @@ -2411,7 +2439,7 @@ static param_array * bind_parameter_arrays(byte *buffer, int *index, } for (j = 0; j < num_param_values; j++) { - if(!decode_params(state, buffer, index, ¶ms, i, j)) { + if(!decode_params(state, buffer, index, ¶ms, i, j, num_param_values)) { /* An input parameter was not of the expected type */ free_params(¶ms, i); return params; diff --git a/lib/odbc/c_src/odbcserver.h b/lib/odbc/c_src/odbcserver.h index 56b6148777..a76cedf1af 100644 --- a/lib/odbc/c_src/odbcserver.h +++ b/lib/odbc/c_src/odbcserver.h @@ -156,7 +156,7 @@ typedef struct { byte *string; SQLINTEGER *integer; double *floating; - Boolean *bool; + byte *bool; }values; } param_array; diff --git a/lib/odbc/doc/src/Makefile b/lib/odbc/doc/src/Makefile index 0d456085f3..cd9bca0a55 100644 --- a/lib/odbc/doc/src/Makefile +++ b/lib/odbc/doc/src/Makefile @@ -125,13 +125,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/odbc/src/Makefile b/lib/odbc/src/Makefile index b48dd768c8..7ca82d784b 100644 --- a/lib/odbc/src/Makefile +++ b/lib/odbc/src/Makefile @@ -108,13 +108,13 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \ - $(RELSYSDIR)/ebin + "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/odbc/src/odbc.appup.src b/lib/odbc/src/odbc.appup.src index 853323da09..c7c83ea079 100644 --- a/lib/odbc/src/odbc.appup.src +++ b/lib/odbc/src/odbc.appup.src @@ -1,12 +1,8 @@ %% -*- erlang -*- {"%VSN%", [ - {"2.10.11", [{restart_application, odbc}]}, - {"2.10.10", [{restart_application, odbc}]}, - {"2.10.9", [{restart_application, odbc}]} + {<<"2\\.*">>, [{restart_application, odbc}]} ], [ - {"2.10.11", [{restart_application, odbc}]}, - {"2.10.10", [{restart_application, odbc}]}, - {"2.10.9", [{restart_application, odbc}]} + {<<"2\\.*">>, [{restart_application, odbc}]} ]}. diff --git a/lib/odbc/src/odbc.erl b/lib/odbc/src/odbc.erl index 36afd1abcf..9633b85cb2 100644 --- a/lib/odbc/src/odbc.erl +++ b/lib/odbc/src/odbc.erl @@ -451,7 +451,7 @@ init(Args) -> %% Start the port program (a c program) that utilizes the odbc driver case os:find_executable(?SERVERPROG, ?SERVERDIR) of FileName when is_list(FileName)-> - Port = open_port({spawn, FileName}, + Port = open_port({spawn, "\""++FileName++"\""}, [{packet, ?LENGTH_INDICATOR_SIZE}, binary, exit_status]), State = #state{listen_sockets = @@ -755,7 +755,10 @@ handle_info({'DOWN', _Ref, _Type, _Process, shutdown}, State) -> handle_info({'DOWN', _Ref, _Type, Process, Reason}, State) -> {stop, {stopped, {'EXIT', Process, Reason}}, State#state{reply_to = undefined}}; - + +handle_info({tcp_closed, Socket}, State = #state{odbc_socket=Socket, + state = disconnecting}) -> + {stop, normal, State}; %--------------------------------------------------------------------------- %% Catch all - throws away unknown messages (This could happen by "accident" %% so we do not want to crash, but we make a log entry as it is an @@ -942,9 +945,11 @@ fix_params({sql_bit, InOut, Values}) -> fix_params({'sql_timestamp', InOut, Values}) -> NewValues = case (catch - lists:map(fun({{Year,Month,Day},{Hour,Minute,Second}}) -> - {Year,Month,Day,Hour,Minute,Second} - end, Values)) of + lists:map( + fun({{Year,Month,Day},{Hour,Minute,Second}}) -> + {Year,Month,Day,Hour,Minute,Second}; + (null) -> null + end, Values)) of Result -> Result end, @@ -960,15 +965,15 @@ fix_inout(out) -> fix_inout(inout) -> ?INOUT. -string_terminate([Value| _ ] = Values) when is_list(Value)-> - case (catch - lists:map(fun(Str) -> Str ++ [?STR_TERMINATOR] end, Values)) of - Result -> - Result - end; -string_terminate([Value| _ ] = Values) when is_binary(Value)-> - case (catch - lists:map(fun(B) -> <<B/binary,0:16>> end, Values)) of +string_terminate(Values) -> + case (catch lists:map(fun string_terminate_value/1, Values)) of Result -> Result end. + +string_terminate_value(String) when is_list(String) -> + String ++ [?STR_TERMINATOR]; +string_terminate_value(Binary) when is_binary(Binary) -> + <<Binary/binary,0:16>>; +string_terminate_value(null) -> + null. diff --git a/lib/odbc/test/Makefile b/lib/odbc/test/Makefile index bc6449242e..84551f0eb1 100644 --- a/lib/odbc/test/Makefile +++ b/lib/odbc/test/Makefile @@ -101,8 +101,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/odbc/test/odbc_test_lib.erl b/lib/odbc/test/odbc_test_lib.erl index a8439d5fb6..e814cd2aca 100644 --- a/lib/odbc/test/odbc_test_lib.erl +++ b/lib/odbc/test/odbc_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,6 +29,7 @@ unique_table_name() -> lists:reverse(lists:foldl(fun($@, Acc) -> [$t, $A |Acc] ; + ($-,Acc) -> Acc; (X, Acc) -> [X |Acc] end, [], atom_to_list(node()))). diff --git a/lib/odbc/vsn.mk b/lib/odbc/vsn.mk index fb6e208a52..3bb2fe5bce 100644 --- a/lib/odbc/vsn.mk +++ b/lib/odbc/vsn.mk @@ -1 +1 @@ -ODBC_VSN = 2.10.12 +ODBC_VSN = 2.10.13 diff --git a/lib/orber/COSS/CosNaming/Makefile b/lib/orber/COSS/CosNaming/Makefile index 064447f148..a61fcd08a6 100644 --- a/lib/orber/COSS/CosNaming/Makefile +++ b/lib/orber/COSS/CosNaming/Makefile @@ -141,11 +141,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/COSS/CosNaming - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(IDL_FILE) $(RELSYSDIR)/COSS/CosNaming - $(INSTALL_DATA) $(GEN_FILES) $(RELSYSDIR)/COSS/CosNaming + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/COSS/CosNaming" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(IDL_FILE) "$(RELSYSDIR)/COSS/CosNaming" + $(INSTALL_DATA) $(GEN_FILES) "$(RELSYSDIR)/COSS/CosNaming" release_docs_spec: diff --git a/lib/orber/c_src/Makefile.in b/lib/orber/c_src/Makefile.in index 56f0d57545..5953d41ff3 100644 --- a/lib/orber/c_src/Makefile.in +++ b/lib/orber/c_src/Makefile.in @@ -89,23 +89,23 @@ include $(ERL_TOP)/make/otp_release_targets.mk ifeq ($(findstring win32,$(TARGET)),win32) release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/src - $(INSTALL_DIR) $(RELSYSDIR)/priv/include - $(INSTALL_PROGRAM) $(CC_FILES) $(RELSYSDIR)/priv/src - $(INSTALL_PROGRAM) $(HH_FILES) $(RELSYSDIR)/priv/include + $(INSTALL_DIR) "$(RELSYSDIR)/priv/src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/include" + $(INSTALL_PROGRAM) $(CC_FILES) "$(RELSYSDIR)/priv/src" + $(INSTALL_PROGRAM) $(HH_FILES) "$(RELSYSDIR)/priv/include" else ifeq ($(findstring vxworks,$(TARGET)),vxworks) release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/priv/src - $(INSTALL_DIR) $(RELSYSDIR)/priv/include - $(INSTALL_PROGRAM) $(CC_FILES) $(RELSYSDIR)/priv/src - $(INSTALL_PROGRAM) $(HH_FILES) $(RELSYSDIR)/priv/include + $(INSTALL_DIR) "$(RELSYSDIR)/priv/src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/include" + $(INSTALL_PROGRAM) $(CC_FILES) "$(RELSYSDIR)/priv/src" + $(INSTALL_PROGRAM) $(HH_FILES) "$(RELSYSDIR)/priv/include" else release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/src - $(INSTALL_DIR) $(RELSYSDIR)/priv/include - $(INSTALL_DATA) $(CC_FILES) $(RELSYSDIR)/priv/src - $(INSTALL_DATA) $(HH_FILES) $(RELSYSDIR)/priv/include + $(INSTALL_DIR) "$(RELSYSDIR)/priv/src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/include" + $(INSTALL_DATA) $(CC_FILES) "$(RELSYSDIR)/priv/src" + $(INSTALL_DATA) $(HH_FILES) "$(RELSYSDIR)/priv/include" endif endif diff --git a/lib/orber/doc/src/Makefile b/lib/orber/doc/src/Makefile index 68fbe3dce0..c429c1d80b 100644 --- a/lib/orber/doc/src/Makefile +++ b/lib/orber/doc/src/Makefile @@ -164,13 +164,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/orber/doc/src/ch_install.xml b/lib/orber/doc/src/ch_install.xml index de9c0e3a9d..89c0e19173 100644 --- a/lib/orber/doc/src/ch_install.xml +++ b/lib/orber/doc/src/ch_install.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2011</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -396,13 +396,13 @@ nodeB@hostB> orber:start(). <row> <cell align="left" valign="middle">ssl_server_options</cell> <cell align="left" valign="middle">list()</cell> - <cell align="left" valign="middle">See the <seealso marker="ssl:ssl">SSL</seealso> application + <cell align="left" valign="middle">See the <seealso marker="ssl:ssl">SSL application</seealso> for valid options.</cell> </row> <row> <cell align="left" valign="middle">ssl_client_options</cell> <cell align="left" valign="middle">list()</cell> - <cell align="left" valign="middle">See the <seealso marker="ssl:ssl">SSL</seealso> application + <cell align="left" valign="middle">See the <seealso marker="ssl:ssl">SSL application</seealso> for valid options.</cell> </row> <row> @@ -631,9 +631,19 @@ nodeB@hostB> orber:start(). <c>{local, DefaultNATPort, [{Port, NATPort}]}</c>. See also <seealso marker="ch_install#firewall">Firewall Configuration</seealso>.</item> <tag><em>ssl_server_options</em></tag> - <item>the file path to a server side CA certificate.</item> + <item>A list of the SSL options when Orber is the server. + In general it's just to remove the 'ssl_server_' prefix from the old options, + i.e. <c>ssl_server_verify</c> will just be <c>verify</c> in this option list. + See the <seealso marker="ssl:ssl">SSL application</seealso> for the correct list of possible + options and their values. + </item> <tag><em>ssl_client_options</em></tag> - <item>The path to a file containing a chain of PEM encoded certificates.</item> + <item>A list of the SSL options when Orber is the client. + In general it's just to remove the <c>ssl_client_</c> prefix from the old options, + i.e. <c>ssl_client_depth</c> will just be <c>depth</c> in this option list. + See the <seealso marker="ssl:ssl">SSL application</seealso> for the correct list of possible + options and their values. + </item> <tag><em>iiop_ssl_out_keepalive</em></tag> <item>Enables periodic transmission on a connected socket, when no other data is being exchanged. If the other end does not respond, the diff --git a/lib/orber/doc/src/notes.xml b/lib/orber/doc/src/notes.xml index 35ee5e35dd..72cd808d20 100644 --- a/lib/orber/doc/src/notes.xml +++ b/lib/orber/doc/src/notes.xml @@ -32,7 +32,29 @@ <file>notes.xml</file> </header> - <section><title>Orber 3.6.23</title> + <section><title>Orber 3.6.24</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix number of arguments in orber dbg printout</p> + <p> + Own Id: OTP-9887</p> + </item> + <item> + <p> The descriptions of <c>ssl_server_options</c> and + <c>ssl_client_options</c> are corrected.<br/> Seq. Id: + seq12018 </p> + <p> + Own Id: OTP-9966 Aux Id: OTP-9773 </p> + </item> + </list> + </section> + +</section> + +<section><title>Orber 3.6.23</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/orber/examples/Stack/Makefile b/lib/orber/examples/Stack/Makefile index b985f348fa..b03d7c3b86 100644 --- a/lib/orber/examples/Stack/Makefile +++ b/lib/orber/examples/Stack/Makefile @@ -118,8 +118,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/Stack - $(INSTALL_DATA) $(ERL_FILES) $(JAVA_FILES) $(CPP_FILES) $(IDL_FILES) $(RELSYSDIR)/examples/Stack + $(INSTALL_DIR) "$(RELSYSDIR)/examples/Stack" + $(INSTALL_DATA) $(ERL_FILES) $(JAVA_FILES) $(CPP_FILES) $(IDL_FILES) "$(RELSYSDIR)/examples/Stack" release_docs_spec: diff --git a/lib/orber/java_src/Orber/Makefile b/lib/orber/java_src/Orber/Makefile index 49da975a8b..4e52ed3635 100644 --- a/lib/orber/java_src/Orber/Makefile +++ b/lib/orber/java_src/Orber/Makefile @@ -63,8 +63,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/java_src/Orber - $(INSTALL_DATA) $(JAVA_FILES) $(RELSYSDIR)/java_src/Orber + $(INSTALL_DIR) "$(RELSYSDIR)/java_src/Orber" + $(INSTALL_DATA) $(JAVA_FILES) "$(RELSYSDIR)/java_src/Orber" release_docs_spec: diff --git a/lib/orber/priv/Makefile b/lib/orber/priv/Makefile index af82177466..7957f7bbf0 100644 --- a/lib/orber/priv/Makefile +++ b/lib/orber/priv/Makefile @@ -58,8 +58,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(HELP_FILES) $(HTML_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(HELP_FILES) $(HTML_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/orber/src/Makefile b/lib/orber/src/Makefile index d2e98686da..a57d6d948b 100644 --- a/lib/orber/src/Makefile +++ b/lib/orber/src/Makefile @@ -254,12 +254,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(GEN_HRL_FILES_LOC) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(GEN_HRL_FILES_EXT) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(GEN_HRL_FILES_LOC) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXTERNAL_HRL_FILES) $(GEN_HRL_FILES_EXT) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/orber/src/orber_ifr.erl b/lib/orber/src/orber_ifr.erl index 6799bc1db4..4004ffa005 100644 --- a/lib/orber/src/orber_ifr.erl +++ b/lib/orber/src/orber_ifr.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/orber/src/orber_tb.erl b/lib/orber/src/orber_tb.erl index d3e3a8497d..26a36df0e9 100644 --- a/lib/orber/src/orber_tb.erl +++ b/lib/orber/src/orber_tb.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 diff --git a/lib/orber/test/Makefile b/lib/orber/test/Makefile index d4be009af3..8eef1934a4 100644 --- a/lib/orber/test/Makefile +++ b/lib/orber/test/Makefile @@ -209,12 +209,12 @@ release_spec: release_docs_spec: release_tests_spec: tests - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(IDL_FILES) $(TEST_SPEC_FILE) $(COVER_FILE) \ - $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_TARGET_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - $(INSTALL_DIR) $(RELSYSDIR)/$(IDLOUTDIR) + $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_TARGET_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELSYSDIR)/$(IDLOUTDIR)" $(INSTALL_DATA) $(GEN_TARGET_FILES) $(GEN_FILES) \ - $(RELSYSDIR)/$(IDLOUTDIR) + "$(RELSYSDIR)/$(IDLOUTDIR)" diff --git a/lib/orber/test/orber_SUITE.erl b/lib/orber/test/orber_SUITE.erl index 72b0db1e0d..26dc656b63 100644 --- a/lib/orber/test/orber_SUITE.erl +++ b/lib/orber/test/orber_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/orber/test/orber_acl_SUITE.erl b/lib/orber/test/orber_acl_SUITE.erl index 49107cde84..ab2c2c872c 100644 --- a/lib/orber/test/orber_acl_SUITE.erl +++ b/lib/orber/test/orber_acl_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 diff --git a/lib/orber/test/orber_test_lib.erl b/lib/orber/test/orber_test_lib.erl index 0ddde49cd6..2bfa1ef871 100644 --- a/lib/orber/test/orber_test_lib.erl +++ b/lib/orber/test/orber_test_lib.erl @@ -282,6 +282,7 @@ starter(Host, Name, Args) -> vxworks -> test_server:start_node(Name, slave, [{args,Args}]); _ -> + io:format("slave:start_link(~p,~p,~p).~n",[Host,Name,Args]), slave:start_link(Host, Name, Args) end. @@ -403,8 +404,8 @@ create_paths() -> filename:join(Path, "idl_output") ++ " -pa " ++ filename:join(Path, "all_SUITE_data") ++ - " -pa " ++ - filename:dirname(code:which(orber)). + " -pa \"" ++ + filename:dirname(code:which(orber))++"\"". %%------------------------------------------------------------ %% function : destroy_node diff --git a/lib/orber/vsn.mk b/lib/orber/vsn.mk index 0eac1e1410..f891440600 100644 --- a/lib/orber/vsn.mk +++ b/lib/orber/vsn.mk @@ -1,3 +1,3 @@ -ORBER_VSN = 3.6.23 +ORBER_VSN = 3.6.24 diff --git a/lib/os_mon/c_src/Makefile.in b/lib/os_mon/c_src/Makefile.in index bac0413ece..45dc01e993 100644 --- a/lib/os_mon/c_src/Makefile.in +++ b/lib/os_mon/c_src/Makefile.in @@ -131,10 +131,10 @@ ifeq ($(findstring vxworks_simso,$(TARGET)),vxworks_simso) release_spec: else release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(C_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_PROGRAM) $(TARGET_FILES) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(C_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_PROGRAM) $(TARGET_FILES) "$(RELSYSDIR)/priv/bin" endif release_docs_spec: diff --git a/lib/os_mon/c_src/cpu_sup.c b/lib/os_mon/c_src/cpu_sup.c index 9c5f9a6aa5..a0432b3093 100644 --- a/lib/os_mon/c_src/cpu_sup.c +++ b/lib/os_mon/c_src/cpu_sup.c @@ -458,8 +458,18 @@ static void error(char* err_msg) { * if we get error here we have trouble, * silence unnecessary warnings */ - if(write(FD_ERR, err_msg, strlen(err_msg))); - if(write(FD_ERR, "\n", 1)); + char buffer[256] = "[os_mon] cpu supervisor port (cpu_sup): "; + int i = strlen(buffer), j = 0; + int n = strlen(err_msg); + + while(i < 253 && j < n) { + buffer[i++] = err_msg[j++]; + } + buffer[i++] = '\r'; + buffer[i++] = '\n'; + + /* try to use one write only */ + if(write(FD_ERR, buffer, i)); exit(-1); } diff --git a/lib/os_mon/c_src/memsup.c b/lib/os_mon/c_src/memsup.c index b6e417eaa0..593a066f98 100644 --- a/lib/os_mon/c_src/memsup.c +++ b/lib/os_mon/c_src/memsup.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1996-2009. All Rights Reserved. + * Copyright Ericsson AB 1996-2012. 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 @@ -493,7 +493,7 @@ get_basic_mem(unsigned long *tot, unsigned long *used, unsigned long *pagesize){ #elif defined(__linux__) && !defined(_SC_AVPHYS_PAGES) memory_ext me; if (get_mem_procfs(&me) < 0) { - print_error("ProcFS read error."); + print_error("ProcFS read error"); exit(1); } *tot = me.total; @@ -582,7 +582,7 @@ message_loop(int erlin_fd) * Wait for command from Erlang */ if ((res = read(erlin_fd, &cmdLen, 1)) < 0) { - print_error("Error reading from Erlang."); + print_error("Error reading from Erlang"); return; } @@ -603,19 +603,19 @@ message_loop(int erlin_fd) break; case 0: - print_error("Erlang has closed."); + print_error("Erlang has closed"); return; default: - print_error("Error reading from Erlang."); + print_error("Error reading from Erlang"); return; } /* switch() */ } else { /* cmdLen != 1 */ - print_error("Invalid command length (%d) received.", cmdLen); + print_error("Invalid command length (%d) received", cmdLen); return; } } else { /* Erlang end closed */ - print_error("Erlang has closed."); + print_error("Erlang has closed"); return; } } @@ -641,15 +641,12 @@ static void print_error(const char *format,...) { va_list args; + char buffer[256]; va_start(args, format); - fprintf(stderr, "%s: ", program_name); - vfprintf(stderr, format, args); + vsnprintf(buffer, 256, format, args); va_end(args); - fprintf(stderr, " \n"); + /* try to use one write only */ + fprintf(stderr, "[os_mon] memory supervisor port (memsup): %s\r\n", buffer); + fflush(stderr); } - - - - - diff --git a/lib/os_mon/c_src/win32sysinfo.c b/lib/os_mon/c_src/win32sysinfo.c index 2a155aae87..9d4587393f 100644 --- a/lib/os_mon/c_src/win32sysinfo.c +++ b/lib/os_mon/c_src/win32sysinfo.c @@ -89,6 +89,7 @@ typedef BOOL (WINAPI *tfpGetDiskFreeSpaceEx)(LPCTSTR, PULARGE_INTEGER,PULARGE_IN static tfpGetDiskFreeSpaceEx fpGetDiskFreeSpaceEx; +static void print_error(const char *msg); static void return_answer(char* value) { @@ -98,7 +99,7 @@ return_answer(char* value) res = write(1,(char*) &bytes,1); if (res != 1) { - fprintf(stderr,"win32sysinfo:Error writing to pipe"); + print_error("Error writing to pipe"); exit(1); } @@ -107,9 +108,8 @@ return_answer(char* value) while (left > 0) { res = write(1, value+bytes-left, left); - if (res <= 0) - { - fprintf(stderr,"win32sysinfo:Error writing to pipe"); + if (res <= 0) { + print_error("Error writing to pipe"); exit(1); } left -= res; @@ -248,7 +248,6 @@ message_loop() char cmd[512]; int res; - fprintf(stderr,"in message_loop\n"); /* Startup ACK. */ return_answer(OK); while (1) @@ -257,12 +256,12 @@ message_loop() * Wait for command from Erlang */ if ((res = read(0, &cmdLen, 1)) < 0) { - fprintf(stderr,"win32sysinfo:Error reading from Erlang."); + print_error("Error reading from Erlang"); return; } if (res != 1){ /* Exactly one byte read ? */ - fprintf(stderr,"win32sysinfo:Erlang has closed."); + print_error("Erlang has closed"); return; } if ((res = read(0, &cmd, cmdLen)) == cmdLen){ @@ -291,11 +290,11 @@ message_loop() return_answer("xEND"); } else if (res == 0) { - fprintf(stderr,"win32sysinfo:Erlang has closed."); + print_error("Erlang has closed"); return; } else { - fprintf(stderr,"win32sysinfo:Error reading from Erlang."); + print_error("Error reading from Erlang"); return; } } @@ -309,10 +308,9 @@ int main(int argc, char ** argv){ message_loop(); return 0; } - - - - - - - +static void +print_error(const char *msg) { + /* try to use one write only */ + fprintf(stderr, "[os_mon] win32 supervisor port (win32sysinfo): %s\r\n", msg); + fflush(stderr); +} diff --git a/lib/os_mon/doc/src/Makefile b/lib/os_mon/doc/src/Makefile index c9765749c9..aa8fb4b60c 100644 --- a/lib/os_mon/doc/src/Makefile +++ b/lib/os_mon/doc/src/Makefile @@ -109,16 +109,16 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/os_mon/doc/src/notes.xml b/lib/os_mon/doc/src/notes.xml index b459e31fa5..6674d7b722 100644 --- a/lib/os_mon/doc/src/notes.xml +++ b/lib/os_mon/doc/src/notes.xml @@ -30,6 +30,25 @@ </header> <p>This document describes the changes made to the OS_Mon application.</p> +<section><title>Os_Mon 2.2.9</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix segmentation fault in memsup</p> + <p> + when /proc/meminfo does not include information about + buffers/cache (for instance inside OpenVZ container) + (Thanks to Anton Vorobev)</p> + <p> + Own Id: OTP-9913</p> + </item> + </list> + </section> + +</section> + <section><title>Os_Mon 2.2.8</title> <section><title>Improvements and New Features</title> diff --git a/lib/os_mon/mibs/Makefile b/lib/os_mon/mibs/Makefile index 655190edf4..a6d8ab5042 100644 --- a/lib/os_mon/mibs/Makefile +++ b/lib/os_mon/mibs/Makefile @@ -88,13 +88,13 @@ $(SNMP_BIN_TARGET_DIR)/OTP-OS-MON-MIB.bin: \ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/mibs - $(INSTALL_DIR) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DATA) $(MIB_FILES) $(FUNCS_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(V1_MIB_FILES) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_TARGETS) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/priv/mibs - $(INSTALL_DATA) $(BIN_TARGETS) $(RELSYSDIR)/priv/mibs + $(INSTALL_DIR) "$(RELSYSDIR)/mibs" + $(INSTALL_DIR) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DATA) $(MIB_FILES) $(FUNCS_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(V1_MIB_FILES) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_TARGETS) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/mibs" + $(INSTALL_DATA) $(BIN_TARGETS) "$(RELSYSDIR)/priv/mibs" release_docs_spec: diff --git a/lib/os_mon/src/Makefile b/lib/os_mon/src/Makefile index 9a75446a89..a8ad26f6a1 100644 --- a/lib/os_mon/src/Makefile +++ b/lib/os_mon/src/Makefile @@ -94,11 +94,11 @@ $(EBIN)/memsup.$(EMULATOR): $(MEMSUP_HRL) include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl index 308cd1b7fa..c7abb7a386 100644 --- a/lib/os_mon/src/disksup.erl +++ b/lib/os_mon/src/disksup.erl @@ -43,24 +43,24 @@ start_link() -> gen_server:start_link({local, disksup}, disksup, [], []). get_disk_data() -> - os_mon:call(disksup, get_disk_data). + os_mon:call(disksup, get_disk_data, infinity). get_check_interval() -> - os_mon:call(disksup, get_check_interval). + os_mon:call(disksup, get_check_interval, infinity). set_check_interval(Minutes) -> case param_type(disk_space_check_interval, Minutes) of true -> - os_mon:call(disksup, {set_check_interval, Minutes}); + os_mon:call(disksup, {set_check_interval, Minutes}, infinity); false -> erlang:error(badarg) end. get_almost_full_threshold() -> - os_mon:call(disksup, get_almost_full_threshold). + os_mon:call(disksup, get_almost_full_threshold, infinity). set_almost_full_threshold(Float) -> case param_type(disk_almost_full_threshold, Float) of true -> - os_mon:call(disksup, {set_almost_full_threshold, Float}); + os_mon:call(disksup, {set_almost_full_threshold, Float}, infinity); false -> erlang:error(badarg) end. diff --git a/lib/os_mon/src/memsup.erl b/lib/os_mon/src/memsup.erl index 49533da1f7..54771b4703 100644 --- a/lib/os_mon/src/memsup.erl +++ b/lib/os_mon/src/memsup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -112,7 +112,7 @@ get_helper_timeout() -> set_helper_timeout(Seconds) -> case param_type(memsup_helper_timeout, Seconds) of true -> - os_mon:call(memsup, {set_helper_timeout, Seconds}); + os_mon:call(memsup, {set_helper_timeout, Seconds}, infinity); false -> erlang:error(badarg) end. diff --git a/lib/os_mon/src/os_mon.erl b/lib/os_mon/src/os_mon.erl index 3098c38808..2b6cd7c498 100644 --- a/lib/os_mon/src/os_mon.erl +++ b/lib/os_mon/src/os_mon.erl @@ -85,13 +85,13 @@ open_port(Name, Opts) -> %% Check os_mon*/priv/bin/Name case filelib:is_regular(ReleasedPath) of true -> - erlang:open_port({spawn, ReleasedPath}, Opts); + erlang:open_port({spawn, "\""++ReleasedPath++"\""}, Opts); false -> %% Use os_mon*/priv/bin/Arch/Name ArchPath = filename:join( [PrivDir,"bin",erlang:system_info(system_architecture),Name]), - erlang:open_port({spawn, ArchPath}, Opts) + erlang:open_port({spawn, "\""++ArchPath++"\""}, Opts) end. diff --git a/lib/os_mon/src/os_mon_sysinfo.erl b/lib/os_mon/src/os_mon_sysinfo.erl index 080885d5d6..fb732d1e3a 100644 --- a/lib/os_mon/src/os_mon_sysinfo.erl +++ b/lib/os_mon/src/os_mon_sysinfo.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/os_mon/test/Makefile b/lib/os_mon/test/Makefile index a240640f92..954cfef7fb 100644 --- a/lib/os_mon/test/Makefile +++ b/lib/os_mon/test/Makefile @@ -84,9 +84,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) os_mon.spec os_mon.cover $(EMAKEFILE) $(SOURCE) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) os_mon.spec os_mon.cover $(EMAKEFILE) $(SOURCE) "$(RELSYSDIR)" -## tar chf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) +## tar chf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/os_mon/vsn.mk b/lib/os_mon/vsn.mk index 89dfa59dd9..b92bdf3c1a 100644 --- a/lib/os_mon/vsn.mk +++ b/lib/os_mon/vsn.mk @@ -1 +1 @@ -OS_MON_VSN = 2.2.8 +OS_MON_VSN = 2.2.9 diff --git a/lib/otp_mibs/doc/src/Makefile b/lib/otp_mibs/doc/src/Makefile index bd2810034b..16a5bb39b4 100644 --- a/lib/otp_mibs/doc/src/Makefile +++ b/lib/otp_mibs/doc/src/Makefile @@ -102,14 +102,14 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/otp_mibs/doc/src/notes.xml b/lib/otp_mibs/doc/src/notes.xml index 71e33fceb9..3d3a1272a4 100644 --- a/lib/otp_mibs/doc/src/notes.xml +++ b/lib/otp_mibs/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/otp_mibs/mibs/Makefile b/lib/otp_mibs/mibs/Makefile index 5e59824d3f..7ef8480b3e 100644 --- a/lib/otp_mibs/mibs/Makefile +++ b/lib/otp_mibs/mibs/Makefile @@ -76,13 +76,13 @@ v1/%.mib.v1: %.mib include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/mibs - $(INSTALL_DIR) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DATA) $(MIB_FILES) $(FUNCS_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(V1_MIB_FILES) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_TARGETS) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/priv/mibs - $(INSTALL_DATA) $(BIN_TARGETS) $(RELSYSDIR)/priv/mibs + $(INSTALL_DIR) "$(RELSYSDIR)/mibs" + $(INSTALL_DIR) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DATA) $(MIB_FILES) $(FUNCS_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(V1_MIB_FILES) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_TARGETS) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/mibs" + $(INSTALL_DATA) $(BIN_TARGETS) "$(RELSYSDIR)/priv/mibs" release_docs_spec: diff --git a/lib/otp_mibs/src/Makefile b/lib/otp_mibs/src/Makefile index 833a439adb..c9c40fec3a 100644 --- a/lib/otp_mibs/src/Makefile +++ b/lib/otp_mibs/src/Makefile @@ -95,10 +95,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGETS) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGETS) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/parsetools/doc/src/Makefile b/lib/parsetools/doc/src/Makefile index 6e693e0cf0..464ea66af0 100644 --- a/lib/parsetools/doc/src/Makefile +++ b/lib/parsetools/doc/src/Makefile @@ -105,14 +105,14 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/parsetools/src/Makefile b/lib/parsetools/src/Makefile index 89e079e411..e228ee88ef 100644 --- a/lib/parsetools/src/Makefile +++ b/lib/parsetools/src/Makefile @@ -89,12 +89,12 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/parsetools/test/Makefile b/lib/parsetools/test/Makefile index 624c4e6975..430386bfe5 100644 --- a/lib/parsetools/test/Makefile +++ b/lib/parsetools/test/Makefile @@ -70,9 +70,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) parsetools.spec parsetools.cover $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - # @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) parsetools.spec parsetools.cover $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + # @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/percept/doc/src/Makefile b/lib/percept/doc/src/Makefile index f0d43c5a01..e749103783 100644 --- a/lib/percept/doc/src/Makefile +++ b/lib/percept/doc/src/Makefile @@ -175,15 +175,15 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTML_EXAMPLE_FILES) $(HTML_STYLESHEET_FILES) \ $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/percept/priv/Makefile b/lib/percept/priv/Makefile index 7c37ccd98b..4d7942c49c 100644 --- a/lib/percept/priv/Makefile +++ b/lib/percept/priv/Makefile @@ -77,20 +77,20 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt # Finished - $(INSTALL_DIR) $(RELSYSDIR)/priv/logs - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/htdocs - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/priv/server_root/htdocs - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/conf - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/priv/server_root/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/scripts - $(INSTALL_DATA) $(SCRIPT_FILES) $(RELSYSDIR)/priv/server_root/scripts - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/css - $(INSTALL_DATA) $(CSS_FILES) $(RELSYSDIR)/priv/server_root/css - $(INSTALL_DIR) $(RELSYSDIR)/priv/server_root/images - $(INSTALL_DATA) $(IMAGE_FILES) $(RELSYSDIR)/priv/server_root/images - $(INSTALL_DIR) $(RELSYSDIR)/priv/fonts - $(INSTALL_DATA) $(FONT_FILES) $(RELSYSDIR)/priv/fonts + $(INSTALL_DIR) "$(RELSYSDIR)/priv/logs" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/htdocs" + $(INSTALL_DATA) $(HTDOCS_FILES) "$(RELSYSDIR)/priv/server_root/htdocs" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/conf" + $(INSTALL_DATA) $(CONF_FILES) "$(RELSYSDIR)/priv/server_root/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/scripts" + $(INSTALL_DATA) $(SCRIPT_FILES) "$(RELSYSDIR)/priv/server_root/scripts" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/css" + $(INSTALL_DATA) $(CSS_FILES) "$(RELSYSDIR)/priv/server_root/css" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/server_root/images" + $(INSTALL_DATA) $(IMAGE_FILES) "$(RELSYSDIR)/priv/server_root/images" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/fonts" + $(INSTALL_DATA) $(FONT_FILES) "$(RELSYSDIR)/priv/fonts" release_docs_spec: diff --git a/lib/percept/src/Makefile b/lib/percept/src/Makefile index 5dfc72575a..4b9a10cdb8 100644 --- a/lib/percept/src/Makefile +++ b/lib/percept/src/Makefile @@ -93,12 +93,12 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/percept/test/Makefile b/lib/percept/test/Makefile index d927386d1c..ee70966b01 100644 --- a/lib/percept/test/Makefile +++ b/lib/percept/test/Makefile @@ -81,10 +81,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) percept.spec percept.cover $(EMAKEFILE) $(SOURCE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) percept.spec percept.cover $(EMAKEFILE) $(SOURCE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/pman/doc/src/Makefile b/lib/pman/doc/src/Makefile index 4e116cb23e..ec759091ab 100644 --- a/lib/pman/doc/src/Makefile +++ b/lib/pman/doc/src/Makefile @@ -103,14 +103,14 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/pman/doc/src/notes.xml b/lib/pman/doc/src/notes.xml index 407a10a50a..778f960004 100644 --- a/lib/pman/doc/src/notes.xml +++ b/lib/pman/doc/src/notes.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -30,6 +30,26 @@ </header> <p>This document describes the changes made to the Pman application.</p> +<section><title>Pman 2.7.1.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>The GS applications is now deprecated and will be + removed in the R16 release. The following GS-based + applications have been superseded by the Observer + application and will removed in R16: Appmon, Pman, + Tv.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9907</p> + </item> + </list> + </section> + +</section> + <section><title>Pman 2.7.1.1</title> <section><title>Improvements and New Features</title> diff --git a/lib/pman/doc/src/pman.xml b/lib/pman/doc/src/pman.xml index 5f95aecc04..48eaf9167c 100644 --- a/lib/pman/doc/src/pman.xml +++ b/lib/pman/doc/src/pman.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/pman/doc/src/pman_chapter.xml b/lib/pman/doc/src/pman_chapter.xml index 8668628bfa..ce4adbec45 100644 --- a/lib/pman/doc/src/pman_chapter.xml +++ b/lib/pman/doc/src/pman_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/pman/priv/Makefile b/lib/pman/priv/Makefile index 43f9d45d94..a89278fd3a 100644 --- a/lib/pman/priv/Makefile +++ b/lib/pman/priv/Makefile @@ -50,8 +50,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TOOLBAR_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TOOLBAR_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/pman/src/Makefile b/lib/pman/src/Makefile index e573e57220..2cd1c64c49 100644 --- a/lib/pman/src/Makefile +++ b/lib/pman/src/Makefile @@ -103,10 +103,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(TOOLBOX_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(TARGET_TOOLBOX_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(TOOLBOX_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(TARGET_TOOLBOX_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/pman/src/pman_buf_converter.erl b/lib/pman/src/pman_buf_converter.erl index c6cd8ec9ee..c8b3fe37aa 100644 --- a/lib/pman/src/pman_buf_converter.erl +++ b/lib/pman/src/pman_buf_converter.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/pman/src/pman_buf_printer.erl b/lib/pman/src/pman_buf_printer.erl index 62407648d5..3284c57559 100644 --- a/lib/pman/src/pman_buf_printer.erl +++ b/lib/pman/src/pman_buf_printer.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/pman/src/pman_main.erl b/lib/pman/src/pman_main.erl index adb5b65cd4..2f51284293 100644 --- a/lib/pman/src/pman_main.erl +++ b/lib/pman/src/pman_main.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/pman/src/pman_module_info.erl b/lib/pman/src/pman_module_info.erl index 58f956aa42..944fd4a462 100644 --- a/lib/pman/src/pman_module_info.erl +++ b/lib/pman/src/pman_module_info.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/pman/src/pman_shell.erl b/lib/pman/src/pman_shell.erl index 393ef5f84d..2d2b8ce000 100644 --- a/lib/pman/src/pman_shell.erl +++ b/lib/pman/src/pman_shell.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/pman/src/pman_tool.erl b/lib/pman/src/pman_tool.erl index c548e17d00..36382745af 100644 --- a/lib/pman/src/pman_tool.erl +++ b/lib/pman/src/pman_tool.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/pman/src/pman_win.erl b/lib/pman/src/pman_win.erl index 9dd446cd81..350bc2dd7f 100644 --- a/lib/pman/src/pman_win.erl +++ b/lib/pman/src/pman_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/pman/vsn.mk b/lib/pman/vsn.mk index a62a9d7c89..13dce1a7a5 100644 --- a/lib/pman/vsn.mk +++ b/lib/pman/vsn.mk @@ -1 +1 @@ -PMAN_VSN = 2.7.1.1 +PMAN_VSN = 2.7.1.2 diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile index 943d97bdb8..d48f48a5d5 100644 --- a/lib/public_key/asn1/Makefile +++ b/lib/public_key/asn1/Makefile @@ -91,13 +91,13 @@ $(INCLUDE)/%.hrl: %.hrl include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/asn1 + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/asn1" $(INSTALL_DATA) $(ASN_ASNS) $(ASN_ERLS) $(ASN_HRLS) $(ASN_CONFIGS) \ - $(GEN_ERLS) $(RELSYSDIR)/asn1 - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(GEN_ERLS) "$(RELSYSDIR)/asn1" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/public_key/asn1/PKCS-1.asn1 b/lib/public_key/asn1/PKCS-1.asn1 index b06f5efa9d..c83289e779 100644 --- a/lib/public_key/asn1/PKCS-1.asn1 +++ b/lib/public_key/asn1/PKCS-1.asn1 @@ -33,6 +33,9 @@ sha1WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 5 } sha256WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 11 } sha384WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 12 } sha512WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 13 } +sha224WithRSAEncryption OBJECT IDENTIFIER ::= { pkcs-1 14 } + + id-sha1 OBJECT IDENTIFIER ::= { iso(1) identified-organization(3) oiw(14) secsig(3) diff --git a/lib/public_key/doc/src/Makefile b/lib/public_key/doc/src/Makefile index 9616a96195..928aa62c1b 100644 --- a/lib/public_key/doc/src/Makefile +++ b/lib/public_key/doc/src/Makefile @@ -114,14 +114,14 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: info: diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml index c9a5561e3f..4d3a9856eb 100644 --- a/lib/public_key/doc/src/notes.xml +++ b/lib/public_key/doc/src/notes.xml @@ -34,6 +34,35 @@ <file>notes.xml</file> </header> +<section><title>Public_Key 0.15</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Changed ssh implementation to use the public_key + application for all public key handling. This is also a + first step for enabling a callback API for supplying + public keys and handling keys protected with password + phrases. </p> + <p> + Additionally the test suites where improved so that they + do not copy the users keys to test server directories as + this is a security liability. Also ipv6 and file access + issues found in the process has been fixed.</p> + <p> + This change also solves OTP-7677 and OTP-7235</p> + <p> + This changes also involves some updates to public_keys + ssh-functions.</p> + <p> + Own Id: OTP-9911</p> + </item> + </list> + </section> + +</section> + <section><title>Public_Key 0.14</title> <section><title>Improvements and New Features</title> diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 0b6673e826..5c227557f2 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2008</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -82,9 +82,9 @@ <p><code> rsa_padding() = 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'</code></p> - <p><code> rsa_digest_type() = 'md5' | 'sha' </code></p> + <p><code> rsa_digest_type() = 'md5' | 'sha' | 'sha224' | 'sha256' | 'sha384' | 'sha512' </code></p> - <p><code> dss_digest_type() = 'none' | 'sha' </code></p> + <p><code> dss_digest_type() = 'sha' </code></p> <p><code> ssh_file() = openssh_public_key | rfc4716_public_key | known_hosts | auth_keys </code></p> @@ -396,14 +396,14 @@ <name>sign(Msg, DigestType, Key) -> binary()</name> <fsummary> Create digital signature.</fsummary> <type> - <v>Msg = binary()</v> + <v>Msg = binary() | {digest,binary()}</v> <d>The msg is either the binary "plain text" data to be - signed or in the case that digest type is <c>none</c> - it is the hashed value of "plain text" i.e. the digest.</d> - <v>DigestType = rsa_digest_type() | dsa_digest_type()</v> + signed or it is the hashed value of "plain text" i.e. the + digest.</d> + <v>DigestType = rsa_digest_type() | dss_digest_type()</v> <v>Key = rsa_private_key() | dsa_private_key()</v> - </type> - <desc> + </type> + <desc> <p> Creates a digital signature.</p> </desc> </func> @@ -461,11 +461,10 @@ <name>verify(Msg, DigestType, Signature, Key) -> boolean()</name> <fsummary>Verifies a digital signature.</fsummary> <type> - <v>Msg = binary()</v> + <v>Msg = binary() | {digest,binary()}</v> <d>The msg is either the binary "plain text" data - or in the case that digest type is <c>none</c> - it is the hashed value of "plain text" i.e. the digest.</d> - <v>DigestType = rsa_digest_type() | dsa_digest_type()</v> + or it is the hashed value of "plain text" i.e. the digest.</d> + <v>DigestType = rsa_digest_type() | dss_digest_type()</v> <v>Signature = binary()</v> <v>Key = rsa_public_key() | dsa_public_key()</v> </type> diff --git a/lib/public_key/src/Makefile b/lib/public_key/src/Makefile index 062c495a65..8a72da477f 100644 --- a/lib/public_key/src/Makefile +++ b/lib/public_key/src/Makefile @@ -102,11 +102,11 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/public_key/src/public_key.appup.src b/lib/public_key/src/public_key.appup.src index 2945fb1213..aacd3b866d 100644 --- a/lib/public_key/src/public_key.appup.src +++ b/lib/public_key/src/public_key.appup.src @@ -1,16 +1,8 @@ %% -*- erlang -*- {"%VSN%", [ - {"0.13", [{restart_application, public_key}]}, - {"0.11", [{restart_application, public_key}]}, - {"0.10", [{restart_application, public_key}]}, - {"0.9", [{restart_application, public_key}]}, - {"0.8", [{restart_application, public_key}]} + {<<"0\\.*">>, [{restart_application, public_key}]} ], [ - {"0.13", [{restart_application, public_key}]}, - {"0.11", [{restart_application, public_key}]}, - {"0.10", [{restart_application, public_key}]}, - {"0.9", [{restart_application, public_key}]}, - {"0.8", [{restart_application, public_key}]} + {<<"0\\.*">>, [{restart_application, public_key}]} ]}. diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index 2e2a6cd296..d5df53e848 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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,8 +48,8 @@ -type rsa_padding() :: 'rsa_pkcs1_padding' | 'rsa_pkcs1_oaep_padding' | 'rsa_no_padding'. -type public_crypt_options() :: [{rsa_pad, rsa_padding()}]. --type rsa_digest_type() :: 'md5' | 'sha'| 'sha256' | 'sha512'. --type dss_digest_type() :: 'none' | 'sha'. +-type rsa_digest_type() :: 'md5' | 'sha'| 'sha224' | 'sha256' | 'sha384' | 'sha512'. +-type dss_digest_type() :: 'none' | 'sha'. %% None is for backwards compatibility -define(UINT32(X), X:32/unsigned-big-integer). -define(DER_NULL, <<5, 0>>). @@ -241,15 +241,15 @@ pkix_encode(Asn1Type, Term0, otp) when is_atom(Asn1Type) -> decrypt_private(CipherText, Key) -> decrypt_private(CipherText, Key, []). -decrypt_private(CipherText, - #'RSAPrivateKey'{modulus = N,publicExponent = E, - privateExponent = D}, - Options) when is_binary(CipherText), - is_list(Options) -> +decrypt_private(CipherText, + #'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D} = Key, + Options) + when is_binary(CipherText), + is_integer(N), is_integer(E), is_integer(D), + is_list(Options) -> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:rsa_private_decrypt(CipherText, - [crypto:mpint(E), crypto:mpint(N), - crypto:mpint(D)], Padding). + crypto:rsa_private_decrypt(CipherText, format_rsa_private_key(Key), Padding). %%-------------------------------------------------------------------- -spec decrypt_public(CipherText :: binary(), rsa_public_key() | rsa_private_key()) -> @@ -307,72 +307,86 @@ encrypt_public(PlainText, #'RSAPrivateKey'{modulus=N,publicExponent=E}, encrypt_private(PlainText, Key) -> encrypt_private(PlainText, Key, []). -encrypt_private(PlainText, #'RSAPrivateKey'{modulus = N, - publicExponent = E, - privateExponent = D}, - Options) when is_binary(PlainText), is_list(Options) -> +encrypt_private(PlainText, + #'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D} = Key, + Options) + when is_binary(PlainText), + is_integer(N), is_integer(E), is_integer(D), + is_list(Options) -> Padding = proplists:get_value(rsa_pad, Options, rsa_pkcs1_padding), - crypto:rsa_private_encrypt(PlainText, [crypto:mpint(E), - crypto:mpint(N), - crypto:mpint(D)], Padding). + crypto:rsa_private_encrypt(PlainText, format_rsa_private_key(Key), Padding). + + +format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D, + prime1 = P1, prime2 = P2, + exponent1 = E1, exponent2 = E2, + coefficient = C}) + when is_integer(P1), is_integer(P2), + is_integer(E1), is_integer(E2), is_integer(C) -> + [crypto:mpint(K) || K <- [E, N, D, P1, P2, E1, E2, C]]; + +format_rsa_private_key(#'RSAPrivateKey'{modulus = N, publicExponent = E, + privateExponent = D}) -> + [crypto:mpint(K) || K <- [E, N, D]]. %%-------------------------------------------------------------------- --spec sign(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(), - rsa_private_key() | +-spec sign(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type(), + rsa_private_key() | dsa_private_key()) -> Signature :: binary(). -%% %% Description: Create digital signature. %%-------------------------------------------------------------------- -sign(PlainText, DigestType, #'RSAPrivateKey'{modulus = N, publicExponent = E, - privateExponent = D}) - when is_binary(PlainText), - (DigestType == md5 orelse - DigestType == sha) -> - - crypto:rsa_sign(DigestType, sized_binary(PlainText), [crypto:mpint(E), - crypto:mpint(N), - crypto:mpint(D)]); - -sign(Digest, none, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) - when is_binary(Digest)-> - crypto:dss_sign(none, Digest, - [crypto:mpint(P), crypto:mpint(Q), +sign({digest,_}=Digest, DigestType, Key = #'RSAPrivateKey'{}) -> + crypto:rsa_sign(DigestType, Digest, format_rsa_private_key(Key)); + +sign(PlainText, DigestType, Key = #'RSAPrivateKey'{}) -> + crypto:rsa_sign(DigestType, sized_binary(PlainText), format_rsa_private_key(Key)); + +sign({digest,_}=Digest, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> + crypto:dss_sign(Digest, + [crypto:mpint(P), crypto:mpint(Q), crypto:mpint(G), crypto:mpint(X)]); - -sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) - when is_binary(PlainText) -> - crypto:dss_sign(sized_binary(PlainText), - [crypto:mpint(P), crypto:mpint(Q), - crypto:mpint(G), crypto:mpint(X)]). + +sign(PlainText, sha, #'DSAPrivateKey'{p = P, q = Q, g = G, x = X}) -> + crypto:dss_sign(sized_binary(PlainText), + [crypto:mpint(P), crypto:mpint(Q), + crypto:mpint(G), crypto:mpint(X)]); + +%% Backwards compatible +sign(Digest, none, #'DSAPrivateKey'{} = Key) -> + sign({digest,Digest}, sha, Key). %%-------------------------------------------------------------------- --spec verify(PlainTextOrDigest :: binary(), rsa_digest_type() | dss_digest_type(), - Signature :: binary(), rsa_public_key() +-spec verify(binary() | {digest, binary()}, rsa_digest_type() | dss_digest_type(), + Signature :: binary(), rsa_public_key() | dsa_public_key()) -> boolean(). -%% %% Description: Verifies a digital signature. %%-------------------------------------------------------------------- -verify(PlainText, DigestType, Signature, - #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) - when is_binary (PlainText) and (DigestType == sha orelse - DigestType == sha256 orelse - DigestType == sha512 orelse - DigestType == md5) -> +verify({digest,_}=Digest, DigestType, Signature, + #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) -> + crypto:rsa_verify(DigestType, Digest, + sized_binary(Signature), + [crypto:mpint(Exp), crypto:mpint(Mod)]); + +verify(PlainText, DigestType, Signature, + #'RSAPublicKey'{modulus = Mod, publicExponent = Exp}) -> crypto:rsa_verify(DigestType, sized_binary(PlainText), sized_binary(Signature), [crypto:mpint(Exp), crypto:mpint(Mod)]); -verify(Digest, none, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) - when is_integer(Key), is_binary(Digest), is_binary(Signature) -> - crypto:dss_verify(none, - Digest, - sized_binary(Signature), +verify({digest,_}=Digest, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) + when is_integer(Key), is_binary(Signature) -> + crypto:dss_verify(Digest, sized_binary(Signature), [crypto:mpint(P), crypto:mpint(Q), crypto:mpint(G), crypto:mpint(Key)]); - +%% Backwards compatibility +verify(Digest, none, Signature, {_, #'Dss-Parms'{}} = Key ) -> + verify({digest,Digest}, sha, Signature, Key); + verify(PlainText, sha, Signature, {Key, #'Dss-Parms'{p = P, q = Q, g = G}}) - when is_integer(Key), is_binary(PlainText), is_binary(Signature) -> + when is_integer(Key), is_binary(PlainText), is_binary(Signature) -> crypto:dss_verify(sized_binary(PlainText), sized_binary(Signature), [crypto:mpint(P), crypto:mpint(Q), diff --git a/lib/public_key/test/Makefile b/lib/public_key/test/Makefile index b7f91981a5..41d77f103b 100644 --- a/lib/public_key/test/Makefile +++ b/lib/public_key/test/Makefile @@ -21,7 +21,7 @@ include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk -INCLUDES= -I. -I ../include +INCLUDES= -I. -I ../include -pa $(ERL_TOP)/lib/public_key/ebin # ---------------------------------------------------- # Target Specs @@ -78,11 +78,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SPEC_FILES) $(ERL_FILES) $(COVER_FILE) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/public_key/vsn.mk b/lib/public_key/vsn.mk index d8f811bf25..c8165fa247 100644 --- a/lib/public_key/vsn.mk +++ b/lib/public_key/vsn.mk @@ -1 +1 @@ -PUBLIC_KEY_VSN = 0.14 +PUBLIC_KEY_VSN = 0.16 diff --git a/lib/reltool/doc/src/Makefile b/lib/reltool/doc/src/Makefile index 8bc1488f77..0b85b7e02c 100644 --- a/lib/reltool/doc/src/Makefile +++ b/lib/reltool/doc/src/Makefile @@ -97,13 +97,13 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(HTMLDIR)/* $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(HTMLDIR)/* "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/reltool/doc/src/notes.xml b/lib/reltool/doc/src/notes.xml index cf2cf7f7bc..0aae128c2b 100644 --- a/lib/reltool/doc/src/notes.xml +++ b/lib/reltool/doc/src/notes.xml @@ -37,7 +37,149 @@ thus constitutes one section in this document. The title of each section is the version number of Reltool.</p> - <section><title>Reltool 0.5.7.1</title> + <section><title>Reltool 0.6</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Miscellaneous corrections: <list> <item> Start of reltool + GUI would sometimes crash with a badmatch in + reltool_sys_win:do_init. This has been corrected. </item> + <item> Minor corrections of documentation and type + specifications of app() and mod() are done. </item> + <item> If a module name is duplicated in an app file + reltool would return an error. This is now changed to a + warning. </item> <item> Reltool would earlier not + necessarily keep the order of applications as listed in + the rel specification in the configuration. This has been + corrected. </item> <item> Reltool would earlier set the + default for included applications to an empty list if it + was not set in the rel specification in the + configuration. This was correct according to + sasl/systools documentation, but not according to + sasl/systools implementation. We decided to change the + documentation and reltool to use the value of + included_applications from the .app file as default + instead of the empty list, since this seems more + intuitive and since systools always has done the same. + </item> <item> The value of included applications in the + rel specification in the configuration did not overwrite + included_applications in the .app file if it was set to + an empty list. This has been corrected. </item> <item> + Reltool would earlier add load instructions in the + script/boot files for ALL modules in the ebin directory + of an application even if mod_cond was set to app + (include only modules listed in the .app file). This has + been corrected. </item> <item> Reltool would earlier add + start instructions in the script/boot file for included + applications. This has been corrected - included + applications shall only be loaded since the including + application is responsible for starting them. </item> + <item> Status bar now indicates that reltool is working + (Processing libraries...) for all configuration changes, + and when generating target system. </item> <item> Title + of dependecies column in app and mod window is changed + from "Modules used by others" to "Modules using this". + </item> </list></p> + <p> + Own Id: OTP-9792</p> + </item> + <item> + <p> + Configuration changes via the GUI had a few bugs related + to error handling, rollback and undo. A major re-write of + the reltool_server has been done in order to align the + way reltool_server updates and uses its state and tables + for all different kinds of configuration change. </p> + <p> + All configuration changes (except undo) now cause a + re-read of the file system, meaning that if something has + changed in the file system it will be reflected in the + result of the configuration change.</p> + <p> + When loading a new configuration file via the GUI, the + old configuration is now completely scratched, and only + the new is valid.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9794</p> + </item> + <item> + <p> + Some bug fixes related to the handling of escripts: + <list> <item> Reltool could not handle escripts with + inlined applications. This has been corrected. Inlined + applications will be visible in the GUI, but not possible + to include/exclude separately. </item> <item> Loading a + config which contains an escript via the GUI menu did not + produce the same result as when using the same + configuration at reltool start. Paths, version and label + could differ. This has been corrected. </item> <item> + Loading config with same escript (source) twice caused + reltool to add same module twice. This has been + corrected. </item> <item> Loading config with same + escript (inlined beam) twice caused reltool to fail + saying module is included by two different applications. + This has been corrected. </item> <item> Loading config + which in addition to an existing escript also adds + another escript, for which the name sorts before the + existing one, would cause reltool to fail saying + "Application name clash". This has been corrected. + </item> </list></p> + <p> + Own Id: OTP-9968</p> + </item> + <item> + <p> + Reltool would sometimes generate a .app file containing + <c>{start_phases,undefined}</c>, which would cause an + exception in systools at parse time. This has been + corrected so reltool now omits the <c>start_phases</c> + entry if the value is <c>undefined</c>. (Thanks to Juan + Jose Comellas)</p> + <p> + In order to align with reltool, sasl will also omit + <c>start_phases</c> entries with value <c>undefined</c> + in .script files.</p> + <p> + Own Id: OTP-10003</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + A warning list is added to the sys window of the reltool + GUI. This list will continuously show all warnings + produced by the current configuration.</p> + <p> + Own Id: OTP-9967</p> + </item> + <item> + <p> + As a way of specifying one specific version of an + application, the following configuration parameter is + added on application level:</p> + <p> + {lib_dir,Dir}, Dir = string()</p> + <p> + This can be useful if the parent directory of the + application directory is not suitable to use as a lib dir + on system level.</p> + <p> + Own Id: OTP-9977</p> + </item> + </list> + </section> + +</section> + +<section><title>Reltool 0.5.7.1</title> <section><title>Improvements and New Features</title> <list> diff --git a/lib/reltool/doc/src/reltool.xml b/lib/reltool/doc/src/reltool.xml index 60e886e8f5..9a4e2d130e 100644 --- a/lib/reltool/doc/src/reltool.xml +++ b/lib/reltool/doc/src/reltool.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2009</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -322,8 +322,21 @@ <item> <p>The version of the application. In an installed system there may exist several versions of an application. The <c>vsn</c> parameter - controls which version of the application will be chosen. If it - is omitted, the latest version will be chosen.</p> + controls which version of the application will be chosen.</p> + <p>This parameter is mutual exclusive with <c>lib_dir</c>. If + <c>vsn</c> and <c>lib_dir</c> are both omitted, the latest version + will be chosen.</p> + </item> + <tag><c>lib_dir</c></tag> + <item> + <p>The directory to read the application from. This parameter + can be used to point out a specific location to fetch the + application from. This is useful for instance if the parent + directory for some reason is no good as a library directory on + system level.</p> + <p>This parameter is mutual exclusive with <c>vsn</c>. If + <c>vsn</c> and <c>lib_dir</c> are both omitted, the latest version + will be chosen.</p> </item> <tag><c>mod</c></tag> <item> @@ -433,7 +446,8 @@ sys() = {root_dir, root_dir()} | {excl_archive_filters, excl_archive_filters()} | {archive_opts, [archive_opt()]} app() = {vsn, app_vsn()} - | {mod, mod_name(), mod()} + | {lib_dir, lib_dir()} + | {mod, mod_name(), [mod()]} | {mod_cond, mod_cond()} | {incl_cond, incl_cond()} | {debug_info, debug_info()} @@ -445,8 +459,7 @@ app() = {vsn, app_vsn()} | {incl_archive_filters, incl_archive_filters()} | {excl_archive_filters, excl_archive_filters()} | {archive_opts, [archive_opt()]} -mod() = {vsn, app_vsn()} - | {incl_cond, incl_cond()} +mod() = {incl_cond, incl_cond()} | {debug_info, debug_info()} rel_app() = app_name() | {app_name(), app_type()} @@ -664,10 +677,10 @@ target_spec() = [target_spec()] </func> <func> - <name>install(Server, TargetDir) -> ok | {error, Reason}</name> + <name>install(RelName, TargetDir) -> ok | {error, Reason}</name> <fsummary>Install a target system</fsummary> <type> - <v>Server = server()</v> + <v>RelName = rel_name()</v> <v>TargetDir = target_dir()</v> <v>Reason = reason()</v> </type> diff --git a/lib/reltool/examples/Makefile b/lib/reltool/examples/Makefile index dacfd4e7d4..afc47e1d12 100644 --- a/lib/reltool/examples/Makefile +++ b/lib/reltool/examples/Makefile @@ -72,8 +72,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) $(DATA_FILES) $(HRL_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/reltool/src/Makefile b/lib/reltool/src/Makefile index 4e6a112b7e..4730b4acff 100644 --- a/lib/reltool/src/Makefile +++ b/lib/reltool/src/Makefile @@ -97,11 +97,11 @@ $(TARGET_FILES): $(HRL_FILES) $(INTERNAL_HRL_FILES) include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/reltool/src/reltool.erl b/lib/reltool/src/reltool.erl index 54eb1ca9e1..2bdf222aa0 100644 --- a/lib/reltool/src/reltool.erl +++ b/lib/reltool/src/reltool.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -59,7 +59,7 @@ start_link(Options) when is_list(Options) -> {ok, _WinPid} = OK -> OK; {error, Reason} -> - {error, lists:flatten(io_lib:format("~p", [Reason]))} + {error, Reason} end. %% Start server process with options @@ -69,7 +69,7 @@ start_server(Options) -> {ok, ServerPid, _Common, _Sys} -> {ok, ServerPid}; {error, Reason} -> - {error, lists:flatten(io_lib:format("~p", [Reason]))} + {error, Reason} end. %% Start server process with options diff --git a/lib/reltool/src/reltool.hrl b/lib/reltool/src/reltool.hrl index 93f47f6381..26dcd82447 100644 --- a/lib/reltool/src/reltool.hrl +++ b/lib/reltool/src/reltool.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -45,6 +45,7 @@ -type profile() :: development | embedded | standalone. -type relocatable() :: boolean(). -type escript_file() :: file(). +-type escript_app_name() :: app_name(). -type mod_name() :: atom(). -type app_name() :: atom(). -type app_vsn() :: string(). % e.g. "4.7" @@ -62,7 +63,8 @@ -type mod() :: {incl_cond, incl_cond()} | {debug_info, debug_info()}. -type app() :: {vsn, app_vsn()} - | {mod, mod_name(), mod()} + | {lib_dir, lib_dir()} + | {mod, mod_name(), [mod()]} | {mod_cond, mod_cond()} | {incl_cond, incl_cond()} | {app_file, app_file()} @@ -126,27 +128,25 @@ { sys_debug :: term(), wx_debug :: term(), - trap_exit :: boolean(), - app_tab :: ets:tab(), - mod_tab :: ets:tab(), - mod_used_by_tab :: ets:tab() + trap_exit :: boolean() }). +%% Types '$1','$2' and '_' are needed for match specs to ets:select -record(mod, { %% Static - name :: mod_name(), - app_name :: app_name(), - incl_cond :: incl_cond() | undefined, - debug_info :: debug_info() | undefined, - is_app_mod :: boolean(), - is_ebin_mod :: boolean(), - uses_mods :: [mod_name()], - exists :: boolean(), + name :: '$1' | mod_name(), + app_name :: '_' | app_name(), + incl_cond :: '_' | incl_cond() | undefined, + debug_info :: '_' | debug_info() | undefined, + is_app_mod :: '_' | boolean(), + is_ebin_mod :: '_' | boolean(), + uses_mods :: '$2' | [mod_name()], + exists :: '_' | boolean(), %% Dynamic - status :: status(), - used_by_mods :: [mod_name()], - is_pre_included :: boolean() | undefined, - is_included :: boolean() | undefined + status :: '_' | status(), + used_by_mods :: '_' | [mod_name()], + is_pre_included :: '_' | boolean() | undefined, + is_included :: '_' | boolean() | undefined }). -record(app_info, @@ -167,48 +167,49 @@ -record(regexp, {source, compiled}). +%% Types '$1','$2' and '_' are needed for match specs to ets:select -record(app, { %% Static info - name :: app_name(), - is_escript :: boolean(), - use_selected_vsn :: boolean() | undefined, - active_dir :: dir(), - sorted_dirs :: [dir()], - vsn :: app_vsn(), - label :: app_label(), - info :: #app_info{} | undefined, - mods :: [#mod{}], + name :: '_' | app_name(), + is_escript :: '_' | boolean() | {inlined, escript_app_name()}, + use_selected_vsn :: '_' | vsn | dir | undefined, + active_dir :: '_' | dir(), + sorted_dirs :: '_' | [dir()], + vsn :: '_' | app_vsn(), + label :: '_' | app_label(), + info :: '_' | #app_info{} | undefined, + mods :: '_' | [#mod{}], %% Static source cond - mod_cond :: mod_cond() | undefined, - incl_cond :: incl_cond() | undefined, + mod_cond :: '_' | mod_cond() | undefined, + incl_cond :: '_' | incl_cond() | undefined, %% Static target cond - debug_info :: debug_info() | undefined, - app_file :: app_file() | undefined, - app_type :: app_type() | undefined, - incl_app_filters :: [#regexp{}], - excl_app_filters :: [#regexp{}], - incl_archive_filters :: [#regexp{}], - excl_archive_filters :: [#regexp{}], - archive_opts :: [archive_opt()], + debug_info :: '_' | debug_info() | undefined, + app_file :: '_' | app_file() | undefined, + app_type :: '_' | app_type() | undefined, + incl_app_filters :: '_' | [#regexp{}], + excl_app_filters :: '_' | [#regexp{}], + incl_archive_filters :: '_' | [#regexp{}], + excl_archive_filters :: '_' | [#regexp{}], + archive_opts :: '_' | [archive_opt()], %% Dynamic - status :: status(), - uses_mods :: [mod_name()], - used_by_mods :: [mod_name()], - uses_apps :: [app_name()], - used_by_apps :: [app_name()], - is_pre_included :: boolean(), - is_included :: boolean(), - rels :: [rel_name()] + status :: '_' | status(), + uses_mods :: '_' | [mod_name()], + used_by_mods :: '_' | [mod_name()], + uses_apps :: '_' | [app_name()], + used_by_apps :: '_' | [app_name()], + is_pre_included :: '_' | '$2' | boolean() | undefined, + is_included :: '_' | '$1' | boolean() | undefined, + rels :: '_' | [rel_name()] }). -record(rel_app, { name :: app_name(), app_type :: app_type() | undefined, - incl_apps = [] :: [incl_app()] + incl_apps :: [incl_app()] | undefined }). -record(rel, @@ -225,7 +226,7 @@ escripts :: [file()], mod_cond :: mod_cond(), incl_cond :: incl_cond(), - apps :: [#app{}], + apps :: [#app{}] | undefined, %% Target cond boot_rel :: boot_rel(), diff --git a/lib/reltool/src/reltool_app_win.erl b/lib/reltool/src/reltool_app_win.erl index 70bd72b258..6cd0d2f90b 100644 --- a/lib/reltool/src/reltool_app_win.erl +++ b/lib/reltool/src/reltool_app_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -271,8 +271,8 @@ create_apps_list_ctrl(Panel, Sizer, Text) -> ListItem = wxListItem:new(), wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), wxListItem:setText(ListItem, Text), + wxListItem:setWidth(ListItem, reltool_utils:get_column_width(ListCtrl)), wxListCtrl:insertColumn(ListCtrl, ?APPS_APP_COL, ListItem), - %% wxListCtrl:setColumnWidth(ListCtrl, ?APPS_APP_COL, ?APPS_APP_COL_WIDTH), wxListItem:destroy(ListItem), wxSizer:add(Sizer, ListCtrl, @@ -292,7 +292,7 @@ create_deps_page(S, Derived) -> UsedByCtrl = create_mods_list_ctrl(Panel, Main, - "Modules used by others", + "Modules using this", " and their applications", undefined, undefined), @@ -611,7 +611,7 @@ handle_event(#state{sys = Sys, app = App} = S, Wx) -> redraw_window(S2); #wx{userData = use_selected_vsn} -> %% Use selected version - App2 = App#app{use_selected_vsn = true}, + App2 = App#app{use_selected_vsn = dir}, {ok, App3} = reltool_sys_win:set_app(S#state.parent_pid, App2), S2 = S#state{app = App3}, redraw_window(S2); @@ -619,7 +619,8 @@ handle_event(#state{sys = Sys, app = App} = S, Wx) -> event = #wxCommand{type = command_radiobox_selected, cmdString = ActiveDir}} -> %% Change app source - S2 = change_version(S, App, ActiveDir), + App2 = App#app{use_selected_vsn = dir}, + S2 = change_version(S, App2, ActiveDir), redraw_window(S2); #wx{userData = {mod_button, Action, ListCtrl}, event = #wxCommand{type = command_button_clicked}} -> @@ -942,11 +943,11 @@ redraw_config(#state{sys = #sys{incl_cond = GlobalIncl, LatestRadio, SelectedRadio, SourceBox, - fun(true) -> + fun(false) -> + 0; + (_) -> reltool_utils:elem_to_index(ActiveDir, - SortedDirs) - 1; - (false) -> - 0 + SortedDirs) - 1 end). redraw_double_box(Global, Local, GlobalRadio, LocalRadio, LocalBox, GetChoice) -> diff --git a/lib/reltool/src/reltool_mod_win.erl b/lib/reltool/src/reltool_mod_win.erl index e1c2fa5100..899423bb6d 100644 --- a/lib/reltool/src/reltool_mod_win.erl +++ b/lib/reltool/src/reltool_mod_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -215,7 +215,7 @@ create_deps_page(S) -> UsedByCtrl = create_mods_list_ctrl(Panel, Main, - "Modules used by others", + "Modules using this", " and their applications"), wxSizer:add(Main, wxStaticLine:new(Panel, [{style, ?wxLI_VERTICAL}]), @@ -314,8 +314,8 @@ do_create_code_page(#state{xref_pid = Xref, mod = M} = S, PageName) -> {ok, App} = reltool_server:get_app(Xref, M#mod.app_name), ErlBin = case App#app.is_escript of - true -> find_escript_bin(App, M); - false -> find_regular_bin(App, M) + false -> find_regular_bin(App, M); + _ -> find_escript_bin(App, M) end, load_code(Editor, ErlBin), diff --git a/lib/reltool/src/reltool_server.erl b/lib/reltool/src/reltool_server.erl index 692baea0a4..034a42e1e2 100644 --- a/lib/reltool/src/reltool_server.erl +++ b/lib/reltool/src/reltool_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -51,7 +51,12 @@ sys, old_sys, status, - old_status}). + old_status, + app_tab, + old_app_tab, + mod_tab, + old_mod_tab, + mod_used_by_tab}). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Client @@ -123,174 +128,165 @@ gen_spec(Pid) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Server -init(Options) -> +init([{parent,Parent}|_] = Options) -> try do_init(Options) catch + throw:{error,Reason} -> + proc_lib:init_ack(Parent,{error,Reason}); error:Reason -> exit({Reason, erlang:get_stacktrace()}) end. do_init(Options) -> - {S, Status} = parse_options(Options), - #state{parent_pid = ParentPid, common = C, sys = Sys} = S, - - %% process_flag(trap_exit, (S#state.common)#common.trap_exit), - proc_lib:init_ack(ParentPid, - {ok, self(), C, Sys#sys{apps = undefined}}), - {S2, Status2} = refresh(S, true, Status), - {S3, Status3} = - analyse(S2#state{old_sys = S2#state.sys}, Status2), - case Status3 of - {ok, _Warnings} -> % BUGBUG: handle warnings - loop(S3#state{status = Status3, old_status = {ok, []}}); - {error, Reason} -> - exit(Reason) - end. - -parse_options(Opts) -> - AppTab = ets:new(reltool_apps, [public, ordered_set, {keypos, #app.name}]), - ModTab = ets:new(reltool_mods, [public, ordered_set, {keypos, #mod.name}]), + AppTab = ets:new(reltool_apps1, [public, ordered_set, {keypos,#app.name}]), + OldAppTab = ets:new(reltool_apps2, [public, ordered_set, {keypos,#app.name}]), + ModTab = ets:new(reltool_mods1, [public, ordered_set, {keypos,#mod.name}]), + OldModTab = ets:new(reltool_mods2, [public, ordered_set, {keypos,#mod.name}]), ModUsesTab = ets:new(reltool_mod_uses, [public, bag, {keypos, 1}]), - Sys = #sys{root_dir = reltool_utils:root_dir(), - lib_dirs = reltool_utils:erl_libs(), - escripts = [], - incl_cond = ?DEFAULT_INCL_COND, - mod_cond = ?DEFAULT_MOD_COND, - apps = ?DEFAULT_APPS, - boot_rel = ?DEFAULT_REL_NAME, - rels = reltool_utils:default_rels(), - emu_name = ?DEFAULT_EMU_NAME, - profile = ?DEFAULT_PROFILE, - incl_sys_filters = dec_re(incl_sys_filters, - ?DEFAULT_INCL_SYS_FILTERS, - []), - excl_sys_filters = dec_re(excl_sys_filters, - ?DEFAULT_EXCL_SYS_FILTERS, - []), - incl_app_filters = dec_re(incl_app_filters, - ?DEFAULT_INCL_APP_FILTERS, - []), - excl_app_filters = dec_re(excl_app_filters, - ?DEFAULT_EXCL_APP_FILTERS, - []), - relocatable = ?DEFAULT_RELOCATABLE, - rel_app_type = ?DEFAULT_REL_APP_TYPE, - embedded_app_type = ?DEFAULT_EMBEDDED_APP_TYPE, - app_file = ?DEFAULT_APP_FILE, - incl_archive_filters = dec_re(incl_archive_filters, - ?DEFAULT_INCL_ARCHIVE_FILTERS, - []), - excl_archive_filters = dec_re(excl_archive_filters, - ?DEFAULT_EXCL_ARCHIVE_FILTERS, - []), - archive_opts = ?DEFAULT_ARCHIVE_OPTS, - debug_info = ?DEFAULT_DEBUG_INFO}, - C2 = #common{sys_debug = [], - wx_debug = 0, - trap_exit = true, - app_tab = AppTab, - mod_tab = ModTab, - mod_used_by_tab = ModUsesTab}, - S = #state{options = Opts}, - parse_options(Opts, S, C2, Sys, {ok, []}). + S = #state{options = Options, + app_tab = AppTab, + old_app_tab = OldAppTab, + mod_tab = ModTab, + old_mod_tab = OldModTab, + mod_used_by_tab = ModUsesTab}, + + S2 = parse_options(S), + {S3, Apps, Status2} = refresh(S2), + Status3 = analyse(S3, Apps, Status2), + %% Set old_xxx equal to xxx to allow undo=nop + FakeBackup = {ets:tab2list(S3#state.app_tab),ets:tab2list(S3#state.mod_tab)}, + S4 = save_old(S3, S3, FakeBackup, Status3), + #state{parent_pid = Parent, sys=Sys, common=C} = S4, + proc_lib:init_ack(Parent, {ok, self(), C, Sys#sys{apps=undefined}}), + loop(S4). + +parse_options(S) -> + Sys = default_sys(), + C = #common{sys_debug = [], + wx_debug = 0, + trap_exit = true}, + parse_options(S#state.options, S, C, Sys). + +default_sys() -> + #sys{root_dir = reltool_utils:root_dir(), + lib_dirs = reltool_utils:erl_libs(), + escripts = [], + incl_cond = ?DEFAULT_INCL_COND, + mod_cond = ?DEFAULT_MOD_COND, + apps = ?DEFAULT_APPS, + boot_rel = ?DEFAULT_REL_NAME, + rels = reltool_utils:default_rels(), + emu_name = ?DEFAULT_EMU_NAME, + profile = ?DEFAULT_PROFILE, + incl_sys_filters = dec_re(incl_sys_filters, + ?DEFAULT_INCL_SYS_FILTERS, + []), + excl_sys_filters = dec_re(excl_sys_filters, + ?DEFAULT_EXCL_SYS_FILTERS, + []), + incl_app_filters = dec_re(incl_app_filters, + ?DEFAULT_INCL_APP_FILTERS, + []), + excl_app_filters = dec_re(excl_app_filters, + ?DEFAULT_EXCL_APP_FILTERS, + []), + relocatable = ?DEFAULT_RELOCATABLE, + rel_app_type = ?DEFAULT_REL_APP_TYPE, + embedded_app_type = ?DEFAULT_EMBEDDED_APP_TYPE, + app_file = ?DEFAULT_APP_FILE, + incl_archive_filters = dec_re(incl_archive_filters, + ?DEFAULT_INCL_ARCHIVE_FILTERS, + []), + excl_archive_filters = dec_re(excl_archive_filters, + ?DEFAULT_EXCL_ARCHIVE_FILTERS, + []), + archive_opts = ?DEFAULT_ARCHIVE_OPTS, + debug_info = ?DEFAULT_DEBUG_INFO}. dec_re(Key, Regexps, Old) -> reltool_utils:decode_regexps(Key, Regexps, Old). -parse_options([{Key, Val} | KeyVals], S, C, Sys, Status) -> +parse_options([{Key, Val} | KeyVals], S, C, Sys) -> case Key of parent -> - parse_options(KeyVals, S#state{parent_pid = Val}, C, Sys, Status); + parse_options(KeyVals, S#state{parent_pid = Val}, C, Sys); sys_debug -> - parse_options(KeyVals, S, C#common{sys_debug = Val}, Sys, Status); + parse_options(KeyVals, S, C#common{sys_debug = Val}, Sys); wx_debug -> - parse_options(KeyVals, S, C#common{wx_debug = Val}, Sys, Status); + parse_options(KeyVals, S, C#common{wx_debug = Val}, Sys); trap_exit -> - parse_options(KeyVals, S, C#common{trap_exit = Val}, Sys, Status); + parse_options(KeyVals, S, C#common{trap_exit = Val}, Sys); config -> - {Sys2, Status2} = read_config(Sys, Val, Status), - parse_options(KeyVals, S, C, Sys2, Status2); + Sys2 = read_config(Sys, Val), + parse_options(KeyVals, S, C, Sys2); sys -> - {Sys2, Status2} = read_config(Sys, {sys, Val}, Status), - parse_options(KeyVals, S, C, Sys2, Status2); + Sys2 = read_config(Sys, {sys, Val}), + parse_options(KeyVals, S, C, Sys2); _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - Status2 = - reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text), - parse_options(KeyVals, S, C, Sys, Status2) + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end; -parse_options([], S, C, Sys, Status) -> - {S#state{common = C, sys = Sys}, Status}; -parse_options(KeyVals, S, C, Sys, Status) -> - Text = lists:flatten(io_lib:format("~p", [KeyVals])), - Status2 = reltool_utils:return_first_error(Status, - "Illegal options: " ++ Text), - {S#state{common = C, sys = Sys}, Status2}. - -loop(#state{common = C, sys = Sys} = S) -> +parse_options([], S, C, Sys) -> + S#state{common = C, sys = Sys}; +parse_options(KeyVals, _S, _C, _Sys) -> + reltool_utils:throw_error("Illegal option: ~p", [KeyVals]). + +loop(#state{sys = Sys} = S) -> receive {system, From, Msg} -> sys:handle_system_msg(Msg, From, S#state.parent_pid, ?MODULE, - C#common.sys_debug, + (S#state.common)#common.sys_debug, S); {call, ReplyTo, Ref, {get_config, InclDef, InclDeriv}} -> Reply = do_get_config(S, InclDef, InclDeriv), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {load_config, SysConfig}} -> - {S2, Reply} = do_load_config(S, SysConfig), - reltool_utils:reply(ReplyTo, Ref, Reply), - ?MODULE:loop(S2); + Fun = fun() -> do_load_config(S, SysConfig) end, + {S3, Status2} = config_and_refresh(S, Fun), + reltool_utils:reply(ReplyTo, Ref, Status2), + ?MODULE:loop(S3); {call, ReplyTo, Ref, {save_config, Filename, InclDef, InclDeriv}} -> Reply = do_save_config(S, Filename, InclDef, InclDeriv), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, reset_config} -> - {S2, Status} = parse_options(S#state.options), - S3 = shrink_sys(S2), - {S4, Status2} = refresh(S3, true, Status), - {S5, Status3} = analyse(S4#state{old_sys = S4#state.sys}, Status2), - S6 = - case Status3 of - {ok, _Warnings} -> - S5#state{status = Status3, old_status = S#state.status}; - {error, _} -> - %% Keep old state - S - end, - reltool_utils:reply(ReplyTo, Ref, Status3), - ?MODULE:loop(S6); + Fun = fun() -> parse_options(S) end, + {S3, Status2} = config_and_refresh(S, Fun), + reltool_utils:reply(ReplyTo, Ref, Status2), + ?MODULE:loop(S3); {call, ReplyTo, Ref, undo_config} -> - reltool_utils:reply(ReplyTo, Ref, ok), S2 = S#state{sys = S#state.old_sys, - old_sys = S#state.sys, - status = S#state.old_status, - old_status = S#state.status}, + old_sys = Sys, + status = S#state.old_status, + old_status = S#state.status, + app_tab = S#state.old_app_tab, + old_app_tab = S#state.app_tab, + mod_tab = S#state.old_mod_tab, + old_mod_tab = S#state.mod_tab}, + reltool_utils:reply(ReplyTo, Ref, ok), ?MODULE:loop(S2); {call, ReplyTo, Ref, {get_rel, RelName}} -> - Sys = S#state.sys, Reply = case lists:keysearch(RelName, #rel.name, Sys#sys.rels) of {value, Rel} -> - reltool_target:gen_rel(Rel, Sys); + reltool_target:gen_rel(Rel, sys_all_apps(S)); false -> {error, "No such release: " ++ RelName} end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {get_script, RelName}} -> - Sys = S#state.sys, Reply = case lists:keysearch(RelName, #rel.name, Sys#sys.rels) of {value, Rel} -> PathFlag = true, Vars = [], - reltool_target:gen_script(Rel, Sys, PathFlag, Vars); + reltool_target:gen_script(Rel, sys_all_apps(S), + PathFlag, Vars); false -> {error, "No such release: " ++ RelName} end, @@ -298,7 +294,7 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {get_mod, ModName}} -> Reply = - case ets:lookup(C#common.mod_tab, ModName) of + case ets:lookup(S#state.mod_tab, ModName) of [M] -> {ok, M}; [] -> @@ -308,92 +304,83 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S); {call, ReplyTo, Ref, {get_app, AppName}} when is_atom(AppName) -> Reply = - case lists:keysearch(AppName, #app.name, Sys#sys.apps) of - {value, App} -> + case ets:lookup(S#state.app_tab,AppName) of + [App] -> {ok, App}; - false -> + [] -> {error, "No such application: " ++ atom_to_list(AppName)} end, reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_app, App}} -> - {S2, Status} = do_set_app(S, App, {ok, []}), - {S3, Status2} = analyse(S2, Status), - case Status2 of - {ok, Warnings} -> - App2 = ?KEYSEARCH(App#app.name, - #app.name, - (S3#state.sys)#sys.apps), - reltool_utils:reply(ReplyTo, Ref, {ok, App2, Warnings}), - ?MODULE:loop(S3); - {error, Reason} -> - %% Keep old state - reltool_utils:reply(ReplyTo, Ref, {error, Reason}), - ?MODULE:loop(S) - end; + Fun = fun() -> do_set_apps(S, [App]) end, + {S3, Status2} = config_and_refresh(S, Fun), + Reply = + case Status2 of + {ok, Warnings} -> + [App2] = ets:lookup(S3#state.app_tab,App#app.name), + {ok, App2, Warnings}; + {error, _} -> + Status2 + end, + reltool_utils:reply(ReplyTo, Ref, Reply), + ?MODULE:loop(S3); {call, ReplyTo, Ref, {get_apps, Kind}} -> AppNames = case Kind of whitelist -> - [A || - A <- Sys#sys.apps, - A#app.is_pre_included =:= true]; - blacklist -> - [A || - A <- Sys#sys.apps, - A#app.is_pre_included =:= false]; - source -> - [A || - A <- Sys#sys.apps, - A#app.is_included =/= true, - A#app.is_pre_included =/= false]; + %% Pre-included + ets:select(S#state.app_tab, + [{#app{is_pre_included=true,_='_'}, + [], + ['$_']}]); + blacklist -> + %% Pre-excluded + ets:select(S#state.app_tab, + [{#app{is_pre_included=false,_='_'}, + [], + ['$_']}]); + source -> + %% Not included and not pre-excluded + ets:select(S#state.app_tab, + [{#app{is_included='$1', + is_pre_included='$2', + _='_'}, + [{'=/=','$1',true}, + {'=/=','$2',false}], + ['$_']}]); derived -> - [A || - A <- Sys#sys.apps, - A#app.is_included =:= true, - A#app.is_pre_included =/= true] + %% Included, but not pre-included + ets:select(S#state.app_tab, + [{#app{is_included='$1', + is_pre_included='$2', + _='_'}, + [{'=:=','$1',true}, + {'=/=','$2',true}], + ['$_']}]) end, reltool_utils:reply(ReplyTo, Ref, {ok, AppNames}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_apps, Apps}} -> - {S2, Status} = - lists:foldl(fun(A, {X, Y}) -> do_set_app(X, A, Y) end, - {S, {ok, []}}, - Apps), - {S3, Status2} = analyse(S2, Status), + Fun = fun() -> do_set_apps(S, Apps) end, + {S3, Status2} = config_and_refresh(S, Fun), reltool_utils:reply(ReplyTo, Ref, Status2), - ?MODULE:loop(S3); + ?MODULE:loop(S3); {call, ReplyTo, Ref, get_sys} -> reltool_utils:reply(ReplyTo, Ref, {ok, Sys#sys{apps = undefined}}), ?MODULE:loop(S); {call, ReplyTo, Ref, {set_sys, Sys2}} -> - S2 = S#state{sys = Sys2#sys{apps = Sys#sys.apps}}, - Force = - (Sys2#sys.root_dir =/= Sys#sys.root_dir) orelse - (Sys2#sys.lib_dirs =/= Sys#sys.lib_dirs) orelse - (Sys2#sys.escripts =/= Sys#sys.escripts), - {S3, Status} = refresh(S2, Force, {ok, []}), - {S4, Status2} = - analyse(S3#state{old_sys = S#state.sys}, Status), - {S5, Status3} = - case Status2 of - {ok, _Warnings} -> % BUGBUG: handle warnings - {S4#state{status = Status2, - old_status = S#state.status}, - Status2}; - {error, _} -> - %% Keep old state - {S, Status2} - end, - reltool_utils:reply(ReplyTo, Ref, Status3), - ?MODULE:loop(S5); + Fun = fun() -> S#state{sys = Sys2#sys{apps = Sys#sys.apps}} end, + {S3, Status} = config_and_refresh(S, Fun), + reltool_utils:reply(ReplyTo, Ref, Status), + ?MODULE:loop(S3); {call, ReplyTo, Ref, get_status} -> reltool_utils:reply(ReplyTo, Ref, S#state.status), ?MODULE:loop(S); {call, ReplyTo, Ref, {gen_rel_files, Dir}} -> Status = - case reltool_target:gen_rel_files(S#state.sys, Dir) of + case reltool_target:gen_rel_files(sys_all_apps(S), Dir) of ok -> {ok, []}; {error, Reason} -> @@ -402,11 +389,11 @@ loop(#state{common = C, sys = Sys} = S) -> reltool_utils:reply(ReplyTo, Ref, Status), ?MODULE:loop(S); {call, ReplyTo, Ref, {gen_target, Dir}} -> - Reply = reltool_target:gen_target(S#state.sys, Dir), + Reply = reltool_target:gen_target(sys_all_apps(S), Dir), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {call, ReplyTo, Ref, gen_spec} -> - Reply = reltool_target:gen_spec(S#state.sys), + Reply = reltool_target:gen_spec(sys_all_apps(S)), reltool_utils:reply(ReplyTo, Ref, Reply), ?MODULE:loop(S); {'EXIT', Pid, Reason} when Pid =:= S#state.parent_pid -> @@ -422,101 +409,215 @@ loop(#state{common = C, sys = Sys} = S) -> ?MODULE:loop(S) end. - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -do_set_app(#state{sys = Sys} = S, App, Status) -> - AppName = App#app.name, - {App2, Status2} = refresh_app(App, false, Status), - Apps = Sys#sys.apps, - Apps2 = lists:keystore(AppName, #app.name, Apps, App2), - Escripts = [A#app.active_dir || A <- Apps2, A#app.is_escript], - Sys2 = Sys#sys{apps = Apps2, escripts = Escripts}, - {S#state{sys = Sys2}, Status2}. - -analyse(#state{common = C, - sys = #sys{apps = Apps0, rels = Rels} = Sys} = S, - Status) -> - Apps = lists:keydelete(?MISSING_APP_NAME, #app.name, Apps0), - ets:delete_all_objects(C#common.app_tab), - ets:delete_all_objects(C#common.mod_tab), - ets:delete_all_objects(C#common.mod_used_by_tab), - MissingApp = default_app(?MISSING_APP_NAME, "missing"), - ets:insert(C#common.app_tab, MissingApp), - - {RevRelApps, Status2} = apps_in_rels(Rels, Apps, Status), - RelApps2 = lists:reverse(RevRelApps), - {Apps2, Status3} = - lists:mapfoldl(fun(App, Acc) -> - app_init_is_included(C, Sys, App, RelApps2, Acc) - end, - Status2, - Apps), - Apps3 = - case app_propagate_is_included(C, Sys, Apps2, []) of - [] -> - Apps2; - MissingMods -> - %% io:format("Missing mods: ~p\n", [MissingMods]), - MissingApp2 = MissingApp#app{label = ?MISSING_APP_TEXT, - info = missing_app_info(""), - mods = MissingMods, - status = missing, - uses_mods = []}, - [MissingApp2 | Apps2] - end, - app_propagate_is_used_by(C, Apps3), - {Apps4,Status4} = app_recap_dependencies(C, Sys, Apps3, [], Status3), - %% io:format("Missing app: ~p\n", - %% [lists:keysearch(?MISSING_APP_NAME, #app.name, Apps4)]), - Sys2 = Sys#sys{apps = Apps4}, - - case verify_config(RelApps2, Sys2, Status4) of - {ok, _Warnings} = Status5 -> - {S#state{sys = Sys2}, Status5}; - {error, _} = Status5 -> - {S, Status5} + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +do_set_apps(#state{sys = Sys} = S, ChangedApps) -> + %% Create new list of configured applications + SysApps = app_update_config(ChangedApps, Sys#sys.apps), + S#state{sys = Sys#sys{apps = SysApps}}. + +%% Re-create the #sys.apps list by +%% 1) taking configurable fields from the changed #app records and +%% create new default records +%% 2) removing #app records if no configurable fields are set +%% 3) keeping #app records that are not changed +app_update_config([#app{name=Name,is_escript={inlined,Escript}}|_],_SysApps) -> + reltool_utils:throw_error("Application ~p is inlined in ~p. Can not change " + "configuration for an inlined application.", + [Name,Escript]); +app_update_config([Config|Configs],SysApps) -> + NewSysApps = + case app_set_config_only(Config) of + {delete,Name} -> + lists:keydelete(Name,#app.name,SysApps); + New -> + lists:ukeymerge(#app.name,[New],SysApps) + end, + app_update_config(Configs,NewSysApps); +app_update_config([],SysApps) -> + SysApps. + +app_set_config_only(#app{mods=ConfigMods} = Config) -> + app_set_config_only(mod_set_config_only(ConfigMods),Config). + +app_set_config_only([],#app{name = Name, + incl_cond = undefined, + mod_cond = undefined, + use_selected_vsn = undefined, + debug_info = undefined, + app_file = undefined, + app_type = undefined, + incl_app_filters = undefined, + excl_app_filters = undefined, + incl_archive_filters = undefined, + excl_archive_filters = undefined, + archive_opts = undefined, + is_escript = false})-> + {delete,Name}; +app_set_config_only(Mods,#app{name = Name, + incl_cond = InclCond, + mod_cond = ModCond, + use_selected_vsn = UseSelectedVsn, + debug_info = DebugInfo, + app_file = AppFile, + app_type = AppType, + incl_app_filters = InclAppFilters, + excl_app_filters = ExclAppFilters, + incl_archive_filters = InclArchiveFilters, + excl_archive_filters = ExclArchiveFilters, + archive_opts = ArchiveOpts, + vsn = Vsn, + is_escript = IsEscript, + label = Label, + info = Info, + active_dir = ActiveDir, + sorted_dirs = SortedDirs}) -> + App = (default_app(Name))#app{incl_cond = InclCond, + mod_cond = ModCond, + use_selected_vsn = UseSelectedVsn, + debug_info = DebugInfo, + app_file = AppFile, + app_type = AppType, + incl_app_filters = InclAppFilters, + excl_app_filters = ExclAppFilters, + incl_archive_filters = InclArchiveFilters, + excl_archive_filters = ExclArchiveFilters, + archive_opts = ArchiveOpts, + vsn = Vsn, + mods = Mods}, + + if IsEscript -> + %% Some fields shall only be set if it is an escript, e.g. label + %% must never be set for any other applications since that will + %% prevent refreshing. + App#app{is_escript = IsEscript, + active_dir = ActiveDir, + sorted_dirs = SortedDirs, + label = Label, + info = Info}; + UseSelectedVsn =:= dir -> + %% Must not loose active_dir if it is configured to be used + App#app{active_dir = ActiveDir, + sorted_dirs = [ActiveDir]}; + true -> + App end. -apps_in_rels(Rels, Apps, Status) -> - lists:foldl(fun(Rel, {RelApps, S}) -> - {MoreRelApps, S2} = apps_in_rel(Rel, Apps, S), - {MoreRelApps ++ RelApps, S2} - end, - {[], Status}, - Rels). +mod_set_config_only(ConfigMods) -> + [#mod{name = Name, + incl_cond = InclCond, + debug_info = DebugInfo} || + #mod{name = Name, + incl_cond = InclCond, + debug_info = DebugInfo} <- ConfigMods, + (InclCond =/= undefined) orelse (DebugInfo =/= undefined)]. -apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps, Status) -> - Mandatory = [{RelName, kernel}, {RelName, stdlib}], - Other = [{RelName, AppName} || - RA <- RelApps, - AppName <- [RA#rel_app.name | RA#rel_app.incl_apps], - not lists:keymember(AppName, 2, Mandatory)], - more_apps_in_rels(Mandatory ++ Other, Apps, [], Status). -more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc, Status) -> + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +analyse(#state{sys=Sys} = S, Apps, Status) -> + %% Create a list of {RelName,AppName}, one element for each + %% AppName that needs to be included for the given release. + RelApps = apps_in_rels(Sys#sys.rels, Apps), + + %% Initiate is_pre_included and is_included for all applications + %% based on #sys.incl_cond, #app.incl_cond and if the application + %% is included in a release (rel spec - see apps_in_rels above). + %% Then initiate the same for each module, and check that there + %% are no duplicated module names (in different applications) + %% where we can not decide which one to use. + %% Write all #app to app_tab and all #mod to mod_tab. + Status2 = apps_init_is_included(S, Apps, RelApps, Status), + + %% For each module that has #mod.is_included==true, propagate + %% is_included to the modules it uses. + propagate_is_included(S), + + %% Insert reverse dependencies - i.e. for each + %% #mod{name=Mod, uses_mods=[UsedMod]}, + %% insert an entry {UsedMod,Mod} in mod_used_by_tab. + propagate_is_used_by(S), + + %% Set the above reverse dependencies in #mod records + %% (used_by_mods) and accumulate in #app records. + %% Make sure #app.is_included is always true if some + %% #mod.is_included==true for at least one module in the app. + %% Set status=missing|ok for #app and #mod - indicates if module + %% (.beam file) is missing in file system. + app_recap_dependencies(S), + + %% Check that the boot_rel exists. + %% Check that all applications that are listed in a 'rel' spec are + %% also really included in the target release. + %% Check that all mandatory applications are included in all rels. + verify_config(S, RelApps, Status2). + +apps_in_rels(Rels, Apps) -> + AllRelApps = + lists:foldl(fun(Rel, RelApps) -> + MoreRelApps = apps_in_rel(Rel, Apps), + MoreRelApps ++ RelApps + end, + [], + Rels), + lists:reverse(AllRelApps). + +apps_in_rel(#rel{name = RelName, rel_apps = RelApps}, Apps) -> + Mandatory = [{RelName, kernel}, {RelName, stdlib}], + Other = + [{RelName, AppName} || + RA <- RelApps, + AppName <- [RA#rel_app.name | + %% Included applications in rel shall overwrite included + %% applications in .app. I.e. included applications in + %% .app shall only be used if it is not defined in rel. + case RA#rel_app.incl_apps of + undefined -> + case lists:keyfind(RA#rel_app.name, + #app.name, + Apps) of + #app{info = #app_info{incl_apps = IA}} -> + IA; + false -> + reltool_utils:throw_error( + "Release ~p uses non existing " + "application ~p", + [RelName,RA#rel_app.name]) + end; + IA -> + IA + end], + not lists:keymember(AppName, 2, Mandatory)], + more_apps_in_rels(Mandatory ++ Other, Apps, []). + +more_apps_in_rels([{RelName, AppName} = RA | RelApps], Apps, Acc) -> case lists:member(RA, Acc) of true -> - more_apps_in_rels(RelApps, Apps, Acc, Status); + more_apps_in_rels(RelApps, Apps, Acc); false -> case lists:keyfind(AppName, #app.name, Apps) of #app{info = #app_info{applications = InfoApps}} -> Extra = [{RelName, N} || N <- InfoApps], - {Acc2, Status2} = - more_apps_in_rels(Extra, Apps, [RA | Acc], Status), - more_apps_in_rels(RelApps, Apps, Acc2, Status2); + Acc2 = more_apps_in_rels(Extra, Apps, [RA | Acc]), + more_apps_in_rels(RelApps, Apps, Acc2); false -> - Text = lists:concat(["Release ", RelName, - " uses non existing application ", - AppName]), - Status2 = reltool_utils:return_first_error(Status, Text), - more_apps_in_rels(RelApps, Apps, Acc, Status2) + reltool_utils:throw_error( + "Release ~p uses non existing application ~p", + [RelName,AppName]) end end; -more_apps_in_rels([], _Apps, Acc, Status) -> - {Acc, Status}. +more_apps_in_rels([], _Apps, Acc) -> + Acc. -app_init_is_included(C, - Sys, + +apps_init_is_included(S, Apps, RelApps, Status) -> + lists:foldl(fun(App, AccStatus) -> + app_init_is_included(S, App, RelApps, AccStatus) + end, + Status, + Apps). + +app_init_is_included(#state{app_tab = AppTab, mod_tab = ModTab, sys=Sys}, #app{name = AppName, mods = Mods} = A, RelApps, Status) -> @@ -538,18 +639,16 @@ app_init_is_included(C, {exclude, []} -> {undefined, false, false, Status}; {exclude, [RelName | _]} -> % App is included in at least one rel - Text = lists:concat(["Application ", AppName, " is used " - "in release ", RelName, " and cannot " - "be excluded"]), - TmpStatus = reltool_utils:return_first_error(Status, Text), - {undefined, false, false, TmpStatus}; + reltool_utils:throw_error( + "Application ~p is used in release ~p and cannot be excluded", + [AppName,RelName]); {derived, []} -> {undefined, undefined, undefined, Status}; {derived, [_ | _]} -> % App is included in at least one rel {true, undefined, true, Status} end, {Mods2,Status3} = lists:mapfoldl(fun(Mod,Acc) -> - mod_init_is_included(C, + mod_init_is_included(ModTab, Mod, ModCond, AppCond, @@ -562,10 +661,10 @@ app_init_is_included(C, is_pre_included = IsPreIncl, is_included = IsIncl, rels = Rels}, - ets:insert(C#common.app_tab, A2), - {A2, Status3}. + ets:insert(AppTab, A2), + Status3. -mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> +mod_init_is_included(ModTab, M, ModCond, AppCond, Default, Status) -> %% print(M#mod.name, hipe, "incl_cond -> ~p\n", [AppCond]), IsIncl = case AppCond of @@ -602,43 +701,33 @@ mod_init_is_included(C, M, ModCond, AppCond, Default, Status) -> M2 = M#mod{is_pre_included = IsIncl, is_included = IsIncl}, Status2 = - case ets:lookup(C#common.mod_tab,M#mod.name) of + case ets:lookup(ModTab,M#mod.name) of [Existing] -> case {Existing#mod.is_included,IsIncl} of {false,_} -> - Warning = - lists:concat( - ["Module ",M#mod.name, - " exists in applications ", Existing#mod.app_name, - " and ", M#mod.app_name, - ". Using module from application ", - M#mod.app_name, "."]), - ets:insert(C#common.mod_tab, M2), - reltool_utils:add_warning(Status,Warning); + ets:insert(ModTab, M2), + reltool_utils:add_warning( + "Module ~p exists in applications ~p and ~p. " + "Using module from application ~p.", + [M#mod.name, Existing#mod.app_name, + M#mod.app_name, M#mod.app_name], + Status); {_,false} -> - Warning = - lists:concat( - ["Module ",M#mod.name, - " exists in applications ", Existing#mod.app_name, - " and ", M#mod.app_name, - ". Using module from application ", - Existing#mod.app_name, "."]), - - %% Don't insert in mod_tab - using Existing - reltool_utils:add_warning(Status,Warning); + %% Don't insert in ModTab - using Existing + reltool_utils:add_warning( + "Module ~p exists in applications ~p and ~p. " + "Using module from application ~p.", + [M#mod.name, Existing#mod.app_name, + M#mod.app_name,Existing#mod.app_name], + Status); {_,_} -> - Error = - lists:concat( - ["Module ",M#mod.name, - " potentially included by ", - "two different applications: ", - Existing#mod.app_name, " and ", - M#mod.app_name, "."]), - %% Don't insert in mod_tab - using Existing - reltool_utils:return_first_error(Status,Error) + reltool_utils:throw_error( + "Module ~p potentially included by two different " + "applications: ~p and ~p.", + [M#mod.name,Existing#mod.app_name,M#mod.app_name]) end; [] -> - ets:insert(C#common.mod_tab, M2), + ets:insert(ModTab, M2), Status end, @@ -651,55 +740,43 @@ false_to_undefined(Bool) -> _ -> Bool end. -app_propagate_is_included(C, Sys, [#app{mods = Mods} = A | Apps], Acc) -> - Acc2 = mod_propagate_is_included(C, Sys, A, Mods, Acc), - app_propagate_is_included(C, Sys, Apps, Acc2); -app_propagate_is_included(_C, _Sys, [], Acc) -> - Acc. - -mod_propagate_is_included(C, Sys, A, [#mod{name = ModName} | Mods], Acc) -> - Acc2 = - case ets:lookup(C#common.mod_tab, ModName) of - [M2] when M2#mod.app_name=:=A#app.name -> - %% print(ModName, file, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - %% print(ModName, filename, "Maybe Prop ~p -> ~p\n", - %% [M2, M2#mod.is_included]), - case M2#mod.is_included of - true -> - %% Propagate include mark - mod_mark_is_included(C,Sys,ModName,M2#mod.uses_mods,Acc); - false -> - Acc; - undefined -> - Acc - end; - [_] -> - %% This module is currently used from a different application - %% Ignore - Acc - end, - mod_propagate_is_included(C, Sys, A, Mods, Acc2); -mod_propagate_is_included(_C, _Sys, _A, [], Acc) -> - Acc. +%% Return the list for {ModName, UsesModNames} for all modules where +%% #mod.is_included==true. +get_all_mods_and_dependencies(S) -> + ets:select(S#state.mod_tab, [{#mod{name='$1', + uses_mods='$2', + is_included=true, + _='_'}, + [], + [{{'$1','$2'}}]}]). + +propagate_is_included(S) -> + case lists:flatmap( + fun({ModName,UsesModNames}) -> + mod_mark_is_included(S,ModName,UsesModNames,[]) + end, + get_all_mods_and_dependencies(S)) of + [] -> + ok; + MissingMods -> + MissingApp = default_app(?MISSING_APP_NAME, "missing"), + MissingApp2 = MissingApp#app{label = ?MISSING_APP_TEXT, + info = missing_app_info(""), + mods = MissingMods, + status = missing, + uses_mods = []}, + ets:insert(S#state.app_tab, MissingApp2), + ok + end. -mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> +mod_mark_is_included(#state{app_tab=AppTab, mod_tab=ModTab, sys=Sys} = S, + UsedByName, [ModName | ModNames], Acc) -> Acc3 = - case ets:lookup(C#common.mod_tab, ModName) of + case ets:lookup(ModTab, ModName) of [M] -> - %% print(UsedByName, file, "Maybe Mark ~p -> ~p\n", - %% [M, M#mod.is_included]), - %% print(UsedByName, filename, "Maybe Mark ~p -> ~p\n", - %% [M, M#mod.is_included]), case M#mod.is_included of - true -> - %% Already marked - Acc; - false -> - %% Already marked - Acc; undefined -> - %% Mark and propagate + %% Not yet marked => mark and propagate M2 = case M#mod.incl_cond of include -> @@ -711,16 +788,10 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> undefined -> M#mod{is_included = true} end, - ets:insert(C#common.mod_tab, M2), - %% io:format("Propagate mod: ~p -> ~p (~p)\n", - %% [UsedByName, ModName, M#mod.incl_cond]), - [A] = ets:lookup(C#common.app_tab, M2#mod.app_name), + ets:insert(ModTab, M2), + [A] = ets:lookup(AppTab, M2#mod.app_name), Acc2 = case A#app.is_included of - true -> - Acc; - false -> - Acc; undefined -> ModCond = case A#app.mod_cond of @@ -738,63 +809,50 @@ mod_mark_is_included(C, Sys, UsedByName, [ModName | ModNames], Acc) -> end end, Mods = lists:filter(Filter, A#app.mods), - %% io:format("Propagate app: ~p ~p -> ~p\n", - %% [UsedByName, A#app.name, - %% [M3#mod.name || M3 <- Mods]]), A2 = A#app{is_included = true}, - ets:insert(C#common.app_tab, A2), - mod_mark_is_included(C, - Sys, + ets:insert(AppTab, A2), + mod_mark_is_included(S, ModName, [M3#mod.name || M3 <- Mods], - Acc) + Acc); + _ -> + %% Already marked true or false + Acc end, - mod_mark_is_included(C, - Sys, - ModName, - M2#mod.uses_mods, - Acc2) + mod_mark_is_included(S, ModName, M2#mod.uses_mods, Acc2); + _ -> + %% Already marked true or false + Acc end; [] -> M = missing_mod(ModName, ?MISSING_APP_NAME), M2 = M#mod{is_included = true}, - ets:insert(C#common.mod_tab, M2), - ets:insert(C#common.mod_used_by_tab, {UsedByName, ModName}), + ets:insert(ModTab, M2), [M2 | Acc] end, - mod_mark_is_included(C, Sys, UsedByName, ModNames, Acc3); -mod_mark_is_included(_C, _Sys, _UsedByName, [], Acc) -> + mod_mark_is_included(S, UsedByName, ModNames, Acc3); +mod_mark_is_included(_S, _UsedByName, [], Acc) -> Acc. -app_propagate_is_used_by(C, [#app{mods = Mods, name = Name} | Apps]) -> - case Name =:= ?MISSING_APP_NAME of - true -> ok; - false -> ok - end, - mod_propagate_is_used_by(C, Mods), - app_propagate_is_used_by(C, Apps); -app_propagate_is_used_by(_C, []) -> - ok. +propagate_is_used_by(S) -> + lists:foreach( + fun({Mod,UsesMods}) -> + lists:foreach( + fun(UsedMod) -> + ets:insert(S#state.mod_used_by_tab,{UsedMod,Mod}) + end, + UsesMods) + end, + get_all_mods_and_dependencies(S)). -mod_propagate_is_used_by(C, [#mod{name = ModName} | Mods]) -> - [M] = ets:lookup(C#common.mod_tab, ModName), - case M#mod.is_included of - true -> - [ets:insert(C#common.mod_used_by_tab, {UsedModName, ModName}) || - UsedModName <- M#mod.uses_mods]; - false -> - ignore; - undefined -> - ignore - end, - mod_propagate_is_used_by(C, Mods); -mod_propagate_is_used_by(_C, []) -> - ok. -app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Apps], Acc, Status) -> - {Mods2, IsIncl2, Status2} = - mod_recap_dependencies(C, Sys, A, Mods, [], IsIncl, Status), +app_recap_dependencies(S) -> + ets:foldl(fun(App,_) -> app_recap_dependencies(S,App) end, + ok, S#state.app_tab). + +app_recap_dependencies(S, #app{mods = Mods, is_included = IsIncl} = A) -> + {Mods2, IsIncl2} = mod_recap_dependencies(S, A, Mods, [], IsIncl), AppStatus = case lists:keymember(missing, #mod.status, Mods2) of true -> missing; @@ -803,12 +861,12 @@ app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Ap UsesMods = [M#mod.uses_mods || M <- Mods2, M#mod.is_included =:= true], UsesMods2 = lists:usort(lists:flatten(UsesMods)), UsesApps = [M#mod.app_name || ModName <- UsesMods2, - M <- ets:lookup(C#common.mod_tab, ModName)], + M <- ets:lookup(S#state.mod_tab, ModName)], UsesApps2 = lists:usort(UsesApps), UsedByMods = [M#mod.used_by_mods || M <- Mods2, M#mod.is_included =:= true], UsedByMods2 = lists:usort(lists:flatten(UsedByMods)), UsedByApps = [M#mod.app_name || ModName <- UsedByMods2, - M <- ets:lookup(C#common.mod_tab, ModName)], + M <- ets:lookup(S#state.mod_tab, ModName)], UsedByApps2 = lists:usort(UsedByApps), A2 = A#app{mods = Mods2, @@ -818,13 +876,11 @@ app_recap_dependencies(C, Sys, [#app{mods = Mods, is_included = IsIncl} = A | Ap uses_apps = UsesApps2, used_by_apps = UsedByApps2, is_included = IsIncl2}, - ets:insert(C#common.app_tab,A2), - app_recap_dependencies(C, Sys, Apps, [A2 | Acc], Status2); -app_recap_dependencies(_C, _Sys, [], Acc, Status) -> - {lists:reverse(Acc), Status}. + ets:insert(S#state.app_tab,A2), + ok. -mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, Status) -> - case ets:lookup(C#common.mod_tab, ModName) of +mod_recap_dependencies(S, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl) -> + case ets:lookup(S#state.mod_tab, ModName) of [M2] when M2#mod.app_name=:=A#app.name -> ModStatus = do_get_status(M2), %% print(M2#mod.name, hipe, "status -> ~p\n", [ModStatus]), @@ -832,32 +888,28 @@ mod_recap_dependencies(C, Sys, A, [#mod{name = ModName}=M1 | Mods], Acc, IsIncl, case M2#mod.is_included of true -> UsedByMods = - [N || {_, N} <- ets:lookup(C#common.mod_used_by_tab, + [N || {_, N} <- ets:lookup(S#state.mod_used_by_tab, ModName)], {true, M2#mod{status = ModStatus, used_by_mods = UsedByMods}}; _ -> {IsIncl, M2#mod{status = ModStatus, used_by_mods = []}} end, - ets:insert(C#common.mod_tab, M3), - mod_recap_dependencies(C, Sys, A, Mods, [M3 | Acc], IsIncl2, Status); + ets:insert(S#state.mod_tab, M3), + mod_recap_dependencies(S, A, Mods, [M3 | Acc], IsIncl2); [_] when A#app.is_included==false; M1#mod.incl_cond==exclude -> %% App is explicitely excluded so it is ok that the module %% record does not exist for this module in this %% application. - mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status); + mod_recap_dependencies(S, A, Mods, [M1 | Acc], IsIncl); [M2] -> %% A module is potensially included by multiple %% applications. This is not allowed! - Error = - lists:concat( - ["Module ",ModName, - " potentially included by two different applications: ", - A#app.name, " and ", M2#mod.app_name, "."]), - Status2 = reltool_utils:return_first_error(Status,Error), - mod_recap_dependencies(C, Sys, A, Mods, [M1 | Acc], IsIncl, Status2) + reltool_utils:throw_error( + "Module ~p potentially included by two different applications: " + "~p and ~p", [ModName,A#app.name, " and ", M2#mod.app_name, "."]) end; -mod_recap_dependencies(_C, _Sys, _A, [], Acc, IsIncl, Status) -> - {lists:reverse(Acc), IsIncl, Status}. +mod_recap_dependencies(_S, _A, [], Acc, IsIncl) -> + {lists:reverse(Acc), IsIncl}. do_get_status(M) -> if @@ -867,48 +919,49 @@ do_get_status(M) -> ok end. -shrink_sys(#state{sys = #sys{apps = Apps} = Sys} = S) -> - Apps2 = lists:zf(fun filter_app/1, Apps), - S#state{sys = Sys#sys{apps = Apps2}}. - -filter_app(A) -> - Mods = [M#mod{is_app_mod = undefined, - is_ebin_mod = undefined, - uses_mods = undefined, - exists = false} || - M <- A#app.mods, - M#mod.incl_cond =/= undefined], - if - A#app.is_escript -> - {true, A#app{vsn = undefined, - label = undefined, - info = undefined, - mods = [], - uses_mods = undefined}}; - Mods =:= [], - A#app.mod_cond =:= undefined, - A#app.incl_cond =:= undefined, - A#app.use_selected_vsn =:= undefined -> - false; +verify_config(#state{app_tab=AppTab, sys=#sys{boot_rel = BootRel, rels = Rels}}, + RelApps, Status) -> + case lists:keymember(BootRel, #rel.name, Rels) of true -> - {Dir, Dirs, OptVsn} = - case A#app.use_selected_vsn of - undefined -> - {shrinked, [], undefined}; - false -> - {shrinked, [], undefined}; - true -> - {A#app.active_dir, [A#app.active_dir], A#app.vsn} - end, - {true, A#app{active_dir = Dir, - sorted_dirs = Dirs, - vsn = OptVsn, - label = undefined, - info = undefined, - mods = Mods, - uses_mods = undefined}} + Status2 = lists:foldl(fun(RA, Acc) -> + check_app(AppTab, RA, Acc) end, + Status, + RelApps), + lists:foldl(fun(#rel{name = RelName}, Acc)-> + check_rel(RelName, RelApps, Acc) + end, + Status2, + Rels); + false -> + reltool_utils:throw_error( + "Release ~p is mandatory (used as boot_rel)",[BootRel]) + end. + +check_app(AppTab, {RelName, AppName}, Status) -> + case ets:lookup(AppTab, AppName) of + [#app{is_pre_included=IsPreIncl, is_included=IsIncl}] + when IsPreIncl; IsIncl -> + Status; + _ -> + reltool_utils:throw_error( + "Release ~p uses non included application ~p",[RelName,AppName]) end. +check_rel(RelName, RelApps, Status) -> + EnsureApp = + fun(AppName, Acc) -> + case lists:member({RelName, AppName}, RelApps) of + true -> + Acc; + false -> + reltool_utils:throw_error( + "Mandatory application ~p is not included in " + "release ~p", [AppName,RelName]) + end + end, + Mandatory = [kernel, stdlib], + lists:foldl(EnsureApp, Status, Mandatory). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% refresh_app(#app{name = AppName, @@ -937,30 +990,57 @@ refresh_app(#app{name = AppName, AppName, DefaultVsn, Status), + + %% And read all modules from ebin and create + %% #mod record with dependencies (uses_mods). {AI, read_ebin_mods(Ebin, AppName), Status2}; - true -> + _ -> {App#app.info, Mods, Status} end, - %% Add non-existing modules - AppInfoMods = AppInfo#app_info.modules, - AppModNames = - case AppInfo#app_info.mod of - {StartModName, _} -> - case lists:member(StartModName, AppInfoMods) of - true -> AppInfoMods; - false -> [StartModName | AppInfoMods] - end; - undefined -> - AppInfoMods - end, - MissingMods = add_missing_mods(AppName, EbinMods, AppModNames), + %% Add non-existing modules - i.e. create default #mod + %% records for all modules that are listed in .app file + %% but do not exist in ebin. + AppInfoMods = lists:usort(AppInfo#app_info.modules), + Status4 = + case AppInfo#app_info.modules -- AppInfoMods of + [] -> + Status3; + DuplicatedMods -> + lists:foldl( + fun(M,S) -> + reltool_utils:add_warning( + "Module ~p duplicated in app file for " + "application ~p.", [M, AppName], S) + end, + Status3, + DuplicatedMods) + end, + AppModNames = + case AppInfo#app_info.mod of + {StartModName, _} -> + case lists:member(StartModName, AppInfoMods) of + true -> AppInfoMods; + false -> [StartModName | AppInfoMods] + end; + undefined -> + AppInfoMods + end, + MissingMods = add_missing_mods(AppName, EbinMods, AppModNames), - %% Add optional user config for each module + %% Add optional user config for each module. + %% The #mod records that are already in the #app record at + %% this point do only contain user defined configuration + %% (set by parse_options/4). So here we merge with the + %% default records from above. Mods2 = add_mod_config(MissingMods ++ EbinMods, Mods), - %% Set app flag for each module in app file + %% Set app flag for each module in app file, i.e. the flag + %% which indicates if the module is listed in the .app + %% file or not. The start module also get the flag set to true. Mods3 = set_mod_flags(Mods2, AppModNames), + + %% Finally, set label and update the #app record AppVsn = AppInfo#app_info.vsn, AppLabel = case AppVsn of @@ -971,7 +1051,7 @@ refresh_app(#app{name = AppName, label = AppLabel, info = AppInfo, mods = lists:keysort(#mod.name, Mods3)}, - {App2, Status3}; + {App2, Status4}; true -> {App, Status} end. @@ -988,22 +1068,21 @@ read_app_info(AppFileOrBin, AppFile, AppName, DefaultVsn, Status) -> AI = #app_info{vsn = DefaultVsn}, parse_app_info(AppFile, Info, AI, Status); {ok, _BadApp} -> - Text = lists:concat([AppName, - ": Illegal contents in app file ", AppFile, - ", application tuple with arity 3 expected."]), {missing_app_info(DefaultVsn), - reltool_utils:add_warning(Status, Text)}; + reltool_utils:add_warning("~p: Illegal contents in app file ~p, " + "application tuple with arity 3 expected.", + [AppName,AppFile], + Status)}; {error, Text} when Text =:= EnoentText -> - Text2 = lists:concat([AppName, - ": Missing app file ", AppFile, "."]), {missing_app_info(DefaultVsn), - reltool_utils:add_warning(Status, Text2)}; + reltool_utils:add_warning("~p: Missing app file ~p.", + [AppName,AppFile], + Status)}; {error, Text} -> - Text2 = lists:concat([AppName, - ": Cannot parse app file ", - AppFile, " (", Text, ")."]), {missing_app_info(DefaultVsn), - reltool_utils:add_warning(Status, Text2)} + reltool_utils:add_warning("~p: Cannot parse app file ~p (~p).", + [AppName,AppFile,Text], + Status)} end. parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) -> @@ -1037,10 +1116,11 @@ parse_app_info(File, [{Key, Val} | KeyVals], AI, Status) -> parse_app_info(File, KeyVals, AI#app_info{start_phases = Val}, Status); _ -> - String = lists:concat(["Unexpected item ", - Key, "in app file ", File]), - parse_app_info(File, KeyVals, AI, - reltool_utils:add_warning(Status, String)) + Status2 = + reltool_utils:add_warning("Unexpected item ~p in app file ~p.", + [Key,File], + Status), + parse_app_info(File, KeyVals, AI, Status2) end; parse_app_info(_, [], AI, Status) -> {AI, Status}. @@ -1171,12 +1251,54 @@ set_mod_flags(Mods, AppModNames) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_get_config(S, InclDef, InclDeriv) -> - S2 = + AppTab = S#state.app_tab, + Sys = case InclDeriv of - false -> shrink_sys(S); - true -> S + false -> + %% Only the apps that exist in #sys.apps shall be + %% included,and they shall be minimized + Apps = [shrink_app(App) || + #app{name=Name} <- (S#state.sys)#sys.apps, + App <- ets:lookup(AppTab,Name)], + (S#state.sys)#sys{apps=Apps}; + true -> + sys_all_apps(S) end, - reltool_target:gen_config(S2#state.sys, InclDef). + reltool_target:gen_config(Sys, InclDef). + +shrink_app(A) -> + Mods = [M#mod{is_app_mod = undefined, + is_ebin_mod = undefined, + uses_mods = undefined, + exists = false} || + M <- A#app.mods, + M#mod.incl_cond =/= undefined], + if + A#app.is_escript -> + A#app{vsn = undefined, + label = undefined, + info = undefined, + mods = [], + uses_mods = undefined}; + true -> + {Dir, Dirs, OptVsn} = + case A#app.use_selected_vsn of + undefined -> + {undefined, [], undefined}; + vsn -> + {undefined, [], A#app.vsn}; + dir -> + {A#app.active_dir, [A#app.active_dir], undefined} + end, + A#app{active_dir = Dir, + sorted_dirs = Dirs, + vsn = OptVsn, + label = undefined, + info = undefined, + mods = Mods, + uses_mods = undefined} + end. + do_save_config(S, Filename, InclDef, InclDeriv) -> {ok, Config} = do_get_config(S, InclDef, InclDeriv), @@ -1187,132 +1309,83 @@ do_save_config(S, Filename, InclDef, InclDeriv) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% do_load_config(S, SysConfig) -> - OldSys = S#state.sys, - S2 = shrink_sys(S), - ShrinkedSys = S2#state.sys, - {NewSys, Status} = - read_config(ShrinkedSys#sys{apps = []}, SysConfig, {ok, []}), - case Status of - {ok, _Warnings} -> - Force = false, - {MergedSys, Status2} = merge_config(OldSys, NewSys, Force, Status), - {S3, Status3} = - analyse(S2#state{sys = MergedSys, old_sys = OldSys}, Status2), - S4 = - case Status3 of - {ok, _Warnings2} -> - S3#state{status = Status3, old_status = S#state.status}; - {error, _} -> - %% Keep old state - S - end, - {S4, Status3}; - {error, _} -> - %% Keep old state - {S, Status} - end. + S#state{sys = read_config(default_sys(), SysConfig)}. -read_config(OldSys, Filename, Status) when is_list(Filename) -> +read_config(OldSys, Filename) when is_list(Filename) -> case file:consult(Filename) of {ok, [SysConfig | _]} -> - read_config(OldSys, SysConfig, Status); + read_config(OldSys, SysConfig); {ok, Content} -> - Text = lists:flatten(io_lib:format("~p", [Content])), - {OldSys, - reltool_utils:return_first_error(Status, - "Illegal file content: " ++ - Text)}; + reltool_utils:throw_error("Illegal file content: ~p",[Content]); {error, Reason} -> - Text = file:format_error(Reason), - {OldSys, - reltool_utils:return_first_error(Status, - "Illegal config file " ++ - Filename ++ ": " ++ Text)} + reltool_utils:throw_error("Illegal config file ~p: ~s", + [Filename,file:format_error(Reason)]) end; -read_config(OldSys, {sys, KeyVals}, Status) -> - {NewSys, Status2} = - decode(OldSys#sys{apps = [], rels = []}, KeyVals, Status), - case Status2 of - {ok, _Warnings} -> % BUGBUG: handle warnings - Apps = [A#app{mods = lists:sort(A#app.mods)} || - A <- NewSys#sys.apps], - case NewSys#sys.rels of - [] -> Rels = reltool_utils:default_rels(); - Rels -> ok - end, - NewSys2 = NewSys#sys{apps = lists:sort(Apps), - rels = lists:sort(Rels)}, - case lists:keymember(NewSys2#sys.boot_rel, - #rel.name, - NewSys2#sys.rels) of - true -> - {NewSys2, Status2}; - false -> - Text2 = lists:concat(["Release " ++ NewSys2#sys.boot_rel, - " is mandatory (used as boot_rel)"]), - {OldSys, reltool_utils:return_first_error(Status2, Text2)} - end; - {error, _} -> - %% Keep old state - {OldSys, Status2} +read_config(OldSys, {sys, KeyVals}) -> + NewSys = decode(OldSys#sys{apps = [], rels = []}, KeyVals), + Apps = [A#app{mods = lists:sort(A#app.mods)} || A <- NewSys#sys.apps], + Rels = + case NewSys#sys.rels of + [] -> reltool_utils:default_rels(); + Rs -> Rs + end, + NewSys2 = NewSys#sys{apps = lists:sort(Apps), + rels = lists:sort(Rels)}, + case lists:keymember(NewSys2#sys.boot_rel, #rel.name, NewSys2#sys.rels) of + true -> + NewSys2; + false -> + reltool_utils:throw_error( + "Release ~p is mandatory (used as boot_rel)", + [NewSys2#sys.boot_rel]) end; -read_config(OldSys, BadConfig, Status) -> - Text = lists:flatten(io_lib:format("~p", [BadConfig])), - {OldSys, - reltool_utils:return_first_error(Status, "Illegal content: " ++ Text)}. +read_config(_OldSys, BadConfig) -> + reltool_utils:throw_error("Illegal content: ~p", [BadConfig]). -decode(#sys{apps = Apps} = Sys, [{erts = Name, AppKeyVals} | SysKeyVals], - Status) +decode(#sys{apps = Apps} = Sys, [{erts = Name, AppKeyVals} | SysKeyVals]) when is_atom(Name), is_list(AppKeyVals) -> App = default_app(Name), - {App2, Status2} = decode(App, AppKeyVals, Status), - decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals, Status2); -decode(#sys{apps = Apps} = Sys, [{app, Name, AppKeyVals} | SysKeyVals], Status) + App2= decode(App, AppKeyVals), + decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals); +decode(#sys{apps = Apps} = Sys, [{app, Name, AppKeyVals} | SysKeyVals]) when is_atom(Name), is_list(AppKeyVals) -> App = default_app(Name), - {App2, Status2} = decode(App, AppKeyVals, Status), - decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals, Status2); + App2 = decode(App, AppKeyVals), + decode(Sys#sys{apps = [App2 | Apps]}, SysKeyVals); decode(#sys{apps = Apps, escripts = Escripts} = Sys, - [{escript, File, AppKeyVals} | SysKeyVals], Status) - when is_list(File), is_list(AppKeyVals) -> - {Name, Label} = split_escript_name(File), - App = default_app(Name, File), - App2 = App#app{is_escript = true, - label = Label, - info = missing_app_info(""), - active_dir = File, - sorted_dirs = [File]}, - {App3, Status2} = decode(App2, AppKeyVals, Status), - decode(Sys#sys{apps = [App3 | Apps], escripts = [File | Escripts]}, - SysKeyVals, - Status2); -decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals], - Status) + [{escript, File0, AppKeyVals} | SysKeyVals]) + when is_list(File0), is_list(AppKeyVals) -> + File = filename:absname(File0), + App = default_escript_app(File), + App2 = decode(App, AppKeyVals), + decode(Sys#sys{apps = [App2 | Apps], escripts = [File | Escripts]}, + SysKeyVals); +decode(#sys{rels = Rels} = Sys, [{rel, Name, Vsn, RelApps} | SysKeyVals]) when is_list(Name), is_list(Vsn), is_list(RelApps) -> Rel = #rel{name = Name, vsn = Vsn, rel_apps = []}, - {Rel2, Status2} = decode(Rel, RelApps, Status), - decode(Sys#sys{rels = [Rel2 | Rels]}, SysKeyVals, Status2); -decode(#sys{} = Sys, [{Key, Val} | KeyVals], Status) -> - {Sys3, Status3} = + Rel2 = decode(Rel, RelApps), + decode(Sys#sys{rels = [Rel2 | Rels]}, SysKeyVals); +decode(#sys{} = Sys, [{Key, Val} | KeyVals]) -> + Sys3 = case Key of root_dir when is_list(Val) -> - {Sys#sys{root_dir = Val}, Status}; + Sys#sys{root_dir = Val}; lib_dirs when is_list(Val) -> - {Sys#sys{lib_dirs = Val}, Status}; + Sys#sys{lib_dirs = Val}; mod_cond when Val =:= all; Val =:= app; Val =:= ebin; Val =:= derived; Val =:= none -> - {Sys#sys{mod_cond = Val}, Status}; + Sys#sys{mod_cond = Val}; incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> - {Sys#sys{incl_cond = Val}, Status}; + Sys#sys{incl_cond = Val}; boot_rel when is_list(Val) -> - {Sys#sys{boot_rel = Val}, Status}; + Sys#sys{boot_rel = Val}; emu_name when is_list(Val) -> - {Sys#sys{emu_name = Val}, Status}; + Sys#sys{emu_name = Val}; profile when Val =:= development; Val =:= embedded; Val =:= standalone -> @@ -1321,198 +1394,167 @@ decode(#sys{} = Sys, [{Key, Val} | KeyVals], Status) -> InclApp = reltool_utils:choose_default(incl_app_filters, Val, false), ExclApp = reltool_utils:choose_default(excl_app_filters, Val, false), AppType = reltool_utils:choose_default(embedded_app_type, Val, false), - {Sys#sys{profile = Val, - incl_sys_filters = dec_re(incl_sys_filters, - InclSys, - Sys#sys.incl_sys_filters), - excl_sys_filters = dec_re(excl_sys_filters, - ExclSys, - Sys#sys.excl_sys_filters), - incl_app_filters = dec_re(incl_app_filters, - InclApp, - Sys#sys.incl_app_filters), - excl_app_filters = dec_re(excl_app_filters, - ExclApp, - Sys#sys.excl_app_filters), - embedded_app_type = AppType}, - Status}; + Sys#sys{profile = Val, + incl_sys_filters = dec_re(incl_sys_filters, + InclSys, + Sys#sys.incl_sys_filters), + excl_sys_filters = dec_re(excl_sys_filters, + ExclSys, + Sys#sys.excl_sys_filters), + incl_app_filters = dec_re(incl_app_filters, + InclApp, + Sys#sys.incl_app_filters), + excl_app_filters = dec_re(excl_app_filters, + ExclApp, + Sys#sys.excl_app_filters), + embedded_app_type = AppType}; incl_sys_filters -> - {Sys#sys{incl_sys_filters = - dec_re(Key, - Val, - Sys#sys.incl_sys_filters)}, - Status}; + Sys#sys{incl_sys_filters = + dec_re(Key, Val, Sys#sys.incl_sys_filters)}; excl_sys_filters -> - {Sys#sys{excl_sys_filters = - dec_re(Key, - Val, - Sys#sys.excl_sys_filters)}, - Status}; + Sys#sys{excl_sys_filters = + dec_re(Key, Val, Sys#sys.excl_sys_filters)}; incl_app_filters -> - {Sys#sys{incl_app_filters = - dec_re(Key, - Val, - Sys#sys.incl_app_filters)}, - Status}; + Sys#sys{incl_app_filters = + dec_re(Key, Val, Sys#sys.incl_app_filters)}; excl_app_filters -> - {Sys#sys{excl_app_filters = - dec_re(Key, - Val, - Sys#sys.excl_app_filters)}, - Status}; + Sys#sys{excl_app_filters = + dec_re(Key, Val, Sys#sys.excl_app_filters)}; incl_archive_filters -> - {Sys#sys{incl_archive_filters = - dec_re(Key, - Val, - Sys#sys.incl_archive_filters)}, - Status}; + Sys#sys{incl_archive_filters = + dec_re(Key, Val, Sys#sys.incl_archive_filters)}; excl_archive_filters -> - {Sys#sys{excl_archive_filters = - dec_re(Key, - Val, - Sys#sys.excl_archive_filters)}, - Status}; + Sys#sys{excl_archive_filters = + dec_re(Key, Val, Sys#sys.excl_archive_filters)}; archive_opts when is_list(Val) -> - {Sys#sys{archive_opts = Val}, Status}; + Sys#sys{archive_opts = Val}; relocatable when Val =:= true; Val =:= false -> - {Sys#sys{relocatable = Val}, Status}; + Sys#sys{relocatable = Val}; rel_app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; Val =:= none -> - {Sys#sys{rel_app_type = Val}, Status}; + Sys#sys{rel_app_type = Val}; embedded_app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; Val =:= none; Val =:= undefined -> - {Sys#sys{embedded_app_type = Val}, Status}; + Sys#sys{embedded_app_type = Val}; app_file when Val =:= keep; Val =:= strip; Val =:= all -> - {Sys#sys{app_file = Val}, Status}; + Sys#sys{app_file = Val}; debug_info when Val =:= keep; Val =:= strip -> - {Sys#sys{debug_info = Val}, Status}; + Sys#sys{debug_info = Val}; _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {Sys, reltool_utils:return_first_error(Status, - "Illegal option: " ++ - Text)} + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end, - decode(Sys3, KeyVals, Status3); -decode(#app{} = App, [{Key, Val} | KeyVals], Status) -> - {App2, Status2} = + decode(Sys3, KeyVals); +decode(#app{} = App, [{Key, Val} | KeyVals]) -> + App2 = case Key of mod_cond when Val =:= all; Val =:= app; Val =:= ebin; Val =:= derived; Val =:= none -> - {App#app{mod_cond = Val}, Status}; + App#app{mod_cond = Val}; incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> - {App#app{incl_cond = Val}, Status}; + App#app{incl_cond = Val}; debug_info when Val =:= keep; Val =:= strip -> - {App#app{debug_info = Val}, Status}; + App#app{debug_info = Val}; app_file when Val =:= keep; Val =:= strip; Val =:= all -> - {App#app{app_file = Val}, Status}; + App#app{app_file = Val}; app_type when Val =:= permanent; Val =:= transient; Val =:= temporary; Val =:= load; Val =:= none; Val =:= undefined -> - {App#app{app_type = Val}, Status}; + App#app{app_type = Val}; incl_app_filters -> - {App#app{incl_app_filters = - dec_re(Key, - Val, - App#app.incl_app_filters)}, - Status}; + App#app{incl_app_filters = + dec_re(Key, Val, App#app.incl_app_filters)}; excl_app_filters -> - {App#app{excl_app_filters = - dec_re(Key, - Val, - App#app.excl_app_filters)}, - Status}; + App#app{excl_app_filters = + dec_re(Key, Val, App#app.excl_app_filters)}; incl_archive_filters -> - {App#app{incl_archive_filters = - dec_re(Key, - Val, - App#app.incl_archive_filters)}, - Status}; + App#app{incl_archive_filters = + dec_re(Key, Val, App#app.incl_archive_filters)}; excl_archive_filters -> - {App#app{excl_archive_filters = - dec_re(Key, - Val, - App#app.excl_archive_filters)}, - Status}; + App#app{excl_archive_filters = + dec_re(Key, Val, App#app.excl_archive_filters)}; archive_opts when is_list(Val) -> - {App#app{archive_opts = Val}, Status}; - vsn when is_list(Val) -> - {App#app{use_selected_vsn = true, vsn = Val}, Status}; + App#app{archive_opts = Val}; + vsn when is_list(Val), App#app.use_selected_vsn=:=undefined -> + App#app{use_selected_vsn = vsn, vsn = Val}; + lib_dir when is_list(Val), App#app.use_selected_vsn=:=undefined -> + case filelib:is_dir(Val) of + true -> + Dir = reltool_utils:normalize_dir(Val), + App#app{use_selected_vsn = dir, + active_dir = Dir, + sorted_dirs = [Dir]}; + false -> + reltool_utils:throw_error("Illegal lib dir for ~p: ~p", + [App#app.name, Val]) + end; + SelectVsn when SelectVsn=:=vsn; SelectVsn=:=lib_dir -> + reltool_utils:throw_error("Mutual exclusive options " + "'vsn' and 'lib_dir'",[]); _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {App, reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text)} + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end, - decode(App2, KeyVals, Status2); -decode(#app{mods = Mods} = App, [{mod, Name, ModKeyVals} | AppKeyVals], - Status) -> - {Mod, Status2} = decode(#mod{name = Name}, ModKeyVals, Status), - decode(App#app{mods = [Mod | Mods]}, AppKeyVals, Status2); -decode(#mod{} = Mod, [{Key, Val} | KeyVals], Status) -> - {Mod2, Status2} = + decode(App2, KeyVals); +decode(#app{mods = Mods} = App, [{mod, Name, ModKeyVals} | AppKeyVals]) -> + Mod = decode(#mod{name = Name}, ModKeyVals), + decode(App#app{mods = [Mod | Mods]}, AppKeyVals); +decode(#mod{} = Mod, [{Key, Val} | KeyVals]) -> + Mod2 = case Key of incl_cond when Val =:= include; Val =:= exclude; Val =:= derived -> - {Mod#mod{incl_cond = Val}, Status}; + Mod#mod{incl_cond = Val}; debug_info when Val =:= keep; Val =:= strip -> - {Mod#mod{debug_info = Val}, Status}; + Mod#mod{debug_info = Val}; _ -> - Text = lists:flatten(io_lib:format("~p", [{Key, Val}])), - {Mod, - reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text)} + reltool_utils:throw_error("Illegal option: ~p", [{Key, Val}]) end, - decode(Mod2, KeyVals, Status2); -decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals], Status) -> + decode(Mod2, KeyVals); +decode(#rel{rel_apps = RelApps} = Rel, [RelApp | KeyVals]) -> {ValidTypesAssigned, RA} = case RelApp of Name when is_atom(Name) -> {true, #rel_app{name = Name}}; - {Name, Type} when is_atom(Name) -> - {is_type(Type), #rel_app{name = Name, app_type = Type}}; {Name, InclApps} when is_atom(Name), is_list(InclApps) -> VI = lists:all(fun erlang:is_atom/1, InclApps), {VI, #rel_app{name = Name, incl_apps = InclApps}}; + {Name, Type} when is_atom(Name) -> + {is_type(Type), #rel_app{name = Name, app_type = Type}}; {Name, Type, InclApps} when is_atom(Name), is_list(InclApps) -> VT = is_type(Type), VI = lists:all(fun erlang:is_atom/1, InclApps), {VT andalso VI, #rel_app{name = Name, app_type = Type, incl_apps = InclApps}}; _ -> - {false, #rel_app{incl_apps = []}} + {false, #rel_app{}} end, case ValidTypesAssigned of true -> - decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals, Status); + decode(Rel#rel{rel_apps = RelApps ++ [RA]}, KeyVals); false -> - Text = lists:flatten(io_lib:format("~p", [RelApp])), - Status2 = - reltool_utils:return_first_error(Status, - "Illegal option: " ++ Text), - decode(Rel, KeyVals, Status2) + reltool_utils:throw_error("Illegal option: ~p", [RelApp]) end; -decode(Acc, [], Status) -> - {Acc, Status}; -decode(Acc, KeyVal, Status) -> - Text = lists:flatten(io_lib:format("~p", [KeyVal])), - {Acc, reltool_utils:return_first_error(Status, "Illegal option: " ++ Text)}. +decode(Acc, []) -> + Acc; +decode(_Acc, KeyVal) -> + reltool_utils:throw_error("Illegal option: ~p", [KeyVal]). is_type(Type) -> case Type of @@ -1529,79 +1571,50 @@ split_escript_name(File) when is_list(File) -> Label = filename:basename(File, ".escript"), {list_to_atom("*escript* " ++ Label), Label}. +default_escript_app(File) -> + {Name, Label} = split_escript_name(File), + App = default_app(Name, File), + App#app{is_escript = true, + label = Label, + info = missing_app_info("")}. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -refresh(#state{sys = Sys} = S, Force, Status) -> - {Sys2, Status2} = merge_config(Sys, Sys#sys{apps = []}, Force, Status), - {S#state{sys = Sys2}, Status2}. - -merge_config(OldSys, NewSys, Force, Status) -> - RootDir = filename:absname(NewSys#sys.root_dir), - LibDirs = [filename:absname(D) || D <- NewSys#sys.lib_dirs], - Escripts = [filename:absname(E) || E <- NewSys#sys.escripts], - {SourceDirs, Status2} = - libs_to_dirs(RootDir, LibDirs, Status), - MergedApps = merge_app_dirs(SourceDirs, NewSys#sys.apps, OldSys#sys.apps), - {AllApps, Status3} = - escripts_to_apps(Escripts, MergedApps, OldSys#sys.apps, Status2), - {RefreshedApps, Status4} = - refresh_apps(OldSys#sys.apps, AllApps, [], Force, Status3), - {PatchedApps, Status5} = - patch_erts_version(RootDir, RefreshedApps, Status4), - Escripts2 = [A#app.active_dir || A <- PatchedApps, A#app.is_escript], - NewSys2 = NewSys#sys{root_dir = RootDir, - lib_dirs = LibDirs, - escripts = Escripts2, - apps = PatchedApps}, - {NewSys2, Status5}. +%% Apps is a list of #app records - sorted on #app.name - containing +%% only the apps that have specific configuration (e.g. in the config +%% file) +refresh(#state{sys=Sys} = S) -> + RootDir = filename:absname(Sys#sys.root_dir), + LibDirs = [filename:absname(D) || D <- Sys#sys.lib_dirs], + Escripts = [filename:absname(E) || E <- Sys#sys.escripts], -verify_config(RelApps, #sys{boot_rel = BootRel, rels = Rels, apps = Apps}, Status) -> - case lists:keymember(BootRel, #rel.name, Rels) of - true -> - Status2 = lists:foldl(fun(RA, Acc) -> - check_app(RA, Apps, Acc) end, - Status, - RelApps), - lists:foldl(fun(#rel{name = RelName}, Acc)-> - check_rel(RelName, RelApps, Acc) - end, - Status2, - Rels); - false -> - Text = lists:concat(["Release ", BootRel, - " is mandatory (used as boot_rel)"]), - reltool_utils:return_first_error(Status, Text) - end. + %% Read all lib dirs and return sorted [{AppName,Dir}] + SourceDirs = libs_to_dirs(RootDir, LibDirs), -check_app({RelName, AppName}, Apps, Status) -> - case lists:keysearch(AppName, #app.name, Apps) of - {value, App} when App#app.is_pre_included -> - Status; - {value, App} when App#app.is_included -> - Status; - _ -> - Text = lists:concat(["Release ", RelName, - " uses non included application ", - AppName]), - reltool_utils:return_first_error(Status, Text) - end. + %% Create #app records for all apps in SourceDirs, and merge with + %% list of apps from config. + MergedApps = merge_app_dirs(SourceDirs, Sys#sys.apps), -check_rel(RelName, RelApps, Status) -> - EnsureApp = - fun(AppName, Acc) -> - case lists:member({RelName, AppName}, RelApps) of - true -> - Acc; - false -> - Text = lists:concat(["Mandatory application ", - AppName, - " is not included in release ", - RelName]), - reltool_utils:return_first_error(Acc, Text) - end - end, - Mandatory = [kernel, stdlib], - lists:foldl(EnsureApp, Status, Mandatory). + %% For each escript, find all related files and convert to #app + %% and #mod records + {AllApps, Status2} = escripts_to_apps(Escripts, MergedApps, {ok,[]}), + + %% Make sure correct version of each application is used according + %% to the user configuration. + %% Then find all modules and their dependencies and set user + %% configuration per module if it exists. + {RefreshedApps, Status3} = refresh_apps(Sys#sys.apps, AllApps, [], + true, Status2), + + %% Make sure erts exists in app list and has a version (or warn) + {PatchedApps, Status4} = patch_erts_version(RootDir, RefreshedApps, Status3), + + %% Update #sys and return + Escripts2 = [A#app.active_dir || A <- PatchedApps, A#app.is_escript], + Sys2 = Sys#sys{root_dir = RootDir, + lib_dirs = LibDirs, + escripts = Escripts2}, + {S#state{sys=Sys2}, PatchedApps, Status4}. patch_erts_version(RootDir, Apps, Status) -> AppName = erts, @@ -1615,18 +1628,17 @@ patch_erts_version(RootDir, Apps, Status) -> Apps2 = lists:keystore(AppName, #app.name, Apps, Erts2), {Apps2, Status}; Vsn =:= "" -> - {Apps, reltool_utils:add_warning(Status, - "erts has no version")}; + {Apps, reltool_utils:add_warning("erts has no version",[], + Status)}; true -> {Apps, Status} end; false -> - Text = "erts cannot be found in the root directory " ++ RootDir, - Status2 = reltool_utils:return_first_error(Status, Text), - {Apps, Status2} + reltool_utils:throw_error( + "erts cannot be found in the root directory ~p", [RootDir]) end. -libs_to_dirs(RootDir, LibDirs, Status) -> +libs_to_dirs(RootDir, LibDirs) -> case file:list_dir(RootDir) of {ok, RootFiles} -> RootLibDir = filename:join([RootDir, "lib"]), @@ -1648,21 +1660,16 @@ libs_to_dirs(RootDir, LibDirs, Status) -> end, ErtsFiles = [{erts, Fun(F)} || F <- RootFiles, lists:prefix("erts", F)], - app_dirs2(AllLibDirs, [ErtsFiles], Status); + app_dirs2(AllLibDirs, [ErtsFiles]); [Duplicate | _] -> - {[], - reltool_utils:return_first_error(Status, - "Duplicate library: " ++ - Duplicate)} + reltool_utils:throw_error("Duplicate library: ~p",[Duplicate]) end; {error, Reason} -> - Text = file:format_error(Reason), - {[], reltool_utils:return_first_error(Status, - "Missing root library " ++ - RootDir ++ ": " ++ Text)} + reltool_utils:throw_error("Missing root library ~p: ~s", + [RootDir,file:format_error(Reason)]) end. -app_dirs2([Lib | Libs], Acc, Status) -> +app_dirs2([Lib | Libs], Acc) -> case file:list_dir(Lib) of {ok, Files} -> Filter = @@ -1682,17 +1689,15 @@ app_dirs2([Lib | Libs], Acc, Status) -> end end, Files2 = lists:zf(Filter, Files), - app_dirs2(Libs, [Files2 | Acc], Status); + app_dirs2(Libs, [Files2 | Acc]); {error, Reason} -> - Text = file:format_error(Reason), - {[], reltool_utils:return_first_error(Status, - "Illegal library " ++ - Lib ++ ": " ++ Text)} + reltool_utils:throw_error("Illegal library ~p: ~s", + [Lib, file:format_error(Reason)]) end; -app_dirs2([], Acc, Status) -> - {lists:sort(lists:append(Acc)), Status}. +app_dirs2([], Acc) -> + lists:sort(lists:append(Acc)). -escripts_to_apps([Escript | Escripts], Apps, OldApps, Status) -> +escripts_to_apps([Escript | Escripts], Apps, Status) -> {EscriptAppName, _Label} = split_escript_name(Escript), Ext = code:objfile_extension(), Fun = fun(FullName, _GetInfo, GetBin, {FileAcc, StatusAcc}) -> @@ -1755,156 +1760,114 @@ escripts_to_apps([Escript | Escripts], Apps, OldApps, Status) -> end, case reltool_utils:escript_foldl(Fun, {[], Status}, Escript) of {ok, {Files, Status2}} -> + EscriptApp = + case lists:keyfind(EscriptAppName,#app.name,Apps) of + false -> default_escript_app(Escript); + EA -> EA + end, {Apps2, Status3} = - files_to_apps(Escript, - lists:sort(Files), - Apps, - Apps, - OldApps, - Status2), - escripts_to_apps(Escripts, Apps2, OldApps, Status3); + escript_files_to_apps(EscriptAppName, + lists:sort(Files), + [EscriptApp], + Apps, + Status2), + escripts_to_apps(Escripts, Apps2, Status3); {error, Reason} -> - Text = lists:flatten(io_lib:format("~p", [Reason])), - {[], reltool_utils:return_first_error(Status, - "Illegal escript " ++ - Escript ++ ": " ++ Text)} + reltool_utils:throw_error("Illegal escript ~p: ~p", [Escript,Reason]) end; -escripts_to_apps([], Apps, _OldApps, Status) -> +escripts_to_apps([], Apps, Status) -> {Apps, Status}. %% Assume that all files for an app are in consecutive order %% Assume the app info is before the mods -files_to_apps(Escript, - [{AppName, Type, Dir, ModOrInfo} | Files] = AllFiles, - Acc, - Apps, - OldApps, - Status) -> - case Type of - mod -> - case Acc of - [] -> - Info = missing_app_info(""), - {NewApp, Status2} = - merge_escript_app(AppName, - Dir, - Info, - [ModOrInfo], - Apps, - OldApps, - Status), - files_to_apps(Escript, - AllFiles, - [NewApp | Acc], - Apps, - OldApps, Status2); - [App | Acc2] when App#app.name =:= ModOrInfo#mod.app_name -> - App2 = App#app{mods = [ModOrInfo | App#app.mods]}, - files_to_apps(Escript, - Files, - [App2 | Acc2], - Apps, - OldApps, - Status); - [App | Acc2] -> - PrevApp = App#app{mods = lists:keysort(#mod.name, - App#app.mods)}, - Info = missing_app_info(""), - {NewApp, Status2} = - merge_escript_app(AppName, - Dir, - Info, - [ModOrInfo], - Apps, - OldApps, - Status), - files_to_apps(Escript, - Files, - [NewApp, PrevApp | Acc2], - Apps, - OldApps, - Status2) - end; - app -> - {App, Status2} = - merge_escript_app(AppName, Dir, ModOrInfo, [], Apps, OldApps, - Status), - files_to_apps(Escript, Files, [App | Acc], Apps, OldApps, Status2) - end; -files_to_apps(_Escript, [], Acc, _Apps, _OldApps, Status) -> - {lists:keysort(#app.name, Acc), Status}. - -merge_escript_app(AppName, Dir, Info, Mods, Apps, OldApps, Status) -> - App1 = case lists:keyfind(AppName, #app.name, OldApps) of - #app{} = App -> - App; - false -> - default_app(AppName, Dir) - end, - App2 = App1#app{is_escript = true, +escript_files_to_apps(EscriptAppName, + [{AppName, Type, Dir, ModOrInfo} | Files], + Acc, + Apps, + Status) -> + {NewAcc,Status3} = + case Type of + mod -> + case Acc of + [App | Acc2] when App#app.name =:= ModOrInfo#mod.app_name -> + Mods = lists:ukeymerge(#mod.name, + [ModOrInfo], + App#app.mods), + {[App#app{mods = Mods} | Acc2], Status}; + Acc -> + {NewApp, Status2} = init_escript_app(AppName, + EscriptAppName, + Dir, + missing_app_info(""), + [ModOrInfo], + Apps, + Status), + {[NewApp | Acc], Status2} + end; + app -> + {App, Status2} = init_escript_app(AppName, + EscriptAppName, + Dir, + ModOrInfo, + [], + Apps, + Status), + {[App | Acc], Status2} + end, + escript_files_to_apps(EscriptAppName, Files, NewAcc, Apps, Status3); +escript_files_to_apps(_EscriptAppName, [], Acc, Apps, Status) -> + {lists:ukeymerge(#app.name, lists:reverse(Acc), Apps), Status}. + +init_escript_app(AppName, EscriptAppName, Dir, Info, Mods, Apps, Status) -> + App1 = default_app(AppName, Dir), + IsEscript = + if AppName=:=EscriptAppName -> true; + true -> {inlined, EscriptAppName} + end, + InclCond = (lists:keyfind(EscriptAppName,#app.name,Apps))#app.incl_cond, + App2 = App1#app{is_escript = IsEscript, label = filename:basename(Dir, ".escript"), info = Info, mods = Mods, active_dir = Dir, - sorted_dirs = [Dir]}, + sorted_dirs = [Dir], + incl_cond = InclCond},% inlined apps inherit incl from escript case lists:keymember(AppName, #app.name, Apps) of true -> - Error = lists:concat([AppName, ": Application name clash. ", - "Escript ", Dir," contains application ", - AppName, "."]), - {App2, reltool_utils:return_first_error(Status, Error)}; + reltool_utils:throw_error( + "~p: Application name clash. Escript ~p contains application ~p.", + [AppName,Dir,AppName]); false -> {App2, Status} end. -merge_app_dirs([{Name, Dir} | Rest], [App | Apps], OldApps) - when App#app.name =:= Name -> - %% Add new dir to app - App2 = App#app{sorted_dirs = [Dir | App#app.sorted_dirs]}, - merge_app_dirs(Rest, [App2 | Apps], OldApps); -merge_app_dirs([{Name, Dir} | Rest], Apps, OldApps) -> - %% Initate app - Apps2 = sort_app_dirs(Apps), - Apps4 = +merge_app_dirs([{Name, Dir} | Rest], Apps) -> + App = case lists:keyfind(Name, #app.name, Apps) of false -> - case lists:keyfind(Name, #app.name, OldApps) of - false -> - App = default_app(Name, Dir), - [App | Apps2]; - #app{active_dir = Dir} = OldApp -> - [OldApp | Apps2]; - OldApp -> - App = - case filter_app(OldApp) of - {true, NewApp} -> - NewApp#app{active_dir = Dir, - sorted_dirs = [Dir]}; - false -> - default_app(Name, Dir) - end, - [App | Apps2] - end; + default_app(Name, Dir); OldApp -> - Apps3 = lists:keydelete(Name, #app.name, Apps2), - App = OldApp#app{sorted_dirs = [Dir | OldApp#app.sorted_dirs]}, - [App | Apps3] + SortedDirs = lists:umerge(fun reltool_utils:app_dir_test/2, + [Dir], OldApp#app.sorted_dirs), + OldApp#app{sorted_dirs = SortedDirs} end, - merge_app_dirs(Rest, Apps4, OldApps); -merge_app_dirs([], Apps, _OldApps) -> - Apps2 = sort_app_dirs(Apps), - lists:reverse(Apps2). - -sort_app_dirs([#app{sorted_dirs = Dirs} = App | Acc]) -> - SortedDirs = lists:sort(fun reltool_utils:app_dir_test/2, Dirs), - case SortedDirs of - [ActiveDir | _] -> ok; - [] -> ActiveDir = undefined - end, - [App#app{active_dir = ActiveDir, sorted_dirs = SortedDirs} | Acc]; -sort_app_dirs([]) -> + Apps2 = lists:ukeymerge(#app.name, [App], Apps), + merge_app_dirs(Rest, Apps2); +merge_app_dirs([], Apps) -> + set_active_dirs(Apps). + +%% First dir, i.e. the one with highest version, is set to active dir, +%% unless a specific dir is given in config +set_active_dirs([#app{use_selected_vsn = dir} = App | Apps]) -> + [App | set_active_dirs(Apps)]; +set_active_dirs([#app{sorted_dirs = [ActiveDir|_]} = App | Apps]) -> + [App#app{active_dir = ActiveDir} | set_active_dirs(Apps)]; +set_active_dirs([#app{sorted_dirs = []} = App | Apps]) -> + [App#app{active_dir = undefined} | set_active_dirs(Apps)]; +set_active_dirs([]) -> []. + default_app(Name, Dir) -> App = default_app(Name), App#app{active_dir = Dir, @@ -1913,91 +1876,55 @@ default_app(Name, Dir) -> default_app(Name) -> #app{name = Name, is_escript = false, - use_selected_vsn = undefined, - active_dir = undefined, sorted_dirs = [], - vsn = undefined, - label = undefined, - info = undefined, mods = [], - - mod_cond = undefined, - incl_cond = undefined, - - status = missing, - uses_mods = undefined, - is_pre_included = undefined, - is_included = undefined, - rels = undefined}. - -%% Assume that the application are sorted -refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) - when New#app.name =:= Old#app.name -> - {Info, ActiveDir, Status2} = ensure_app_info(New, Status), - OptLabel = - case Info#app_info.vsn =:= New#app.vsn of - true -> New#app.label; - false -> undefined % Cause refresh - end, - {Refreshed, Status3} = - refresh_app(New#app{label = OptLabel, - active_dir = ActiveDir, - vsn = Info#app_info.vsn, - info = Info}, - Force, - Status2), - refresh_apps(OldApps, NewApps, [Refreshed | Acc], Force, Status3); -refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) - when New#app.name < Old#app.name -> - %% No old app version exists. Use new as is. - %% BUGBUG: Issue warning if the active_dir is not defined - {New2, Status2} = refresh_app(New, Force, Status), - refresh_apps([Old | OldApps], NewApps, [New2 | Acc], Force, Status2); -refresh_apps([Old | OldApps], [New | NewApps], Acc, Force, Status) - when New#app.name > Old#app.name -> - %% No new version. Remove the old. - Status2 = - case Old#app.name =:= ?MISSING_APP_NAME of - true -> - Status; - false -> - Warning = - lists:concat([Old#app.name, - ": The source dirs does not ", - "contain the application anymore."]), - reltool_utils:add_warning(Status, Warning) - end, - refresh_apps(OldApps, [New | NewApps], Acc, Force, Status2); -refresh_apps([], [New | NewApps], Acc, Force, Status) -> - %% No old app version exists. Use new as is. - {New2, Status2} = refresh_app(New, Force, Status), - refresh_apps([], NewApps, [New2 | Acc], Force, Status2); -refresh_apps([Old | OldApps], [], Acc, Force, Status) -> - %% No new version. Remove the old. - Status2 = - case Old#app.name =:= ?MISSING_APP_NAME of - true -> - Status; - false -> - Warning = - lists:concat([Old#app.name, - ": The source dirs does not " - "contain the application anymore."]), - reltool_utils:add_warning(Status, Warning) - end, - refresh_apps(OldApps, [], Acc, Force, Status2); -refresh_apps([], [], Acc, _Force, Status) -> + status = missing}. + + + +refresh_apps(ConfigApps, [New | NewApps], Acc, Force, Status) -> + {New2, Status3} = + case lists:keymember(New#app.name,#app.name,ConfigApps) of + true -> + %% There is user defined config for this application, make + %% sure that the application exists and that correct + %% version is used. Set active directory. + {Info, ActiveDir, Status2} = ensure_app_info(New, Status), + OptLabel = + case Info#app_info.vsn =:= New#app.vsn of + true -> New#app.label; + false -> undefined % Cause refresh + end, + refresh_app(New#app{label = OptLabel, + active_dir = ActiveDir, + vsn = Info#app_info.vsn, + info = Info}, + Force, + Status2); + false -> + %% There is no user defined config for this + %% application. This means that the app is found in the + %% lib dirs, and that the highest version shall be + %% used. I.e. the active_dir and vsn are already correct + %% from merge_app_dirs. + refresh_app(New, Force, Status) + end, + refresh_apps(ConfigApps, NewApps, [New2 | Acc], Force, Status3); +refresh_apps(_ConfigApps, [], Acc, _Force, Status) -> {lists:reverse(Acc), Status}. -ensure_app_info(#app{is_escript = true, active_dir = Dir, info = Info}, - Status) -> + +ensure_app_info(#app{is_escript = IsEscript, active_dir = Dir, info = Info}, + Status) + when IsEscript=/=false -> + %% Escript or application which is inlined in an escript {Info, Dir, Status}; -ensure_app_info(#app{name = Name, sorted_dirs = []}, Status) -> - Error = lists:concat([Name, ": Missing application directory."]), - Status2 = reltool_utils:return_first_error(Status, Error), - {missing_app_info(""), undefined, Status2}; +ensure_app_info(#app{name = Name, sorted_dirs = []}, _Status) -> + reltool_utils:throw_error("~p: : Missing application directory.",[Name]); ensure_app_info(#app{name = Name, vsn = Vsn, + use_selected_vsn = UseSelectedVsn, + active_dir = ActiveDir, sorted_dirs = Dirs, info = undefined}, Status) -> @@ -2017,32 +1944,36 @@ ensure_app_info(#app{name = Name, %% No redundant info Status2; [BadVsn | _] -> - Error2 = - lists:concat([Name, ": Application version clash. ", - "Multiple directories contains version \"", - BadVsn, "\"."]), - reltool_utils:return_first_error(Status2, Error2) + reltool_utils:throw_error( + "~p: Application version clash. " + "Multiple directories contains version ~p.", + [Name,BadVsn]) end, FirstInfo = hd(AllInfo), FirstDir = hd(Dirs), if - Vsn =:= undefined -> - {FirstInfo, FirstDir, Status3}; - Vsn =:= FirstInfo#app_info.vsn -> - {FirstInfo, FirstDir, Status3}; - true -> - case find_vsn(Vsn, AllInfo, Dirs) of - {Info, VsnDir} -> - {Info, VsnDir, Status3}; - false -> - Error3 = - lists:concat([Name, - ": No application directory contains ", - "selected version \"", - Vsn, "\"."]), - Status4 = reltool_utils:return_first_error(Status3, Error3), - {FirstInfo, FirstDir, Status4} - end + UseSelectedVsn =:= dir -> + if ActiveDir =:= FirstDir -> + {FirstInfo, FirstDir, Status3}; + true -> + Info = find_dir(ActiveDir, AllInfo, Dirs), + {Info, ActiveDir, Status3} + end; + UseSelectedVsn =:= vsn -> + if Vsn =:= FirstInfo#app_info.vsn -> + {FirstInfo, FirstDir, Status3}; + true -> + case find_vsn(Vsn, AllInfo, Dirs) of + {Info, VsnDir} -> + {Info, VsnDir, Status3}; + false -> + reltool_utils:throw_error( + "~p: No application directory contains " + "selected version ~p", [Name,Vsn]) + end + end; + true -> + {FirstInfo, FirstDir, Status3} end; ensure_app_info(#app{active_dir = Dir, info = Info}, Status) -> {Info, Dir, Status}. @@ -2054,6 +1985,11 @@ find_vsn(Vsn, [_ | MoreInfo], [_ | MoreDirs]) -> find_vsn(_, [], []) -> false. +find_dir(Dir, [Info | _], [Dir | _]) -> + Info; +find_dir(Dir, [_ | MoreInfo], [_ | MoreDirs]) -> + find_dir(Dir, MoreInfo, MoreDirs). + get_base(Name, Dir) -> case Name of erts -> @@ -2067,6 +2003,54 @@ get_base(Name, Dir) -> filename:basename(Dir) end. +sys_all_apps(#state{app_tab=AppTab, sys=Sys}) -> + Sys#sys{apps = ets:match_object(AppTab,'_')}. + +config_and_refresh(OldS, Fun) -> + try + S = Fun(), + {S2, Apps, Status2} = refresh(S), + %% Analyse will write to app_tab and mod_tab, so we first + %% backup these tables and clear them + Backup = backup(OldS), + try + Status3 = analyse(S2, Apps, Status2), + S3 = save_old(OldS, S2, Backup, Status3), + {S3, Status3} + catch throw:{error,_} = Error1 -> + restore(Backup,OldS), + throw(Error1) + end + catch throw:{error,_} = Error2 -> + {OldS, Error2} + end. + + +backup(S) -> + Apps = ets:tab2list(S#state.app_tab), + Mods = ets:tab2list(S#state.mod_tab), + ets:delete_all_objects(S#state.app_tab), + ets:delete_all_objects(S#state.mod_tab), + ets:delete_all_objects(S#state.mod_used_by_tab), %tmp tab, no backup needed + {Apps,Mods}. + +restore({Apps,Mods}, S) -> + insert_all(S#state.app_tab,Apps), + insert_all(S#state.mod_tab,Mods). + +save_old(#state{status=OldStatus,sys=OldSys},NewS,{OldApps,OldMods},NewStatus) -> + ets:delete_all_objects(NewS#state.old_app_tab), + ets:delete_all_objects(NewS#state.old_mod_tab), + insert_all(NewS#state.old_app_tab,OldApps), + insert_all(NewS#state.old_mod_tab,OldMods), + NewS#state{old_sys=OldSys, + old_status=OldStatus, + status=NewStatus}. + +insert_all(Tab,Items) -> + lists:foreach(fun(Item) -> ets:insert(Tab,Item) end, Items). + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sys callbacks diff --git a/lib/reltool/src/reltool_sys_win.erl b/lib/reltool/src/reltool_sys_win.erl index 8b0f64eb45..0c0b295db1 100644 --- a/lib/reltool/src/reltool_sys_win.erl +++ b/lib/reltool/src/reltool_sys_win.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -56,7 +56,9 @@ derived, fgraph_wins, app_box, - mod_box + mod_box, + warning_list, + warning_wins }). -define(WIN_WIDTH, 800). @@ -88,6 +90,10 @@ -define(blacklist, "Excluded"). -define(derived, "Derived"). +-define(WARNING_COL, 0). +-define(DEFAULT_WARNING_TIP, "Warnings are listed in this window"). +-define(WARNING_POPUP_SIZE, {400,150}). + -define(safe_config,{sys,[{incl_cond,exclude}, {app,kernel,[{incl_cond,include}]}, {app,stdlib,[{incl_cond,include}]}, @@ -141,48 +147,44 @@ do_init([{safe_config, Safe}, {parent, Parent} | Options]) -> wx:debug(C#common.wx_debug), %% wx_misc:beginBusyCursor(), - case reltool_server:get_status(ServerPid) of - {ok, Warnings} -> - exit_dialog(Warnings), - {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(S2#state.frame, - Title), - %% wxFrame:setMinSize(Frame, - %% {?WIN_WIDTH, ?WIN_HEIGHT}), - wxStatusBar:setStatusText( - S2#state.status_bar, - "Done."), - 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} -> - restart_server_safe_config(Safe,Parent,Reason) - end; + {ok, Warnings} = reltool_server:get_status(ServerPid), + exit_dialog(Warnings), + 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 = [], + warning_wins = []}, + S2 = create_window(S), + S5 = wx:batch(fun() -> + Title = atom_to_list(?APPLICATION), + wxFrame:setTitle(S2#state.frame, + Title), + %% wxFrame:setMinSize(Frame, + %% {?WIN_WIDTH, ?WIN_HEIGHT}), + wxStatusBar:setStatusText( + S2#state.status_bar, + "Done."), + 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. -restart_server_safe_config(true,_Parent,Reason) -> +restart_server_safe_config(true,Parent,Reason) -> io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), - exit(Reason); + proc_lib:init_ack(Parent, {error,Reason}); restart_server_safe_config(false,Parent,Reason) -> + wx:new(), Strings = [{?wxBLACK,"Could not start reltool server:\n\n"}, {?wxRED,Reason++"\n\n"}, @@ -197,7 +199,7 @@ restart_server_safe_config(false,Parent,Reason) -> do_init([{safe_config,true},{parent,Parent},?safe_config]); ?wxID_CANCEL -> io:format("~p(~p): <ERROR> ~p\n", [?MODULE, ?LINE, Reason]), - exit(Reason) + proc_lib:init_ack(Parent,{error,Reason}) end. exit_dialog([]) -> @@ -240,10 +242,18 @@ loop(S) -> lists:keydelete(ObjRef, #fgraph_win.frame, FWs), ?MODULE:loop(S#state{fgraph_wins = FWs2}); false -> - error_logger:format("~p~p got unexpected " - "message:\n\t~p\n", - [?MODULE, self(), Msg]), - ?MODULE:loop(S) + WWs = S#state.warning_wins, + case lists:member(ObjRef, WWs) of + true -> + wxFrame:destroy(ObjRef), + WWs2 = lists:delete(ObjRef, WWs), + ?MODULE:loop(S#state{warning_wins = WWs2}); + false -> + error_logger:format("~p~p got unexpected " + "message:\n\t~p\n", + [?MODULE, self(), Msg]), + ?MODULE:loop(S) + end end end; #wx{id = ?CLOSE_ITEM, @@ -297,8 +307,8 @@ handle_child_exit({'EXIT', Pid, _Reason} = Exit, FWs, AWs) -> msg_warning(Exit, application_window), {FWs, lists:keydelete(Pid, #app_win.pid, AWs)}; false -> - msg_warning(Exit, unknown), - {FWs, AWs} + msg_warning(Exit, unknown), + {FWs, AWs} end end. @@ -340,8 +350,12 @@ create_window(S) -> fun create_main_release_page/1, fun create_config_page/1 ]), + + S4 = create_warning_list(S3), + Sizer = wxBoxSizer:new(?wxVERTICAL), wxSizer:add(Sizer, Book, [{flag, ?wxEXPAND}, {proportion, 1}]), + wxSizer:add(Sizer, S4#state.warning_list, [{flag, ?wxEXPAND}]), wxPanel:setSizer(Panel, Sizer), wxSizer:fit(Sizer, Frame), @@ -349,7 +363,7 @@ create_window(S) -> wxFrame:connect(Frame, close_window), wxFrame:show(Frame), - S3. + S4. create_menubar(Frame) -> MenuBar = wxMenuBar:new(), @@ -444,6 +458,7 @@ create_app_list_ctrl(Panel, OuterSz, Title, Tick, Cross) -> ListItem = wxListItem:new(), wxListItem:setAlign(ListItem, ?wxLIST_FORMAT_LEFT), wxListItem:setText(ListItem, Title), + wxListItem:setWidth(ListItem, reltool_utils:get_column_width(ListCtrl)), wxListCtrl:insertColumn(ListCtrl, ?APPS_APP_COL, ListItem), wxListItem:destroy(ListItem), @@ -642,6 +657,49 @@ redraw_config_page(#state{sys = Sys, app_box = AppBox, mod_box = ModBox} = S) -> wxRadioBox:setSelection(ModBox, ModChoice), S. +create_warning_list(#state{panel = Panel} = S) -> + ListCtrl = wxListCtrl:new(Panel, + [{style, + ?wxLC_REPORT bor + ?wxLC_HRULES bor + ?wxVSCROLL}, + {size, {?WIN_WIDTH,80}}]), + reltool_utils:assign_image_list(ListCtrl), + wxListCtrl:insertColumn(ListCtrl, ?WARNING_COL, "Warnings", + [{format,?wxLIST_FORMAT_LEFT}, + {width,reltool_utils:get_column_width(ListCtrl)}]), + wxListCtrl:setToolTip(ListCtrl, ?DEFAULT_WARNING_TIP), + wxEvtHandler:connect(ListCtrl, size, + [{skip, true}, {userData, warnings}]), + wxEvtHandler:connect(ListCtrl, command_list_item_activated, + [{userData, warnings}]), + wxEvtHandler:connect(ListCtrl, motion, [{userData, warnings}]), + wxEvtHandler:connect(ListCtrl, enter_window), + S#state{warning_list=ListCtrl}. + +redraw_warnings(S) -> + {ok,Warnings} = reltool_server:get_status(S#state.server_pid), + redraw_warnings(S#state.warning_list,Warnings), + length(Warnings). + +redraw_warnings(ListCtrl, []) -> + wxListCtrl:deleteAllItems(ListCtrl), + ok; +redraw_warnings(ListCtrl, Warnings) -> + wxListCtrl:deleteAllItems(ListCtrl), + Show = fun(Warning, Row) -> + wxListCtrl:insertItem(ListCtrl, Row, ""), + wxListCtrl:setItem(ListCtrl, + Row, + ?WARNING_COL, + Warning, + [{imageId, ?WARN_IMAGE}]), + Row + 1 + end, + wx:foldl(Show, 0, Warnings), + ok. + + create_main_release_page(#state{book = Book} = S) -> Panel = wxPanel:new(Book, []), RelBook = wxNotebook:new(Panel, ?wxID_ANY, []), @@ -783,6 +841,9 @@ escript_popup(S, File, Tree, Item) -> handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = _Wx) -> %% io:format("wx: ~p\n", [Wx]), case Event of + _ when UserData =:= warnings; + is_tuple(UserData), element(1,UserData)=:=warning -> + handle_warning_event(S, ObjRef, UserData, Event); #wxSize{type = size, size = {W, _H}} when UserData =:= app_list_ctrl -> wxListCtrl:setColumnWidth(ObjRef, ?APPS_APP_COL, W), S; @@ -848,7 +909,9 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = when S#state.popup_menu =/= undefined -> handle_popup_event(S, Type, Id, ObjRef, UserData, Str); #wxMouse{type = enter_window} -> - wxWindow:setFocus(ObjRef), + %% The following is commented out because it raises the + %% main system window on top of popup windows. + %% wxWindow:setFocus(ObjRef), S; _ -> case wxNotebook:getPageText(S#state.book, @@ -860,6 +923,110 @@ handle_event(S, #wx{id = Id, obj= ObjRef, userData = UserData, event = Event} = end end. +handle_warning_event(S, ObjRef, _, #wxSize{type = size}) -> + ColumnWidth = reltool_utils:get_column_width(ObjRef), + wxListCtrl:setColumnWidth(ObjRef, ?WARNING_COL, ColumnWidth), + S; +handle_warning_event(S, ObjRef, _, #wxMouse{type = motion, x=X, y=Y}) -> + Pos = reltool_utils:wait_for_stop_motion(ObjRef, {X,Y}), + warning_list_set_tool_tip(os:type(),ObjRef,Pos), + S; +handle_warning_event(S, ObjRef, _, #wxList{type = command_list_item_activated, + itemIndex = Pos}) -> + Text = wxListCtrl:getItemText(ObjRef, Pos), + S#state{warning_wins = [display_warning(S,Text) | S#state.warning_wins]}; +handle_warning_event(S, _ObjRef, {warning,Frame}, + #wxCommand{type = command_button_clicked}) -> + wxFrame:destroy(Frame), + S#state{warning_wins = lists:delete(Frame,S#state.warning_wins)}. + +warning_list_set_tool_tip({win32,_},ListCtrl,{_X,Y}) -> + case win_find_item(ListCtrl,Y,0) of + -1 -> + wxListCtrl:setToolTip(ListCtrl,?DEFAULT_WARNING_TIP); + _Index -> + %% The following is commented out because there seems to + %% be an utomatic tooltip under Windows that shows the + %% expanded list item in case it is truncated because it + %% is too long for column width. + %% Tip = + %% case wxListCtrl:getItemText(ListCtrl,Index) of + %% "" -> + %% ?DEFAULT_WARNING_TIP; + %% Text -> + %% "WARNING:\n" ++ Text + %% end, + %% wxListCtrl:setToolTip(ListCtrl,Tip), + ok + end; +warning_list_set_tool_tip(_,ListCtrl,Pos) -> + case wxListCtrl:findItem(ListCtrl,-1,Pos,0) of + Index when Index >= 0 -> + Tip = + case wxListCtrl:getItemText(ListCtrl,Index) of + "" -> + ?DEFAULT_WARNING_TIP; + Text -> + "WARNING:\n" ++ Text + end, + wxListCtrl:setToolTip(ListCtrl, Tip); + _ -> + ok + end. + +win_find_item(ListCtrl,YPos,Index) -> + case wxListCtrl:getItemRect(ListCtrl,Index) of + {true,{_,Y,_,H}} when YPos>=Y, YPos=<Y+H -> + Index; + {true,_} -> + win_find_item(ListCtrl,YPos,Index+1); + {false,_} -> + -1 + end. + +display_warning(S,Warning) -> + Pos = warning_popup_position(S,?WARNING_POPUP_SIZE), + Frame = wxFrame:new(wx:null(), ?wxID_ANY, "Warning",[{pos,Pos}]), + Panel = wxPanel:new(Frame, []), + TextStyle = ?wxTE_READONLY bor ?wxTE_WORDWRAP bor ?wxTE_MULTILINE, + Text = wxTextCtrl:new(Panel, ?wxID_ANY, [{value, Warning}, + {style, TextStyle}, + {size, ?WARNING_POPUP_SIZE}]), + Attr = wxTextAttr:new(), + wxTextAttr:setLeftIndent(Attr,10), + wxTextAttr:setRightIndent(Attr,10), + true = wxTextCtrl:setDefaultStyle(Text, Attr), + wxTextAttr:destroy(Attr), + Sizer = wxBoxSizer:new(?wxVERTICAL), + wxSizer:add(Sizer, Text, [{border, 2}, + {flag, ?wxEXPAND bor ?wxALL}, + {proportion, 1}]), + + Close = wxButton:new(Panel, ?wxID_ANY, [{label, "Close"}]), + wxButton:setToolTip(Close, "Close window."), + wxButton:connect(Close, command_button_clicked, [{userData,{warning,Frame}}]), + wxSizer:add(Sizer, Close, [{flag, ?wxALIGN_CENTER_HORIZONTAL}]), + + wxPanel:setSizer(Panel, Sizer), + wxSizer:fit(Sizer, Frame), + wxSizer:setSizeHints(Sizer, Frame), + wxFrame:connect(Frame, close_window), + + wxFrame:show(Frame), + Frame. + +warning_popup_position(#state{frame=MF,warning_list=WL},{WFW,WFH}) -> + {MFX,MFY} = wxWindow:getPosition(MF), + {MFW,MFH} = wxWindow:getSize(MF), + {_WLW,WLH} = wxWindow:getSize(WL), + + %% Position the popup in the middle of the main frame, above the + %% warning list, and with a small offset from the exact middle... + Offset = 50, + X = MFX + (MFW-WFW) div 2 - Offset, + Y = MFY + (MFH-WLH-WFH) div 2 - Offset, + {X,Y}. + handle_popup_event(S, _Type, 0, _ObjRef, _UserData, _Str) -> S#state{popup_menu = undefined}; handle_popup_event(#state{popup_menu = #root_popup{dir = OldDir, @@ -1079,16 +1246,16 @@ handle_app_event(S, Event, ObjRef, UserData) -> [?MODULE, self(), ObjRef, UserData, Event]), S. -handle_app_button(#state{server_pid = ServerPid, app_wins = AppWins} = S, +handle_app_button(#state{server_pid = ServerPid, + status_bar = Bar, + app_wins = AppWins} = S, Items, Action) -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), NewApps = [move_app(S, Item, Action) || Item <- Items], case reltool_server:set_apps(ServerPid, NewApps) of - {ok, []} -> + {ok, _Warnings} -> ok; - {ok, Warnings} -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING); {error, Reason} -> display_message(Reason, ?wxICON_ERROR) end, @@ -1125,23 +1292,22 @@ move_app(S, {_ItemNo, AppBase}, Action) -> end, OldApp#app{incl_cond = AppCond}. -do_set_app(#state{server_pid = ServerPid, app_wins = AppWins} = S, NewApp) -> +do_set_app(#state{server_pid = ServerPid, + status_bar = Bar, + app_wins = AppWins} = S, NewApp) -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), Result = reltool_server:set_app(ServerPid, NewApp), - [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], - S2 = redraw_apps(S), ReturnApp = case Result of - {ok, AnalysedApp, []} -> - AnalysedApp; - {ok, AnalysedApp, Warnings} -> - Msg = lists:flatten([[W, $\n] || W <- Warnings]), - display_message(Msg, ?wxICON_WARNING), + {ok, AnalysedApp, _Warnings} -> AnalysedApp; {error, Reason} -> display_message(Reason, ?wxICON_ERROR), {ok,OldApp} = reltool_server:get_app(ServerPid, NewApp#app.name), OldApp end, + [ok = reltool_app_win:refresh(AW#app_win.pid) || AW <- AppWins], + S2 = redraw_apps(S), {ok, ReturnApp, S2}. redraw_apps(#state{server_pid = ServerPid, @@ -1164,8 +1330,14 @@ redraw_apps(#state{server_pid = ServerPid, WhiteN = redraw_apps(WhiteApps, WhiteCtrl, ?TICK_IMAGE, ?ERR_IMAGE), redraw_apps(BlackApps2, BlackCtrl, ?CROSS_IMAGE, ?WARN_IMAGE), DerivedN = redraw_apps(DerivedApps, DerivedCtrl, ?TICK_IMAGE, ?ERR_IMAGE), + + WarningsN = redraw_warnings(S), + WarningText = if WarningsN==1 -> "warning"; + true -> "warnings" + end, Status = lists:concat([WhiteN, " whitelisted modules and ", - DerivedN, " derived modules."]), + DerivedN, " derived modules, ", + WarningsN, " ", WarningText, "."]), wxStatusBar:setStatusText(S#state.status_bar, Status), S. @@ -1323,26 +1495,28 @@ save_config(#state{config_file = OldFile} = S, InclDefaults, InclDerivates) -> S end. -gen_rel_files(#state{target_dir = OldDir} = S) -> +gen_rel_files(#state{status_bar = Bar, target_dir = OldDir} = S) -> Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, case select_dir(S#state.frame, "Select a directory to generate rel, script and boot files to", OldDir, Style) of {ok, NewDir} -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), Status = reltool_server:gen_rel_files(S#state.server_pid, NewDir), check_and_refresh(S, Status); cancel -> S end. -gen_target(#state{target_dir = OldDir} = S) -> +gen_target(#state{status_bar = Bar, target_dir = OldDir} = S) -> Style = ?wxFD_SAVE bor ?wxFD_OVERWRITE_PROMPT, case select_dir(S#state.frame, "Select a directory to generate a target system to", OldDir, Style) of {ok, NewDir} -> + wxStatusBar:setStatusText(Bar, "Processing libraries..."), Status = reltool_server:gen_target(S#state.server_pid, NewDir), check_and_refresh(S#state{target_dir = NewDir}, Status); cancel -> @@ -1380,8 +1554,8 @@ check_and_refresh(S, Status) -> case Status of ok -> true; - {ok, Warnings} -> - undo_dialog(S, Warnings); + {ok, _Warnings} -> + true; {error, Reason} when is_list(Reason) -> display_message(Reason, ?wxICON_ERROR), false; @@ -1435,19 +1609,6 @@ question_dialog(Question, Details) -> wxDialog:destroy(Dialog), Answer. -undo_dialog(_S, []) -> - true; -undo_dialog(S, Warnings) -> - Question = "Do you want to perform the update despite these warnings?", - Details = lists:flatten([[W, $\n] || W <- Warnings]), - case question_dialog(Question, Details) of - ?wxID_OK -> - true; - ?wxID_CANCEL -> - reltool_server:undo_config(S#state.server_pid), - false - end. - display_message(Message, Icon) -> Dialog = wxMessageDialog:new(wx:null(), Message, @@ -1491,8 +1652,6 @@ add_text(_,_,[]) -> ok. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sys callbacks diff --git a/lib/reltool/src/reltool_target.erl b/lib/reltool/src/reltool_target.erl index 0fcf89a360..e3a7b02143 100644 --- a/lib/reltool/src/reltool_target.erl +++ b/lib/reltool/src/reltool_target.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -101,7 +101,7 @@ do_gen_config(#sys{root_dir = RootDir, || A <- Apps, A#app.name =/= ?MISSING_APP_NAME, A#app.name =/= erts, - not A#app.is_escript], + A#app.is_escript =/= true], EscriptItems = [{escript, A#app.active_dir, emit(incl_cond, A#app.incl_cond, undefined, InclDefs)} @@ -155,6 +155,7 @@ do_gen_config(#app{name = Name, archive_opts = ArchiveOpts, use_selected_vsn = UseSelected, vsn = Vsn, + active_dir = ActiveDir, mods = Mods, is_included = IsIncl}, InclDefs) -> @@ -170,9 +171,10 @@ do_gen_config(#app{name = Name, emit(excl_archive_filters, ExclArchiveDirs, undefined, InclDefs), emit(archive_opts, ArchiveOpts, undefined, InclDefs), if - IsIncl, InclDefs -> [{vsn, Vsn}]; - UseSelected -> [{vsn, Vsn}]; - true -> [] + IsIncl, InclDefs -> [{vsn, Vsn}, {lib_dir, ActiveDir}]; + UseSelected =:= vsn -> [{vsn, Vsn}]; + UseSelected =:= dir -> [{lib_dir, ActiveDir}]; + true -> [] end, [do_gen_config(M, InclDefs) || M <- Mods] ], @@ -208,10 +210,10 @@ do_gen_config(#rel_app{name = Name, incl_apps = InclApps}, _InclDefs) -> case {Type, InclApps} of - {undefined, []} -> Name; - {undefined, _} -> {Name, InclApps}; - {_, []} -> {Name, Type}; - {_, _} -> {Name, Type, InclApps} + {undefined, undefined} -> Name; + {undefined, _} -> {Name, InclApps}; + {_, undefined} -> {Name, Type}; + {_, _} -> {Name, Type, InclApps} end; do_gen_config({Tag, Val}, InclDefs) -> emit(Tag, Val, undefined, InclDefs); @@ -247,10 +249,15 @@ gen_app(#app{name = Name, env = Env, mod = StartMod, start_phases = StartPhases}}) -> - StartMod2 = - case StartMod =:= undefined of - true -> []; - false -> [{mod, StartMod}] + StartPhases2 = + case StartPhases of + undefined -> []; + _ -> [{start_phases, StartPhases}] + end, + Tail = + case StartMod of + undefined -> StartPhases2; + _ -> [{mod, StartMod} | StartPhases2] end, {application, Name, [{description, Desc}, @@ -261,10 +268,9 @@ gen_app(#app{name = Name, {applications, ReqApps}, {included_applications, InclApps}, {env, Env}, - {start_phases, StartPhases}, {maxT, MaxT}, {maxP, MaxP} | - StartMod2]}. + Tail]}. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate the contents of a rel file @@ -279,7 +285,7 @@ gen_rel(Rel, Sys) -> {error, Text} end. -do_gen_rel(#rel{name = RelName, vsn = RelVsn}, +do_gen_rel(#rel{name = RelName, vsn = RelVsn, rel_apps = RelApps}, #sys{apps = Apps}, MergedApps) -> ErtsName = erts, @@ -288,7 +294,7 @@ do_gen_rel(#rel{name = RelName, vsn = RelVsn}, {release, {RelName, RelVsn}, {ErtsName, Erts#app.vsn}, - [strip_rel_info(App) || App <- MergedApps]}; + [strip_rel_info(App, RelApps) || App <- MergedApps]}; false -> reltool_utils:throw_error("Mandatory application ~p is " "not included", @@ -298,13 +304,17 @@ do_gen_rel(#rel{name = RelName, vsn = RelVsn}, strip_rel_info(#app{name = Name, vsn = Vsn, app_type = Type, - info = #app_info{incl_apps = InclApps}}) - when Type =/= undefined -> - case {Type, InclApps} of - {permanent, []} -> {Name, Vsn}; - {permanent, _} -> {Name, Vsn, InclApps}; - {_, []} -> {Name, Vsn, Type}; - {_, _} -> {Name, Vsn, Type, InclApps} + info = #app_info{incl_apps = AppInclApps}}, + RelApps) when Type =/= undefined -> + RelInclApps = case lists:keyfind(Name,#rel_app.name,RelApps) of + #rel_app{incl_apps = RIA} when RIA =/= undefined -> RIA; + _ -> undefined + end, + case {Type, RelInclApps} of + {permanent, undefined} -> {Name, Vsn}; + {permanent, _} -> {Name, Vsn, AppInclApps}; + {_, undefined} -> {Name, Vsn, Type}; + {_, _} -> {Name, Vsn, Type, AppInclApps} end. merge_apps(#rel{name = RelName, @@ -323,7 +333,7 @@ merge_apps(#rel{name = RelName, A#app.name =/= ?MISSING_APP_NAME, not lists:keymember(A#app.name, #app.name, MergedApps2)], MergedApps3 = do_merge_apps(RelName, Embedded, Apps, EmbAppType, MergedApps2), - sort_apps(MergedApps3). + sort_apps(lists:reverse(MergedApps3)). do_merge_apps(RelName, [#rel_app{name = Name} = RA | RelApps], Apps, RelAppType, Acc) -> case is_already_merged(Name, RelApps, Acc) of @@ -341,25 +351,18 @@ do_merge_apps(RelName, [Name | RelApps], Apps, RelAppType, Acc) -> true -> do_merge_apps(RelName, RelApps, Apps, RelAppType, Acc); false -> - RelApp = init_rel_app(Name, Apps), + RelApp = #rel_app{name = Name}, do_merge_apps(RelName, [RelApp | RelApps], Apps, RelAppType, Acc) end; do_merge_apps(_RelName, [], _Apps, _RelAppType, Acc) -> - lists:reverse(Acc). - -init_rel_app(Name, Apps) -> - {value, App} = lists:keysearch(Name, #app.name, Apps), - Info = App#app.info, - #rel_app{name = Name, - app_type = undefined, - incl_apps = Info#app_info.incl_apps}. + Acc. merge_app(RelName, - #rel_app{name = Name, - app_type = Type, - incl_apps = InclApps}, - RelAppType, - App) -> + #rel_app{name = Name, + app_type = Type, + incl_apps = InclApps0}, + RelAppType, + App) -> Type2 = case {Type, App#app.app_type} of {undefined, undefined} -> RelAppType; @@ -367,6 +370,11 @@ merge_app(RelName, {_, _} -> Type end, Info = App#app.info, + InclApps = + case InclApps0 of + undefined -> Info#app_info.incl_apps; + _ -> InclApps0 + end, case InclApps -- Info#app_info.incl_apps of [] -> App#app{app_type = Type2, info = Info#app_info{incl_apps = InclApps}}; @@ -421,7 +429,10 @@ do_gen_script(#rel{name = RelName, vsn = RelVsn}, Mandatory = mandatory_modules(), Early = Mandatory ++ Preloaded, {value, KernelApp} = lists:keysearch(kernel, #app.name, MergedApps), - InclApps = [I || #app{info = #app_info{incl_apps = I}} <- MergedApps], + InclApps = lists:flatmap(fun(#app{info = #app_info{incl_apps = I}}) -> + I + end, + MergedApps), %% Create the script DeepList = @@ -471,7 +482,7 @@ load_app_mods(#app{mods = Mods} = App, Mand, PathFlag, Variables) -> Path = cr_path(App, PathFlag, Variables), PartNames = lists:sort([{packages:split(M),M} || - #mod{name = M} <- Mods, + #mod{name = M, is_included=true} <- Mods, not lists:member(M, Mand)]), SplitMods = lists:foldl( @@ -513,7 +524,12 @@ sort_apps([#app{name = Name, info = Info} = App | Apps], Circular, Visited) -> {Uses, Apps1, NotFnd1} = - find_all(Name, Info#app_info.applications, Apps, Visited, [], []), + find_all(Name, + lists:reverse(Info#app_info.applications), + Apps, + Visited, + [], + []), {Incs, Apps2, NotFnd2} = find_all(Name, lists:reverse(Info#app_info.incl_apps), @@ -895,7 +911,7 @@ spec_escripts(#sys{apps = Apps}, ErtsBin, BinFiles) -> if Name =:= ?MISSING_APP_NAME -> false; - not IsEscript -> + IsEscript =/= true -> false; IsIncl; IsPre -> {true, do_spec_escript(File, ErtsBin, BinFiles)}; @@ -957,7 +973,7 @@ spec_lib_files(#sys{apps = Apps} = Sys) -> if Name =:= ?MISSING_APP_NAME -> false; - IsEscript -> + IsEscript =/= false -> false; IsIncl; IsPre -> true; diff --git a/lib/reltool/src/reltool_utils.erl b/lib/reltool/src/reltool_utils.erl index 39d057d994..6149d6ef06 100644 --- a/lib/reltool/src/reltool_utils.erl +++ b/lib/reltool/src/reltool_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,15 +22,17 @@ -export([root_dir/0, erl_libs/0, lib_dirs/1, split_app_name/1, prim_consult/1, default_rels/0, choose_default/3, + normalize_dir/1, - assign_image_list/1, get_latest_resize/1, + assign_image_list/1, get_latest_resize/1, wait_for_stop_motion/2, mod_conds/0, list_to_mod_cond/1, mod_cond_to_index/1, incl_conds/0, list_to_incl_cond/1, incl_cond_to_index/1, elem_to_index/2, app_dir_test/2, split_app_dir/1, get_item/1, get_items/1, get_selected_items/3, select_items/3, select_item/2, + get_column_width/1, - safe_keysearch/5, print/4, return_first_error/2, add_warning/2, + safe_keysearch/5, print/4, add_warning/3, create_dir/1, list_dir/1, read_file_info/1, write_file_info/2, read_file/1, write_file/2, @@ -86,6 +88,21 @@ split_app_name(Name) -> {list_to_atom(Name), ""} end. + +normalize_dir(RelDir) -> + Tokens = filename:split(filename:absname(RelDir)), + filename:join(lists:reverse(normalize_dir(Tokens, []))). + +normalize_dir([".."|Dirs], [_Dir|Path]) -> + normalize_dir(Dirs, Path); +normalize_dir(["."|Dirs], Path) -> + normalize_dir(Dirs, Path); +normalize_dir([Dir|Dirs], Path) -> + normalize_dir(Dirs, [Dir|Path]); +normalize_dir([], Path) -> + Path. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% prim_consult(Bin) when is_binary(Bin) -> @@ -126,18 +143,14 @@ prim_parse(Tokens, Acc) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% default_rels() -> - %%Kernel = #rel_app{name = kernel, incl_apps = []}, - %%Stdlib = #rel_app{name = stdlib, incl_apps = []}, - Sasl = #rel_app{name = sasl, incl_apps = []}, + %% kernel and stdlib are added automatically in every release [ #rel{name = ?DEFAULT_REL_NAME, vsn = "1.0", rel_apps = []}, - %%rel_apps = [Kernel, Stdlib]}, #rel{name = "start_sasl", vsn = "1.0", - rel_apps = [Sasl]} - %%rel_apps = [Kernel, Sasl, Stdlib]} + rel_apps = [#rel_app{name = sasl}]} ]. choose_default(Tag, Profile, InclDefs) @@ -191,6 +204,16 @@ get_latest_resize(#wx{obj = ObjRef, event = #wxSize{}} = Wx) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +wait_for_stop_motion(ObjRef, {_,_}=Pos) -> + receive + #wx{obj = ObjRef, event = #wxMouse{type = motion, x=X, y=Y}} -> + wait_for_stop_motion(ObjRef, {X,Y}) + after 100 -> + Pos + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + mod_conds() -> ["all (ebin + app file)", "ebin + derived", "app file + derived", "derived", "none"]. @@ -249,7 +272,7 @@ app_dir_test(Dir1, Dir2) -> Name1 > Name2 -> false; Vsn1 < Vsn2 -> false; Vsn1 > Vsn2 -> true; - Parent1 < Parent2 -> true; + Parent1 =< Parent2 -> true; true -> false end. @@ -377,6 +400,26 @@ select_item(ListCtrl, [{ItemNo, Text} | Items]) -> select_item(_ListCtrl, []) -> ok. +get_column_width(ListCtrl) -> + wx:batch(fun() -> + {Total, _} = wxWindow:getClientSize(ListCtrl), + Total - scroll_size(ListCtrl) + end). + +scroll_size(ObjRef) -> + case os:type() of + {win32, nt} -> 0; + {unix, darwin} -> + %% I can't figure out is there is a visible scrollbar + %% Always make room for it + wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); + _ -> + case wxWindow:hasScrollbar(ObjRef, ?wxVERTICAL) of + true -> wxSystemSettings:getMetric(?wxSYS_VSCROLL_X); + false -> 0 + end + end. + safe_keysearch(Key, Pos, List, Mod, Line) -> case lists:keysearch(Key, Pos, List) of false -> @@ -392,31 +435,13 @@ print(X, X, Format, Args) -> print(_, _, _, _) -> ok. -%% -define(SAFE(M,F,A), safe(M, F, A, ?MODULE, ?LINE)). -%% -%% safe(M, F, A, Mod, Line) -> -%% case catch apply(M, F, A) of -%% {'EXIT', Reason} -> -%% io:format("~p(~p): ~p:~p~p -> ~p\n", [Mod, Line, M, F, A, Reason]), -%% timer:sleep(infinity); -%% Res -> -%% Res -%% end. - -return_first_error(Status, NewError) when is_list(NewError) -> - case Status of - {ok, _Warnings} -> - {error, NewError}; - {error, OldError} -> - {error, OldError} - end. - -add_warning(Status, Warning) -> - case Status of - {ok, Warnings} -> - {ok, [Warning | Warnings]}; - {error, Error} -> - {error, Error} +add_warning(Format, Args, {ok,Warnings}) -> + Warning = lists:flatten(io_lib:format(Format,Args)), + case lists:member(Warning,Warnings) of + true -> + {ok,Warnings}; + false -> + {ok,[Warning|Warnings]} end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% diff --git a/lib/reltool/test/Makefile b/lib/reltool/test/Makefile index 767454b66a..52cdef44da 100644 --- a/lib/reltool/test/Makefile +++ b/lib/reltool/test/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2009-2011. All Rights Reserved. +# Copyright Ericsson AB 2009-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -28,6 +28,7 @@ MODULES= \ reltool_app_SUITE \ reltool_wx_SUITE \ reltool_server_SUITE \ + reltool_manual_gui_SUITE \ reltool_test_lib @@ -48,7 +49,7 @@ RELSYSDIR = $(RELEASE_PATH)/reltool_test # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -#ERL_COMPILE_FLAGS += +ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/reltool/ebin/ EBIN = . @@ -72,12 +73,12 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) reltool.spec reltool.cover $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_SCRIPT) rtt $(INSTALL_PROGS) $(RELSYSDIR) - $(INSTALL_DATA) $(INSTALL_PROGS) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) reltool.spec reltool.cover $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_SCRIPT) rtt $(INSTALL_PROGS) "$(RELSYSDIR)" + $(INSTALL_DATA) $(INSTALL_PROGS) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/reltool/test/reltool.spec b/lib/reltool/test/reltool.spec index 2995720105..2501a7a203 100644 --- a/lib/reltool/test/reltool.spec +++ b/lib/reltool/test/reltool.spec @@ -1 +1,2 @@ {suites,"../reltool_test",all}. +{skip_suites,"../reltool_test",[reltool_manual_gui_SUITE],"Manual only"}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE.erl b/lib/reltool/test/reltool_manual_gui_SUITE.erl new file mode 100644 index 0000000000..0dcc5cbf15 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE.erl @@ -0,0 +1,266 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% + +-module(reltool_manual_gui_SUITE). + +-export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, + init_per_suite/1, end_per_suite/1, + init_per_testcase/2, end_per_testcase/2]). + +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include("reltool_test_lib.hrl"). + +%% Initialization functions. +init_per_suite(Config) -> + reltool_test_lib:wx_init_per_suite(Config). + +end_per_suite(Config) -> + reltool_test_lib:wx_end_per_suite(Config). + +init_per_testcase(Func,Config) -> + reltool_test_lib:init_per_testcase(Func,Config). +end_per_testcase(Func,Config) -> + reltool_test_lib:end_per_testcase(Func,Config). + +%% SUITE specification +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + [config, depgraphs]. + +groups() -> + []. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + + +%% The test cases + +%% Semi-automatic walkthrough of the GUI +config(Config) -> + DataDir = ?config(data_dir,Config), + PrivDir = ?config(priv_dir,Config), + {ok, SysPid} = ?msym({ok, _}, reltool:start([])), + link(SysPid), + + SimpleConfigFile = create_simple_config(PrivDir), + WarningConfigFile = create_warning_config(PrivDir,DataDir), + + break("there are no modules in the 'Included' and 'Excluded' columns, " + "and now warnings are displayed", + {"load configuration ~p",[SimpleConfigFile]}), + break("kernel, stdlib and sasl are included and all other are excluded", + "undo"), + break("we are back to default - no included and no excluded applications", + "undo again"), + break("kernel, stdlib and sasl are included and all other are excluded", + {"load configuration ~p", + [WarningConfigFile]}), + break("a warning is displayed in the warning list", + "undo"), + break("no warning is displayed in the warning list", + "load same configuration again"), + break("application a is added in the 'Included' column and " + "one warning is displayed", + "reset configuration"), + break("we are back to default - no included and no excluded applications, " + "and no warnings", + "undo"), + break("a, kernel, stdlib and sasl are included and all other are excluded. " + "One warning should now exist through the rest of this test case", + "double click the warning"), + break("a popup window occurs displaying the warning text", + "close it with the 'Close' button"), + break("it disappears", + "open it again"), + break("the warning text can be marked, copied and pasted", + "close the popup with the close box on the top frame"), + break("it disappears", + "select application a from 'Included' column and click 'cross'-button " + "with to exclude it"), + break("application a is moved to 'Excluded' column", + "select application tools from 'Excluded' column and click " + "'tick'-button to include it"), + break("application tools is moved to 'Included' column", + "select application runtime_tools from 'Excluded' column and click " + "'tick'-button to include it"), + break("application runtime_tools is moved to 'Included' column", + "undo"), + + ExplicitConfig = filename:join(PrivDir,"explicit.config"), + break("application runtime_tools is moved back to 'Excluded' column", + {"save configuration as 'explicit' to ~p",[ExplicitConfig]}), + ExpectedExplicitConfig = + {sys,[{lib_dirs,[filename:join(DataDir,"faulty_app_file")]}, + {incl_cond,exclude}, + {app,a,[{incl_cond,exclude}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + check_config(ExpectedExplicitConfig,ExplicitConfig), + + break("The saved configuration file is checked and is ok.\n" + "Now go to the 'Libraries' tab and change the root directory to " + "some invalid directory."), + break("an error dialog occurs saying that there is no lib dir", + {"add library directory ~p", + [filename:join(DataDir,"dependencies")]}), + break("applications x, y and z are added to the 'Excluded' column in " + "'Applications' tab", + "go to the 'System settings' tab and set application inclusion policy " + "to 'derived'"), + break("a is excluded, kernel, stdlib, sasl and tools are included and " + "x, y and z are available in the 'Applications' tab", + "undo"), + break("all non included application are moved to the 'Excluded' column " + "and that 'Application inclusion policy' under 'System settings' " + "tab is set back to 'exclude'", + "undo again"), + break("applications x, y and z are in the 'Available' column", + "open application x, go to the 'Application settings' tab and set " + "'Application inclusion policy' to 'Use application specific config' " + "and 'include'. Close the window"), + break("application x is moved to the 'Included' column and z and y are moved " + "to the 'Derived' column in the system window", + "open application y"), + break("modules y1, y2 and y3 are in the 'Derived' column", + "go to 'Application dependencies' tab"), + break("application y is used by x, requires kernel and stdlib, and uses z", + "go to 'Module dependencies' tab"), + break("y is used by x2 and x3, and uses z1", + "got to 'Application settings' tab and select " + "'Source selection policy' 'Use selected version'"), + break("'Directories' frame becomes enabled (clickable)", + "select 'Module inclusion policy' 'Use application specific config' " + "and select 'app file + derived'"), + break("module y3 is moved to the 'Available' column in the 'Modules' tab", + "open module y1"), + break("module y1 is used by x2 and uses z1", + "go to the 'Code' tab and double click the line with call to z1:f()"), + break("new tab is opened with module z1", + "find something"), + break("it is found", + "goto some line"), + break("the cursor is moved to that line", + "click the 'Back' button"), + break("the cursor is moved back to the line of your previous 'find'", + "terminate the application window (but keep the module window)."), + + {ok,ServerPid} = reltool:get_server(SysPid), + unlink(SysPid), + break("the system window is still alive", + "terminate reltool by hitting 'Ctrl-q' (linux) or clicking the " + "close box on the top fram when system window is active"), + false = erlang:is_process_alive(SysPid), + false = erlang:is_process_alive(ServerPid), + + break("Check that both module window and system window are terminated"), + + ok. + + +depgraphs(Config) -> + PrivDir = ?config(priv_dir,Config), + SimpleConfigFile = create_simple_config(PrivDir), + {ok, SysPid} = ?msym({ok, _}, reltool:start([{config,SimpleConfigFile}])), + link(SysPid), + + break("Open the application dependency graph and \n\n" + "*move the complete graph by left clicking somewhere over it and drag\n" + "*move one node left clicking the node and drag\n" + "*lock node to position by holding down shift while releasing\n" + "*select several nodes with ctrl and left mouse button\n" + "*lock/unlock selected nodes with suitable buttons\n" + "*freeze\n" + "*reset\n" + "*left slider: push nodes apart\n" + "*right slider: pull nodes together\n" + "*middle slider: adjust length of links\n" + "*select node and delete\n"), + break("Open the module dependency graph and meditate over it... "), + + unlink(SysPid), + break("Terminate reltool from the file menu in the system window"), + false = erlang:is_process_alive(SysPid), + + break("Check that system window and graphs are terminated"), + + ok. + + + + +%%%----------------------------------------------------------------- +%%% Internal functions +break(CheckStr,DoStr) when is_list(CheckStr), is_list(DoStr) -> + Str = io_lib:format("Check that ~s.~n~nThen ~s.",[CheckStr,DoStr]), + break(Str); +break(Check,Do) -> + CheckStr = + case Check of + {CheckFormat,CheckArgs} -> io_lib:format(CheckFormat,CheckArgs); + _ -> Check + end, + DoStr = + case Do of + {DoFormat,DoArgs} -> io_lib:format(DoFormat,DoArgs); + _ -> Do + end, + break(CheckStr,DoStr). + +break(Str) -> + Count = + case get(count) of + undefined -> 1; + C -> C + end, + put(count,Count+1), + test_server:break("Step " ++ integer_to_list(Count) ++ ":\n" ++ Str). + +check_config(Expected,File) -> + {ok,[Config]} = file:consult(File), + ?m(Expected,Config). + +create_simple_config(PrivDir) -> + SimpleConfigFile = filename:join(PrivDir,"simple.config"), + SimpleConfig = {sys,[{incl_cond,exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}]}, + ok=file:write_file(SimpleConfigFile,io_lib:format("~p.~n",[SimpleConfig])), + SimpleConfigFile. + +create_warning_config(PrivDir,DataDir) -> + WarningConfigFile = filename:join(PrivDir,"warning.config"), + FaultyAppFileDir = filename:join(DataDir,"faulty_app_file"), + WarningConfig = {sys,[{lib_dirs,[FaultyAppFileDir]}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,a,[{incl_cond,include}]} + ]}, + ok=file:write_file(WarningConfigFile,io_lib:format("~p.~n",[WarningConfig])), + WarningConfigFile. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src b/lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src new file mode 100644 index 0000000000..9a340274ad --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/Makefile.src @@ -0,0 +1,28 @@ +EFLAGS=+debug_info + +DEPENDENCIES= \ + dependencies/x-1.0/ebin/x1.@EMULATOR@ \ + dependencies/x-1.0/ebin/x2.@EMULATOR@ \ + dependencies/x-1.0/ebin/x3.@EMULATOR@ \ + dependencies/y-1.0/ebin/y1.@EMULATOR@ \ + dependencies/y-1.0/ebin/y2.@EMULATOR@ \ + dependencies/y-1.0/ebin/y3.@EMULATOR@ \ + dependencies/z-1.0/ebin/z1.@EMULATOR@ + + +all: $(DEPENDENCIES) + +dependencies/x-1.0/ebin/x1.@EMULATOR@: dependencies/x-1.0/src/x1.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x1.erl +dependencies/x-1.0/ebin/x2.@EMULATOR@: dependencies/x-1.0/src/x2.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x2.erl +dependencies/x-1.0/ebin/x3.@EMULATOR@: dependencies/x-1.0/src/x3.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x3.erl +dependencies/y-1.0/ebin/y1.@EMULATOR@: dependencies/y-1.0/src/y1.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y1.erl +dependencies/y-1.0/ebin/y2.@EMULATOR@: dependencies/y-1.0/src/y2.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y2.erl +dependencies/y-1.0/ebin/y3.@EMULATOR@: dependencies/y-1.0/src/y3.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y3.erl +dependencies/z-1.0/ebin/z1.@EMULATOR@: dependencies/z-1.0/src/z1.erl + erlc $(EFLAGS) -odependencies/z-1.0/ebin dependencies/z-1.0/src/z1.erl diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app new file mode 100644 index 0000000000..ccaab8a8c7 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Main application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [x1,x2,x3]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl new file mode 100644 index 0000000000..bf1e7f9279 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x1.erl @@ -0,0 +1,5 @@ +-module(x1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl new file mode 100644 index 0000000000..82191ba278 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x2.erl @@ -0,0 +1,5 @@ +-module(x2). +-compile(export_all). + +f() -> + y1:f(). diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl new file mode 100644 index 0000000000..618c75c9a7 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/x-1.0/src/x3.erl @@ -0,0 +1,5 @@ +-module(x3). +-compile(export_all). + +f() -> + y2:f(). diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app new file mode 100644 index 0000000000..4f9b610b69 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, y, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [y1,y2]}, % y3 is skipped on purpose - to test module inclusion policy + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl new file mode 100644 index 0000000000..dd21b33292 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y1.erl @@ -0,0 +1,5 @@ +-module(y1). +-compile(export_all). + +f() -> + z1:f(). diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl new file mode 100644 index 0000000000..bf8ddf6080 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y2.erl @@ -0,0 +1,5 @@ +-module(y2). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl new file mode 100644 index 0000000000..915d64d5b9 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/y-1.0/src/y3.erl @@ -0,0 +1,5 @@ +-module(y3). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app new file mode 100644 index 0000000000..437a0968e9 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/ebin/z.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, z, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [z1]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl new file mode 100644 index 0000000000..97ef90b87f --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/dependencies/z-1.0/src/z1.erl @@ -0,0 +1,5 @@ +-module(z1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app new file mode 100644 index 0000000000..ea77103598 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/ebin/a.app @@ -0,0 +1 @@ +faulty app file diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a.erl @@ -0,0 +1,49 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/reltool/test/reltool_manual_gui_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index 9ed79e8c95..8a98dc36cf 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -24,6 +24,7 @@ -compile(export_all). +-include_lib("reltool/src/reltool.hrl"). -include("reltool_test_lib.hrl"). -include_lib("common_test/include/ct.hrl"). @@ -51,10 +52,45 @@ end_per_testcase(Func,Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [start_server, set_config, create_release, - create_script, create_target, create_embedded, - create_standalone, create_old_target, - otp_9135, otp_9229_exclude_app, otp_9229_exclude_mod]. + [start_server, + set_config, + get_config, + create_release, + create_release_sort, + create_script, + create_script_sort, + create_target, + create_embedded, + create_standalone, + create_standalone_beam, + create_standalone_app, + create_standalone_app_clash, + create_multiple_standalone, + create_old_target, + eval_target_spec, + otp_9135, + otp_9229_dupl_mod_exclude_app, + otp_9229_dupl_mod_exclude_mod, + dupl_mod_in_app_file, + get_apps, + get_mod, + get_sys, + set_app_and_undo, + set_apps_and_undo, + set_apps_inlined, + set_sys_and_undo, + load_config_and_undo, + load_config_fail, + load_config_escript_path, + load_config_same_escript_source, + load_config_same_escript_beam, + load_config_add_escript, + reset_config_and_undo, + gen_rel_files, + save_config, + dependencies, + use_selected_vsn, + use_selected_vsn_relative_path]. groups() -> []. @@ -71,8 +107,6 @@ end_per_group(_GroupName, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Start a server process and check that it does not crash -start_server(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); start_server(_Config) -> {ok, Pid} = ?msym({ok, _}, reltool:start_server([])), Libs = lists:sort(erl_libs()), @@ -88,8 +122,6 @@ start_server(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Start a server process and check that it does not crash -set_config(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); set_config(_Config) -> Libs = lists:sort(erl_libs()), Default = @@ -112,10 +144,102 @@ set_config(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Check that get_config returns the expected derivates and defaults +%% as specified +get_config(_Config) -> + + KVsn = latest(kernel), + StdVsn = latest(stdlib), + SaslVsn = latest(sasl), + + LibDir = code:lib_dir(), + KLibDir = filename:join(LibDir,"kernel-"++KVsn), + StdLibDir = filename:join(LibDir,"stdlib-"++StdVsn), + SaslLibDir = filename:join(LibDir,"sasl-"++SaslVsn), + + Sys = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}]}, + {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + ?m({ok, Sys}, reltool:get_config(Pid,false,false)), + + %% Include derived info + ?msym({ok,{sys,[{incl_cond, exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{lib_dir,StdLibDir}, + {mod,_,[]}|_]}]}}, + reltool:get_config(Pid,false,true)), + + %% Include defaults + ?msym({ok,{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include},{vsn,undefined}, + {lib_dir,undefined}]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}, + {lib_dir,undefined}]}, + {app,stdlib,[{incl_cond,include},{vsn,undefined}, + {lib_dir,StdLibDir}]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}}, + reltool:get_config(Pid,true,false)), + + %% Include both defaults and derived info + ?msym({ok,{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{vsn,KVsn}, + {lib_dir,KLibDir},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}, + {lib_dir,SaslLibDir},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{vsn,StdVsn}, + {lib_dir,StdLibDir},{mod,_,[]}|_]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}}, + reltool:get_config(Pid,true,true)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% OTP-9135, test that app_file option can be set to all | keep | strip -otp_9135(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); otp_9135(_Config) -> Libs = lists:sort(erl_libs()), StrippedDefaultSys = @@ -145,8 +269,6 @@ otp_9135(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate releases -create_release(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_release(_Config) -> %% Configure the server RelName = "Just testing...", @@ -170,10 +292,135 @@ create_release(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate releases and make sure order of applications specified in +%% 'rel' parameter is preserved and that included applications are +%% started before the including application. +%% Circular dependencies shall also be detected and cause error. + +create_release_sort(_Config) -> {skip, "Two bugs related to sorting"}; +create_release_sort(Config) -> + DataDir = ?config(data_dir,Config), + %% Configure the server + RelName1 = "MnesiaFirst", + RelName2 = "SaslFirst", + RelName3 = "Include-both", + RelName4 = "Include-only-app", + RelName5 = "Include-only-rel", + RelName6 = "Include-missing-app", + RelName7 = "Circular", + RelName8 = "Include-both-missing-app", + RelName9 = "Include-overwrite", + RelName10= "Uses-order-as-rel", + RelVsn = "1.0", + %% Application z (.app file): + %% includes [tools, mnesia] + %% uses [kernel, stdlib, sasl, inets] + Sys = + {sys, + [ + {lib_dirs, [filename:join(DataDir,"sort_apps")]}, + {boot_rel, RelName1}, + {rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]}, + {rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]}, + {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]}, + {rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]}, + {rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]}, + {rel, RelName6, RelVsn, [stdlib, kernel, z]}, + {rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]}, + {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]}, + {rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]}, + {rel, RelName10, RelVsn, [stdlib, kernel, {z,[]}, inets, sasl]}, + {incl_cond,exclude}, + {mod_cond,app}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,mnesia,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,inets,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include}]}, + {app,z,[{incl_cond,include}]}, + {app,tools,[{mod_cond,app},{incl_cond,include}]} + ]}, + %% Generate release + + ?msym({ok, {release, {RelName1, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {mnesia, _}, + {sasl, _}]}}, + reltool:get_rel([{config, Sys}], RelName1)), + + ?msym({ok, {release, {RelName2, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {sasl, _}, + {mnesia, _}]}}, + reltool:get_rel([{config, Sys}], RelName2)), + + ?msym({ok, {release, {RelName3, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {sasl, _}, + {inets, _}, + {tools, _}, + {z, _, [tools]}, + {mnesia, _}]}}, + reltool:get_rel([{config, Sys}], RelName3)), + + %%! BUG: same as OTP-4121, but for reltool???? Or revert tools and mnesia + ?msym({ok, {release, {RelName4, RelVsn}, + {erts, _}, + [{kernel, _}, + {stdlib, _}, + {sasl, _}, + {inets, _}, + {mnesia, _}, + {tools, _}, + {z, _}]}}, + reltool:get_rel([{config, Sys}], RelName4)), + + ?m({error,"sasl: These applications are used by release " + "Include-only-rel but are missing as included_applications " + "in the app file: [tools]"}, + reltool:get_rel([{config, Sys}], RelName5)), + + ?m({error, "Undefined applications: [tools,mnesia]"}, + reltool:get_rel([{config, Sys}], RelName6)), + + ?m({error,"Circular dependencies: [x,y]"}, + reltool:get_rel([{config, Sys}], RelName7)), + + ?m({error,"Undefined applications: [tools]"}, + reltool:get_rel([{config, Sys}], RelName8)), + + ?msym({ok,{release,{RelName9,RelVsn}, + {erts,_}, + [{kernel,_}, + {stdlib,_}, + {sasl, _}, + {inets, _}, + {z,_,[]}]}}, + reltool:get_rel([{config, Sys}], RelName9)), + + %%! BUG: same as OTP-9984, but for reltool???? Or revert inets and sasl? + ?msym({ok,{release,{RelName10,RelVsn}, + {erts,_}, + [{kernel,_}, + {stdlib,_}, + {inets, _}, + {sasl, _}, + {z,_,[]}]}}, + reltool:get_rel([{config, Sys}], RelName10)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate boot scripts -create_script(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_script(_Config) -> %% Configure the server RelName = "Just testing", @@ -218,10 +465,197 @@ create_script(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test creation of .script with different sorting of applications and +%% included applications. +%% Test that result is equal to what systools produces +create_script_sort(Config) -> + DataDir = ?config(data_dir,Config), + %% Configure the server + RelName1 = "MnesiaFirst", + RelName2 = "SaslFirst", + RelName3 = "Include-both", + RelName4 = "Include-only-app", + RelName5 = "Include-only-rel", + RelName6 = "Include-missing-app", + RelName7 = "Circular", + RelName8 = "Include-both-missing-app", + RelName9 = "Include-overwrite", + RelVsn = "1.0", + LibDir = filename:join(DataDir,"sort_apps"), + Sys = + {sys, + [ + {lib_dirs, [LibDir]}, + {boot_rel, RelName1}, + {rel, RelName1, RelVsn, [stdlib, kernel, mnesia, sasl]}, + {rel, RelName2, RelVsn, [stdlib, kernel, sasl, mnesia]}, + {rel, RelName3, RelVsn, [stdlib, kernel, {z,[tools]}, tools, mnesia]}, + {rel, RelName4, RelVsn, [stdlib, kernel, z, mnesia, tools]}, + {rel, RelName5, RelVsn, [stdlib, kernel, {sasl,[tools]}]}, + {rel, RelName6, RelVsn, [stdlib, kernel, z]}, + {rel, RelName7, RelVsn, [stdlib, kernel, mnesia, y, sasl, x]}, + {rel, RelName8, RelVsn, [stdlib, kernel, {z,[tools]}]}, + {rel, RelName9, RelVsn, [stdlib, kernel, {z,[]}]}, + {incl_cond,exclude}, + {mod_cond,app}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,mnesia,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,inets,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,include}]}, + {app,z,[{incl_cond,include}]}, + {app,tools,[{mod_cond,app},{incl_cond,include}]} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + %% Generate release files + application:load(sasl), + application:load(inets), + application:load(mnesia), + application:load(tools), + {ok,KernelVsn} = application:get_key(kernel,vsn), + {ok,StdlibVsn} = application:get_key(stdlib,vsn), + {ok,SaslVsn} = application:get_key(sasl,vsn), + {ok,InetsVsn} = application:get_key(inets,vsn), + {ok,MnesiaVsn} = application:get_key(mnesia,vsn), + {ok,ToolsVsn} = application:get_key(tools,vsn), + ErtsVsn = erlang:system_info(version), + + Rel1 = {release, {RelName1,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {mnesia,MnesiaVsn}, + {sasl,SaslVsn}]}, + FullName1 = filename:join(?WORK_DIR,RelName1), + ?m(ok, file:write_file(FullName1 ++ ".rel", io_lib:format("~p.\n", [Rel1]))), + Rel2 = {release, {RelName2,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {sasl,SaslVsn}, + {mnesia,MnesiaVsn}]}, + FullName2 = filename:join(?WORK_DIR,RelName2), + ?m(ok, file:write_file(FullName2 ++ ".rel", io_lib:format("~p.\n", [Rel2]))), + Rel3 = {release, {RelName3,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0",[tools]}, + {tools,ToolsVsn}, + {mnesia,MnesiaVsn}, + {sasl,SaslVsn}, + {inets,InetsVsn}]}, + FullName3 = filename:join(?WORK_DIR,RelName3), + ?m(ok, file:write_file(FullName3 ++ ".rel", io_lib:format("~p.\n", [Rel3]))), + Rel4 = {release, {RelName4,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0"}, + {tools,ToolsVsn}, + {mnesia,MnesiaVsn}, + {sasl,SaslVsn}, + {inets,InetsVsn}]}, + FullName4 = filename:join(?WORK_DIR,RelName4), + ?m(ok, file:write_file(FullName4 ++ ".rel", io_lib:format("~p.\n", [Rel4]))), + Rel5 = {release, {RelName5,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {sasl,SaslVsn,[tools]}, + {tools,ToolsVsn}]}, + FullName5 = filename:join(?WORK_DIR,RelName5), + ?m(ok, file:write_file(FullName5 ++ ".rel", io_lib:format("~p.\n", [Rel5]))), + Rel6 = {release, {RelName6,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0"}]}, + FullName6 = filename:join(?WORK_DIR,RelName6), + ?m(ok, file:write_file(FullName6 ++ ".rel", io_lib:format("~p.\n", [Rel6]))), + Rel7 = {release, {RelName7,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {mnesia,MnesiaVsn}, + {y,"1.0"}, + {sasl,SaslVsn}, + {x,"1.0"}]}, + FullName7 = filename:join(?WORK_DIR,RelName7), + ?m(ok, file:write_file(FullName7 ++ ".rel", io_lib:format("~p.\n", [Rel7]))), + Rel8 = {release, {RelName8,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0",[tools]}]}, + FullName8 = filename:join(?WORK_DIR,RelName8), + ?m(ok, file:write_file(FullName8 ++ ".rel", io_lib:format("~p.\n", [Rel8]))), + Rel9 = {release, {RelName9,RelVsn}, {erts,ErtsVsn}, + [{kernel,KernelVsn}, + {stdlib,StdlibVsn}, + {z,"1.0",[]}, + {sasl,SaslVsn}, + {inets,InetsVsn}]}, + FullName9 = filename:join(?WORK_DIR,RelName9), + ?m(ok, file:write_file(FullName9 ++ ".rel", io_lib:format("~p.\n", [Rel9]))), + + %% Generate script files with systools and reltool and compare + ZPath = filename:join([LibDir,"*",ebin]), + + ?msym({ok,_,_}, systools_make_script(FullName1,ZPath)), + {ok, [SystoolsScript1]} = ?msym({ok,[_]}, file:consult(FullName1++".script")), + {ok, Script1} = ?msym({ok, _}, reltool:get_script(Pid, RelName1)), + ?m(equal, diff_script(SystoolsScript1, Script1)), + + ?msym({ok,_,_}, systools_make_script(FullName2,ZPath)), + {ok, [SystoolsScript2]} = ?msym({ok,[_]}, file:consult(FullName2++".script")), + {ok, Script2} = ?msym({ok, _}, reltool:get_script(Pid, RelName2)), + ?m(equal, diff_script(SystoolsScript2, Script2)), + + ?msym({ok,_,_}, systools_make_script(FullName3,ZPath)), + {ok, [SystoolsScript3]} = ?msym({ok,[_]}, file:consult(FullName3++".script")), + {ok, Script3} = ?msym({ok, _}, reltool:get_script(Pid, RelName3)), + ?m(equal, diff_script(SystoolsScript3, Script3)), + + ?msym({ok,_,_}, systools_make_script(FullName4,ZPath)), + {ok, [SystoolsScript4]} = ?msym({ok,[_]}, file:consult(FullName4++".script")), + {ok, Script4} = ?msym({ok, _}, reltool:get_script(Pid, RelName4)), + ?m(equal, diff_script(SystoolsScript4, Script4)), + + ?msym({error,_,[{error_reading,{sasl,{override_include,_}}}]}, + systools_make_script(FullName5,ZPath)), + ?m({error,"sasl: These applications are used by release " + "Include-only-rel but are missing as included_applications " + "in the app file: [tools]"}, + reltool:get_script(Pid, RelName5)), + + ?msym({error,_,{undefined_applications,_}}, + systools_make_script(FullName6,ZPath)), + ?m({error, "Undefined applications: [tools,mnesia]"}, + reltool:get_script(Pid, RelName6)), + + ?msym({error,_,{circular_dependencies,_}}, + systools_make_script(FullName7,ZPath)), + ?m({error,"Circular dependencies: [x,y]"}, + reltool:get_script(Pid, RelName7)), + + ?msym({error,_,{undefined_applications,_}}, + systools_make_script(FullName8,ZPath)), + ?m({error, "Undefined applications: [tools]"}, + reltool:get_script(Pid, RelName8)), + + ?msym({ok,_,_}, systools_make_script(FullName9,ZPath)), + {ok, [SystoolsScript9]} = ?msym({ok,[_]}, file:consult(FullName9++".script")), + {ok, Script9} = ?msym({ok, _}, reltool:get_script(Pid, RelName9)), + ?m(equal, diff_script(SystoolsScript9, Script9)), + + %% Stop server + ?m(ok, reltool:stop(Pid)), + ok. + + +systools_make_script(Name,Path) -> + systools:make_script(Name,[{path,[Path]},{outdir,?WORK_DIR},silent]). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate target system -create_target(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_target(_Config) -> %% Configure the server RelName1 = "Just testing", @@ -243,7 +677,7 @@ create_target(_Config) -> ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Config}])]), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -254,8 +688,6 @@ create_target(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate embedded target system -create_embedded(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_embedded(_Config) -> %% Configure the server RelName1 = "Just testing", @@ -276,7 +708,7 @@ create_embedded(_Config) -> TargetDir = filename:join([?WORK_DIR, "target_embedded"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -287,8 +719,6 @@ create_embedded(_Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate standalone system -create_standalone(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); create_standalone(_Config) -> %% Configure the server ExDir = code:lib_dir(reltool, examples), @@ -306,30 +736,223 @@ create_standalone(_Config) -> TargetDir = filename:join([?WORK_DIR, "target_standalone"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + %% Start the target system and fetch root dir BinDir = filename:join([TargetDir, "bin"]), Erl = filename:join([BinDir, "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), ?msym(ok, stop_node(Node)), + %% Execute escript Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", "Smp: false\n", "ExitCode:0"]), io:format("Expected: ~s\n", [Expected]), - ?m(Expected, run(BinDir, EscriptName ++ " -arg1 arg2 arg3")), + ?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")), ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with inlined beam file + +create_standalone_beam(Config) -> + %% Read beam file + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + + %% Create the escript + EscriptName = "mymod.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_standalone_beam"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)), + + %% Start the target system and fetch root dir + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + %% Execute escript + Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "ExitCode:0"]), + io:format("Expected: ~s\n", [Expected]), + ?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with inlined archived application + +create_standalone_app(Config) -> + %% Create archive + DataDir = ?config(data_dir,Config), + EscriptDir = filename:join(DataDir,escript), + {ok,{_Archive,Bin}} = zip:create("someapp-1.0.ez",["someapp-1.0"], + [memory, + {cwd,EscriptDir}, + {compress,all}, + {uncompress,[".beam",".app"]}]), + + %% Create the escript + EscriptName = "someapp.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang, + {emu_args,"-escript main mymod"}, + {archive,Bin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_standalone_app"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Sys}], TargetDir)), + + %% Start the target system and fetch root dir + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + %% Execute escript + Expected = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "ExitCode:0"]), + io:format("Expected: ~s\n", [Expected]), + ?m(Expected, run(BinDir, EscriptName, "-arg1 arg2 arg3")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with inlined archived application +%% Check that the inlined app can not be explicitly configured + +create_standalone_app_clash(Config) -> + %% Create archive + DataDir = ?config(data_dir,Config), + EscriptDir = filename:join(DataDir,escript), + {ok,{_Archive,Bin}} = zip:create("someapp-1.0.ez",["someapp-1.0"], + [memory, + {cwd,EscriptDir}, + {compress,all}, + {uncompress,[".beam",".app"]}]), + + %% Create the escript + EscriptName = "someapp.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang, + {emu_args,"-escript main mymod"}, + {archive,Bin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone}, + {app, someapp, [{incl_cond,include}]} + ]}, + + ?msym({error,"someapp: Application name clash. Escript "++_}, + reltool:start_server([{config,Sys}])), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate standalone system with multiple escripts + +create_multiple_standalone(Config) -> + %% First escript + ExDir = code:lib_dir(reltool, examples), + EscriptName1 = "display_args", + Escript1 = filename:join([ExDir, EscriptName1]), + + %% Second escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName2 = "mymod.escript", + Escript2 = filename:join(?WORK_DIR,EscriptName2), + ok = escript:create(Escript2,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript2,8#00744), + + %% Configure server + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript1, [{incl_cond, include}]}, + {escript, Escript2, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + %% Generate target system + TargetDir = filename:join([?WORK_DIR, "target_multiple_standalone"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ok = ?m(ok, reltool:create_target([{config,Sys}], TargetDir)), + + %% Start the target system and fetch root dir + BinDir = filename:join([TargetDir, "bin"]), + Erl = filename:join([BinDir, "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + RootDir = ?ignore(rpc:call(Node, code, root_dir, [])), + ?msym(ok, stop_node(Node)), + + %% Execute escript1 + Expected1 = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "Smp: false\n", + "ExitCode:0"]), + io:format("Expected1: ~s\n", [Expected1]), + ?m(Expected1, run(BinDir, EscriptName1, "-arg1 arg2 arg3")), + + + %% Execute escript2 + Expected2 = iolist_to_binary(["Root dir: ", RootDir, "\n" + "Script args: [\"-arg1\",\"arg2\",\"arg3\"]\n", + "ExitCode:0"]), + io:format("Expected2: ~s\n", [Expected2]), + ?m(Expected2, run(BinDir, EscriptName2, "-arg1 arg2 arg3")), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Generate old type of target system -create_old_target(TestInfo) when is_atom(TestInfo) -> - reltool_test_lib:tc_info(TestInfo); +create_old_target(_Config) -> {skip, "Old style of target"}; create_old_target(_Config) -> - ?skip("Old style of target", []), %% Configure the server RelName1 = "Just testing", @@ -350,10 +973,10 @@ create_old_target(_Config) -> TargetDir = filename:join([?WORK_DIR, "target_old_style"]), ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), - ?m(ok, reltool:create_target([{config, Config}], TargetDir)), + ok = ?m(ok, reltool:create_target([{config, Config}], TargetDir)), %% io:format("Will fail on Windows (should patch erl.ini)\n", []), - ?m(ok, reltool:install(RelName2, TargetDir)), + ok = ?m(ok, reltool:install(RelName2, TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -362,11 +985,43 @@ create_old_target(_Config) -> ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Generate target system with eval_target_spec/3 + +eval_target_spec(_Config) -> + %% Configure the server + RelName1 = "Just testing", + RelName2 = "Just testing with SASL", + RelVsn = "1.0", + Config = + {sys, + [ + {root_dir, code:root_dir()}, + {lib_dirs, []}, + {boot_rel, RelName2}, + {rel, RelName1, RelVsn, [stdlib, kernel]}, + {rel, RelName2, RelVsn, [sasl, stdlib, kernel]}, + {app, sasl, [{incl_cond, include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "eval_target_spec"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + {ok, Spec} = ?msym({ok,_}, reltool:get_target_spec([{config, Config}])), + ok = ?m(ok, reltool:eval_target_spec(Spec, code:root_dir(), TargetDir)), + + Erl = filename:join([TargetDir, "bin", "erl"]), + {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), + ?msym(ok, stop_node(Node)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% OTP-9229 - handle duplicated module names, i.e. same module name %% exists in two applications. %% Include on app, exclude the other -otp_9229_exclude_app(Config) -> +otp_9229_dupl_mod_exclude_app(Config) -> DataDir = ?config(data_dir,Config), LibDir = filename:join(DataDir,"otp_9229"), @@ -389,8 +1044,8 @@ otp_9229_exclude_app(Config) -> ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclApp}])]), - {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclApp}]), - ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), + ?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclApp}])), + ok = ?m(ok, reltool:create_target([{config, ExclApp}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -413,7 +1068,7 @@ otp_9229_exclude_app(Config) -> ok. %% Include both apps, but exclude common module from one app -otp_9229_exclude_mod(Config) -> +otp_9229_dupl_mod_exclude_mod(Config) -> DataDir = ?config(data_dir,Config), LibDir = filename:join(DataDir,"otp_9229"), @@ -436,8 +1091,8 @@ otp_9229_exclude_mod(Config) -> ?m(ok, reltool_utils:recursive_delete(TargetDir)), ?m(ok, file:make_dir(TargetDir)), ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, ExclMod}])]), - {ok,["Module mylib exists in applications x and y. Using module from application x."]} = reltool:get_status([{config, ExclMod}]), - ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), + ?m({ok,["Module mylib exists in applications x and y. Using module from application x."]}, reltool:get_status([{config, ExclMod}])), + ok = ?m(ok, reltool:create_target([{config, ExclMod}], TargetDir)), Erl = filename:join([TargetDir, "bin", "erl"]), {ok, Node} = ?msym({ok, _}, start_node(?NODE_NAME, Erl)), @@ -466,6 +1121,1029 @@ otp_9229_exclude_mod(Config) -> ok. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that if a module is duplicated in a .app file, then a warning +%% is produced, but target can still be created. +dupl_mod_in_app_file(Config) -> + DataDir = ?config(data_dir,Config), + LibDir = filename:join(DataDir,"dupl_mod"), + + %% Configure the server + Sys = + {sys, + [ + {lib_dirs, [LibDir]}, + {incl_cond,exclude}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]} + ]}, + + %% Generate target file + TargetDir = filename:join([?WORK_DIR, "target_dupl_mod_in_app_file"]), + ?m(ok, reltool_utils:recursive_delete(TargetDir)), + ?m(ok, file:make_dir(TargetDir)), + ?log("SPEC: ~p\n", [reltool:get_target_spec([{config, Sys}])]), + ?m({ok,["Module a duplicated in app file for application a."]}, + reltool:get_status([{config, Sys}])), + + %%! test that only one module installed (in spec) + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test the interface used by the GUI: +%% get_app +%% get_apps +%% set_app +%% set_apps +%% load_config +%% reset_config +%% +%% Also, for each operation which manipulates the config, test +%% get_status and undo_config. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_apps(_Config) -> + Sys = {sys,[{app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,runtime_tools,[{incl_cond,exclude}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + {ok,Sasl} = ?msym({ok,#app{name=sasl}}, reltool_server:get_app(Pid,sasl)), + {ok,[#app{name=kernel}, + #app{name=sasl}=Sasl, + #app{name=stdlib}] = White} = + ?msym({ok,_}, reltool_server:get_apps(Pid,whitelist)), + {ok,[#app{name=runtime_tools}] = Black} = + ?msym({ok,_}, reltool_server:get_apps(Pid,blacklist)), + + {ok,Derived} = ?msym({ok,_}, reltool_server:get_apps(Pid,derived)), + true = lists:keymember(tools,#app.name,Derived), + + {ok,Source} = ?msym({ok,_}, reltool_server:get_apps(Pid,source)), + true = lists:keymember(common_test,#app.name,Source), + + %% Check that the four lists are disjoint + Number = length(White) + length(Black) + length(Derived) + length(Source), + WN = lists:usort([N || #app{name=N} <- White]), + BN = lists:usort([N || #app{name=N} <- Black]), + DN = lists:usort([N || #app{name=N} <- Derived]), + SN = lists:usort([N || #app{name=N} <- Source]), + AllN = lists:umerge([WN,BN,DN,SN]), + ?m(Number,length(AllN)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_mod(_Config) -> + Sys = {sys,[{app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,runtime_tools,[{incl_cond,exclude}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + %% Read app and get a module from the #app record + {ok,Tools} = ?msym({ok,#app{name=tools}}, reltool_server:get_app(Pid,tools)), + Cover = lists:keyfind(cover,#mod.name,Tools#app.mods), + + %% get_mod - and check that it is equal to the one in #app.mods + ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +get_sys(_Config) -> + Sys = {sys,[{app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}, + {app,runtime_tools,[{incl_cond,exclude}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + RootDir = code:root_dir(), + ?msym({ok,#sys{root_dir=RootDir,apps=undefined}},reltool_server:get_sys(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +set_app_and_undo(Config) -> + Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + {ok,Cover} = ?msym({ok,#mod{name=cover, is_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Exclude one module with set_app + ExclCover = Cover#mod{incl_cond=exclude}, + Mods = Tools#app.mods, + Tools1 = Tools#app{mods = lists:keyreplace(cover,#mod.name,Mods,ExclCover)}, + {ok,ToolsNoCover,["a: Cannot parse app file"++_|_]} = + ?msym({ok,_,["a: Cannot parse app file"++_|_]}, + reltool_server:set_app(Pid,Tools1)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + %% Check that the module is no longer included + ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), + {ok,NoIncludeCover} = ?msym({ok,#mod{name=cover, is_included=false}}, + reltool_server:get_mod(Pid,cover)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?msym(ok, reltool_server:undo_config(Pid)), + ?m({ok,ToolsNoCover}, reltool_server:get_app(Pid,tools)), + ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +set_apps_and_undo(Config) -> + Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(true, Tools#app.is_pre_included), + ?m(true, Tools#app.is_included), + {ok,Cover} = ?msym({ok,#mod{name=cover, is_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Exclude one application with set_apps + ExclTools = Tools#app{incl_cond=exclude}, + ?msym({ok,["a: Cannot parse app file"++_]}, + reltool_server:set_apps(Pid,[ExclTools])), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + %% Check that the app and its modules (one of them) are no longer included + {ok,NoTools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(false, NoTools#app.is_pre_included), + ?m(false, NoTools#app.is_included), + {ok,NoIncludeCover} = ?msym({ok,#mod{name=cover, is_included=false}}, + reltool_server:get_mod(Pid,cover)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,NoTools}, reltool_server:get_app(Pid,tools)), + ?m({ok,NoIncludeCover}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]}, reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that escript can be configured, but not its inlined applications +set_apps_inlined(Config) -> + %% Create archive + DataDir = ?config(data_dir,Config), + EscriptDir = filename:join(DataDir,escript), + {ok,{_Archive,Bin}} = zip:create("someapp-1.0.ez",["someapp-1.0"], + [memory, + {cwd,EscriptDir}, + {compress,all}, + {uncompress,[".beam",".app"]}]), + + %% Create the escript + EscriptName = "someapp.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang, + {emu_args,"-escript main mymod"}, + {archive,Bin}]), + ok = file:change_mode(Escript,8#00744), + + %% Configure the server + Sys = {sys,[{incl_cond, exclude}, + {escript,Escript,[]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?msym({ok,[]},reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,EApp} = ?msym({ok,_}, reltool_server:get_app(Pid,'*escript* someapp')), + {ok,Someapp} = ?msym({ok,_}, reltool_server:get_app(Pid,someapp)), + ?m(undefined, EApp#app.incl_cond), + ?m(undefined, Someapp#app.incl_cond), + ?m(false, Someapp#app.is_included), + ?m(false, Someapp#app.is_pre_included), + + %% Include escript + EApp1 = EApp#app{incl_cond=include}, + ?m({ok,[]}, reltool_server:set_apps(Pid,[EApp1])), + ExpectedEApp = EApp1#app{is_included=true,is_pre_included=true}, + ?m({ok,ExpectedEApp}, reltool_server:get_app(Pid,'*escript* someapp')), + {ok,Someapp1} = ?msym({ok,_}, reltool_server:get_app(Pid,someapp)), + ?m(include, Someapp1#app.incl_cond), + ?m(true, Someapp1#app.is_included), + ?m(true, Someapp1#app.is_pre_included), + + %% Check that inlined app can not be configured + Someapp2 = Someapp1#app{incl_cond=exclude}, + ?msym({error, + "Application someapp is inlined in '*escript* someapp'. " + "Can not change configuration for an inlined application."}, + reltool_server:set_apps(Pid,[Someapp2])), + ?m({ok,Someapp1}, reltool_server:get_app(Pid,someapp)), + + %% Exclude escript + {ok,EApp2} = ?msym({ok,_}, reltool_server:get_app(Pid,'*escript* someapp')), + EApp3 = EApp2#app{incl_cond=exclude}, + ?m({ok,[]}, reltool_server:set_apps(Pid,[EApp3])), + ExpectedEApp3 = EApp3#app{is_included=false,is_pre_included=false}, + ?m({ok,ExpectedEApp3}, reltool_server:get_app(Pid,'*escript* someapp')), + {ok,Someapp3} = ?msym({ok,_}, reltool_server:get_app(Pid,someapp)), + ?m(exclude, Someapp3#app.incl_cond), + ?m(false, Someapp3#app.is_included), + ?m(false, Someapp3#app.is_pre_included), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +set_sys_and_undo(Config) -> + Sys1 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Read sys record + {ok, SysRec} = reltool_server:get_sys(Pid), + + %% Set lib dirs by call to set_sys + NewLib = filename:join(datadir(Config),"faulty_app_file"), + NewLibDirs = [NewLib | SysRec#sys.lib_dirs], + NewSysRec = SysRec#sys{lib_dirs=NewLibDirs}, + ?msym({ok,["a: Cannot parse app file"++_]}, + reltool_server:set_sys(Pid, NewSysRec)), + ?m({ok,NewSysRec}, reltool_server:get_sys(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,SysRec}, reltool_server:get_sys(Pid)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok,reltool_server:undo_config(Pid)), + ?m({ok,NewSysRec}, reltool_server:get_sys(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +load_config_and_undo(Config) -> + Sys1 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok, Sys1}, reltool:get_config(Pid)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(true, Tools1#app.is_pre_included), + ?m(true, Tools1#app.is_included), + {ok,Cover1} = ?msym({ok,#mod{name=cover, + is_included=true, + is_pre_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Change tools from include to derived by loading new config + Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,derived}]}]}, + ?msym({ok,["a: Cannot parse app file"++_]}, + reltool_server:load_config(Pid,Sys2)), +%%% OTP-0702, 15) ?m({ok, Sys2}, reltool:get_config(Pid)), +%%% Note that {incl_cond,exclude} is removed compared to Sys1 - +%%% config is merged, not overwritten - is this correct??? + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Check that tools is included (since it is used by sasl) but not + %% pre-included (neither included or excluded => undefined) + {ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(undefined, Tools2#app.is_pre_included), + ?m(true, Tools2#app.is_included), + {ok,Cover2} = ?msym({ok,#mod{name=cover, + is_included=true, + is_pre_included=undefined}}, + reltool_server:get_mod(Pid,cover)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test that load_config is properly rolled back if it fails +load_config_fail(_Config) -> + Sys1 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok, Sys1}, reltool:get_config(Pid)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + + %% Try to load a config with a faulty rel statement (includes a + %% non-existing application) + Sys2 = {sys,[{incl_cond, exclude}, + {boot_rel, "faulty_rel"}, + {rel, "faulty_rel", "1.0", [kernel, sasl, stdlib, xxx]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + ?msym({error,"Release \"faulty_rel\" uses non existing application xxx"}, + reltool_server:load_config(Pid,Sys2)), + + %% Check that a rollback is done to the old configuration + ?m({ok, Sys1}, reltool:get_config(Pid,false,false)), + + %% and that tools is not changed (i.e. that the new configuration + %% is not applied) + ?m({ok,Tools}, reltool_server:get_app(Pid,tools)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with escript + +load_config_escript_path(Config) -> + %% Create escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName = "mymod.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript,8#00744), + + %% Start reltool_server with one escript in configuration + EscriptSys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid1} = ?msym({ok, _}, reltool:start_server([{config, EscriptSys}])), + {ok,[#app{name='*escript* mymod'}=A]} = + ?msym({ok,[_]}, reltool_server:get_apps(Pid1,whitelist)), + ?m(ok, reltool:stop(Pid1)), + + + %% Do same again, but now start reltool first with simple config, + %% then add escript by loading new configuration and check that + %% #app is the same + SimpleSys = + {sys, + [ + {lib_dirs, []} + ]}, + + {ok, Pid2} = ?msym({ok, _}, reltool:start_server([{config, SimpleSys}])), + ?m({ok,[]}, reltool_server:get_apps(Pid2,whitelist)), + ?m({ok,[]}, reltool_server:load_config(Pid2,EscriptSys)), + ?m({ok,[A]}, reltool_server:get_apps(Pid2,whitelist)), + + ?m(ok, reltool:stop(Pid2)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with same (source) escript twice and check that the +%% application information is not changed. + +load_config_same_escript_source(_Config) -> + %% Create escript + ExDir = code:lib_dir(reltool, examples), + EscriptName = "display_args", + Escript = filename:join([ExDir, EscriptName]), + + %% Start reltool_server with one escript in configuration + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), +% {ok,[#app{name='*escript* display_args'}]} = + ?msym({ok,[#app{name='*escript* display_args',mods=[_]}]}, + reltool_server:get_apps(Pid,whitelist)), + + %% Load the same config again, then check that app is not changed + ?m({ok,[]}, reltool_server:load_config(Pid,Sys)), + ?msym({ok,[#app{name='*escript* display_args',mods=[_]}]}, + reltool_server:get_apps(Pid,whitelist)), + + ?m(ok, reltool:stop(Pid)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with same (beam) escript twice and check that the +%% application information is not changed. + +load_config_same_escript_beam(Config) -> + %% Create escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName = "mymod.escript", + Escript = filename:join(?WORK_DIR,EscriptName), + ok = escript:create(Escript,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript,8#00744), + + %% Start reltool_server with one escript in configuration + Sys = + {sys, + [ + {lib_dirs, []}, + {escript, Escript, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + {ok,[#app{name='*escript* mymod'}=A]} = + ?msym({ok,[_]}, reltool_server:get_apps(Pid,whitelist)), + + %% Load the same config again, then check that app is not changed + ?m({ok,[]}, reltool_server:load_config(Pid,Sys)), + ?m({ok,[A]}, reltool_server:get_apps(Pid,whitelist)), + + ?m(ok, reltool:stop(Pid)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Load config with escript + +load_config_add_escript(Config) -> + %% First escript + ExDir = code:lib_dir(reltool, examples), + EscriptName1 = "display_args", + Escript1 = filename:join([ExDir, EscriptName1]), + + %% Second escript + DataDir = ?config(data_dir,Config), + BeamFile = filename:join([DataDir,escript,"someapp-1.0",ebin,"mymod.beam"]), + {ok,BeamBin} = file:read_file(BeamFile), + EscriptName2 = "mymod.escript", + Escript2 = filename:join(?WORK_DIR,EscriptName2), + ok = escript:create(Escript2,[shebang,{beam,BeamBin}]), + ok = file:change_mode(Escript2,8#00744), + + %% Start reltool_server with one escript in configuration + Sys1 = + {sys, + [ + {lib_dirs, []}, + {escript, Escript2, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + + %% Add second escript by loading new configuration + Sys2 = + {sys, + [ + {lib_dirs, []}, + {escript, Escript1, [{incl_cond, include}]}, + {escript, Escript2, [{incl_cond, include}]}, + {profile, standalone} + ]}, + + {ok,[]} = ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + {ok,[#app{name='*escript* display_args'}, + #app{name='*escript* mymod'}]} = + ?msym({ok,[_,_]}, reltool_server:get_apps(Pid,whitelist)), + + ?m(ok, reltool:stop(Pid)), + + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +reset_config_and_undo(Config) -> + Sys1 = {sys,[{lib_dirs,[filename:join(datadir(Config),"faulty_app_file")]}, + {incl_cond, exclude}, + {app,a,[{incl_cond,include}]}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + ?m({ok, Sys1}, reltool:get_config(Pid)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Get app and mod + {ok,Tools1} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(true, Tools1#app.is_pre_included), + ?m(true, Tools1#app.is_included), + {ok,Cover1} = ?msym({ok,#mod{name=cover, + is_included=true, + is_pre_included=true}}, + reltool_server:get_mod(Pid,cover)), + + %% Exclude tools by loading new config + Sys2 = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,tools,[{incl_cond,exclude}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Check that tools is excluded + {ok,Tools2} = ?msym({ok,_}, reltool_server:get_app(Pid,tools)), + ?m(false, Tools2#app.is_pre_included), + ?m(false, Tools2#app.is_included), + {ok,Cover2} = ?msym({ok,#mod{name=cover, + is_included=false, + is_pre_included=false}}, + reltool_server:get_mod(Pid,cover)), + + %% Reset + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:reset_config(Pid)), + ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + %% Undo + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools2}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover2}, reltool_server:get_mod(Pid,cover)), + ?m({ok,[]}, reltool_server:get_status(Pid)), + + %% Undo again, to check that it toggles + ?m(ok, reltool_server:undo_config(Pid)), + ?m({ok,Tools1}, reltool_server:get_app(Pid,tools)), + ?m({ok,Cover1}, reltool_server:get_mod(Pid,cover)), + ?msym({ok,["a: Cannot parse app file"++_]},reltool_server:get_status(Pid)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +gen_rel_files(_Config) -> + %% Configure the server + RelName = "gen_fel_files_test", + RelVsn = "1.0", + Sys = + {sys, + [ + {lib_dirs, []}, + {boot_rel, RelName}, + {rel, RelName, RelVsn, [kernel, stdlib]} + ]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + %% Generate .rel, .script and .boot + Dir = filename:join(?WORK_DIR,"gen_rel_files"), + ok = file:make_dir(Dir), + ?m({ok,[]}, reltool_server:gen_rel_files(Pid,Dir)), + + Script = RelName ++ ".script", + Rel = RelName ++ ".rel", + Boot = RelName ++ ".boot", + {ok,Files} = ?msym({ok,_}, file:list_dir(Dir)), + [Boot,Rel,Script] = lists:sort(Files), + + %% Check that contents is reasonable + {ok,[S]} = ?msym({ok,[{script,_,_}]},file:consult(filename:join(Dir,Script))), + ?msym({ok,[{release,_,_,_}]}, file:consult(filename:join(Dir,Rel))), + {ok,Bin} = ?msym({ok,_}, file:read_file(filename:join(Dir,Boot))), + ?m(S,binary_to_term(Bin)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +save_config(Config) -> + PrivDir = ?config(priv_dir,Config), + Sys = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + ?m({ok, Sys}, reltool:get_config(Pid)), + + Simple = filename:join(PrivDir,"save_simple.reltool"), + ?m(ok, reltool_server:save_config(Pid,Simple,false,false)), + ?m({ok,[Sys]}, file:consult(Simple)), + + Derivates = filename:join(PrivDir,"save_derivates.reltool"), + ?m(ok, reltool_server:save_config(Pid,Derivates,false,true)), + ?msym({ok,[{sys,[{incl_cond, exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{mod,_,[]}|_]}]}]}, + file:consult(Derivates)), + + Defaults = filename:join(PrivDir,"save_defaults.reltool"), + ?m(ok, reltool_server:save_config(Pid,Defaults,true,false)), + ?msym({ok,[{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {app,kernel,[{incl_cond,include},{vsn,undefined}, + {lib_dir,undefined}]}, + {app,sasl,[{incl_cond,include},{vsn,undefined}, + {lib_dir,undefined}]}, + {app,stdlib,[{incl_cond,include},{vsn,undefined}, + {lib_dir,undefined}]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}, + file:consult(Defaults)), + + KVsn = latest(kernel), + StdVsn = latest(stdlib), + SaslVsn = latest(sasl), + + LibDir = code:lib_dir(), + KLibDir = filename:join(LibDir,"kernel-"++KVsn), + StdLibDir = filename:join(LibDir,"stdlib-"++StdVsn), + SaslLibDir = filename:join(LibDir,"sasl-"++SaslVsn), + + All = filename:join(PrivDir,"save_all.reltool"), + ?m(ok, reltool_server:save_config(Pid,All,true,true)), + ?msym({ok,[{sys,[{root_dir,_}, + {lib_dirs,_}, + {mod_cond,all}, + {incl_cond,exclude}, + {erts,[]}, + {app,kernel,[{incl_cond,include},{vsn,KVsn}, + {lib_dir,KLibDir},{mod,_,[]}|_]}, + {app,sasl,[{incl_cond,include},{vsn,SaslVsn}, + {lib_dir,SaslLibDir},{mod,_,[]}|_]}, + {app,stdlib,[{incl_cond,include},{vsn,StdVsn}, + {lib_dir,StdLibDir},{mod,_,[]}|_]}, + {boot_rel,"start_clean"}, + {rel,"start_clean","1.0",[]}, + {rel,"start_sasl","1.0",[sasl]}, + {emu_name,"beam"}, + {relocatable,true}, + {profile,development}, + {incl_sys_filters,[".*"]}, + {excl_sys_filters,[]}, + {incl_app_filters,[".*"]}, + {excl_app_filters,[]}, + {incl_archive_filters,[".*"]}, + {excl_archive_filters,["^include$","^priv$"]}, + {archive_opts,[]}, + {rel_app_type,permanent}, + {app_file,keep}, + {debug_info,keep}]}]}, + file:consult(All)), + + ?m(ok, reltool:stop(Pid)), + ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test calculation of dependencies +%% The following test applications are used +%% +%% x-1.0: x1.erl x2.erl x3.erl +%% \ / (x2 calls y1, x3 calls y2) +%% y-1.0: y1.erl y2.erl +%% \ (y1 calls z1) +%% z-1.0 z1.erl +%% +%% Test includes x and derives y and z. +%% +dependencies(Config) -> + %% Default: all modules included => y and z are included (derived) + Sys = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[z]}, + #app{name=z}], + rm_missing_app(Der)), + ?msym({ok,[]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding x2 => y still included since y2 is used by x3 + %% z still included since z1 is used by y1 + Sys2 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include},{mod,x2,[{incl_cond,exclude}]}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys2)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der2} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[z]}, + #app{name=z}], + rm_missing_app(Der2)), + ?msym({ok,[]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding x3 => y still included since y1 is used by x2 + %% z still included since z1 is used by y1 + Sys3 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include},{mod,x3,[{incl_cond,exclude}]}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys3)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der3} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[z]}, + #app{name=z}], + rm_missing_app(Der3)), + ?msym({ok,[]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding x2 and x3 => y and z excluded + Sys4 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}, + {mod,x2,[{incl_cond,exclude}]}, + {mod,x3,[{incl_cond,exclude}]}]}, + {app,y,[{incl_cond,derived}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys4)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der4} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([], rm_missing_app(Der4)), + ?msym({ok,[#app{name=y}, + #app{name=z}]}, + reltool_server:get_apps(Pid,source)), + + %% Excluding y1 => y still included since y2 is used by x3 + %% z excluded since not used by any other than y1 + Sys5 = {sys,[{lib_dirs,[filename:join(datadir(Config),"dependencies")]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,x,[{incl_cond,include}]}, + {app,y,[{incl_cond,derived}, + {mod,y1,[{incl_cond,exclude}]}]}, + {app,z,[{incl_cond,derived}]}]}, + ?m({ok,[]}, reltool_server:load_config(Pid,Sys5)), + ?msym({ok,[#app{name=kernel}, + #app{name=sasl}, + #app{name=stdlib}, + #app{name=x,uses_apps=[y]}]}, + reltool_server:get_apps(Pid,whitelist)), + {ok, Der5} = ?msym({ok,_}, + reltool_server:get_apps(Pid,derived)), + ?msym([#app{name=y,uses_apps=[]}], rm_missing_app(Der5)), + ?msym({ok,[#app{name=z}]}, + reltool_server:get_apps(Pid,source)), + + ?m(ok, reltool:stop(Pid)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +use_selected_vsn(Config) -> + LibDir1 = filename:join(datadir(Config),"use_selected_vsn"), + B1Dir = filename:join(LibDir1,"b-1.0"), + B3Dir = filename:join(LibDir1,"b-3.0"), + + LibDir2 = filename:join(LibDir1,"lib2"), + B2Dir = filename:join(LibDir2,"b-2.0"), + + %%----------------------------------------------------------------- + %% Pre-selected vsn of app b + Sys1 = {sys,[{lib_dirs,[LibDir1]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,b,[{incl_cond,include},{vsn,"1.0"}]}]}, + {ok, Pid1} = ?msym({ok, _}, reltool:start_server([{config, Sys1}])), + {ok,B11} = ?msym({ok,#app{vsn="1.0",active_dir=B1Dir}}, + reltool_server:get_app(Pid1,b)), + + %% Change from a pre-selected vsn to use a specific dir + ?msym({ok, #app{vsn ="3.0", active_dir = B3Dir}, []}, + reltool_server:set_app(Pid1, + B11#app{active_dir = B3Dir, + use_selected_vsn = dir, + label = undefined, + vsn = undefined, + info = undefined})), + ?m(ok, reltool:stop(Pid1)), + + + %%----------------------------------------------------------------- + %% Pre-selected vsn of app b + Sys2 = {sys,[{lib_dirs,[LibDir1]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,b,[{incl_cond,include},{vsn,"1.0"}]}]}, + {ok, Pid2} = ?msym({ok, _}, reltool:start_server([{config, Sys2}])), + {ok,B21} = ?msym({ok,#app{vsn="1.0",active_dir=B1Dir}}, + reltool_server:get_app(Pid2,b)), + + %% Change from a pre-selected vsn to use latest + ?msym({ok, #app{vsn ="3.0", active_dir = B3Dir}, []}, + reltool_server:set_app(Pid2, + B21#app{use_selected_vsn=undefined, + label = undefined, + vsn = undefined, + info = undefined})), + ?m(ok, reltool:stop(Pid2)), + + + %%----------------------------------------------------------------- + %% Pre-selected directory for app b + Sys3 = {sys,[{lib_dirs,[LibDir1]}, + {incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,b,[{incl_cond,include},{lib_dir,B2Dir}]}]}, + {ok, Pid3} = ?msym({ok, _}, reltool:start_server([{config, Sys3}])), +% test_server:break("Pid3 = list_to_pid(\""++pid_to_list(Pid3)++"\")."), + {ok,B31} = ?msym({ok,#app{vsn="2.0",active_dir=B2Dir}}, + reltool_server:get_app(Pid3,b)), + %% Change from a pre-selected dir to use latest + {ok,B32,_} = ?msym({ok, #app{vsn ="3.0", active_dir = B3Dir}, []}, + reltool_server:set_app(Pid3, + B31#app{use_selected_vsn=undefined, + label = undefined, + vsn = undefined, + info = undefined})), + %% Change back to use selected dir + {ok,B33,_} = ?msym({ok, #app{vsn ="3.0", active_dir = B3Dir}, []}, + reltool_server:set_app(Pid3, + B32#app{use_selected_vsn = dir})), + %% use dir 1 + {ok,B34,_} = ?msym({ok, #app{vsn ="1.0", active_dir = B1Dir}, []}, + reltool_server:set_app(Pid3, + B33#app{active_dir = B1Dir, + label = undefined, + vsn = undefined, + info = undefined})), + %% use dir 2 + {ok,B35,_} = ?msym({ok, #app{vsn ="2.0", active_dir = B2Dir}, []}, + reltool_server:set_app(Pid3, + B34#app{active_dir = B2Dir, + label = undefined, + vsn = undefined, + info = undefined})), + %% use dir 3 + ?msym({ok, #app{vsn ="3.0", active_dir = B3Dir}, []}, + reltool_server:set_app(Pid3, + B35#app{active_dir = B3Dir, + label = undefined, + vsn = undefined, + info = undefined})), + ?m(ok, reltool:stop(Pid3)), + ok. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +use_selected_vsn_relative_path(Config) -> + LibDir = filename:join([datadir(Config),"use_selected_vsn","b-1.0"]), + RelDir = filename:join(LibDir,"rel"), + + {ok,Cwd} = file:get_cwd(), + ok = file:set_cwd(RelDir), + + Sys = {sys,[{incl_cond, exclude}, + {app,kernel,[{incl_cond,include}]}, + {app,sasl,[{incl_cond,include}]}, + {app,stdlib,[{incl_cond,include}]}, + {app,b,[{incl_cond,include},{lib_dir,".."}]}]}, + {ok, Pid} = ?msym({ok, _}, reltool:start_server([{config, Sys}])), + + ?msym({ok,#app{vsn="1.0",active_dir=LibDir}},reltool_server:get_app(Pid,b)), + + ?m(ok, reltool:stop(Pid)), + + ok = file:set_cwd(Cwd), + ok. @@ -478,6 +2156,20 @@ erl_libs() -> LibStr -> string:tokens(LibStr, ":;") end. +datadir(Config) -> + %% Removes the trailing slash... + filename:nativename(?config(data_dir,Config)). + +latest(App) -> + AppStr = atom_to_list(App), + AppDirs = filelib:wildcard(filename:join(code:lib_dir(),AppStr++"-*")), + [LatestAppDir|_] = lists:reverse(AppDirs), + [_,Vsn] = string:tokens(filename:basename(LatestAppDir),"-"), + Vsn. + +rm_missing_app(Apps) -> + lists:keydelete(?MISSING_APP_NAME,#app.name,Apps). + diff_script(Script, Script) -> equal; diff_script({script, Rel, Commands1}, {script, Rel, Commands2}) -> @@ -610,14 +2302,16 @@ wait_for_process(Node, Name, N) when is_integer(N), N > 0 -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Run escript -run(Dir, Cmd0) -> +run(Dir, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, Cmd = case os:type() of {win32,_} -> filename:nativename(Dir) ++ "\\" ++ Cmd0; _ -> Cmd0 end, do_run(Dir, Cmd). -run(Dir, Opts, Cmd0) -> +run(Dir, Opts, Script, Args) -> + Cmd0 = filename:rootname(Script) ++ " " ++ Args, Cmd = case os:type() of {win32,_} -> Opts ++ " " ++ filename:nativename(Dir) ++ "\\" ++ Cmd0; _ -> Opts ++ " " ++ Dir ++ "/" ++ Cmd0 @@ -626,7 +2320,9 @@ run(Dir, Opts, Cmd0) -> do_run(Dir, Cmd) -> io:format("Run: ~p\n", [Cmd]), - Env = [{"PATH",Dir++":"++os:getenv("PATH")}], + Env = [{"PATH",Dir++":"++os:getenv("PATH")}, + {"ERL_FLAGS",""}, % Make sure no flags are set that can override + {"ERL_ZFLAGS",""}], % any of the flags set in the escript. Port = open_port({spawn,Cmd}, [exit_status,eof,in,{env,Env}]), Res = get_data(Port, []), receive diff --git a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src index 049e8dd6cc..21410ceaa9 100644 --- a/lib/reltool/test/reltool_server_SUITE_data/Makefile.src +++ b/lib/reltool/test/reltool_server_SUITE_data/Makefile.src @@ -6,8 +6,23 @@ OTP9229= \ otp_9229/y-1.0/ebin/y.@EMULATOR@ \ otp_9229/y-1.0/ebin/mylib.@EMULATOR@ +DEPENDENCIES= \ + dependencies/x-1.0/ebin/x1.@EMULATOR@ \ + dependencies/x-1.0/ebin/x2.@EMULATOR@ \ + dependencies/x-1.0/ebin/x3.@EMULATOR@ \ + dependencies/y-1.0/ebin/y1.@EMULATOR@ \ + dependencies/y-1.0/ebin/y2.@EMULATOR@ \ + dependencies/z-1.0/ebin/z1.@EMULATOR@ -all: $(OTP9229) +ESCRIPT= \ + escript/someapp-1.0/ebin/mymod.@EMULATOR@ + +SEL_VSN= \ + use_selected_vsn/b-1.0/ebin/b.@EMULATOR@ \ + use_selected_vsn/b-3.0/ebin/b.@EMULATOR@ \ + use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@ + +all: $(OTP9229) $(DEPENDENCIES) $(ESCRIPT) $(SEL_VSN) otp_9229/x-1.0/ebin/x.@EMULATOR@: otp_9229/x-1.0/src/x.erl erlc $(EFLAGS) -ootp_9229/x-1.0/ebin otp_9229/x-1.0/src/x.erl @@ -17,3 +32,26 @@ otp_9229/y-1.0/ebin/y.@EMULATOR@: otp_9229/y-1.0/src/y.erl erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/y.erl otp_9229/y-1.0/ebin/mylib.@EMULATOR@: otp_9229/y-1.0/src/mylib.erl erlc $(EFLAGS) -ootp_9229/y-1.0/ebin otp_9229/y-1.0/src/mylib.erl + +dependencies/x-1.0/ebin/x1.@EMULATOR@: dependencies/x-1.0/src/x1.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x1.erl +dependencies/x-1.0/ebin/x2.@EMULATOR@: dependencies/x-1.0/src/x2.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x2.erl +dependencies/x-1.0/ebin/x3.@EMULATOR@: dependencies/x-1.0/src/x3.erl + erlc $(EFLAGS) -odependencies/x-1.0/ebin dependencies/x-1.0/src/x3.erl +dependencies/y-1.0/ebin/y1.@EMULATOR@: dependencies/y-1.0/src/y1.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y1.erl +dependencies/y-1.0/ebin/y2.@EMULATOR@: dependencies/y-1.0/src/y2.erl + erlc $(EFLAGS) -odependencies/y-1.0/ebin dependencies/y-1.0/src/y2.erl +dependencies/z-1.0/ebin/z1.@EMULATOR@: dependencies/z-1.0/src/z1.erl + erlc $(EFLAGS) -odependencies/z-1.0/ebin dependencies/z-1.0/src/z1.erl + +escript/someapp-1.0/ebin/mymod.@EMULATOR@: escript/someapp-1.0/src/mymod.erl + erlc $(EFLAGS) -oescript/someapp-1.0/ebin escript/someapp-1.0/src/mymod.erl + +use_selected_vsn/b-1.0/ebin/b.@EMULATOR@: use_selected_vsn/b-1.0/src/b.erl + erlc $(EFLAGS) -ouse_selected_vsn/b-1.0/ebin use_selected_vsn/b-1.0/src/b.erl +use_selected_vsn/b-3.0/ebin/b.@EMULATOR@: use_selected_vsn/b-3.0/src/b.erl + erlc $(EFLAGS) -ouse_selected_vsn/b-3.0/ebin use_selected_vsn/b-3.0/src/b.erl +use_selected_vsn/lib2/b-2.0/ebin/b.@EMULATOR@: use_selected_vsn/lib2/b-2.0/src/b.erl + erlc $(EFLAGS) -ouse_selected_vsn/lib2/b-2.0/ebin use_selected_vsn/lib2/b-2.0/src/b.erl diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app new file mode 100644 index 0000000000..ccaab8a8c7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Main application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [x1,x2,x3]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl new file mode 100644 index 0000000000..bf1e7f9279 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x1.erl @@ -0,0 +1,5 @@ +-module(x1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl new file mode 100644 index 0000000000..82191ba278 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x2.erl @@ -0,0 +1,5 @@ +-module(x2). +-compile(export_all). + +f() -> + y1:f(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl new file mode 100644 index 0000000000..618c75c9a7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/x-1.0/src/x3.erl @@ -0,0 +1,5 @@ +-module(x3). +-compile(export_all). + +f() -> + y2:f(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app new file mode 100644 index 0000000000..d9dac371d7 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, y, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [y1,y2]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl new file mode 100644 index 0000000000..dd21b33292 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y1.erl @@ -0,0 +1,5 @@ +-module(y1). +-compile(export_all). + +f() -> + z1:f(). diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl new file mode 100644 index 0000000000..bf8ddf6080 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/y-1.0/src/y2.erl @@ -0,0 +1,5 @@ +-module(y2). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app new file mode 100644 index 0000000000..437a0968e9 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/ebin/z.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, z, + [{description, "Library application in reltool dependency test"}, + {vsn, "1.0"}, + {modules, [z1]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl new file mode 100644 index 0000000000..97ef90b87f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dependencies/z-1.0/src/z1.erl @@ -0,0 +1,5 @@ +-module(z1). +-compile(export_all). + +f() -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app new file mode 100644 index 0000000000..fada34847a --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/dupl_mod/a-1.0/ebin/a.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, a, + [{description, "Application with duplicated module name in .app file"}, + {vsn, "1.0"}, + {modules, [a,a]}, + {registered, []}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app new file mode 100644 index 0000000000..ea2209941e --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/ebin/someapp.app @@ -0,0 +1,6 @@ +%% -*- erlang -*- +{application, someapp, + [{description, "Some app for reltool test including archives in escripts"}, + {vsn, "1.0"}, + {modules, [someapp]}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl new file mode 100644 index 0000000000..b6c71c666d --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/escript/someapp-1.0/src/mymod.erl @@ -0,0 +1,26 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(mymod). + +-export([main/1]). + +%%%----------------------------------------------------------------- +%%% escript main function +main(Args) -> + io:format("Root dir: ~s\n", [code:root_dir()]), + io:format("Script args: ~p\n", [Args]). diff --git a/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app new file mode 100644 index 0000000000..ea77103598 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/ebin/a.app @@ -0,0 +1 @@ +faulty app file diff --git a/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a.erl b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a.erl new file mode 100644 index 0000000000..bb500bed69 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a.erl @@ -0,0 +1,49 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a). + + +-behaviour(gen_server). + +-vsn(1). + +%% External exports +-export([start_link/0, a/0]). +%% Internal exports +-export([init/1, handle_call/3, handle_info/2, terminate/2]). + +start_link() -> gen_server:start_link({local, aa}, a, [], []). + +a() -> gen_server:call(aa, a). + +%%----------------------------------------------------------------- +%% Callback functions from gen_server +%%----------------------------------------------------------------- +init([]) -> + process_flag(trap_exit, true), + {ok, state}. + +handle_call(a, _From, State) -> + X = application:get_all_env(a), + {reply, X, State}. + +handle_info(_, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl new file mode 100644 index 0000000000..a141c1767b --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/faulty_app_file/a-1.0/src/a_sup.erl @@ -0,0 +1,37 @@ +%% ``The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved via the world wide web at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% The Initial Developer of the Original Code is Ericsson Utvecklings AB. +%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings +%% AB. All Rights Reserved.'' +%% +%% $Id$ +%% +-module(a_sup). + + +-behaviour(supervisor). + +%% External exports +-export([start/2]). + +%% Internal exports +-export([init/1]). + +start(_, _) -> + supervisor:start_link({local, a_sup}, a_sup, []). + +init([]) -> + SupFlags = {one_for_one, 4, 3600}, + Config = {a, + {a, start_link, []}, + permanent, 2000, worker, [a]}, + {ok, {SupFlags, [Config]}}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app new file mode 100644 index 0000000000..5fa2a92969 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/x-1.0/ebin/x.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, x, + [{description, "Application in reltool sort app test - circular dependency"}, + {vsn, "1.0"}, + {modules,[]}, + {registered, []}, + {applications, [kernel, stdlib, y]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app new file mode 100644 index 0000000000..c4bc62f55f --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/y-1.0/ebin/y.app @@ -0,0 +1,7 @@ +% -*-erlang-*- +{application, y, + [{description, "Application in reltool sort app test - circular dependency"}, + {vsn, "1.0"}, + {modules,[]}, + {registered, []}, + {applications, [kernel, stdlib, x]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app new file mode 100644 index 0000000000..8608bc554b --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/sort_apps/z-1.0/ebin/z.app @@ -0,0 +1,8 @@ +% -*-erlang-*- +{application, z, + [{description, "Application in reltool sort app test - included applications"}, + {vsn, "1.0"}, + {modules,[]}, + {registered, []}, + {applications, [kernel, stdlib, sasl, inets]}, + {included_applications, [tools, mnesia]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-1.0/ebin/b.app b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-1.0/ebin/b.app new file mode 100644 index 0000000000..c511dcc8f1 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-1.0/ebin/b.app @@ -0,0 +1,6 @@ +%% -*- erlang -*- +{application, b, + [{description, "Reltool test app for using selected version of app"}, + {vsn, "1.0"}, + {modules, [b]}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-1.0/rel/.gitignore b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-1.0/rel/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-1.0/rel/.gitignore diff --git a/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-1.0/src/b.erl b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-1.0/src/b.erl new file mode 100644 index 0000000000..a6b4ff1c05 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-1.0/src/b.erl @@ -0,0 +1,4 @@ +-module(b). +-compile(export_all). + +foo() -> ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-3.0/ebin/b.app b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-3.0/ebin/b.app new file mode 100644 index 0000000000..9ed7695a40 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-3.0/ebin/b.app @@ -0,0 +1,6 @@ +%% -*- erlang -*- +{application, b, + [{description, "Reltool test app for using selected version of app"}, + {vsn, "3.0"}, + {modules, [b]}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-3.0/src/b.erl b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-3.0/src/b.erl new file mode 100644 index 0000000000..a6b4ff1c05 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/b-3.0/src/b.erl @@ -0,0 +1,4 @@ +-module(b). +-compile(export_all). + +foo() -> ok. diff --git a/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/lib2/b-2.0/ebin/b.app b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/lib2/b-2.0/ebin/b.app new file mode 100644 index 0000000000..33c633d635 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/lib2/b-2.0/ebin/b.app @@ -0,0 +1,6 @@ +%% -*- erlang -*- +{application, b, + [{description, "Reltool test app for using selected version of app"}, + {vsn, "2.0"}, + {modules, [b]}, + {applications, [kernel, stdlib]}]}. diff --git a/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/lib2/b-2.0/src/b.erl b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/lib2/b-2.0/src/b.erl new file mode 100644 index 0000000000..a6b4ff1c05 --- /dev/null +++ b/lib/reltool/test/reltool_server_SUITE_data/use_selected_vsn/lib2/b-2.0/src/b.erl @@ -0,0 +1,4 @@ +-module(b). +-compile(export_all). + +foo() -> ok. diff --git a/lib/reltool/test/reltool_test_lib.erl b/lib/reltool/test/reltool_test_lib.erl index b8bcbcd009..61f783190c 100644 --- a/lib/reltool/test/reltool_test_lib.erl +++ b/lib/reltool/test/reltool_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -138,10 +138,6 @@ end_per_testcase(_Func, Config) when is_list(Config) -> reset_kill_timer(Config), Config. -%% Backwards compatible with test_server -tc_info(suite) -> []; -tc_info(doc) -> "". - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Use ?log(Format, Args) as wrapper diff --git a/lib/reltool/test/reltool_test_lib.hrl b/lib/reltool/test/reltool_test_lib.hrl index b592ebb2f0..0dfc08b81c 100644 --- a/lib/reltool/test/reltool_test_lib.hrl +++ b/lib/reltool/test/reltool_test_lib.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2010. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. 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 @@ -18,20 +18,10 @@ -include_lib("wx/include/wx.hrl"). --define(flat_format(Format,Args), lists:flatten(io_lib:format(Format,Args))). -define(log(Format,Args), reltool_test_lib:log(Format,Args,?FILE,?LINE)). --define(warning(Format,Args), ?log("<WARNING>\n " ++ Format,Args)). -define(error(Format,Args), reltool_test_lib:error(Format,Args,?FILE,?LINE)). -define(verbose(Format,Args), reltool_test_lib:verbose(Format,Args,?FILE,?LINE)). --define(fatal(Format,Args), - ?error(Format, Args), - exit({test_case_fatal, Format, Args, ?FILE, ?LINE})). - --define(skip(Format,Args), - ?warning(Format, Args), - exit({skipped, ?flat_format(Format, Args)})). - -define(ignore(Expr), fun() -> AcTuAlReS = (catch (Expr)), @@ -68,25 +58,3 @@ AcTuAlReS end end()). - --define(m_receive(ExpectedMsg), - ?m(ExpectedMsg,reltool_test_lib:pick_msg())). - --define(m_multi_receive(ExpectedMsgs), - fun() -> - TmPeXpCtEdMsGs = lists:sort(ExpectedMsgs), - AcTuAlReS = - lists:sort(lists:map(fun(_) -> - reltool_test_lib:pick_msg() - end, TmPeXpCtEdMsGs)), - case AcTuAlReS of - TmPeXpCtEdMsGs -> - ?verbose("ok: ~p\n",[AcTuAlReS]), - AcTuAlReS; - _ -> - reltool_test_lib:error("Not matching actual result was:\n ~p \nExpected ~p\n", - [AcTuAlReS, ExpectedMsgs], - ?FILE, ?LINE), - AcTuAlReS - end - end()). diff --git a/lib/reltool/test/reltool_wx_SUITE.erl b/lib/reltool/test/reltool_wx_SUITE.erl index 424bc7d189..13d71f4fd6 100644 --- a/lib/reltool/test/reltool_wx_SUITE.erl +++ b/lib/reltool/test/reltool_wx_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -61,9 +61,46 @@ start_all_windows(TestInfo) when is_atom(TestInfo) -> reltool_test_lib:tc_info(TestInfo); start_all_windows(_Config) -> {ok, SysPid} = ?msym({ok, _}, reltool:start([{trap_exit, false}])), + erlang:monitor(process,SysPid), {ok, AppPid} = ?msym({ok, _}, reltool_sys_win:open_app(SysPid, stdlib)), - ?msym({ok, _}, reltool_app_win:open_mod(AppPid, escript)), + erlang:monitor(process,AppPid), + {ok, ModPid} = ?msym({ok, _}, reltool_app_win:open_mod(AppPid, escript)), + erlang:monitor(process,ModPid), + + %% Let all windows get started timer:sleep(timer:seconds(10)), + + %% Test that server pid can be fetched, and that server is alive + {ok, Server} = ?msym({ok,_}, reltool:get_server(SysPid)), + ?m(true, erlang:is_process_alive(Server)), + ?m({ok,{sys,[]}}, reltool:get_config(Server)), + + %% Terminate + check_no_win_crash(), ?m(ok, reltool:stop(SysPid)), - + wait_terminate([{sys,SysPid},{app,AppPid},{mod,ModPid}]), + ok. + + +%%%----------------------------------------------------------------- +%%% Internal functions +check_no_win_crash() -> + receive {'DOWN',_,_,_,_} = Down -> + ct:log("Unexpected termination of window:~n~p",[Down]), + ct:fail("window crashed") + after 0 -> + ok + end. + +wait_terminate([]) -> + ok; +wait_terminate([{Win,P}|Rest]) -> + receive + {'DOWN',_,process,P,shutdown} -> + wait_terminate(Rest); + {'DOWN',_,process,P,Reason} -> + ct:log("~p window terminated with unexpected reason:~n~p", + [Win,Reason]), + ct:fail("unexpected exit reason from window") + end. diff --git a/lib/reltool/vsn.mk b/lib/reltool/vsn.mk index 3869284ee7..3811d897c7 100644 --- a/lib/reltool/vsn.mk +++ b/lib/reltool/vsn.mk @@ -1 +1 @@ -RELTOOL_VSN = 0.5.7.1 +RELTOOL_VSN = 0.6 diff --git a/lib/runtime_tools/c_src/Makefile.in b/lib/runtime_tools/c_src/Makefile.in index 3d9a7ed69d..754e6ccd78 100644 --- a/lib/runtime_tools/c_src/Makefile.in +++ b/lib/runtime_tools/c_src/Makefile.in @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2011. All Rights Reserved. +# Copyright Ericsson AB 1999-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -21,6 +21,11 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk include $(ERL_TOP)/make/$(TARGET)/otp_ded.mk # ---------------------------------------------------- +# Items from top-level configure +# ---------------------------------------------------- +DTRACE_ENABLED=@DTRACE_ENABLED@ +DTRACE_ENABLED_2STEP=@DTRACE_ENABLED_2STEP@ +# ---------------------------------------------------- # Application version # ---------------------------------------------------- include ../vsn.mk @@ -38,6 +43,8 @@ SHELL = /bin/sh LIBS = $(DED_LIBS) LDFLAGS += $(DED_LDFLAGS) +DTRACE_LIBNAME = dyntrace + SYSINCLUDE = $(DED_SYS_INCLUDE) ifeq ($(findstring vxworks,$(TARGET)),vxworks) SYSINCLUDE += -I$(ERL_TOP)/erts/etc/vxworks @@ -45,15 +52,20 @@ endif TRACE_DRV_INCLUDES = $(SYSINCLUDE) -ALL_CFLAGS = $(CFLAGS) @DEFS@ $(TYPE_FLAGS) $(TRACE_DRV_INCLUDES) - +ALL_CFLAGS = $(CFLAGS) @DEFS@ $(TYPE_FLAGS) $(TRACE_DRV_INCLUDES) \ + -I$(OBJDIR) -I$(ERL_TOP)/erts/emulator/$(TARGET) ifeq ($(TYPE),debug) TYPEMARKER = .debug -TYPE_FLAGS = -g -DDEBUG @DEBUG_FLAGS@ +TYPE_FLAGS = $(subst -O3,,$(subst -O2,,$(CFLAGS))) -DDEBUG @DEBUG_FLAGS@ +else +ifeq ($(TYPE),valgrind) +TYPEMARKER = .valgrind +TYPE_FLAGS = $(subst -O3,,$(subst -O2,,$(CFLAGS))) -DVALGRIND else TYPEMARKER = -TYPE_FLAGS = -O2 +TYPE_FLAGS = $(CFLAGS) +endif endif ROOTDIR = $(ERL_TOP)/lib @@ -69,6 +81,16 @@ RELSYSDIR = $(RELEASE_PATH)/lib/runtime_tools-$(VSN) # ---------------------------------------------------- # Misc Macros # ---------------------------------------------------- +before_DTrace_OBJS = $(OBJDIR)/dyntrace$(TYPEMARKER).o +## NIF_MAKEFILE = $(PRIVDIR)/Makefile + +# Higher-level makefiles says that we can only compile on UNIX flavors +NIF_LIB = $(LIBDIR)/dyntrace$(TYPEMARKER).@DED_EXT@ + +ifeq ($(HOST_OS),) +HOST_OS := $(shell $(ERL_TOP)/erts/autoconf/config.guess) +endif + TRACE_IP_DRV_OBJS = \ $(OBJDIR)/trace_ip_drv.o @@ -91,7 +113,44 @@ endif _create_dirs := $(shell mkdir -p $(OBJDIR) $(LIBDIR)) -debug opt: $(SOLIBS) +debug opt valgrind: $(SOLIBS) $(OBJDIR) $(LIBDIR) $(NIF_LIB) + +ifdef DTRACE_ENABLED +DTRACE_USER_HEADER=$(OBJDIR)/dtrace_user.h +$(OBJDIR)/dtrace_user.h: ./dtrace_user.d + dtrace -h -C $(INCLUDES) \ + -s ./dtrace_user.d \ + -o ./dtrace_user.tmp + sed -e '/^#define[ ]*ERLANG_[A-Z0-9_]*(.*)/y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/' ./dtrace_user.tmp > $@ + rm ./dtrace_user.tmp +else +DTRACE_USER_HEADER= +endif + +DTRACE_OBJS = +ifdef DTRACE_ENABLED_2STEP +DTRACE_OBJS += $(OBJDIR)/dtrace_user.o +$(OBJDIR)/dtrace_user.o: $(before_DTrace_OBJS) $(OBJDIR)/dtrace_user.h + dtrace -G -C \ + -s ./dtrace_user.d \ + -o $@ $(before_DTrace_OBJS) +endif + +DYNTRACE_OBJS = $(before_DTrace_OBJS) $(DTRACE_OBJS) + +$(OBJDIR): + -@mkdir -p $(OBJDIR) + +$(LIBDIR): + -@mkdir -p $(LIBDIR) + +$(OBJDIR)/dyntrace$(TYPEMARKER).o: dyntrace.c $(DTRACE_USER_HEADER) + $(INSTALL_DIR) $(OBJDIR) + $(CC) -c -o $@ $(ALL_CFLAGS) $< + +$(NIF_LIB): $(DYNTRACE_OBJS) + $(INSTALL_DIR) $(LIBDIR) + $(LD) $(LDFLAGS) -o $@ $^ $(LDLIBS) $(OBJDIR)/%.o: %.c $(CC) -c -o $@ $(ALL_CFLAGS) $< @@ -118,6 +177,12 @@ $(LIBDIR)/trace_file_drv.eld: $(TRACE_FILE_DRV_OBJS) clean: rm -f $(SOLIBS) $(TRACE_IP_DRV_OBJS) $(TRACE_FILE_DRV_OBJS) + rm -f $(LIBDIR)/dyntrace.@DED_EXT@ + rm -f $(LIBDIR)/dyntrace.debug.@DED_EXT@ + rm -f $(LIBDIR)/dyntrace.valgrind.@DED_EXT@ + rm -f $(OBJDIR)/dyntrace.o + rm -f $(OBJDIR)/dyntrace.debug.o + rm -f $(OBJDIR)/dyntrace.valgrind.o rm -f core *~ docs: @@ -128,8 +193,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/lib - $(INSTALL_PROGRAM) $(SOLIBS) $(RELSYSDIR)/priv/lib + $(INSTALL_DIR) "$(RELSYSDIR)/priv/obj" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/lib" + $(INSTALL_PROGRAM) $(DYNTRACE_OBJS) "$(RELSYSDIR)/priv/obj" + $(INSTALL_PROGRAM) $(NIF_LIB) $(SOLIBS) "$(RELSYSDIR)/priv/lib" release_docs_spec: diff --git a/lib/runtime_tools/c_src/dtrace_user.d b/lib/runtime_tools/c_src/dtrace_user.d new file mode 100644 index 0000000000..9e180a3cb2 --- /dev/null +++ b/lib/runtime_tools/c_src/dtrace_user.d @@ -0,0 +1,33 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. + * All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +provider erlang { + /* + * The set of probes for use by Erlang code ... moved from here to + * erts/emulator/beam/erlang_dtrace.d until a more portable solution is + * found; see erlang_dtrace.d for details. + */ +}; + +#pragma D attributes Evolving/Evolving/Common provider erlang provider +#pragma D attributes Private/Private/Common provider erlang module +#pragma D attributes Private/Private/Common provider erlang function +#pragma D attributes Evolving/Evolving/Common provider erlang name +#pragma D attributes Evolving/Evolving/Common provider erlang args diff --git a/lib/runtime_tools/c_src/dyntrace.c b/lib/runtime_tools/c_src/dyntrace.c new file mode 100644 index 0000000000..eef03afd1c --- /dev/null +++ b/lib/runtime_tools/c_src/dyntrace.c @@ -0,0 +1,127 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +/* + * Purpose: Dynamically loadable NIF library for DTrace + */ + + + +#include "erl_nif.h" +#include "config.h" +#include "sys.h" +#include "dtrace-wrapper.h" +#if defined(USE_DYNAMIC_TRACE) && (defined(USE_DTRACE) || defined(USE_SYSTEMTAP)) +#define HAVE_USE_DTRACE 1 +#endif +#ifdef HAVE_USE_DTRACE +#include "dtrace_user.h" +#endif + +void dtrace_nifenv_str(ErlNifEnv *env, char *process_buf); +void get_string_maybe(ErlNifEnv *env, const ERL_NIF_TERM term, char **ptr, char *buf, int bufsiz); +#ifdef HAVE_USE_DTRACE +ERL_NIF_TERM erl_nif_user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM erl_nif_user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#endif + +#ifdef VALGRIND + # include <valgrind/memcheck.h> +#endif + +#ifdef __GNUC__ + # define INLINE __inline__ +#else + # define INLINE +#endif + +#define MESSAGE_BUFSIZ 1024 + +/* NIF interface declarations */ +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info); + +/* The NIFs: */ +static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +static ERL_NIF_TERM user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +static ErlNifFunc nif_funcs[] = { + {"available", 0, available}, + {"user_trace_s1", 1, user_trace_s1}, + {"user_trace_i4s4", 9, user_trace_i4s4}, + {"user_trace_n", 10, user_trace_n} +}; + +ERL_NIF_INIT(dyntrace, nif_funcs, load, NULL, NULL, NULL) + +static ERL_NIF_TERM atom_true; +static ERL_NIF_TERM atom_false; +static ERL_NIF_TERM atom_error; +static ERL_NIF_TERM atom_not_available; +static ERL_NIF_TERM atom_badarg; +static ERL_NIF_TERM atom_ok; + +static int load(ErlNifEnv* env, void** priv_data, ERL_NIF_TERM load_info) +{ + atom_true = enif_make_atom(env,"true"); + atom_false = enif_make_atom(env,"false"); + atom_error = enif_make_atom(env,"error"); + atom_not_available = enif_make_atom(env,"not_available"); + atom_badarg = enif_make_atom(env,"badarg"); + atom_ok = enif_make_atom(env,"ok"); + + return 0; +} + +static ERL_NIF_TERM available(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef HAVE_USE_DTRACE + return atom_true; +#else + return atom_false; +#endif +} + +static ERL_NIF_TERM user_trace_s1(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef HAVE_USE_DTRACE + return erl_nif_user_trace_s1(env, argc, argv); +#else + return atom_error; +#endif +} + +static ERL_NIF_TERM user_trace_i4s4(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef HAVE_USE_DTRACE + return erl_nif_user_trace_i4s4(env, argc, argv); +#else + return atom_error; +#endif +} + +static ERL_NIF_TERM user_trace_n(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{ +#ifdef HAVE_USE_DTRACE + return erl_nif_user_trace_n(env, argc, argv); +#else + return atom_error; +#endif +} diff --git a/lib/runtime_tools/c_src/trace_ip_drv.c b/lib/runtime_tools/c_src/trace_ip_drv.c index 7ae6c1b329..6b77128761 100644 --- a/lib/runtime_tools/c_src/trace_ip_drv.c +++ b/lib/runtime_tools/c_src/trace_ip_drv.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 1999-2011. All Rights Reserved. + * Copyright Ericsson AB 1999-2012. 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 @@ -590,8 +590,8 @@ static void *my_alloc(size_t size) void *ret; if ((ret = driver_alloc(size)) == NULL) { /* May or may not work... */ - fprintf(stderr, "Could not allocate %d bytes of memory in %s.", - (int) size, __FILE__); + fprintf(stderr, "Could not allocate %lu bytes of memory in %s.", + (unsigned long) size, __FILE__); exit(1); } return ret; @@ -605,8 +605,8 @@ static ErlDrvBinary *my_alloc_binary(int size) ErlDrvBinary *ret; if ((ret = driver_alloc_binary(size)) == NULL) { /* May or may not work... */ - fprintf(stderr, "Could not allocate a binary of %d bytes in %s.", - (int) size, __FILE__); + fprintf(stderr, "Could not allocate a binary of %lu bytes in %s.", + (unsigned long) size, __FILE__); exit(1); } return ret; diff --git a/lib/runtime_tools/doc/src/Makefile b/lib/runtime_tools/doc/src/Makefile index dbbae81cfe..d240b287c3 100644 --- a/lib/runtime_tools/doc/src/Makefile +++ b/lib/runtime_tools/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2009. All Rights Reserved. +# Copyright Ericsson AB 1999-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -40,7 +40,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = ref_man.xml -XML_REF3_FILES = dbg.xml erts_alloc_config.xml +XML_REF3_FILES = dbg.xml dyntrace.xml erts_alloc_config.xml XML_REF6_FILES = runtime_tools_app.xml XML_PART_FILES = part_notes.xml part_notes_history.xml @@ -108,16 +108,16 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/runtime_tools/doc/src/dyntrace.xml b/lib/runtime_tools/doc/src/dyntrace.xml new file mode 100644 index 0000000000..5fc5530f25 --- /dev/null +++ b/lib/runtime_tools/doc/src/dyntrace.xml @@ -0,0 +1,209 @@ +<?xml version="1.0" encoding="latin1" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>1996</year><year>2012</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + The contents of this file are subject to the Erlang Public License, + Version 1.1, (the "License"); you may not use this file except in + compliance with the License. You should have received a copy of the + Erlang Public License along with this software. If not, it can be + retrieved online at http://www.erlang.org/. + + Software distributed under the License is distributed on an "AS IS" + basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + the License for the specific language governing rights and limitations + under the License. + + </legalnotice> + + <title>dyntrace</title> + <prepared>Patrik Nyblom</prepared> + <responsible></responsible> + <docno>1</docno> + <approved>ETX/B/SFP (Kenneth Lundin)</approved> + <checked></checked> + <date>12-03-20</date> + <rev>A</rev> + <file>dyntrace.xml</file> + </header> + <module>dyntrace</module> + <modulesummary>Interface to dynamic tracing</modulesummary> + <description> + <p>This module implements interfaces to dynamic tracing, should such be compiled into the virtual machine. For a standard and/or commercial build, no dynamic tracing is available, in which case none of the functions in this module is usable or give any effect.</p> + <p>Should dynamic tracing be enabled in the current build, either by configuring with <c>./configure --with-dynamic-trace=dtrace</c> or with <c>./configure --with-dynamic-trace=systemtap</c>, the module can be used for two things:</p> + <list type="bulleted"> + <item>Trigger the user-probe <c>user_trace_i4s4</c> in the NIF library <c>dyntrace.so</c> by calling <c>dyntrace:p/{1,2,3,4,5,6,7,8}</c>.</item> + <item>Set a user specified tag that will be present in the trace messages of both the <c>efile_drv</c> and the user-probe mentioned above.</item> + </list> + <p>Both building with dynamic trace probes and using them is experimental and unsupported by Erlang/OTP. It is included as an option for the developer to trace and debug performance issues in their systems.</p> + <p>The original implementation is mostly done by Scott Lystiger Fritchie as an Open Source Contribution and it should be viewed as such even though the source for dynamic tracing as well as this module is included in the main distribution. However, the ability to use dynamic tracing of the virtual machine is a very valuable contribution which OTP has every intention to maintain as a tool for the developer.</p> + <p>How to write <c>d</c> programs or <c>systemtap</c> scripts can be learned from books and from a lot of pages on the Internet. This manual page does not include any documentation about using the dynamic trace tools of respective platform. The <c>examples</c> directory of the <c>runtime_tools</c> application however contains comprehensive examples of both <c>d</c> and <c>systemtap</c> programs that will help you get started. Another source of information is the <c>README.dtrace(.md)</c> and <c>README.systemtap(.md)</c> files in the Erlang source top directory.</p> + </description> + <funcs> + <func> + <name>available() -> boolean()</name> + <fsummary>Check if dynamic tracing is available</fsummary> + <desc> + <p>This function uses the NIF library to determine if dynamic + tracing is available. Usually calling <seealso + marker="erts:erlang#system_info/1">erlang:system_info/1</seealso> + is a better indicator of the availability of dynamic + tracing.</p> + <p>The function will throw an exception if the <c>dyntrace</c> NIF library could not be loaded by the on_load function of this module.</p> + </desc> + </func> + <func> + <name>p() -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message only containing the user tag and zeroes/empty strings in all other fields.</p> + </desc> + </func> + <func> + <name>p(integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer or string parameter in the first integer/string field.</p> + </desc> + </func> + <func> + <name>p(integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters. I.e. <c>p(1,"Hello")</c> is ok, as is <c>p(1,1)</c> and <c>p("Hello","Again")</c>, but not <c>p("Hello",1)</c>.</p> + </desc> + </func> + <func> + <name>p(integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + </desc> + </func> + <func> + <name>p(integer() | string(), integer() | string(), integer() | string(), integer() | string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + </desc> + </func> + <func> + <name>p(integer(), integer() | string(), integer() | string(), integer() | string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + <p>There can be no more than four parameters of any type (integer() or string()), so the first parameter has to be an integer() and the last a string().</p> + </desc> + </func> + <func> + <name>p(integer(), integer(), integer() | string(), integer() | string(), string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + <p>There can be no more than four parameters of any type (integer() or string()), so the first two parameters has to be integer()'s and the last two string()'s.</p> + </desc> + </func> + <func> + <name>p(integer(), integer(), integer(), integer() | string(), string(), string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing the user tag and the integer() or string() parameters as the first fields of respective type. integer() parameters should be put before any string() parameters, as in <seealso marker="#p/2">p/2</seealso>.</p> + <p>There can be no more than four parameters of any type (integer() or string()), so the first three parameters has to be integer()'s and the last three string()'s.</p> + </desc> + </func> + <func> + <name>p(integer(), integer(), integer(), integer(), string(), string(), string(), string()) -> true | false | error | badarg</name> + <fsummary>Trigger the user trace probe.</fsummary> + <desc> + <p>Calling this function will trigger the "user" trace probe user_trace_i4s4 in the dyntrace NIF module, sending a trace message containing all the integer()'s and string()'s provided, as well as any user tag set in the current process.</p> + </desc> + </func> + <func> + <name>get_tag() -> binary() | undefined</name> + <fsummary>Get the user tag set in the process.</fsummary> + <desc> + <p>This function returns the user tag set in the current + process. If no tag is set or dynamic tracing is not available, + it returns <c>undefined</c></p> + </desc> + </func> + <func> + <name>get_tag() -> binary() | undefined</name> + <fsummary>Get the user tag set in the process or sent to the process.</fsummary> + <desc> + <p>This function returns the user tag set in the current + process or, if no user tag is present, the last user tag sent + to the process together with a message (in the same way as + <seealso marker="kernel:seq_trace">sequential trace + tokens</seealso> are spread to other processes together with + messages. For an explanation of how user tags can be spread + together with messages, see <seealso + marker="#spread_tag/1">spread_tag/1</seealso>. If no tag is + found or dynamic tracing is not available, it returns + <c>undefined</c></p> + </desc> + </func> + + <func> + <name>put_tag(Item) -> binary() | undefined </name> + <fsummary>Set the user tag of the current process.</fsummary> + <type> + <v>Item = iodata()</v> + </type> + <desc> + <p>This function sets the user tag of the current process. The user tag is a binary(), but can be specified as any iodata(), which is automatically converted to a binary by this function.</p> + <p>The user tag is provided to the user probes triggered by calls top <c>dyntrace:p/{1,2,3,4,5,6,7,8}</c> as well as probes in the efile_driver. In the future, user tags might be added to more probes.</p> + <p>The old user tag (if any) is returned, or <c>undefined</c> if no user tag was present or dynamic tracing is not enabled.</p> + </desc> + </func> + <func> + <name>spread_tag(boolean()) -> TagData</name> + <fsummary>Start or stop spreading dynamic trace user tags with the next message.</fsummary> + <type> + <v>TagData = opaque data that can be used as parameter to <seealso marker="#restore_tag/1">restore_tag/1</seealso></v> + </type> + <desc> + <p>This function controls if user tags are to be spread to other processes with the next message. Spreading of user tags work like spreading of sequential trace tokens, so that a received user tag will be active in the process until the next message arrives (if that message does not also contain the user tag.</p> + <p>This functionality is used when a client process communicates with a file i/o-server to spread the user tag to the I/O-server and then down to the efile_drv driver. By using <c>spread_tag/1</c> and <c>restore_tag/1</c>, one can enable or disable spreading of user tags to other processes and then restore the previous state of the user tag. The TagData returned from this call contains all previous information so the state (including any previously spread user tags) will be completely restored by a later call to <c>restore_tag/1</c>.</p> + <p>The <seealso marker="kernel:file">file</seealso> module already spread's tags, so there is noo need to manually call these function to get user tags spread to the efile driver through that module.</p> + <p>The most use of this function would be if one for example uses the <seealso marker="stdlib:io">io</seealso> module to communicate with an I/O-server for a regular file, like in the following example:</p> +<pre> +f() -> + {ok, F} = file:open("test.tst",[write]), + Saved = dyntrace:spread_tag(true), + io:format(F,"Hello world!",[]), + dyntrace:restore_tag(Saved), + file:close(F). +</pre> + <p>In this example, any user tag set in the calling process will be spread to the I/O-server when the io:format call is done.</p> + </desc> + </func> + <func> + <name>restore_tag(TagData) -> true</name> + <fsummary>Restore to a previous state of user tag spreading.</fsummary> + <type> + <v>TagData = opaque data returned by <seealso marker="#spread_tag/1">spread_tag/1</seealso></v> + </type> + <desc> + <p>Restores the previous state of user tags and their spreading as it was before a call to <seealso marker="#spread_tag/1">spread_tag/1</seealso>. Note that the restoring is not limited to the same process, one can utilize this to turn off spreding in one process and restore it in a newly created, the one that actually is going to send messages:</p> +<pre> +f() -> + TagData=dyntrace:spread_tag(false), + spawn(fun() -> + dyntrace:restore_tag(TagData), + do_something() + end), + do_something_else(), + dyntrace:restore_tag(TagData). +</pre> + <p>Correctly handling user tags and their spreading might take some effort, as Erlang programs tend to send and receive messages so that sometimes the user tag gets lost due to various things, like double receives or communication with a port (ports do not handle user tags, in the same way as they do not handle regular sequential trace tokens).</p> + </desc> + </func> + </funcs> + </erlref> + diff --git a/lib/runtime_tools/doc/src/notes.xml b/lib/runtime_tools/doc/src/notes.xml index ccf11bf0fe..2d4a206e1c 100644 --- a/lib/runtime_tools/doc/src/notes.xml +++ b/lib/runtime_tools/doc/src/notes.xml @@ -31,6 +31,28 @@ <p>This document describes the changes made to the Runtime_Tools application.</p> +<section><title>Runtime_Tools 1.8.8</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The DTrace source patch from Scott Lystig Fritchie is + integrated in the source tree. Using an emulator with + dtrace probe is still not supported for production use, + but may be a valuable debugging tool. Configure with + --with-dynamic-trace=dtrace (or + --with-dynamic-trace=systemtap) to create a build with + dtrace probes enabled. See runtime_tools for + documentation and examples.</p> + <p> + Own Id: OTP-10017</p> + </item> + </list> + </section> + +</section> + <section><title>Runtime_Tools 1.8.7</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/runtime_tools/doc/src/ref_man.xml b/lib/runtime_tools/doc/src/ref_man.xml index 579efcd969..df3446cd17 100644 --- a/lib/runtime_tools/doc/src/ref_man.xml +++ b/lib/runtime_tools/doc/src/ref_man.xml @@ -4,7 +4,7 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1999</year><year>2009</year> + <year>1999</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -33,6 +33,7 @@ </description> <xi:include href="runtime_tools_app.xml"/> <xi:include href="dbg.xml"/> + <xi:include href="dyntrace.xml"/> <xi:include href="erts_alloc_config.xml"/> </application> diff --git a/lib/runtime_tools/examples/dist.d b/lib/runtime_tools/examples/dist.d new file mode 100644 index 0000000000..7e2d7f0e35 --- /dev/null +++ b/lib/runtime_tools/examples/dist.d @@ -0,0 +1,62 @@ +/* example usage: dtrace -q -s /path/to/dist.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::dist-monitor +{ + printf("monitor: pid %d, who %s, what %s, node %s, type %s, reason %s\n", + pid, + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3), + copyinstr(arg4)); +} + +erlang*:::dist-port_busy +{ + printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); + /* + * For variable use advice, see: + * http://dtrace.org/blogs/brendan/2011/11/25/dtrace-variable-types/ + * + * Howevever, it's quite possible for the blocked events to span + * threads, so we'll use globals. + */ + blocked_procs[copyinstr(arg3)] = timestamp; +} + +erlang*:::dist-output +{ + printf("dist output: node %s, port %s, remote_node %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::dist-outputv +{ + printf("port outputv: node %s, port %s, remote_node %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::process-scheduled +/blocked_procs[copyinstr(arg0)]/ +{ + pidstr = copyinstr(arg0); + printf("blocked pid %s scheduled now, waited %d microseconds\n", + pidstr, (timestamp - blocked_procs[pidstr]) / 1000); + blocked_procs[pidstr] = 0; +} diff --git a/lib/runtime_tools/examples/dist.systemtap b/lib/runtime_tools/examples/dist.systemtap new file mode 100644 index 0000000000..8935e19e28 --- /dev/null +++ b/lib/runtime_tools/examples/dist.systemtap @@ -0,0 +1,76 @@ +/* example usage: stap /path/to/dist.systemtap -x <pid> */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("dist-monitor") +{ + printf("monitor: pid %d, who %s, what %s, node %s, type %s, reason %s\n", + pid(), + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4), + user_string($arg5)); +} + +probe process("beam").mark("dist-port_busy") +{ + printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); + blocked_procs[user_string($arg4)] = timestamp; +} + +probe process("beam").mark("dist-port_busy") +{ + printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); + blocked_procs[user_string($arg4)] = timestamp; +} + +probe process("beam").mark("dist-output") +{ + printf("dist output: node %s, port %s, remote_node %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("dist-outputv") +{ + printf("port outputv: node %s, port %s, remote_node %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("process-scheduled") +{ + pidstr = user_string($arg1); + if (pidstr in blocked_procs) { + printf("blocked pid %s scheduled now, waited %d microseconds\n", + pidstr, (timestamp - blocked_procs[pidstr]) / 1000); + delete blocked_procs[pidstr]; + } +} + +global blocked_procs; diff --git a/lib/runtime_tools/examples/driver1.d b/lib/runtime_tools/examples/driver1.d new file mode 100644 index 0000000000..4871a8ee69 --- /dev/null +++ b/lib/runtime_tools/examples/driver1.d @@ -0,0 +1,114 @@ +/* example usage: dtrace -q -s /path/to/driver1.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::driver-init +{ + printf("driver init name %s major %d minor %d flags %d\n", + copyinstr(arg0), arg1, arg2, arg3); +} + +erlang*:::driver-start +{ + printf("driver start pid %s driver name %s port %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-stop +{ + printf("driver stop pid %s driver name %s port %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-finish +{ + printf("driver finish driver name %s port %s\n", + copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::driver-flush +{ + printf("driver flush pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-output +{ + printf("driver output pid %s port %s port name %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::driver-outputv +{ + printf("driver outputv pid %s port %s port name %s bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::driver-control +{ + printf("driver control pid %s port %s port name %s command %d bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, arg4); +} + +erlang*:::driver-call +{ + printf("driver call pid %s port %s port name %s command %d bytes %d\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, arg4); +} + +erlang*:::driver-event +{ + printf("driver event pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-ready_input +{ + printf("driver ready_input pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-ready_output +{ + printf("driver ready_output pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-timeout +{ + printf("driver timeout pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-ready_async +{ + printf("driver ready_async pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-process_exit +{ + printf("driver process_exit pid %s port %s port name %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::driver-stop_select +{ + printf("driver stop_select driver name %s\n", copyinstr(arg0)); +} diff --git a/lib/runtime_tools/examples/driver1.systemtap b/lib/runtime_tools/examples/driver1.systemtap new file mode 100644 index 0000000000..deae82f8fb --- /dev/null +++ b/lib/runtime_tools/examples/driver1.systemtap @@ -0,0 +1,125 @@ +/* example usage: stap /path/to/driver1.systemtap -x <pid> */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("driver-init") +{ + printf("driver init name %s major %d minor %d flags %d\n", + user_string($arg1), $arg2, $arg3, $arg4); +} + +probe process("beam").mark("driver-start") +{ + printf("driver start pid %s driver name %s port %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-stop") +{ + printf("driver stop pid %s driver name %s port %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-finish") +{ + printf("driver finish driver name %s\n", + user_string($arg1)); +} + +probe process("beam").mark("driver-flush") +{ + printf("driver flush pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-output") +{ + printf("driver output pid %s port %s port name %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("driver-outputv") +{ + printf("driver outputv pid %s port %s port name %s bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); +} + +probe process("beam").mark("driver-control") +{ + printf("driver control pid %s port %s port name %s command %d bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); +} + +probe process("beam").mark("driver-call") +{ + printf("driver call pid %s port %s port name %s command %d bytes %d\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); +} + +probe process("beam").mark("driver-event") +{ + printf("driver event pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-ready_input") +{ + printf("driver ready_input pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-ready_output") +{ + printf("driver ready_output pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-timeout") +{ + printf("driver timeout pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-ready_async") +{ + printf("driver ready_async pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-process_exit") +{ + printf("driver process_exit pid %s port %s port name %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("driver-stop_select") +{ + printf("driver stop_select driver name %s\n", user_string($arg1)); +} diff --git a/lib/runtime_tools/examples/efile_drv.d b/lib/runtime_tools/examples/efile_drv.d new file mode 100644 index 0000000000..2442222552 --- /dev/null +++ b/lib/runtime_tools/examples/efile_drv.d @@ -0,0 +1,104 @@ +/* example usage: dtrace -q -s /path/to/efile_drv.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +BEGIN +{ + op_map[1] = "OPEN"; + op_map[2] = "READ"; + op_map[3] = "LSEEK"; + op_map[4] = "WRITE"; + op_map[5] = "FSTAT"; + op_map[6] = "PWD"; + op_map[7] = "READDIR"; + op_map[8] = "CHDIR"; + op_map[9] = "FSYNC"; + op_map[10] = "MKDIR"; + op_map[11] = "DELETE"; + op_map[12] = "RENAME"; + op_map[13] = "RMDIR"; + op_map[14] = "TRUNCATE"; + op_map[15] = "READ_FILE"; + op_map[16] = "WRITE_INFO"; + op_map[19] = "LSTAT"; + op_map[20] = "READLINK"; + op_map[21] = "LINK"; + op_map[22] = "SYMLINK"; + op_map[23] = "CLOSE"; + op_map[24] = "PWRITEV"; + op_map[25] = "PREADV"; + op_map[26] = "SETOPT"; + op_map[27] = "IPREAD"; + op_map[28] = "ALTNAME"; + op_map[29] = "READ_LINE"; + op_map[30] = "FDATASYNC"; + op_map[31] = "FADVISE"; +} + +erlang*:::aio_pool-add +{ + printf("async I/O pool port %s queue len %d\n", copyinstr(arg0), arg1); +} + +erlang*:::aio_pool-get +{ + printf("async I/O pool port %s queue len %d\n", copyinstr(arg0), arg1); +} + +erlang*:::efile_drv-entry +{ + printf("efile_drv enter tag={%d,%d} %s%s | %s (%d) | args: %s %s , %d %d (port %s)\n", + arg0, arg1, + arg2 == NULL ? "" : "user tag ", + arg2 == NULL ? "" : copyinstr(arg2), + op_map[arg3], arg3, + arg4 == NULL ? "" : copyinstr(arg4), + arg5 == NULL ? "" : copyinstr(arg5), arg6, arg7, + /* NOTE: port name in args[10] is experimental */ + (args[10] == NULL) ? + "?" : copyinstr((user_addr_t) args[10])); +} + +erlang*:::efile_drv-int* +{ + printf("async I/O worker tag={%d,%d} | %s (%d) | %s\n", + arg0, arg1, op_map[arg2], arg2, probename); +} + +/* efile_drv-return error case */ +erlang*:::efile_drv-return +/arg4 == 0/ +{ + printf("efile_drv return tag={%d,%d} %s%s | %s (%d) | errno %d\n", + arg0, arg1, + arg2 == NULL ? "" : "user tag ", + arg2 == NULL ? "" : copyinstr(arg2), + op_map[arg3], arg3, + arg5); +} + +/* efile_drv-return success case */ +erlang*:::efile_drv-return +/arg4 != 0/ +{ + printf("efile_drv return tag={%d,%d} %s | %s (%d) ok\n", + arg0, arg1, + arg2 == NULL ? "" : copyinstr(arg2), + op_map[arg3], arg3); +} diff --git a/lib/runtime_tools/examples/efile_drv.systemtap b/lib/runtime_tools/examples/efile_drv.systemtap new file mode 100644 index 0000000000..4b92e67048 --- /dev/null +++ b/lib/runtime_tools/examples/efile_drv.systemtap @@ -0,0 +1,112 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe begin +{ + op_map[1] = "OPEN"; + op_map[2] = "READ"; + op_map[3] = "LSEEK"; + op_map[4] = "WRITE"; + op_map[5] = "FSTAT"; + op_map[6] = "PWD"; + op_map[7] = "READDIR"; + op_map[8] = "CHDIR"; + op_map[9] = "FSYNC"; + op_map[10] = "MKDIR"; + op_map[11] = "DELETE"; + op_map[12] = "RENAME"; + op_map[13] = "RMDIR"; + op_map[14] = "TRUNCATE"; + op_map[15] = "READ_FILE"; + op_map[16] = "WRITE_INFO"; + op_map[19] = "LSTAT"; + op_map[20] = "READLINK"; + op_map[21] = "LINK"; + op_map[22] = "SYMLINK"; + op_map[23] = "CLOSE"; + op_map[24] = "PWRITEV"; + op_map[25] = "PREADV"; + op_map[26] = "SETOPT"; + op_map[27] = "IPREAD"; + op_map[28] = "ALTNAME"; + op_map[29] = "READ_LINE"; + op_map[30] = "FDATASYNC"; + op_map[31] = "FADVISE"; +} + +probe process("beam").mark("aio_pool-add") +{ + printf("async I/O pool port %s queue len %d\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("aio_pool-get") +{ + printf("async I/O pool port %s queue len %d\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("efile_drv-entry") +{ + printf("efile_drv enter tag={%d,%d} %s%s | %s (%d) | args: %s %s , %d %d (port %s)\n", + $arg1, $arg2, + $arg3 == NULL ? "" : "user tag ", + $arg3 == NULL ? "" : user_string($arg3), + op_map[$arg4], $arg4, + $arg5 == NULL ? "" : user_string($arg5), + $arg6 == NULL ? "" : user_string($arg6), $arg7, $arg8, + /* NOTE: port name in $arg[11] is experimental */ + user_string($arg11)) +} + +probe process("beam").mark("efile_drv-int*") +{ + printf("async I/O worker tag={%d,%d} | %s (%d) | %s\n", + $arg1, $arg2, op_map[$arg3], $arg3, probefunc()); +} + +probe process("beam").mark("efile_drv-return") +{ + if ($arg5 == 0) { + /* efile_drv-return error case */ + printf("efile_drv return tag={%d,%d} %s%s | %s (%d) | errno %d\n", + $arg1, $arg2, + $arg3 == NULL ? "" : "user tag ", + $arg3 == NULL ? "" : user_string($arg3), + op_map[$arg4], $arg4, + $arg6); + } else { + /* efile_drv-return success case */ + printf("efile_drv return tag={%d,%d} %s | %s (%d) ok\n", + $arg1, $arg2, + $arg3 == NULL ? "" : user_string($arg3), + op_map[$arg4], $arg4); + } +} + +global op_map; diff --git a/lib/runtime_tools/examples/function-calls.d b/lib/runtime_tools/examples/function-calls.d new file mode 100644 index 0000000000..ec6090553e --- /dev/null +++ b/lib/runtime_tools/examples/function-calls.d @@ -0,0 +1,57 @@ +/* example usage: dtrace -q -s /path/to/function-calls.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::local-function-entry +{ + printf("pid %s enter (local) %s depth %d\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::global-function-entry +{ + printf("pid %s enter (global) %s depth %d\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::function-return +{ + printf("pid %s return %s depth %d\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::bif-entry +{ + printf("pid %s BIF entry mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::bif-return +{ + printf("pid %s BIF return mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::nif-entry +{ + printf("pid %s NIF entry mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::nif-return +{ + printf("pid %s NIF return mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} diff --git a/lib/runtime_tools/examples/function-calls.systemtap b/lib/runtime_tools/examples/function-calls.systemtap new file mode 100644 index 0000000000..4de54cfb5a --- /dev/null +++ b/lib/runtime_tools/examples/function-calls.systemtap @@ -0,0 +1,67 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("local-function-entry") +{ + printf("pid %s enter (local) %s depth %d\n", + user_string($arg1), user_string($arg2), $arg3); +} + +probe process("beam").mark("global-function-entry") +{ + printf("pid %s enter (global) %s depth %d\n", + user_string($arg1), user_string($arg2), $arg3); +} + +probe process("beam").mark("function-return") +{ + printf("pid %s return %s depth %d\n", + user_string($arg1), user_string($arg2), $arg3); +} + +probe process("beam").mark("bif-entry") +{ + printf("pid %s BIF entry mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("bif-return") +{ + printf("pid %s BIF return mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("nif-entry") +{ + printf("pid %s NIF entry mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("nif-return") +{ + printf("pid %s NIF return mfa %s\n", user_string($arg1), user_string($arg2)); +} diff --git a/lib/runtime_tools/examples/garbage-collection.d b/lib/runtime_tools/examples/garbage-collection.d new file mode 100644 index 0000000000..ebc40a19ec --- /dev/null +++ b/lib/runtime_tools/examples/garbage-collection.d @@ -0,0 +1,39 @@ +/* example usage: dtrace -q -s /path/to/garbage-collection.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::gc_major-start +{ + printf("GC major start pid %s need %d words\n", copyinstr(arg0), arg1); +} + +erlang*:::gc_minor-start +{ + printf("GC minor start pid %s need %d words\n", copyinstr(arg0), arg1); +} + +erlang*:::gc_major-end +{ + printf("GC major end pid %s reclaimed %d words\n", copyinstr(arg0), arg1); +} + +erlang*:::gc_minor-start +{ + printf("GC minor end pid %s reclaimed %d words\n", copyinstr(arg0), arg1); +} diff --git a/lib/runtime_tools/examples/garbage-collection.systemtap b/lib/runtime_tools/examples/garbage-collection.systemtap new file mode 100644 index 0000000000..81e9d38196 --- /dev/null +++ b/lib/runtime_tools/examples/garbage-collection.systemtap @@ -0,0 +1,49 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("gc_major-start") +{ + printf("GC major start pid %s need %d words\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("gc_minor-start") +{ + printf("GC minor start pid %s need %d words\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("gc_major-end") +{ + printf("GC major end pid %s reclaimed %d words\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("gc_minor-start") +{ + printf("GC minor end pid %s reclaimed %d words\n", user_string($arg1), $arg2); +} diff --git a/lib/runtime_tools/examples/memory1.d b/lib/runtime_tools/examples/memory1.d new file mode 100644 index 0000000000..b222aeae62 --- /dev/null +++ b/lib/runtime_tools/examples/memory1.d @@ -0,0 +1,41 @@ +/* example usage: dtrace -q -s /path/to/memory1.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::copy-struct +{ + printf("copy_struct %d bytes\n", arg0); +} + +erlang*:::copy-object +{ + printf("copy_object pid %s %d bytes\n", copyinstr(arg0), arg1); +} + +erlang*:::process-heap_grow +{ + printf("proc heap grow pid %s %d -> %d bytes\n", copyinstr(arg0), + arg1, arg2); +} + +erlang*:::process-heap_shrink +{ + printf("proc heap shrink pid %s %d -> %d bytes\n", copyinstr(arg0), + arg1, arg2); +} diff --git a/lib/runtime_tools/examples/memory1.systemtap b/lib/runtime_tools/examples/memory1.systemtap new file mode 100644 index 0000000000..9374b97d18 --- /dev/null +++ b/lib/runtime_tools/examples/memory1.systemtap @@ -0,0 +1,51 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("copy-struct") +{ + printf("copy_struct %d bytes\n", $arg1); +} + +probe process("beam").mark("copy-object") +{ + printf("copy_object pid %s %d bytes\n", user_string($arg1), $arg2); +} + +probe process("beam").mark("process-heap_grow") +{ + printf("proc heap grow pid %s %d -> %d bytes\n", user_string($arg1), + $arg2, $arg3); +} + +probe process("beam").mark("process-heap_shrink") +{ + printf("proc heap shrink pid %s %d -> %d bytes\n", user_string($arg1), + $arg2, $arg3); +} diff --git a/lib/runtime_tools/examples/messages.d b/lib/runtime_tools/examples/messages.d new file mode 100644 index 0000000000..08cf52f3d2 --- /dev/null +++ b/lib/runtime_tools/examples/messages.d @@ -0,0 +1,94 @@ +/* example usage: dtrace -q -s /path/to/messages.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +BEGIN +{ + printf("\n"); + printf("NOTE: message-queue message size 4294967295 means an external\n"); + printf(" message that the code isn't smart enough to determine\n"); + printf(" the actual size.\n"); + printf("\n"); +} + +erlang*:::message-send +/arg3 == 0 && arg4 == 0 && arg5 == 0/ +{ + printf("send: %s -> %s: %d words\n", + copyinstr(arg0), copyinstr(arg1), arg2); +} + +erlang*:::message-send +/arg3 != 0 || arg4 != 0 || arg5 != 0/ +{ + printf("send: %s label %d token {%d,%d} -> %s: %d words\n", + copyinstr(arg0), + arg3, arg4, arg5, + copyinstr(arg1), arg2); +} + +/* + * TODO: + * Weird, on my OS X box, beam says arg6 = 0 but this script says 4294967296. + */ + +erlang*:::message-send-remote +/arg4 == 0 && arg5 == 0 && (arg6 == 0 || arg6 >= 4294967296)/ +{ + printf("send : %s -> %s %s: %d words\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::message-send-remote +/arg4 != 0 || arg5 != 0 || arg6 < 4294967296/ +{ + printf("send : %s label %d token {%d,%d} -> %s %s: %d words\n", + copyinstr(arg0), + arg4, arg5, arg6, + copyinstr(arg1), copyinstr(arg2), arg3); +} + +erlang*:::message-queued +/arg3 == 0 && arg4 == 0 && arg5 == 0/ +{ + printf("queued: %s: %d words, queue len %d\n", copyinstr(arg0), arg1, arg2); +} + +erlang*:::message-queued +/arg3 != 0 || arg4 != 0 || arg5 != 0/ +{ + printf("queued: %s label %d token {%d,%d}: %d words, queue len %d\n", + copyinstr(arg0), arg3, arg4, arg5, + arg1, arg2); +} + +erlang*:::message-receive +/arg3 == 0 && arg4 == 0 && arg5 == 0/ +{ + printf("receive: %s: %d words, queue len %d\n", + copyinstr(arg0), arg1, arg2); +} + +erlang*:::message-receive +/arg3 != 0 || arg4 != 0 || arg5 != 0/ +{ + printf("receive: %s label %d token {%d,%d}: %d words, queue len %d\n", + copyinstr(arg0), arg3, arg4, arg5, + arg1, arg2); +} diff --git a/lib/runtime_tools/examples/messages.systemtap b/lib/runtime_tools/examples/messages.systemtap new file mode 100644 index 0000000000..c58e1168f9 --- /dev/null +++ b/lib/runtime_tools/examples/messages.systemtap @@ -0,0 +1,87 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe begin +{ + printf("\n"); + printf("NOTE: message-queue message size 4294967295 means an external\n"); + printf(" message that the code isn't smart enough to determine\n"); + printf(" the actual size.\n"); + printf("\n"); +} + +probe process("beam").mark("message-send") +{ + if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { + printf("send: %s -> %s: %d words\n", + user_string($arg1), user_string($arg2), $arg3); + } else { + printf("send: %s label %d token {%d,%d} -> %s: %d words\n", + user_string($arg1), + $arg4, $arg5, $arg6, + user_string($arg2), $arg3); + } +} + +probe process("beam").mark("message-send-remote") +{ + if ($arg5 == 0 && $arg6 == 0 && $arg7 == 0) { + printf("send : %s -> %s %s: %d words\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4); + } else { + printf("send : %s label %d token {%d,%d} -> %s %s: %d words\n", + user_string($arg1), + $arg5, $arg6, $arg7, + user_string($arg2), user_string($arg3), $arg4); + } +} + +probe process("beam").mark("message-queued") +{ + if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { + printf("queued: %s: %d words, queue len %d\n", user_string($arg1), $arg2, $arg3); + } else { + printf("queued: %s label %d token {%d,%d}: %d words, queue len %d\n", + user_string($arg1), $arg4, $arg5, $arg6, + $arg2, $arg3); + } +} + +probe process("beam").mark("message-receive") +{ + if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { + printf("receive: %s: %d words, queue len %d\n", + user_string($arg1), $arg2, $arg3); + } else { + printf("receive: %s label %d token {%d,%d}: %d words, queue len %d\n", + user_string($arg1), $arg4, $arg5, $arg6, + $arg2, $arg3); + } +} diff --git a/lib/runtime_tools/examples/port1.d b/lib/runtime_tools/examples/port1.d new file mode 100644 index 0000000000..1e3bd3d8a9 --- /dev/null +++ b/lib/runtime_tools/examples/port1.d @@ -0,0 +1,142 @@ +/* example usage: dtrace -q -s /path/to/port1.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +BEGIN +{ + driver_map["tcp_inet", 1] = "OPEN"; + driver_map["tcp_inet", 2] = "CLOSE"; + driver_map["tcp_inet", 3] = "CONNECT"; + driver_map["tcp_inet", 4] = "PEER"; + driver_map["tcp_inet", 5] = "NAME"; + driver_map["tcp_inet", 6] = "BIND"; + driver_map["tcp_inet", 7] = "SETOPTS"; + driver_map["tcp_inet", 8] = "GETOPTS"; + driver_map["tcp_inet", 11] = "GETSTAT"; + driver_map["tcp_inet", 12] = "GETHOSTNAME"; + driver_map["tcp_inet", 13] = "FDOPEN"; + driver_map["tcp_inet", 14] = "GETFD"; + driver_map["tcp_inet", 15] = "GETTYPE"; + driver_map["tcp_inet", 16] = "GETSTATUS"; + driver_map["tcp_inet", 17] = "GETSERVBYNAME"; + driver_map["tcp_inet", 18] = "GETSERVBYPORT"; + driver_map["tcp_inet", 19] = "SETNAME"; + driver_map["tcp_inet", 20] = "SETPEER"; + driver_map["tcp_inet", 21] = "GETIFLIST"; + driver_map["tcp_inet", 22] = "IFGET"; + driver_map["tcp_inet", 23] = "IFSET"; + driver_map["tcp_inet", 24] = "SUBSCRIBE"; + driver_map["tcp_inet", 25] = "GETIFADDRS"; + driver_map["tcp_inet", 40] = "ACCEPT"; + driver_map["tcp_inet", 41] = "LISTEN"; + driver_map["tcp_inet", 42] = "RECV"; + driver_map["tcp_inet", 43] = "UNRECV"; + driver_map["tcp_inet", 44] = "SHUTDOWN"; + driver_map["tcp_inet", 60] = "RECV"; + driver_map["tcp_inet", 61] = "LISTEN"; + driver_map["tcp_inet", 62] = "BINDX"; + /* No looping constructs, so repeat for udp_inet */ + driver_map["udp_inet", 1] = "OPEN"; + driver_map["udp_inet", 2] = "CLOSE"; + driver_map["udp_inet", 3] = "CONNECT"; + driver_map["udp_inet", 4] = "PEER"; + driver_map["udp_inet", 5] = "NAME"; + driver_map["udp_inet", 6] = "BIND"; + driver_map["udp_inet", 7] = "SETOPTS"; + driver_map["udp_inet", 8] = "GETOPTS"; + driver_map["udp_inet", 11] = "GETSTAT"; + driver_map["udp_inet", 12] = "GETHOSTNAME"; + driver_map["udp_inet", 13] = "FDOPEN"; + driver_map["udp_inet", 14] = "GETFD"; + driver_map["udp_inet", 15] = "GETTYPE"; + driver_map["udp_inet", 16] = "GETSTATUS"; + driver_map["udp_inet", 17] = "GETSERVBYNAME"; + driver_map["udp_inet", 18] = "GETSERVBYPORT"; + driver_map["udp_inet", 19] = "SETNAME"; + driver_map["udp_inet", 20] = "SETPEER"; + driver_map["udp_inet", 21] = "GETIFLIST"; + driver_map["udp_inet", 22] = "IFGET"; + driver_map["udp_inet", 23] = "IFSET"; + driver_map["udp_inet", 24] = "SUBSCRIBE"; + driver_map["udp_inet", 25] = "GETIFADDRS"; + driver_map["udp_inet", 40] = "ACCEPT"; + driver_map["udp_inet", 41] = "LISTEN"; + driver_map["udp_inet", 42] = "RECV"; + driver_map["udp_inet", 43] = "UNRECV"; + driver_map["udp_inet", 44] = "SHUTDOWN"; + driver_map["udp_inet", 60] = "RECV"; + driver_map["udp_inet", 61] = "LISTEN"; + driver_map["udp_inet", 62] = "BINDX"; +} + +erlang*:::port-open +{ + printf("port open pid %s port name %s port %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::port-command +{ + printf("port command pid %s port %s port name %s command type %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} + +erlang*:::port-control +{ + /* http://dtrace.org/blogs/brendan/2011/11/25/dtrace-variable-types/ */ + this->cmd = driver_map[copyinstr(arg2), arg3]; + this->cmd_str = (this->cmd == 0) ? "unknown" : this->cmd; + printf("port control pid %s port %s port name %s command %d %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), arg3, + this->cmd_str); +} + +/* port-exit is fired as a result of port_close() or exit signal */ + +erlang*:::port-exit +{ + printf("port exit pid %s port %s port name %s reason %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} + +erlang*:::port-connect +{ + printf("port connect pid %s port %s port name %s new pid %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} + +erlang*:::port-busy +{ + printf("port busy %s\n", copyinstr(arg0)); +} + +erlang*:::port-not_busy +{ + printf("port not busy %s\n", copyinstr(arg0)); +} + +erlang*:::aio_pool-add +{ + printf("async I/O pool add thread %d queue len %d\n", arg0, arg1); +} + +erlang*:::aio_pool-get +{ + printf("async I/O pool get thread %d queue len %d\n", arg0, arg1); +} diff --git a/lib/runtime_tools/examples/port1.systemtap b/lib/runtime_tools/examples/port1.systemtap new file mode 100644 index 0000000000..c8af240d85 --- /dev/null +++ b/lib/runtime_tools/examples/port1.systemtap @@ -0,0 +1,152 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe begin +{ + driver_map["tcp_inet", 1] = "OPEN"; + driver_map["tcp_inet", 2] = "CLOSE"; + driver_map["tcp_inet", 3] = "CONNECT"; + driver_map["tcp_inet", 4] = "PEER"; + driver_map["tcp_inet", 5] = "NAME"; + driver_map["tcp_inet", 6] = "BIND"; + driver_map["tcp_inet", 7] = "SETOPTS"; + driver_map["tcp_inet", 8] = "GETOPTS"; + driver_map["tcp_inet", 11] = "GETSTAT"; + driver_map["tcp_inet", 12] = "GETHOSTNAME"; + driver_map["tcp_inet", 13] = "FDOPEN"; + driver_map["tcp_inet", 14] = "GETFD"; + driver_map["tcp_inet", 15] = "GETTYPE"; + driver_map["tcp_inet", 16] = "GETSTATUS"; + driver_map["tcp_inet", 17] = "GETSERVBYNAME"; + driver_map["tcp_inet", 18] = "GETSERVBYPORT"; + driver_map["tcp_inet", 19] = "SETNAME"; + driver_map["tcp_inet", 20] = "SETPEER"; + driver_map["tcp_inet", 21] = "GETIFLIST"; + driver_map["tcp_inet", 22] = "IFGET"; + driver_map["tcp_inet", 23] = "IFSET"; + driver_map["tcp_inet", 24] = "SUBSCRIBE"; + driver_map["tcp_inet", 25] = "GETIFADDRS"; + driver_map["tcp_inet", 40] = "ACCEPT"; + driver_map["tcp_inet", 41] = "LISTEN"; + driver_map["tcp_inet", 42] = "RECV"; + driver_map["tcp_inet", 43] = "UNRECV"; + driver_map["tcp_inet", 44] = "SHUTDOWN"; + driver_map["tcp_inet", 60] = "RECV"; + driver_map["tcp_inet", 61] = "LISTEN"; + driver_map["tcp_inet", 62] = "BINDX"; + /* No looping constructs, so repeat for udp_inet */ + driver_map["udp_inet", 1] = "OPEN"; + driver_map["udp_inet", 2] = "CLOSE"; + driver_map["udp_inet", 3] = "CONNECT"; + driver_map["udp_inet", 4] = "PEER"; + driver_map["udp_inet", 5] = "NAME"; + driver_map["udp_inet", 6] = "BIND"; + driver_map["udp_inet", 7] = "SETOPTS"; + driver_map["udp_inet", 8] = "GETOPTS"; + driver_map["udp_inet", 11] = "GETSTAT"; + driver_map["udp_inet", 12] = "GETHOSTNAME"; + driver_map["udp_inet", 13] = "FDOPEN"; + driver_map["udp_inet", 14] = "GETFD"; + driver_map["udp_inet", 15] = "GETTYPE"; + driver_map["udp_inet", 16] = "GETSTATUS"; + driver_map["udp_inet", 17] = "GETSERVBYNAME"; + driver_map["udp_inet", 18] = "GETSERVBYPORT"; + driver_map["udp_inet", 19] = "SETNAME"; + driver_map["udp_inet", 20] = "SETPEER"; + driver_map["udp_inet", 21] = "GETIFLIST"; + driver_map["udp_inet", 22] = "IFGET"; + driver_map["udp_inet", 23] = "IFSET"; + driver_map["udp_inet", 24] = "SUBSCRIBE"; + driver_map["udp_inet", 25] = "GETIFADDRS"; + driver_map["udp_inet", 40] = "ACCEPT"; + driver_map["udp_inet", 41] = "LISTEN"; + driver_map["udp_inet", 42] = "RECV"; + driver_map["udp_inet", 43] = "UNRECV"; + driver_map["udp_inet", 44] = "SHUTDOWN"; + driver_map["udp_inet", 60] = "RECV"; + driver_map["udp_inet", 61] = "LISTEN"; + driver_map["udp_inet", 62] = "BINDX"; +} + +probe process("beam").mark("port-open") +{ + printf("port open pid %s port name %s port %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("port-command") +{ + printf("port command pid %s port %s port name %s command type %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} + +probe process("beam").mark("port-control") +{ + cmd = driver_map[user_string($arg3), $arg4]; + cmd_str = (cmd == "") ? "unknown" : cmd; + printf("port control pid %s port %s port name %s command %d %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), $arg4, cmd_str); +} + +/* port-exit is fired as a result of port_close() or exit signal */ + +probe process("beam").mark("port-exit") +{ + printf("port exit pid %s port %s port name %s reason %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} + +probe process("beam").mark("port-connect") +{ + printf("port connect pid %s port %s port name %s new pid %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} + +probe process("beam").mark("port-busy") +{ + printf("port busy %s\n", user_string($arg1)); +} + +probe process("beam").mark("port-not_busy") +{ + printf("port not busy %s\n", user_string($arg1)); +} + +probe process("beam").mark("aio_pool-add") +{ + printf("async I/O pool add thread %d queue len %d\n", $arg1, $arg2); +} + +probe process("beam").mark("aio_pool-get") +{ + printf("async I/O pool get thread %d queue len %d\n", $arg1, $arg2); +} + +global driver_map;
\ No newline at end of file diff --git a/lib/runtime_tools/examples/process-scheduling.d b/lib/runtime_tools/examples/process-scheduling.d new file mode 100644 index 0000000000..6408d7d5f2 --- /dev/null +++ b/lib/runtime_tools/examples/process-scheduling.d @@ -0,0 +1,35 @@ +/* example usage: dtrace -q -s /path/to/process-scheduling.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::process-scheduled +{ + printf(" Schedule pid %s mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::process-unscheduled +{ + printf("Unschedule pid %s\n", copyinstr(arg0)); +} + +erlang*:::process-hibernate +{ + printf(" Hibernate pid %s resume mfa %s\n", + copyinstr(arg0), copyinstr(arg1)); +} diff --git a/lib/runtime_tools/examples/process-scheduling.systemtap b/lib/runtime_tools/examples/process-scheduling.systemtap new file mode 100644 index 0000000000..4cc3ef555c --- /dev/null +++ b/lib/runtime_tools/examples/process-scheduling.systemtap @@ -0,0 +1,45 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("process-scheduled") +{ + printf(" Schedule pid %s mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("process-unscheduled") +{ + printf("Unschedule pid %s\n", user_string($arg1)); +} + +probe process("beam").mark("process-hibernate") +{ + printf(" Hibernate pid %s resume mfa %s\n", + user_string($arg1), user_string($arg2)); +} diff --git a/lib/runtime_tools/examples/spawn-exit.d b/lib/runtime_tools/examples/spawn-exit.d new file mode 100644 index 0000000000..ae690b819a --- /dev/null +++ b/lib/runtime_tools/examples/spawn-exit.d @@ -0,0 +1,41 @@ +/* example usage: dtrace -q -s /path/to/spawn-exit.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::process-spawn +{ + printf("pid %s mfa %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::process-exit +{ + printf("pid %s reason %s\n", copyinstr(arg0), copyinstr(arg1)); +} + +erlang*:::process-exit_signal +{ + printf("sender %s -> pid %s reason %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2)); +} + +erlang*:::process-exit_signal-remote +{ + printf("sender %s -> node %s pid %s reason %s\n", + copyinstr(arg0), copyinstr(arg1), copyinstr(arg2), copyinstr(arg3)); +} diff --git a/lib/runtime_tools/examples/spawn-exit.systemtap b/lib/runtime_tools/examples/spawn-exit.systemtap new file mode 100644 index 0000000000..4b69e4aea6 --- /dev/null +++ b/lib/runtime_tools/examples/spawn-exit.systemtap @@ -0,0 +1,51 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("process-spawn") +{ + printf("pid %s mfa %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("process-exit") +{ + printf("pid %s reason %s\n", user_string($arg1), user_string($arg2)); +} + +probe process("beam").mark("process-exit_signal") +{ + printf("sender %s -> pid %s reason %s\n", + user_string($arg1), user_string($arg2), user_string($arg3)); +} + +probe process("beam").mark("process-exit_signal-remote") +{ + printf("sender %s -> node %s pid %s reason %s\n", + user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); +} diff --git a/lib/runtime_tools/examples/user-probe-n.d b/lib/runtime_tools/examples/user-probe-n.d new file mode 100644 index 0000000000..06a3e5c9b9 --- /dev/null +++ b/lib/runtime_tools/examples/user-probe-n.d @@ -0,0 +1,44 @@ +/* example usage: dtrace -q -s /path/to/user-probe.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::user_trace-n0 +{ + printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + copyinstr(arg0), + arg1 == NULL ? "" : copyinstr(arg1), + arg2, arg3, arg4, arg5, + arg6 == NULL ? "" : copyinstr(arg6), + arg7 == NULL ? "" : copyinstr(arg7), + arg8 == NULL ? "" : copyinstr(arg8), + arg9 == NULL ? "" : copyinstr(arg9)); +} + +erlang*:::user_trace-n1 +{ + printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + copyinstr(arg0), + arg1 == NULL ? "" : copyinstr(arg1), + arg2, arg3, arg4, arg5, + arg6 == NULL ? "" : copyinstr(arg6), + arg7 == NULL ? "" : copyinstr(arg7), + arg8 == NULL ? "" : copyinstr(arg8), + arg9 == NULL ? "" : copyinstr(arg9)); +} + diff --git a/lib/runtime_tools/examples/user-probe-n.systemtap b/lib/runtime_tools/examples/user-probe-n.systemtap new file mode 100644 index 0000000000..6aa415bb67 --- /dev/null +++ b/lib/runtime_tools/examples/user-probe-n.systemtap @@ -0,0 +1,53 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("user_trace-n0") +{ + printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + user_string($arg1), + $arg2 == NULL ? "" : user_string($arg2), + $arg3, $arg4, $arg5, $arg6, + $arg7 == NULL ? "" : user_string($arg7), + $arg8 == NULL ? "" : user_string($arg8), + $arg9 == NULL ? "" : user_string($arg9), + $arg9 == NULL ? "" : user_string($arg9)); +} + +probe process("beam").mark("user_trace-n1") +{ + printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + user_string($arg1), + $arg2 == NULL ? "" : user_string($arg2), + $arg3, $arg4, $arg5, $arg6, + $arg7 == NULL ? "" : user_string($arg7), + $arg8 == NULL ? "" : user_string($arg8), + $arg9 == NULL ? "" : user_string($arg9), + $arg9 == NULL ? "" : user_string($arg9)); +} diff --git a/lib/runtime_tools/examples/user-probe.d b/lib/runtime_tools/examples/user-probe.d new file mode 100644 index 0000000000..5cb5d61a76 --- /dev/null +++ b/lib/runtime_tools/examples/user-probe.d @@ -0,0 +1,36 @@ +/* example usage: dtrace -q -s /path/to/user-probe.d */ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ + +erlang*:::user_trace-s1 +{ + printf("%s\n", copyinstr(arg0)); +} + +erlang*:::user_trace-i4s4 +{ + printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + copyinstr(arg0), + arg1 == NULL ? "" : copyinstr(arg1), + arg2, arg3, arg4, arg5, + arg6 == NULL ? "" : copyinstr(arg6), + arg7 == NULL ? "" : copyinstr(arg7), + arg8 == NULL ? "" : copyinstr(arg8), + arg9 == NULL ? "" : copyinstr(arg9)); +} diff --git a/lib/runtime_tools/examples/user-probe.systemtap b/lib/runtime_tools/examples/user-probe.systemtap new file mode 100644 index 0000000000..0482235324 --- /dev/null +++ b/lib/runtime_tools/examples/user-probe.systemtap @@ -0,0 +1,46 @@ +/* + * %CopyrightBegin% + * + * Copyright Scott Lystig Fritchie and Andreas Schultz, 2011-2012. All Rights Reserved. + * + * The contents of this file are subject to the Erlang Public License, + * Version 1.1, (the "License"); you may not use this file except in + * compliance with the License. You should have received a copy of the + * Erlang Public License along with this software. If not, it can be + * retrieved online at http://www.erlang.org/. + * + * Software distributed under the License is distributed on an "AS IS" + * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See + * the License for the specific language governing rights and limitations + * under the License. + * + * %CopyrightEnd% + */ +/* + * Note: This file assumes that you're using the non-SMP-enabled Erlang + * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note that other variations of the virtual machine also have + * different names, e.g. the debug build of the SMP-enabled VM + * is "beam.debug.smp". + * + * To use a different virtual machine, replace each instance of + * "beam" with "beam.smp" or the VM name appropriate to your + * environment. + */ + +probe process("beam").mark("user_trace-s1") +{ + printf("%s\n", user_string($arg1)); +} + +probe process("beam").mark("user_trace-i4s4") +{ + printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", + user_string($arg1), + $arg2 == NULL ? "" : user_string($arg2), + $arg3, $arg4, $arg5, $arg6, + $arg7 == NULL ? "" : user_string($arg7), + $arg8 == NULL ? "" : user_string($arg8), + $arg9 == NULL ? "" : user_string($arg9), + $arg9 == NULL ? "" : user_string($arg9)); +} diff --git a/lib/runtime_tools/src/Makefile b/lib/runtime_tools/src/Makefile index 946409b262..810e3e8741 100644 --- a/lib/runtime_tools/src/Makefile +++ b/lib/runtime_tools/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2011. All Rights Reserved. +# Copyright Ericsson AB 1999-2012. 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 @@ -45,6 +45,7 @@ MODULES= \ runtime_tools \ runtime_tools_sup \ dbg \ + dyntrace \ percept_profile \ observer_backend \ ttb_autostart @@ -64,10 +65,15 @@ APPUP_FILE= runtime_tools.appup APPUP_SRC= $(APPUP_FILE).src APPUP_TARGET= $(EBIN)/$(APPUP_FILE) +EXAMPLE_FILES= \ + ../examples/* # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -I../include +ERL_COMPILE_FLAGS += \ + -I../include \ + -I ../../et/include \ + -I ../../../libraries/et/include # ---------------------------------------------------- # Targets @@ -93,12 +99,14 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLE_FILES) "$(RELSYSDIR)/examples" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/runtime_tools/src/dyntrace.erl b/lib/runtime_tools/src/dyntrace.erl new file mode 100644 index 0000000000..b4579fd5ce --- /dev/null +++ b/lib/runtime_tools/src/dyntrace.erl @@ -0,0 +1,352 @@ +-module(dyntrace). + +%%% @doc The Dynamic tracing interface module +%%% +%%% This Dynamic tracing interface module, with the corresponding NIFs, should +%%% work on any operating system platform where user-space DTrace/Systemtap +%%% (and in the future LttNG UST) probes are supported. +%%% +%%% It is recommended that you use the `dyntrace:p()' function to add +%%% Dynamic trace probes to your Erlang code. This function can accept up to +%%% four integer arguments and four string arguments; the integer +%%% argument(s) must come before any string argument. +%%% +%%% If using DTrace, enable the dynamic trace probe using the 'dtrace' +%%% command, for example: +%%% +%%% dtrace -s /your/path/to/lib/runtime_tools-1.8.7/examples/user-probe.d +%%% +%%% Then, back at the Erlang shell, try this example: +%%% ``` +%%% 1> dyntrace:put_tag("GGOOOAAALL!!!!!"). +%%% true +%%% +%%% 2> dyntrace:p(7, 8, 9, "one", "four"). +%%% true +%%% ''' +%%% +%%% Output from the example D script `user-probe.d' looks like: +%%% ``` +%%% <0.34.0> GGOOOAAALL!!!!! 7 8 9 0 'one' 'four' '' '' +%%% ''' +%%% +%%% If the expected type of variable is not present, e.g. integer when +%%% integer() is expected, or an I/O list when iolist() is expected, +%%% then the driver will ignore the user's input and use a default +%%% value of 0 or NULL, respectively. + +-export([available/0, + user_trace_s1/1, % TODO: unify with pid & tag args like user_trace_i4s4 + p/0, p/1, p/2, p/3, p/4, p/5, p/6, p/7, p/8, + pn/1, pn/2, pn/3, pn/4, pn/5, pn/6, pn/7, pn/8, pn/9]). +-export([put_tag/1, get_tag/0, get_tag_data/0, spread_tag/1, restore_tag/1]). + +-export([user_trace_i4s4/9]). % Know what you're doing! +-on_load(on_load/0). + +-type probe_arg() :: integer() | iolist(). +-type int_p_arg() :: integer() | iolist() | undef. +-type n_probe_label() :: 0..1023. + +%% The *_maybe() types use atom() instead of a stricter 'undef' +%% because user_trace_i4s4/9 is exposed to the outside world, and +%% because the driver will allow any atom to be used as a "not +%% present" indication, we'll allow any atom in the types. + +-type integer_maybe() :: integer() | atom(). +-type iolist_maybe() :: iolist() | atom(). + +on_load() -> + PrivDir = code:priv_dir(runtime_tools), + LibName = "dyntrace", + Lib = filename:join([PrivDir, "lib", LibName]), + Status = case erlang:load_nif(Lib, 0) of + ok -> ok; + {error, {load_failed, _}}=Error1 -> + ArchLibDir = + filename:join([PrivDir, "lib", + erlang:system_info(system_architecture)]), + Candidate = + filelib:wildcard(filename:join([ArchLibDir,LibName ++ "*" ])), + case Candidate of + [] -> Error1; + _ -> + ArchLib = filename:join([ArchLibDir, LibName]), + erlang:load_nif(ArchLib, 0) + end; + Error1 -> Error1 + end, + case Status of + ok -> ok; + {error, {E, Str}} -> + case erlang:system_info(dynamic_trace) of + none -> + ok; + _ -> + error_logger:error_msg("Unable to load dyntrace library. Failed with error:~n +\"~p, ~s\"~n" + "Dynamic tracing is enabled but the driver is not built correctly~n",[ + E,Str]), + Status + end + end. + +%%% +%%% NIF placeholders +%%% + +-spec available() -> true | false. + +available() -> + erlang:nif_error(nif_not_loaded). + +-spec user_trace_s1(iolist()) -> true | false | error | badarg. + +user_trace_s1(_Message) -> + erlang:nif_error(nif_not_loaded). + +-spec user_trace_i4s4(iolist(), + integer_maybe(), integer_maybe(), + integer_maybe(), integer_maybe(), + iolist_maybe(), iolist_maybe(), + iolist_maybe(), iolist_maybe()) -> + true | false | error | badarg. + +user_trace_i4s4(_, _, _, _, _, _, _, _, _) -> + erlang:nif_error(nif_not_loaded). + +-spec user_trace_n(n_probe_label(), iolist(), + integer_maybe(), integer_maybe(), + integer_maybe(), integer_maybe(), + iolist_maybe(), iolist_maybe(), + iolist_maybe(), iolist_maybe()) -> + true | false | error | badarg. + +user_trace_n(_, _, _, _, _, _, _, _, _, _) -> + erlang:nif_error(nif_not_loaded). + +%%% +%%% Erlang support functions +%%% + +-spec p() -> true | false | error | badarg. + +p() -> + user_trace_int(undef, undef, undef, undef, undef, undef, undef, undef). + +-spec p(probe_arg()) -> true | false | error | badarg. + +p(I1) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, undef, undef, undef, undef); +p(S1) -> + user_trace_int(undef, undef, undef, undef, S1, undef, undef, undef). + +-spec p(probe_arg(), probe_arg()) -> true | false | error | badarg. + +p(I1, I2) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, undef, undef, undef, undef); +p(I1, S1) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, undef, undef, undef); +p(S1, S2) -> + user_trace_int(undef, undef, undef, undef, S1, S2, undef, undef). + +-spec p(probe_arg(), probe_arg(), probe_arg()) -> true | false | error | badarg. + +p(I1, I2, I3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, undef, undef, undef, undef); +p(I1, I2, S1) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, undef, undef, undef); +p(I1, S1, S2) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, S2, undef, undef); +p(S1, S2, S3) -> + user_trace_int(undef, undef, undef, undef, S1, S2, S3, undef). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, undef, undef, undef, undef); +p(I1, I2, I3, S1) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, undef, undef, undef); +p(I1, I2, S1, S2) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, S2, undef, undef); +p(I1, S1, S2, S3) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, S2, S3, undef); +p(S1, S2, S3, S4) -> + user_trace_int(undef, undef, undef, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, undef, undef, undef); +p(I1, I2, I3, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, S2, undef, undef); +p(I1, I2, S1, S2, S3) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, S2, S3, undef); +p(I1, S1, S2, S3, S4) when is_integer(I1) -> + user_trace_int(I1, undef, undef, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, S2, undef, undef); +p(I1, I2, I3, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, S2, S3, undef); +p(I1, I2, S1, S2, S3, S4) when is_integer(I1), is_integer(I2) -> + user_trace_int(I1, I2, undef, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, S2, S3, undef); +p(I1, I2, I3, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_int(I1, I2, I3, undef, S1, S2, S3, S4). + +-spec p(probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +p(I1, I2, I3, I4, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_int(I1, I2, I3, I4, S1, S2, S3, S4). + +-spec user_trace_int(int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg(), + int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg()) -> + true | false | error | badarg. + +user_trace_int(I1, I2, I3, I4, S1, S2, S3, S4) -> + UTag = get_tag(), + try + user_trace_i4s4(UTag, I1, I2, I3, I4, S1, S2, S3, S4) + catch + error:nif_not_loaded -> + false + end. + +-spec pn(n_probe_label()) -> true | false | error | badarg. + +pn(ProbeLabel) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, undef, undef, undef, undef). + +-spec pn(n_probe_label(), probe_arg()) -> true | false | error | badarg. + +pn(ProbeLabel, I1) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, undef, undef, undef, undef); +pn(ProbeLabel, S1) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, undef, undef, undef). + +-spec pn(n_probe_label(), probe_arg(), probe_arg()) -> true | false | error | badarg. + +pn(ProbeLabel, I1, I2) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, undef, undef, undef, undef); +pn(ProbeLabel, I1, S1) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, undef, undef, undef); +pn(ProbeLabel, S1, S2) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, S2, undef, undef). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg()) -> true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, undef, undef, undef, undef); +pn(ProbeLabel, I1, I2, S1) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, undef, undef, undef); +pn(ProbeLabel, I1, S1, S2) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, S2, undef, undef); +pn(ProbeLabel, S1, S2, S3) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, S2, S3, undef). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, undef, undef, undef, undef); +pn(ProbeLabel, I1, I2, I3, S1) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, undef, undef, undef); +pn(ProbeLabel, I1, I2, S1, S2) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, S2, undef, undef); +pn(ProbeLabel, I1, S1, S2, S3) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, S2, S3, undef); +pn(ProbeLabel, S1, S2, S3, S4) -> + user_trace_n_int(ProbeLabel, undef, undef, undef, undef, S1, S2, S3, S4). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4, S1) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, undef, undef, undef); +pn(ProbeLabel, I1, I2, I3, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, S2, undef, undef); +pn(ProbeLabel, I1, I2, S1, S2, S3) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, S2, S3, undef); +pn(ProbeLabel, I1, S1, S2, S3, S4) when is_integer(I1) -> + user_trace_n_int(ProbeLabel, I1, undef, undef, undef, S1, S2, S3, S4). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4, S1, S2) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, undef, undef); +pn(ProbeLabel, I1, I2, I3, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, S2, S3, undef); +pn(ProbeLabel, I1, I2, S1, S2, S3, S4) when is_integer(I1), is_integer(I2) -> + user_trace_n_int(ProbeLabel, I1, I2, undef, undef, S1, S2, S3, S4). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4, S1, S2, S3) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, undef); +pn(ProbeLabel, I1, I2, I3, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, undef, S1, S2, S3, S4). + +-spec pn(n_probe_label(), probe_arg(), probe_arg(), probe_arg(), probe_arg(), + probe_arg(), probe_arg(), probe_arg(), probe_arg()) -> + true | false | error | badarg. + +pn(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, S4) when is_integer(I1), is_integer(I2), is_integer(I3), is_integer(I4) -> + user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, S4). + +-spec user_trace_n_int(n_probe_label(), + int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg(), + int_p_arg(), int_p_arg(), int_p_arg(), int_p_arg()) -> + true | false | error | badarg. + +user_trace_n_int(ProbeLabel, I1, I2, I3, I4, S1, S2, S3, S4) -> + UTag = get_tag(), + try + user_trace_n(ProbeLabel, UTag, I1, I2, I3, I4, S1, S2, S3, S4) + catch + error:nif_not_loaded -> + false + end. + +-spec put_tag(undefined | iodata()) -> binary() | undefined. +put_tag(Data) -> + erlang:dt_put_tag(unicode:characters_to_binary(Data)). + +-spec get_tag() -> binary() | undefined. +get_tag() -> + erlang:dt_get_tag(). + +-spec get_tag_data() -> binary() | undefined. +%% Gets tag if set, otherwise the spread tag data from last incoming message +get_tag_data() -> + erlang:dt_get_tag_data(). + +-spec spread_tag(boolean()) -> true | {non_neg_integer(), binary() | []}. +%% Makes the tag behave as a sequential trace token, will spread with +%% messages to be picked up by someone using get_tag_data +spread_tag(B) -> + erlang:dt_spread_tag(B). + +-spec restore_tag(true | {non_neg_integer(), binary() | []}) -> true. +restore_tag(T) -> + erlang:dt_restore_tag(T). diff --git a/lib/runtime_tools/src/runtime_tools.app.src b/lib/runtime_tools/src/runtime_tools.app.src index 76fd998530..1152f7259d 100644 --- a/lib/runtime_tools/src/runtime_tools.app.src +++ b/lib/runtime_tools/src/runtime_tools.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,7 +23,7 @@ inviso_rt,inviso_rt_lib,inviso_rt_meta, inviso_as_lib,inviso_autostart,inviso_autostart_server, runtime_tools,runtime_tools_sup,erts_alloc_config, - ttb_autostart]}, + ttb_autostart,dyntrace]}, {registered, [runtime_tools_sup,inviso_rt,inviso_rt_meta]}, {applications, [kernel, stdlib]}, % {env, [{inviso_autostart_mod,your_own_autostart_module}]}, diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile index cfaf420d65..4979b9c7b1 100644 --- a/lib/runtime_tools/test/Makefile +++ b/lib/runtime_tools/test/Makefile @@ -3,6 +3,7 @@ include $(ERL_TOP)/make/target.mk include $(ERL_TOP)/make/$(TARGET)/otp.mk MODULES = \ + dyntrace_SUITE \ runtime_tools_SUITE \ inviso_testmodule1_foo \ inviso_SUITE \ @@ -56,10 +57,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) runtime_tools.spec runtime_tools.cover $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) runtime_tools.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) runtime_tools.spec runtime_tools.cover $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) runtime_tools.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/runtime_tools/test/dyntrace_SUITE.erl b/lib/runtime_tools/test/dyntrace_SUITE.erl new file mode 100644 index 0000000000..0e4f369ed0 --- /dev/null +++ b/lib/runtime_tools/test/dyntrace_SUITE.erl @@ -0,0 +1,224 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(dyntrace_SUITE). +-include_lib("test_server/include/test_server.hrl"). + +-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, + init_per_group/2,end_per_group/2]). +-export([init_per_testcase/2, end_per_testcase/2]). + +%% Test cases +-export([smoke/1,process/1]). + +%% Default timetrap timeout (set in init_per_testcase) +-define(default_timeout, ?t:minutes(1)). + +init_per_testcase(_Case, Config) -> + Dog = test_server:timetrap(?default_timeout), + [{watchdog,Dog}|Config]. + +end_per_testcase(_Case, Config) -> + Dog = ?config(watchdog, Config), + ?t:timetrap_cancel(Dog), + ok. + +suite() -> [{ct_hooks,[ts_install_cth]}]. + +all() -> + case erlang:system_info(dynamic_trace) of + none -> + {skip,"No dynamic trace in this run-time system"}; + dtrace -> + [{group,smoke}]; + systemtap -> + {skip,"SystemTap tests currently not supported"} + end. + +groups() -> + [{smoke,[sequence],[smoke,{group,rest}]}, + {rest,[], + [process]}]. + +init_per_suite(Config) -> + N = "beam" ++ + case erlang:system_info(debug_compiled) of + false -> ""; + true -> ".debug" + end ++ + case erlang:system_info(smp_support) of + false -> ""; + true -> ".smp" + end, + [{emu_name,N}|Config]. + +end_per_suite(_Config) -> + ok. + +init_per_group(_GroupName, Config) -> + Config. + +end_per_group(_GroupName, Config) -> + Config. + +smoke(Config) -> + Emu = ?t:lookup_config(emu_name, Config), + BinEmu = list_to_binary(Emu), + case erlang:system_info(dynamic_trace) of + dtrace -> + Probes = os:cmd("sudo /usr/sbin/dtrace -l -m" ++ Emu), + io:put_chars(Probes), + [_|Lines] = re:split(Probes, "\n", [trim]), + [{_,_} = binary:match(L, BinEmu) || L <- Lines], + ok + end, + + %% Test that the framework for running dtrace/systemtap works + %% by executing an empty script. + {ok,[]} = dyntrace("", fun() -> ok end), + ok. + + +process(_Config) -> + Script = [{probe,"process-spawn"}, + {action,[{printf,["spawn %s %s\n",{arg,0},{arg,1}]}]}, + {probe,"process-scheduled"}, + {action,[{printf,["in %s\n",{arg,0}]}]}, + {probe,"process-unscheduled"}, + {action,[{printf,["out %s\n",{arg,0}]}]}, + {probe,"process-hibernate"}, + {action,[{printf,["hibernate %s %s\n",{arg,0},{arg,1}]}]}, + {probe,"process-exit"}, + {action,[{printf,["exit %s %s\n",{arg,0},{arg,1}]}]} + ], + F = fun() -> + {Pid,Ref} = spawn_monitor(fun my_process/0), + Pid ! hibernate, + Pid ! quit, + receive + {'DOWN',Ref,process,Pid,{terminated,Pid}} -> + ok + end, + Pid + end, + {Pid,Output0} = dyntrace(Script, F), + Output1 = [termify_line(L) || L <- Output0], + PidStr = pid_to_list(Pid), + Output = [L || L <- Output1, element(2, L) =:= PidStr], + Reason = "{terminated,"++PidStr++"}", + io:format("~p\n", [Output]), + [{spawn,PidStr,"erlang:apply/2"}, + {in,PidStr}, + {hibernate,PidStr,"erlang:apply/2"}, + {out,PidStr}, + {in,PidStr}, + {exit,PidStr,Reason}, + {out,PidStr}] = Output, + ok. + +termify_line(L) -> + [H|T] = re:split(L, " ", [{return,list}]), + list_to_tuple([list_to_atom(H)|T]). + +my_process() -> + receive + hibernate -> + erlang:hibernate(erlang, apply, [fun my_process/0,[]]); + quit -> + exit({terminated,self()}) + end. + +%%% +%%% Utility functions. +%%% + +dyntrace(Script0, Action) -> + Sudo = os:find_executable(sudo), + {Termination,Pid} = termination_probe(), + Script1 = Script0++Termination, + Script = translate_script(Script1), + io:format("~s\n", [Script]), + SrcFile = "test-dyntrace.d", + ok = file:write_file(SrcFile, Script), + Args = ["/usr/sbin/dtrace", "-q","-s",SrcFile], + Port = open_port({spawn_executable,Sudo}, + [{args,Args},stream,in,stderr_to_stdout,eof]), + receive + {Port,{data,Sofar}} -> + Res = Action(), + Pid ! quit, + {Res,get_data(Port, Sofar)} + end. + +get_data(Port, Sofar) -> + receive + {Port,{data,Bytes}} -> + get_data(Port, [Sofar|Bytes]); + {Port,eof} -> + port_close(Port), + [$\n|T] = lists:flatten(Sofar), + re:split(T, "\n", [{return,list},trim]) + end. + +termination_probe() -> + Pid = spawn(fun() -> + receive + _ -> + exit(done) + end + end), + S = [{'BEGIN',[{printf,["\n"]}]}, + {probe,"process-exit"}, + {pred,{'==',{arg,0},Pid}}, + {action,[{exit,[0]}]}], + {S,Pid}. + +translate_script(Script) -> + [dtrace_op(Op) || Op <- Script]. + +dtrace_op({probe,Function}) -> + OsPid = os:getpid(), + ["erlang",OsPid,":::",Function,$\n]; +dtrace_op({pred,Pred}) -> + ["/",dtrace_op(Pred),"/\n"]; +dtrace_op({action,List}) -> + ["{ ",action_list(List)," }\n\n"]; +dtrace_op({'BEGIN',List}) -> + ["BEGIN { ",action_list(List)," }\n\n"]; +dtrace_op({'==',Op1,Op2}) -> + [dtrace_op(Op1)," == ",dtrace_op(Op2)]; +dtrace_op({arg,N}) -> + ["copyinstr(arg",integer_to_list(N),")"]; +dtrace_op({Func,List}) when is_atom(Func), is_list(List) -> + [atom_to_list(Func),"(",comma_sep_ops(List),")"]; +dtrace_op(Pid) when is_pid(Pid) -> + ["\"",pid_to_list(Pid),"\""]; +dtrace_op(Str) when is_integer(hd(Str)) -> + io_lib:format("~p", [Str]); +dtrace_op(Int) when is_integer(Int) -> + integer_to_list(Int). + +comma_sep_ops([A,B|T]) -> + [dtrace_op(A),","|comma_sep_ops([B|T])]; +comma_sep_ops([H]) -> + dtrace_op(H); +comma_sep_ops([]) -> []. + +action_list([H|T]) -> + [dtrace_op(H),";"|action_list(T)]; +action_list([]) -> []. diff --git a/lib/runtime_tools/test/inviso_SUITE.erl b/lib/runtime_tools/test/inviso_SUITE.erl index 33626ffd9e..c64c40b945 100644 --- a/lib/runtime_tools/test/inviso_SUITE.erl +++ b/lib/runtime_tools/test/inviso_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 diff --git a/lib/runtime_tools/vsn.mk b/lib/runtime_tools/vsn.mk index 3fbc1b3379..c9b5cc1a41 100644 --- a/lib/runtime_tools/vsn.mk +++ b/lib/runtime_tools/vsn.mk @@ -1 +1 @@ -RUNTIME_TOOLS_VSN = 1.8.7 +RUNTIME_TOOLS_VSN = 1.8.8 diff --git a/lib/sasl/doc/src/Makefile b/lib/sasl/doc/src/Makefile index eb880ccb78..b0ec671adc 100644 --- a/lib/sasl/doc/src/Makefile +++ b/lib/sasl/doc/src/Makefile @@ -116,18 +116,18 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man4 - $(INSTALL_DATA) $(MAN4_FILES) $(RELEASE_PATH)/man/man4 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DATA) $(MAN4_FILES) "$(RELEASE_PATH)/man/man4" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/sasl/doc/src/appup.xml b/lib/sasl/doc/src/appup.xml index 770b7c2492..bacfaa76ef 100644 --- a/lib/sasl/doc/src/appup.xml +++ b/lib/sasl/doc/src/appup.xml @@ -4,7 +4,7 @@ <fileref> <header> <copyright> - <year>1997</year><year>2011</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -56,12 +56,20 @@ the application.</p> </item> <item> - <p><c>UpFromVsn = string()</c> is an earlier version of - the application to upgrade from.</p> + <p><c>UpFromVsn = string() | binary()</c> is an earlier + version of the application to upgrade from. If it is a + string, it will be interpreted as a specific version + number. If it is a binary, it will be interpreted as a + regular expression which can match multiple version + numbers.</p> </item> <item> - <p><c>DownToVsn = string()</c> is an earlier version of - the application to downgrade to.</p> + <p><c>DownToVsn = string() | binary()</c> is an earlier + version of the application to downgrade to. If it is a + string, it will be interpreted as a specific version + number. If it is a binary, it will be interpreted as a + regular expression which can match multiple version + numbers.</p> </item> <item> <p><c>Instructions</c> is a list of <em>release upgrade instructions</em>, see below. It is recommended to use @@ -70,6 +78,12 @@ creating the <c>relup</c> file.</p> </item> </list> + <p>In order to avoid duplication of upgrade instructions it is + allowed to use regular expressions to specify the <c>UpFromVsn</c> + and <c>DownToVsn</c>. To be considered a regular expression, the + version identifier must be specified as a binary, e.g.</p> + <code type="none"><<"2\\.1\\.[0-9]+">></code> + <p>will match all versions <c>2.1.x</c>, where x is any number.</p> </section> <section> diff --git a/lib/sasl/doc/src/notes.xml b/lib/sasl/doc/src/notes.xml index 2f22a8ec43..e4a2c98db7 100644 --- a/lib/sasl/doc/src/notes.xml +++ b/lib/sasl/doc/src/notes.xml @@ -30,6 +30,102 @@ </header> <p>This document describes the changes made to the SASL application.</p> +<section><title>SASL 2.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If sys.config existed, but was not readable or parseable, + this would not be detected until after the upgrade and at + the next node restart. The possibility for this to happen + is now reduced by adding a check to systools:make_tar + which fails the creation of the tar file if sys.config or + relup does not have reasonable contents. Note that there + are no detailed checks, only parsing and erlang term + format check.</p> + <p> + Own Id: OTP-9539</p> + </item> + <item> + <p> + systools:make_script would allow {kernel,Vsn,load} in the + .rel file, causing a .boot file which only loaded kernel + and did not start it. This has been corrected. Only start + type 'permanent', which is the default, is now allowed + for kernel and stdlib.</p> + <p> + Own Id: OTP-9652</p> + </item> + <item> + <p> + release_handler:remove_release/1 now handles symlinked + files properly</p> + <p> + Own Id: OTP-9864</p> + </item> + <item> + <p> + If stdlib was stated with a start type different from + <c>permanent</c> in a .rel file, systools would + incorrectly say that sasl had faulty start type. This has + been corrected.</p> + <p> + Own Id: OTP-9888</p> + </item> + <item> + <p> + Sasl documentation earlier said that the InclApps + parameters in a .rel file defaults to the empty list. + This is not correct. It defaults to the same value as + specified in the .app file. This has been corrected.</p> + <p> + Own Id: OTP-9980</p> + </item> + <item> + <p> + Applications that are listed in + <c>{applications,Apps}</c> in the .app file were not + sorted correctly by <c>systools:make_script/1,2</c>. They + got the reverse order of how they were listed in the .app + file. This is corrected so they are now sorted + (internally between each other) in the same order as they + are listed in the .rel file (i.e. the order they are + listed in the .app file does no longer matter).</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9984</p> + </item> + <item> + <p> + Documentation of .appup files now also states that + <c>UpFromVsn</c> and <c>DownToVsn</c> can be specified as + regular expressions in order to avoid duplicated upgrade + instructions.</p> + <p> + Own Id: OTP-10001</p> + </item> + <item> + <p> + Reltool would sometimes generate a .app file containing + <c>{start_phases,undefined}</c>, which would cause an + exception in systools at parse time. This has been + corrected so reltool now omits the <c>start_phases</c> + entry if the value is <c>undefined</c>. (Thanks to Juan + Jose Comellas)</p> + <p> + In order to align with reltool, sasl will also omit + <c>start_phases</c> entries with value <c>undefined</c> + in .script files.</p> + <p> + Own Id: OTP-10003</p> + </item> + </list> + </section> + +</section> + <section><title>SASL 2.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/sasl/doc/src/rel.xml b/lib/sasl/doc/src/rel.xml index 470adf3c03..68ef90330f 100644 --- a/lib/sasl/doc/src/rel.xml +++ b/lib/sasl/doc/src/rel.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1997</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> @@ -88,7 +88,7 @@ <p>The list must be a subset of the included applications specified in the application resource file (<c>Application.app</c>) and overrides this value. Defaults - to the empty list.</p> + to the same value as in the application resource file.</p> </item> </list> <note> diff --git a/lib/sasl/examples/src/Makefile b/lib/sasl/examples/src/Makefile index 9cf0d4c25d..47c8626205 100644 --- a/lib/sasl/examples/src/Makefile +++ b/lib/sasl/examples/src/Makefile @@ -63,10 +63,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/src - $(INSTALL_DIR) $(RELSYSDIR)/examples/ebin - (cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -)) - chmod -R ug+w $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/ebin" + (cd ..; tar cf - src ebin | (cd "$(RELSYSDIR)/examples"; tar xf -)) + chmod -R ug+w "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/sasl/src/Makefile b/lib/sasl/src/Makefile index 9a5d1e42d9..cae8146ebc 100644 --- a/lib/sasl/src/Makefile +++ b/lib/sasl/src/Makefile @@ -91,10 +91,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/sasl/src/release_handler.erl b/lib/sasl/src/release_handler.erl index 8d2b9c35d3..5efd932c92 100644 --- a/lib/sasl/src/release_handler.erl +++ b/lib/sasl/src/release_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/sasl/src/systools.hrl b/lib/sasl/src/systools.hrl index 9a3e98221c..da531dbee5 100644 --- a/lib/sasl/src/systools.hrl +++ b/lib/sasl/src/systools.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -60,7 +60,8 @@ %% integer() | infinity. mod = [], %% [] | {Mod, StartArgs}, Mod= atom(), %% StartArgs = list(). - start_phases = [], %% [] | {Phase, PhaseArgs}, Phase = atom(), + start_phases, %% [{Phase, PhaseArgs}] | undefined, + %% Phase = atom(), %% PhaseArgs = list(). dir = "" %% The directory where the .app file was %% found (internal use). diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index 12ba2a5476..61e660e918 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -375,7 +375,7 @@ get_release1(File, Path, ModTestP, Machine) -> {ok, Release, Warnings1} = read_release(File, Path), {ok, Appls0} = collect_applications(Release, Path), {ok, Appls1} = check_applications(Appls0), - {ok, Appls2} = sort_included_applications(Appls1, Release), % OTP-4121 + {ok, Appls2} = sort_used_and_incl_appls(Appls1, Release), % OTP-4121, OTP-9984 {ok, Warnings2} = check_modules(Appls2, Path, ModTestP, Machine), {ok, Appls} = sort_appls(Appls2), {ok, Release, Appls, Warnings1 ++ Warnings2}. @@ -482,7 +482,7 @@ mandatory_applications([_|Apps],Kernel,Stdlib,Sasl) -> mandatory_applications([],Type,_,_) when Type=/=permanent -> error_mandatory_application(kernel,Type); mandatory_applications([],_,Type,_) when Type=/=permanent -> - error_mandatory_application(sasl,Type); + error_mandatory_application(stdlib,Type); mandatory_applications([],_,_,undefined) -> {ok, [{warning,missing_sasl}]}; mandatory_applications([],_,_,_) -> @@ -842,34 +842,45 @@ undefined_applications(Appls) -> filter(fun(X) -> not member(X, Defined) end, Uses). %%______________________________________________________________________ -%% sort_included_applications(Applications, Release) -> Applications +%% sort_used_and_incl_appls(Applications, Release) -> Applications %% Applications = [{{Name,Vsn},#application}] %% Release = #release{} %% -%% Check that included applications are given in the same order as in -%% the release resource file (.rel). Otherwise load instructions in -%% the boot script, and consequently release upgrade instructions in -%% relup, may end up in the wrong order. +%% OTP-4121, OTP-9984 +%% Check that used and included applications are given in the same +%% order as in the release resource file (.rel). Otherwise load and +%% start instructions in the boot script, and consequently release +%% upgrade instructions in relup, may end up in the wrong order. -sort_included_applications(Applications, Release) when is_tuple(Release) -> +sort_used_and_incl_appls(Applications, Release) when is_tuple(Release) -> {ok, - sort_included_applications(Applications, Release#release.applications)}; - -sort_included_applications([{Tuple,Appl}|Appls], OrderedAppls) -> - case Appl#application.includes of - Incls when length(Incls)>1 -> - IndexedIncls = find_pos(Incls, OrderedAppls), - SortedIndexedIncls = lists:keysort(1, IndexedIncls), - Incls2 = lists:map(fun({_Index,Name}) -> Name end, - SortedIndexedIncls), - Appl2 = Appl#application{includes=Incls2}, - [{Tuple,Appl2}|sort_included_applications(Appls, OrderedAppls)]; - _Incls -> - [{Tuple,Appl}|sort_included_applications(Appls, OrderedAppls)] - end; -sort_included_applications([], _OrderedAppls) -> + sort_used_and_incl_appls(Applications, Release#release.applications)}; + +sort_used_and_incl_appls([{Tuple,Appl}|Appls], OrderedAppls) -> + Incls2 = + case Appl#application.includes of + Incls when length(Incls)>1 -> + sort_appl_list(Incls, OrderedAppls); + Incls -> + Incls + end, + Uses2 = + case Appl#application.uses of + Uses when length(Uses)>1 -> + sort_appl_list(Uses, OrderedAppls); + Uses -> + Uses + end, + Appl2 = Appl#application{includes=Incls2, uses=Uses2}, + [{Tuple,Appl2}|sort_used_and_incl_appls(Appls, OrderedAppls)]; +sort_used_and_incl_appls([], _OrderedAppls) -> []. +sort_appl_list(List, Order) -> + IndexedList = find_pos(List, Order), + SortedIndexedList = lists:keysort(1, IndexedList), + lists:map(fun({_Index,Name}) -> Name end, SortedIndexedList). + find_pos([Name|Incs], OrderedAppls) -> [find_pos(1, Name, OrderedAppls)|find_pos(Incs, OrderedAppls)]; find_pos([], _OrderedAppls) -> @@ -1253,7 +1264,8 @@ sort_appls(Appls) -> {ok, sort_appls(Appls, [], [], [])}. sort_appls([{N, A}|T], Missing, Circular, Visited) -> {Name,_Vsn} = N, - {Uses, T1, NotFnd1} = find_all(Name, A#application.uses, T, Visited, [], []), + {Uses, T1, NotFnd1} = find_all(Name, lists:reverse(A#application.uses), + T, Visited, [], []), {Incs, T2, NotFnd2} = find_all(Name, lists:reverse(A#application.includes), T1, Visited, [], []), Missing1 = NotFnd1 ++ NotFnd2 ++ Missing, @@ -1456,15 +1468,18 @@ pack_app(#application{name=Name,vsn=V,id=Id,description=D,modules=M, {applications, App}, {included_applications, Incs}, {env, Env}, - {start_phases, SF}, {maxT, MaxT}, {maxP, MaxP} | - behave(Mod)]}. - + behave([{start_phases,SF},{mod,Mod}])]}. + +behave([{mod,[]}|T]) -> + behave(T); +behave([{start_phases,undefined}|T]) -> + behave(T); +behave([H|T]) -> + [H|behave(T)]; behave([]) -> - []; -behave(Mod) -> - [{mod, Mod}]. + []. %%______________________________________________________________________ %% mandatory modules; this modules must be loaded before processes diff --git a/lib/sasl/test/Makefile b/lib/sasl/test/Makefile index 91a8c42484..bb3f82f411 100644 --- a/lib/sasl/test/Makefile +++ b/lib/sasl/test/Makefile @@ -85,10 +85,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) sasl.spec sasl.cover $(EMAKEFILE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cfh - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) sasl.spec sasl.cover $(EMAKEFILE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cfh - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/sasl/test/installer.erl b/lib/sasl/test/installer.erl index 6942ec21ea..634218e3fb 100644 --- a/lib/sasl/test/installer.erl +++ b/lib/sasl/test/installer.erl @@ -876,7 +876,9 @@ trace_disallowed_calls(Node) -> MasterProc = self(), rpc:call(Node,dbg,tracer,[process,{fun(T,_) -> MasterProc ! T end,[]}]), rpc:call(Node,dbg,p,[all,call]), - rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]). + rpc:call(Node,dbg,tp,[file,[{'_',[],[{message,{caller}}]}]]), + %% File:native_name_encoding/0 is a BIF and OK to use + rpc:call(Node,dbg,ctp,[file,native_name_encoding,0]). check_disallowed_calls(TestNode,Line) -> receive diff --git a/lib/sasl/test/release_handler_SUITE.erl b/lib/sasl/test/release_handler_SUITE.erl index 467d1226b3..a8e3cc3d88 100644 --- a/lib/sasl/test/release_handler_SUITE.erl +++ b/lib/sasl/test/release_handler_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -59,11 +59,11 @@ win32_cases() -> cases() -> [otp_2740, otp_2760, otp_5761, otp_9402, otp_9417, otp_9395_check_old_code, otp_9395_check_and_purge, - otp_9395_update_many_mods, otp_9395_rm_many_mods, otp_9864, + otp_9395_update_many_mods, otp_9395_rm_many_mods, instructions, eval_appup, eval_appup_with_restart, supervisor_which_children_timeout, release_handler_which_releases, install_release_syntax_check, - upgrade_supervisor, upgrade_supervisor_fail]. + upgrade_supervisor, upgrade_supervisor_fail, otp_9864]. groups() -> [{release,[], diff --git a/lib/sasl/test/systools_SUITE.erl b/lib/sasl/test/systools_SUITE.erl index 4cf7364d74..878d582e6b 100644 --- a/lib/sasl/test/systools_SUITE.erl +++ b/lib/sasl/test/systools_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011. All Rights Reserved. +%% Copyright Ericsson AB 2012. 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 @@ -45,7 +45,7 @@ abnormal_script/1, src_tests_script/1, crazy_script/1, included_script/1, included_override_script/1, included_fail_script/1, included_bug_script/1, exref_script/1, - otp_3065_circular_dependenies/1]). + otp_3065_circular_dependenies/1, included_and_used_sort_script/1]). -export([tar_options/1, normal_tar/1, no_mod_vsn_tar/1, system_files_tar/1, system_files_tar/2, invalid_system_files_tar/1, invalid_system_files_tar/2, variable_tar/1, @@ -80,7 +80,7 @@ groups() -> no_sasl_script, src_tests_script, crazy_script, included_script, included_override_script, included_fail_script, included_bug_script, exref_script, - otp_3065_circular_dependenies]}, + otp_3065_circular_dependenies, included_and_used_sort_script]}, {tar, [], [tar_options, normal_tar, no_mod_vsn_tar, system_files_tar, invalid_system_files_tar, variable_tar, @@ -500,6 +500,22 @@ crazy_script(Config) when is_list(Config) -> {error, _, {mandatory_app,kernel,load}} = systools:make_script(LatestName3, [silent,{path,P}]), + %% Run with .rel file with non-permanent stdlib + {LatestDir4, LatestName4} = create_script(latest_stdlib_start_type, Config), + ok = file:set_cwd(LatestDir4), + + error = systools:make_script(LatestName4), + {error, _, {mandatory_app,stdlib,load}} = + systools:make_script(LatestName4, [silent,{path,P}]), + + %% Run with .rel file lacking stdlib + {LatestDir5, LatestName5} = create_script(latest_no_stdlib, Config), + ok = file:set_cwd(LatestDir5), + + error = systools:make_script(LatestName5), + {error, _, {missing_mandatory_app,stdlib}} = + systools:make_script(LatestName5, [silent,{path,P}]), + ok = file:set_cwd(OldDir), ok. @@ -600,6 +616,24 @@ otp_3065_circular_dependenies(Config) when is_list(Config) -> ok = file:set_cwd(OldDir), ok. +%% Test sorting of included applications and used applications +included_and_used_sort_script(Config) when is_list(Config) -> + {ok, OldDir} = file:get_cwd(), + {LatestDir1, LatestName1} = create_include_files(sort_apps, Config), + ok = file:set_cwd(LatestDir1), + ok = systools:make_script(LatestName1), + ok = check_include_script(LatestName1, + [t20,t19,t18,t17,t16,t15,t14],[t20,t19,t18,t14]), + + {LatestDir2, LatestName2} = create_include_files(sort_apps_rev, Config), + ok = file:set_cwd(LatestDir2), + ok = systools:make_script(LatestName2), + ok = check_include_script(LatestName2, + [t18,t19,t20,t15,t16,t17,t14],[t18,t19,t20,t14]), + + ok = file:set_cwd(OldDir), + ok. + %% make_script: Check that make_script exref option works. exref_script(Config) when is_list(Config) -> @@ -2026,8 +2060,14 @@ create_script(latest_nokernel,Config) -> Apps = [{db,"2.1"},{fe,"3.1"}], do_create_script(latest_nokernel,Config,"4.4",Apps); create_script(latest_kernel_start_type,Config) -> - Apps = [{kernel,"1.0",load},{db,"2.1"},{fe,"3.1"}], + Apps = [{kernel,"1.0",load},{stdlib,"1.0"},{db,"2.1"},{fe,"3.1"}], do_create_script(latest_kernel_start_type,Config,"4.4",Apps); +create_script(latest_stdlib_start_type,Config) -> + Apps = [{kernel,"1.0"},{stdlib,"1.0",load},{db,"2.1"},{fe,"3.1"}], + do_create_script(latest_stdlib_start_type,Config,"4.4",Apps); +create_script(latest_no_stdlib,Config) -> + Apps = [{kernel,"1.0"},{db,"2.1"},{fe,"3.1"}], + do_create_script(latest_no_stdlib,Config,"4.4",Apps); create_script(latest_app_start_type1,Config) -> Apps = core_apps(current), do_create_script(latest_app_start_type1,Config,current,Apps); @@ -2301,8 +2341,53 @@ create_include_files(otp_3065_circular_dependenies, Config) -> " {chAts, \"1.0\"}, {aa12, \"1.0\"}, \n" " {chTraffic, \"1.0\"}]}.\n", file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(sort_apps, Config) -> + PrivDir = ?privdir, + Name = fname(PrivDir, sort_apps), + create_sort_apps(PrivDir), + + Apps = application_controller:which_applications(), + {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t14, \"1.0\"}, \n" + " {t20, \"1.0\"}, \n" + " {t19, \"1.0\"}, \n" + " {t18, \"1.0\"}, \n" + " {t17, \"1.0\"}, \n" + " {t16, \"1.0\"}, \n" + " {t15, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), + {filename:dirname(Name), filename:basename(Name)}; + +create_include_files(sort_apps_rev, Config) -> + PrivDir = ?privdir, + Name = fname(PrivDir, sort_apps_rev), + create_sort_apps(PrivDir), + + Apps = application_controller:which_applications(), + {value,{_,_,KernelVer}} = lists:keysearch(kernel,1,Apps), + {value,{_,_,StdlibVer}} = lists:keysearch(stdlib,1,Apps), + + Rel = "{release, {\"test\",\"R1A\"}, {erts, \"45\"},\n" + " [{kernel, \"" ++ KernelVer ++ "\"}, {stdlib, \"" + ++ StdlibVer ++ "\"},\n" + " {t14, \"1.0\"}, \n" + " {t18, \"1.0\"}, \n" + " {t19, \"1.0\"}, \n" + " {t20, \"1.0\"}, \n" + " {t15, \"1.0\"}, \n" + " {t16, \"1.0\"}, \n" + " {t17, \"1.0\"}]}.\n", + file:write_file(Name ++ ".rel", list_to_binary(Rel)), {filename:dirname(Name), filename:basename(Name)}. + create_apps(Dir) -> T1 = "{application, t1,\n" " [{vsn, \"1.0\"},\n" @@ -2451,6 +2536,70 @@ create_apps_3065(Dir) -> " {registered, []}]}.\n", file:write_file(fname(Dir, 'aa12.app'), list_to_binary(T13)). +create_sort_apps(Dir) -> + T14 = "{application, t14,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, [t18,t20,t19]},\n" + " {included_applications, [t15,t17,t16]},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't14.app'), list_to_binary(T14)), + + T15 = "{application, t15,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't15.app'), list_to_binary(T15)), + + T16 = "{application, t16,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't16.app'), list_to_binary(T16)), + + T17 = "{application, t17,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't17.app'), list_to_binary(T17)), + + T18 = "{application, t18,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't18.app'), list_to_binary(T18)), + + T19 = "{application, t19,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't19.app'), list_to_binary(T19)), + + T20 = "{application, t20,\n" + " [{vsn, \"1.0\"},\n" + " {description, \"test\"},\n" + " {modules, []},\n" + " {applications, []},\n" + " {included_applications, []},\n" + " {registered, []}]}.\n", + file:write_file(fname(Dir, 't20.app'), list_to_binary(T20)). + fname(N) -> filename:join(N). diff --git a/lib/sasl/vsn.mk b/lib/sasl/vsn.mk index 23694f1399..6e9e2c9ff8 100644 --- a/lib/sasl/vsn.mk +++ b/lib/sasl/vsn.mk @@ -1 +1 @@ -SASL_VSN = 2.2 +SASL_VSN = 2.2.1 diff --git a/lib/snmp/doc/src/Makefile b/lib/snmp/doc/src/Makefile index 8645886590..6c58ae994a 100644 --- a/lib/snmp/doc/src/Makefile +++ b/lib/snmp/doc/src/Makefile @@ -170,20 +170,20 @@ $(MAN1DIR)/snmpc.1: snmpc_cmd.xml include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man1 - $(INSTALL_DATA) $(MAN1DIR)/* $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6DIR)/* $(RELEASE_PATH)/man/man6 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man7 - $(INSTALL_DATA) $(MAN7DIR)/* $(RELEASE_PATH)/man/man7 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN1DIR)/* "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6DIR)/* "$(RELEASE_PATH)/man/man6" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man7" + $(INSTALL_DATA) $(MAN7DIR)/* "$(RELEASE_PATH)/man/man7" release_spec: diff --git a/lib/snmp/doc/src/notes.xml b/lib/snmp/doc/src/notes.xml index 704ff0a20f..442837d57d 100644 --- a/lib/snmp/doc/src/notes.xml +++ b/lib/snmp/doc/src/notes.xml @@ -34,6 +34,180 @@ <section> + <title>SNMP Development Toolkit 4.22.1</title> + <p>Version 4.22.1 supports code replacement in runtime from/to + version 4.22, 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and + 4.21. </p> + + <section> + <title>Improvements and new features</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>[agent] Sematic fixes to SNMP-USER-BASED-SM-MIB. + The semantics allow the <c>usmUserAuthKeyChange</c> and + <c>usmUserPrivKeyChange</c> objects to be written to in the + same set requests that also creates and clones the user. + This was not possible beforehand, causing test tools checking + semantic SNMPv3 behaviour to fail on a lot of test cases. </p> + <p>Furthermore, once the user has been cloned by writing to an + instance of <c>usmUserCloneFrom</c>, further set-operations to + the same object will not return an error, but be no-ops. + Especially, it must be avoided to copy security parameters + again (possibly even from a different user). </p> + <p>Stefan Zegenhagen</p> + <p>Own Id: OTP-10166</p> + </item> + + <item> + <p>[agent] Errors in <c>vacmAccessTable</c> RowStatus handling. + There are problems with the handling of vacmAccessTableStatus + that cause some SNMP test suites to report errors. + Most notably, erroneous set operations frequently cause "genErr" + errors to be returned. These "genErr" errors are usually caused + by badmatch exceptions coming from + <c>{ok, Row} = snmpa_vacm:get_row(RowIndex)</c> + if the row does not exist. </p> + <p>The semantics of the RowStatus handling in that table has + been adjusted to be compliant with the RowStatus + textual description of SNPMv2-TC MIB. </p> + <p>Stefan Zegenhagen</p> + <p>Own Id: OTP-10164</p> + </item> + </list> + + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>[agent] Fix walk over vacmAccessTable. + Fix the get_next implementation of vacmAccessTable to + return all table entries. </p> + <p>The get_next implementation of vacmAccessTable did not return + all available table data. Instead, it only returned the first + column for each row, and all columns for the last row available. </p> + <p>Stefan Zegenhagen</p> + <p>Own Id: OTP-10165</p> + </item> + + <item> + <p>[manager] + <seealso marker="snmpm#log_to_io">snmpm:log_to_io/6</seealso> + did not use the LogName argument. </p> + <p>Own Id: OTP-10066</p> + </item> + + <item> + <p>Incorrect TimeTicks decode. Also bad handling of + invalid encode (value outside of value range) for both + <c>TimeTicks</c> and <c>Unsigned32</c>. </p> + <p>Own Id: OTP-10132</p> + </item> + + </list> + + </section> + + <section> + <title>Incompatibilities</title> + <p>-</p> + </section> + + </section> <!-- 4.22.1 --> + + + <section> + <title>SNMP Development Toolkit 4.22</title> + <p>Version 4.22 supports code replacement in runtime from/to + version 4.21.7 4.21.6 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1 and 4.21. </p> + + <section> + <title>Improvements and new features</title> +<!-- + <p>-</p> +--> + + <list type="bulleted"> + <item> + <p>[compiler] The table information the MIB compiler provides with + augmented tables has been extended with <c>nbr_of_cols</c>, + <c>first_accessible</c> and <c>not_accessible</c>. </p> + <p>Own Id: OTP-9969</p> + </item> + + <item> + <p>Added the <c>log_to_io</c> audit-trail-log converter function + to the api modules of both the + <seealso marker="snmpm#log_to_io">manager</seealso> + and + <seealso marker="snmpa#log_to_io">agent</seealso>. </p> + <p>Own Id: OTP-9940</p> + </item> + + <item> + <p>[manager] Introduced a new transport module, + <c>snmpm_net_if_mt</c>, + which handles all incomming and outgoing + traffic in newly created processes. The message/request is + processed and then the process exits. </p> + <p>Own Id: OTP-9876</p> + </item> + + <item> + <p>[agent] Documenting previously existing but undocumented function, + <seealso marker="snmp_generic#get_table_info">snmp_generic:get_table_info/2</seealso>. </p> + <p>Own Id: OTP-9942</p> + </item> + + <item> + <p>[agent] Improve error handling while reading agent config files. + Some files contain mandatory information and is therefor themself + mandatory. </p> + <p>Own Id: OTP-9943</p> + </item> + </list> + + </section> + + <section> + <title>Fixed Bugs and Malfunctions</title> + <p>-</p> + + <!-- + <list type="bulleted"> + <item> + <p>[agent] Simultaneous + <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso> + calls can interfere. + The master agent did not check if a backup was already in + progress when a backup request was accepted. </p> + <p>Own Id: OTP-9884</p> + <p>Aux Id: Seq 11995</p> + </item> + + </list> + --> + + </section> + + <section> + <title>Incompatibilities</title> + <p>-</p> + </section> + + </section> <!-- 4.22 --> + + + <section> <title>SNMP Development Toolkit 4.21.7</title> <p>Version 4.21.7 supports code replacement in runtime from/to version 4.21.6, 4.21.5, 4.21.4, 4.21.3, 4.21.2, 4.21.1, 4.21, 4.20.1 and @@ -47,14 +221,14 @@ <list type="bulleted"> <item> <p>[agent] DoS attack using GET-BULK with large value of - MaxRepetitions. - A preventive method has been implementing by simply - limit the number of varbinds that can be included in - a Get-BULK response message. This is specified by the - new config option, - <seealso marker="snmp_app#agent_gb_max_vbs">gb_max_vbs</seealso>. - </p> - <p>Own Id: OTP-9700</p> + MaxRepetitions. + A preventive method has been implementing by simply + limit the number of varbinds that can be included in + a Get-BULK response message. This is specified by the + new config option, + <seealso marker="snmp_app#agent_gb_max_vbs">gb_max_vbs</seealso>. + </p> + <p>Own Id: OTP-9700</p> </item> </list> @@ -63,7 +237,7 @@ </section> <section> - <title>Reported Fixed Bugs and Malfunctions</title> + <title>Fixed Bugs and Malfunctions</title> <!-- <p>-</p> --> @@ -71,10 +245,10 @@ <list type="bulleted"> <item> <p>[agent] Simultaneous - <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso> - calls can interfere. - The master agent did not check if a backup was already in - progress when a backup request was accepted. </p> + <seealso marker="snmpa#backup">snmpa:backup/1,2</seealso> + calls can interfere. + The master agent did not check if a backup was already in + progress when a backup request was accepted. </p> <p>Own Id: OTP-9884</p> <p>Aux Id: Seq 11995</p> </item> @@ -106,14 +280,14 @@ <list type="bulleted"> <item> <p>[agent] DoS attack using GET-BULK with large value of - MaxRepetitions. - A preventive method has been implementing by simply - limit the number of varbinds that can be included in - a Get-BULK response message. This is specified by the - new config option, - <seealso marker="snmp_app#agent_gb_max_vbs">gb_max_vbs</seealso>. - </p> - <p>Own Id: OTP-9700</p> + MaxRepetitions. + A preventive method has been implementing by simply + limit the number of varbinds that can be included in + a Get-BULK response message. This is specified by the + new config option, + <seealso marker="snmp_app#agent_gb_max_vbs">gb_max_vbs</seealso>. + </p> + <p>Own Id: OTP-9700</p> </item> </list> @@ -121,7 +295,7 @@ </section> <section> - <title>Reported Fixed Bugs and Malfunctions</title> + <title>Fixed Bugs and Malfunctions</title> <!-- <p>-</p> --> @@ -129,12 +303,12 @@ <list type="bulleted"> <item> <p>[agent] Mib server cache gclimit update function incorrectly calls - age update function. - The gclimit update function, - <seealso marker="snmpa#update_mibs_cache_gclimit">update_mibs_cache_gclimit/1</seealso>, - <em>incorrectly</em> called the age update function, - <seealso marker="snmpa#update_mibs_cache_age">update_mibs_cache_age/2</seealso>. </p> - <p>Johan Claesson</p> + age update function. + The gclimit update function, + <seealso marker="snmpa#update_mibs_cache_gclimit">update_mibs_cache_gclimit/1</seealso>, + <em>incorrectly</em> called the age update function, + <seealso marker="snmpa#update_mibs_cache_age">update_mibs_cache_age/2</seealso>. </p> + <p>Johan Claesson</p> <p>Own Id: OTP-9868</p> </item> diff --git a/lib/snmp/doc/src/snmp.xml b/lib/snmp/doc/src/snmp.xml index af0833f005..3e6610891f 100644 --- a/lib/snmp/doc/src/snmp.xml +++ b/lib/snmp/doc/src/snmp.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -391,6 +391,30 @@ version of the protocol data unit. There is a new line between <c>Vsn</c> and <c>PDU</c>.</p> + <marker id="log_to_io"></marker> + </desc> + </func> + + <func> + <name>log_to_io(LogDir, Mibs, LogName, LogFile) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {error, Reason}</name> + <fsummary>Convert an Audit Trail Log to text format</fsummary> + <type> + <v>LogDir = string()</v> + <v>Mibs = [MibName]</v> + <v>MibName = string()</v> + <v>LogName = string()</v> + <v>LogFile = string()</v> + <v>Start = Stop = null | datetime() | {local_time,datetime()} | {universal_time,datetime()} </v> + <v>Reason = term()</v> + </type> + <desc> + <p>Converts an Audit Trail Log to a readable format and + prints it on stdio. See + <seealso marker="snmp#log_to_txt">log_to_txt</seealso> + above for more info.</p> + <marker id="change_log_size"></marker> </desc> </func> diff --git a/lib/snmp/doc/src/snmp_app.xml b/lib/snmp/doc/src/snmp_app.xml index f6abe783b3..62dfa515d1 100644 --- a/lib/snmp/doc/src/snmp_app.xml +++ b/lib/snmp/doc/src/snmp_app.xml @@ -4,7 +4,7 @@ <appref> <header> <copyright> - <year>1997</year><year>2011</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -35,8 +35,8 @@ <appsummary>The SNMP Application</appsummary> <description> <p>This chapter describes the <c>snmp</c> - application in OTP. The SNMP application provides the following - services:</p> + application in OTP. The SNMP application provides the following + services:</p> <list type="bulleted"> <item> <p>a multilingual extensible SNMP agent</p> @@ -589,16 +589,16 @@ {no_reuse, no_reuse()} | {filter, manager_net_if_filter_options()} </c></p> <p>These options are actually specific to the used module. - The ones shown here are applicable to the default - <c>manager_net_if_module()</c>.</p> + The ones shown here are applicable to the default + <c>manager_net_if_module()</c>.</p> <p>For defaults see the options in <c>manager_net_if_option()</c>.</p> </item> <marker id="manager_ni_module"></marker> <tag><c><![CDATA[manager_net_if_module() = atom() <optional>]]></c></tag> <item> - <p>Module which handles the network interface part for the - SNMP manager. Must implement the + <p>The module which handles the network interface part for the + SNMP manager. It must implement the <seealso marker="snmpm_network_interface">snmpm_network_interface</seealso> behaviour.</p> <p>Default is <c>snmpm_net_if</c>.</p> </item> diff --git a/lib/snmp/doc/src/snmp_config.xml b/lib/snmp/doc/src/snmp_config.xml index 0a49b7a62e..eec53162a1 100644 --- a/lib/snmp/doc/src/snmp_config.xml +++ b/lib/snmp/doc/src/snmp_config.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2011</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -586,18 +586,18 @@ {no_reuse, no_reuse()} | {filter, manager_net_if_filter_options()}</c></p> <p>These options are actually specific to the used module. - The ones shown here are applicable to the default - <c>manager_net_if_module()</c>.</p> + The ones shown here are applicable to the default + <c>manager_net_if_module()</c>. </p> <p>For defaults see the options in <c>manager_net_if_option()</c>.</p> </item> <marker id="manager_ni_module"></marker> <tag><c><![CDATA[manager_net_if_module() = atom() <optional>]]></c></tag> <item> - <p>Module which handles the network interface part for the - SNMP manager. Must implement the - <seealso marker="snmpm_network_interface">snmpm_network_interface</seealso> behaviour.</p> - <p>Default is <c>snmpm_net_if</c>.</p> + <p>The module which handles the network interface part for the + SNMP manager. It must implement the + <seealso marker="snmpm_network_interface">snmpm_network_interface</seealso> behaviour. </p> + <p>Default is <c>snmpm_net_if</c>. </p> </item> <marker id="manager_ni_filter_opts"></marker> @@ -963,7 +963,8 @@ Manager snmp config: 7b. User name? hobbes 7c. Security name? [hobbes] 7d. Authentication protocol (no/sha/md5)? [no] sha -7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] +7e Authentication [sha] key (length 0 or 20)? [""] [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16, \ + 17,18,19,20] 7d. Priv protocol (no/des/aes)? [no] des 7f Priv [des] key (length 0 or 16)? [""] 10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25 7. Configure an usm user handled by this manager (y/n)? [y] n diff --git a/lib/snmp/doc/src/snmp_generic.xml b/lib/snmp/doc/src/snmp_generic.xml index 77f3cefaa2..79a22323d9 100644 --- a/lib/snmp/doc/src/snmp_generic.xml +++ b/lib/snmp/doc/src/snmp_generic.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -34,15 +34,16 @@ <module>snmp_generic</module> <modulesummary>Generic Functions for Implementing SNMP Objects in a Database</modulesummary> <description> - <p>The module <c>snmp_generic</c> contains generic functions for implementing tables - (and variables) using the SNMP built-in database or Mnesia. These - default functions are used if no instrumentation function is - provided for a managed object in a MIB. Sometimes, it might be - necessary to customize the behaviour of the default functions. For - example, in some situations a trap should be sent if a row is - deleted or modified, or some hardware is to be informed, when - information is changed. - </p> + <marker id="description"></marker> + <p>The module <c>snmp_generic</c> contains generic functions for + implementing tables (and variables) using the SNMP built-in database + or Mnesia. These default functions are used if no instrumentation + function is provided for a managed object in a MIB. Sometimes, + it might be necessary to customize the behaviour of the default + functions. For example, in some situations a trap should be sent + if a row is deleted or modified, or some hardware is to be informed, + when information is changed. </p> + <p>The overall structure is shown in the following figure:</p> <pre> +---------------+ @@ -93,6 +94,7 @@ </description> <section> + <marker id="data_types"></marker> <title>DATA TYPES</title> <p>In the functions defined below, the following types are used:</p> <code type="none"> @@ -118,7 +120,10 @@ value() = term() case of a <c>set</c> operation. </p> </item> </taglist> + + <marker id="get_status_col2"></marker> </section> + <funcs> <func> <name>get_status_col(Name, Cols)</name> @@ -136,8 +141,11 @@ value() = term() <p>This function can be used in instrumentation functions for <c>is_set_ok</c>, <c>undo</c> or <c>set</c> to check if the status column of a table is modified.</p> + + <marker id="get_index_types"></marker> </desc> </func> + <func> <name>get_index_types(Name)</name> <fsummary>Get the index types of <c>Name</c></fsummary> @@ -147,9 +155,36 @@ value() = term() <desc> <p>Gets the index types of <c>Name</c></p> <p>This function can be used in instrumentation functions to - retrieve the index types part of the table info.</p> + retrieve the index types part of the table info.</p> + + <marker id="get_table_info"></marker> </desc> </func> + + <func> + <name>get_table_info(Name, Item) -> table_info_result()</name> + <fsummary>Get table info item of MIB table <c>Name</c></fsummary> + <type> + <v>Name = name()</v> + <v>Item = table_item() | all</v> + <v>table_item() = nbr_of_cols | defvals | status_col | not_accessible | + index_types | first_accessible | first_own_index</v> + <v>table_info_result() = Value | [{table_item(), Value}]</v> + <v>Value = term()</v> + </type> + <desc> + <p>Get a specific table info item or, if <c>Item</c> has the + value <c>all</c>, a two tuple list (property list) is instead + returned with all the items and their respctive values of the + given table. </p> + + <p>This function can be used in instrumentation functions to + retrieve a given part of the table info.</p> + + <marker id="table_func"></marker> + </desc> + </func> + <func> <name>table_func(Op1, NameDb)</name> <name>table_func(Op2, RowIndex, Cols, NameDb) -> Ret</name> @@ -190,8 +225,11 @@ value() = term() <p>The function returns according to the specification of an instrumentation function. </p> + + <marker id="table_get_elements"></marker> </desc> </func> + <func> <name>table_get_elements(NameDb, RowIndex, Cols) -> Values</name> <fsummary>Get elements in a table row</fsummary> @@ -204,8 +242,11 @@ value() = term() <desc> <p>Returns a list with values for all columns in <c>Cols</c>. If a column is undefined, its value is <c>noinit</c>.</p> + + <marker id="table_next"></marker> </desc> </func> + <func> <name>table_next(NameDb, RestOid) -> RowIndex | endOfTable</name> <fsummary>Find the next row in the table</fsummary> @@ -217,8 +258,11 @@ value() = term() <desc> <p>Finds the indices of the next row in the table. <c>RestOid</c> does not have to specify an existing row.</p> + + <marker id="table_row_exists"></marker> </desc> </func> + <func> <name>table_row_exists(NameDb, RowIndex) -> bool()</name> <fsummary>Check if a row in a table exists</fsummary> @@ -228,8 +272,11 @@ value() = term() </type> <desc> <p>Checks if a row in a table exists.</p> + + <marker id="table_set_elements"></marker> </desc> </func> + <func> <name>table_set_elements(NameDb, RowIndex, Cols) -> bool()</name> <fsummary>Set elements in a table row</fsummary> @@ -246,8 +293,11 @@ value() = term() <c>mnesia:write</c> to store the values. This means that this function must be called from within a transaction (<c>mnesia:transaction/1</c> or <c>mnesia:dirty/1</c>).</p> + + <marker id="variable_func"></marker> </desc> </func> + <func> <name>variable_func(Op1, NameDb)</name> <name>variable_func(Op2, Val, NameDb) -> Ret</name> @@ -268,8 +318,11 @@ value() = term() the database. </p> <p>The function returns according to the specification of an instrumentation function. </p> + + <marker id="variable_get"></marker> </desc> </func> + <func> <name>variable_get(NameDb) -> {value, Value} | undefined</name> <fsummary>Get the value of a variable</fsummary> @@ -279,8 +332,11 @@ value() = term() </type> <desc> <p>Gets the value of a variable.</p> + + <marker id="variable_set"></marker> </desc> </func> + <func> <name>variable_set(NameDb, NewVal) -> true | false</name> <fsummary>Set a value for a variable</fsummary> @@ -299,6 +355,7 @@ value() = term() </funcs> <section> + <marker id="example"></marker> <title>Example</title> <p>The following example shows an implementation of a table which is stored in Mnesia, but with some checks performed at set-request diff --git a/lib/snmp/doc/src/snmp_manager_netif.xml b/lib/snmp/doc/src/snmp_manager_netif.xml index 2738ca76c1..169e20d10b 100644 --- a/lib/snmp/doc/src/snmp_manager_netif.xml +++ b/lib/snmp/doc/src/snmp_manager_netif.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE chapter SYSTEM "chapter.dtd"> <chapter> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -35,31 +35,34 @@ <image file="snmp_manager_netif_1.gif"> <icaption>The Purpose of Manager Net if</icaption> </image> + <p>The Network Interface (Net if) process delivers SNMP PDUs to the - manager server, and receives SNMP PDUs from the manager server. - The most common behaviour of a Net if process is that is receives - request PDU from the manager server, encodes the PDU into bytes - and transmits the bytes onto the network to an agent. When the - reply from the agent is received by the Net if process, which it - decodes into an SNMP PDU, which it sends to the manager server. - </p> + manager server, and receives SNMP PDUs from the manager server. + The most common behaviour of a Net if process is that is receives + request PDU from the manager server, encodes the PDU into bytes + and transmits the bytes onto the network to an agent. When the + reply from the agent is received by the Net if process, which it + decodes into an SNMP PDU, which it sends to the manager server. </p> + <p>However, that simple behaviour can be modified in numerous - ways. For example, the Net if process can apply some kind of - encrypting/decrypting scheme on the bytes. - </p> - <p>It is also possible to write your own Net if process. The default - Net if process is implemented in the module <c>snmpm_net_if</c> and - it uses UDP as the transport protocol. - </p> - <p>This section describes how to write a Net if process. - </p> + ways. For example, the Net if process can apply some kind of + encrypting/decrypting scheme on the bytes. </p> + + <p>The snmp application provides two different modules, + <c>snmpm_net_if</c> (the default) and <c>snmpm_net_if_mt</c>, + both uses the UDP as the transport protocol. The difference + between the two modules is that the latter is "multi-threaded", + i.e. for each message/request a new process is created that + process the message/request and then exits. </p> + + <p>It is also possible to write your own Net if process, + this section describes how to write a Net if processdo that.</p> <section> <marker id="mandatory_functions"></marker> <title>Mandatory Functions</title> <p>A Net if process must implement the SNMP manager - <seealso marker="snmpm_network_interface">network interface behaviour</seealso>. - </p> + <seealso marker="snmpm_network_interface">network interface behaviour</seealso>. </p> </section> <section> diff --git a/lib/snmp/doc/src/snmpa.xml b/lib/snmp/doc/src/snmpa.xml index 2322af28cc..86fde03205 100644 --- a/lib/snmp/doc/src/snmpa.xml +++ b/lib/snmp/doc/src/snmpa.xml @@ -501,6 +501,7 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} </func> <func> + <name>log_to_txt(LogDir)</name> <name>log_to_txt(LogDir, Mibs)</name> <name>log_to_txt(LogDir, Mibs, OutFile) -> ok | {error, Reason}</name> <name>log_to_txt(LogDir, Mibs, OutFile, LogName) -> ok | {error, Reason}</name> @@ -528,6 +529,37 @@ notification_delivery_info() = #snmpa_notification_delivery_info{} See <seealso marker="snmp#log_to_txt">snmp:log_to_txt</seealso> for more info.</p> + <marker id="log_to_io"></marker> + </desc> + </func> + + <func> + <name>log_to_io(LogDir) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName, LogFile) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {error, Reason}</name> + <fsummary>Convert an Audit Trail Log to text format</fsummary> + <type> + <v>LogDir = string()</v> + <v>Mibs = [MibName]</v> + <v>MibName = string()</v> + <v>LogName = string()</v> + <v>LogFile = string()</v> + <v>Start = Stop = null | datetime() | {local_time,datetime()} | {universal_time,datetime()} </v> + <v>Reason = disk_log_open_error() | file_open_error() | term()</v> + <v>disk_log_open_error() = {LogName, term()}</v> + <v>file_open_error() = {OutFile, term()}</v> + </type> + <desc> + <p>Converts an Audit Trail Log to a readable format and + prints it on stdio. + <c>LogName</c> defaults to "snmpa_log". + <c>LogFile</c> defaults to "snmpa.log". + See <seealso marker="snmp#log_to_io">snmp:log_to_io</seealso> + for more info.</p> + <marker id="change_log_size"></marker> </desc> </func> diff --git a/lib/snmp/doc/src/snmpa_network_interface_filter.xml b/lib/snmp/doc/src/snmpa_network_interface_filter.xml index 10419517dd..84953c5270 100644 --- a/lib/snmp/doc/src/snmpa_network_interface_filter.xml +++ b/lib/snmp/doc/src/snmpa_network_interface_filter.xml @@ -1,4 +1,4 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> @@ -84,7 +84,9 @@ <title>DATA TYPES</title> <code type="none"> port() = integer() > 0 -pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report +pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | + 'set-request' | trap | 'get-bulk-request' | 'inform-request' | + report </code> <marker id="accept_recv"></marker> </section> diff --git a/lib/snmp/doc/src/snmpm.xml b/lib/snmp/doc/src/snmpm.xml index c36a1b2a24..8ab3be8e18 100644 --- a/lib/snmp/doc/src/snmpm.xml +++ b/lib/snmp/doc/src/snmpm.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2004</year><year>2011</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -173,16 +173,16 @@ sec_level() = noAuthNoPriv | authNoPriv | authPriv </type> <desc> <p>Register the manager entity (=user) responsible for specific - agent(s). </p> + agent(s). </p> <p><c>Module</c> is the callback module (snmpm_user behaviour) which - will be called whenever something happens (detected - agent, incoming reply or incoming trap/notification). - Note that this could have already been done as a - consequence of the node config. (see users.conf).</p> + will be called whenever something happens (detected + agent, incoming reply or incoming trap/notification). + Note that this could have already been done as a + consequence of the node config. (see users.conf).</p> - <p>The argument <c>DefaultAgentConfig</c> is used as default values when - this user register agents.</p> + <p>The argument <c>DefaultAgentConfig</c> is used as default + values when this user register agents.</p> <p>The type of <c>Val</c> depends on <c>Item</c>: </p> <code type="none"><![CDATA[ @@ -1237,6 +1237,38 @@ priv_key = [integer()] (length is 16 if priv = usmDESPrivProtocol | usmAesCfb1 See <seealso marker="snmp#log_to_txt">snmp:log_to_txt</seealso> for more info.</p> + <marker id="log_to_io"></marker> + </desc> + </func> + + <func> + <name>log_to_io(LogDir) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName, LogFile) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> ok | {error, Reason}</name> + <name>log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> ok | {error, Reason}</name> + <fsummary>Convert an Audit Trail Log to text format</fsummary> + <type> + <v>LogDir = string()</v> + <v>Mibs = [MibName]</v> + <v>MibName = string()</v> + <v>LogName = string()</v> + <v>LogFile = string()</v> + <v>Start = Stop = null | datetime() | {local_time,datetime()} | {universal_time,datetime()} </v> + <v>Reason = disk_log_open_error() | file_open_error() | term()</v> + <v>disk_log_open_error() = {LogName, term()}</v> + <v>file_open_error() = {OutFile, term()}</v> + </type> + <desc> + <p>Converts an Audit Trail Log to a readable format and + prints it on stdio. + <c>LogName</c> defaults to "snmpm_log". + <c>LogFile</c> defaults to "snmpm.log". + See <seealso marker="snmp#log_to_io">snmp:log_to_io</seealso> + for more info.</p> + <marker id="change_log_size"></marker> </desc> </func> diff --git a/lib/snmp/doc/src/snmpm_network_interface_filter.xml b/lib/snmp/doc/src/snmpm_network_interface_filter.xml index ea1e183848..4dc133dd71 100644 --- a/lib/snmp/doc/src/snmpm_network_interface_filter.xml +++ b/lib/snmp/doc/src/snmpm_network_interface_filter.xml @@ -1,10 +1,10 @@ -<?xml version="1.0" encoding="latin1" ?> +<?xml version="1.0" encoding="iso-8859-1" ?> <!DOCTYPE erlref SYSTEM "erlref.dtd"> <erlref> <header> <copyright> - <year>2007</year><year>2009</year> + <year>2007</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -72,16 +72,19 @@ </list> <p>Note that the network interface filter is something which is used by the network interface implementation provided by the application - (<c>snmpm_net_if</c>). The default filter accepts all messages.</p> + (<c>snmpm_net_if</c> and <c>snmpm_net_if_mt</c>). + The default filter accepts all messages.</p> <p>A network interface filter can e.g. be used during testing or for load - regulation. </p> + regulation. </p> </description> <section> <title>DATA TYPES</title> <code type="none"> port() = integer() > 0 -pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | 'set-request' | trap | 'get-bulk-request' | 'inform-request' | report | trappdu +pdu_type() = 'get-request' | 'get-next-request' | 'get-response' | + 'set-request' | trap | 'get-bulk-request' | 'inform-request' | + report | trappdu </code> <marker id="accept_recv"></marker> </section> diff --git a/lib/snmp/examples/ex1/Makefile b/lib/snmp/examples/ex1/Makefile index 1a55ba0470..996b686e0e 100644 --- a/lib/snmp/examples/ex1/Makefile +++ b/lib/snmp/examples/ex1/Makefile @@ -46,7 +46,7 @@ ERL_COMPILE_FLAGS += -I../include \ # Release directory specification # ---------------------------------------------------- RELSYSDIR = $(RELEASE_PATH)/lib/snmp-$(VSN) -EXAMPLE_RELSYSDIR = $(RELSYSDIR)/examples +EXAMPLE_RELSYSDIR = "$(RELSYSDIR)/examples" EX1_RELSYSDIR = $(EXAMPLE_RELSYSDIR)/ex1 # ---------------------------------------------------- diff --git a/lib/snmp/examples/ex2/Makefile b/lib/snmp/examples/ex2/Makefile index 7fbd7f530b..090b9274d4 100644 --- a/lib/snmp/examples/ex2/Makefile +++ b/lib/snmp/examples/ex2/Makefile @@ -47,7 +47,7 @@ ERL_COMPILE_FLAGS += -I../include \ # Release directory specification # ---------------------------------------------------- RELSYSDIR = $(RELEASE_PATH)/lib/snmp-$(VSN) -EXAMPLE_RELSYSDIR = $(RELSYSDIR)/examples +EXAMPLE_RELSYSDIR = "$(RELSYSDIR)/examples" EX2_RELSYSDIR = $(EXAMPLE_RELSYSDIR)/ex2 # ---------------------------------------------------- diff --git a/lib/snmp/examples/ex2/snmp_ex2_manager.erl b/lib/snmp/examples/ex2/snmp_ex2_manager.erl index ff873327bc..1b247d713d 100644 --- a/lib/snmp/examples/ex2/snmp_ex2_manager.erl +++ b/lib/snmp/examples/ex2/snmp_ex2_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2010. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -39,7 +39,7 @@ %% Manager callback API: -export([handle_error/3, - handle_agent/4, + handle_agent/5, handle_pdu/4, handle_trap/3, handle_inform/3, @@ -265,16 +265,17 @@ handle_snmp_callback(handle_error, {ReqId, Reason}) -> "~n Reason: ~p" "~n", [ReqId, Reason]), ok; -handle_snmp_callback(handle_agent, {Addr, Port, SnmpInfo}) -> +handle_snmp_callback(handle_agent, {Addr, Port, Type, SnmpInfo}) -> {ES, EI, VBs} = SnmpInfo, io:format("*** UNKNOWN AGENT ***" "~n Address: ~p" "~n Port: ~p" + "~n Type: ~p" "~n SNMP Info: " "~n Error Status: ~w" "~n Error Index: ~w" "~n Varbinds: ~p" - "~n", [Addr, Port, ES, EI, VBs]), + "~n", [Addr, Port, Type, ES, EI, VBs]), ok; handle_snmp_callback(handle_pdu, {TargetName, ReqId, SnmpResponse}) -> {ES, EI, VBs} = SnmpResponse, @@ -382,8 +383,8 @@ handle_error(ReqId, Reason, Server) when is_pid(Server) -> ignore. -handle_agent(Addr, Port, SnmpInfo, Server) when is_pid(Server) -> - report_callback(Server, handle_agent, {Addr, Port, SnmpInfo}), +handle_agent(Addr, Port, Type, SnmpInfo, Server) when is_pid(Server) -> + report_callback(Server, handle_agent, {Addr, Port, Type, SnmpInfo}), ignore. diff --git a/lib/snmp/include/snmp_types.hrl b/lib/snmp/include/snmp_types.hrl index 4adb24361c..399379a49b 100644 --- a/lib/snmp/include/snmp_types.hrl +++ b/lib/snmp/include/snmp_types.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -78,23 +78,36 @@ %%----------------------------------------------------------------- %% TableInfo - stored in snmp_symbolic_store for use by the -%% generic table functions. +%% generic table functions. For an ordinary table, the +%% types will be the following: %% nbr_of_cols is an integer +%% pos_integer() %% defvals is a list of {Col, Defval}, ordered by column %% number +%% [{Col :: integer(), Defval :: term()}] %% status_col is an integer +%% pos_integer() %% not_accessible a sorted list of columns (> first_accessible) %% that are 'not-accessible' -%% indextypes is a list of #asn1_type for the index-columns, -%% ordered by column number -%% first_accessible is an integer, the first non-accessible -%% column +%% [pos_integer()] +%% index_types is a list of #asn1_type for the index-columns, +%% ordered by column number or an "augment"-tuple +%% [asn1_type()] +%% first_accessible is an integer, the first accessible column +%% pos_integer() %% first_own_index is an integer. 0 if there is no such index for %% this table. %% This is not the same as the last integer in the oid! %% Example: If a table has one own index (oid.1), one %% column (oid.2) and one imported index then %% first_own_index will be 2. +%% non_neg_integer() +%% For a augmented table, it will instead look like this: +%% index_types {augments, {atom(), asn1_type()}} +%% nbr_of_cols pos_integer() +%% not_accessible [pos_integer()] +%% first_accessible pos_integer() +%% %%----------------------------------------------------------------- -record(table_info, @@ -192,7 +205,7 @@ %%---------------------------------------------------------------------- -record(mib, {misc = [], - mib_format_version = "3.2", + mib_format_version = "3.3", name = "", module_identity, %% Not in SMIv1, and only with +module_identity mes = [], diff --git a/lib/snmp/mibs/Makefile.in b/lib/snmp/mibs/Makefile.in index 993a67c6f2..c74c0b2ac4 100644 --- a/lib/snmp/mibs/Makefile.in +++ b/lib/snmp/mibs/Makefile.in @@ -211,7 +211,7 @@ info: @echo "" @echo "SNMP_VSN = $(SNMP_VSN)" @echo "VSN = $(VSN)" - @echo "RELSYSDIR = $(RELSYSDIR)" + @echo "RELSYSDIR = "$(RELSYSDIR)"" v1/%.mib.v1: %.mib $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 -o $@ $< @@ -223,17 +223,17 @@ v1/%.mib.v1: %.mib $(ERL_TOP)/lib/snmp/bin/snmp-v2tov1 include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/mibs - $(INSTALL_DIR) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DATA) $(MIB_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(STD_v2_MIB_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(FUNCS_FILES) $(RELSYSDIR)/mibs - $(INSTALL_DATA) $(STD_v1_MIB_FILES) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DATA) $(V1_MIB_FILES) $(RELSYSDIR)/mibs/v1 - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/priv/mibs - $(INSTALL_DATA) $(BIN_TARGETS) $(RELSYSDIR)/priv/mibs + $(INSTALL_DIR) "$(RELSYSDIR)/mibs" + $(INSTALL_DIR) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DATA) $(MIB_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(STD_v2_MIB_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(FUNCS_FILES) "$(RELSYSDIR)/mibs" + $(INSTALL_DATA) $(STD_v1_MIB_FILES) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DATA) $(V1_MIB_FILES) "$(RELSYSDIR)/mibs/v1" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/mibs" + $(INSTALL_DATA) $(BIN_TARGETS) "$(RELSYSDIR)/priv/mibs" release_docs_spec: diff --git a/lib/snmp/priv/conf/agent/Makefile b/lib/snmp/priv/conf/agent/Makefile index ee3a915c2b..c7921bacc2 100644 --- a/lib/snmp/priv/conf/agent/Makefile +++ b/lib/snmp/priv/conf/agent/Makefile @@ -55,9 +55,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/conf/agent - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/priv/conf/agent + $(INSTALL_DIR) "$(RELSYSDIR)/priv/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/conf/agent" + $(INSTALL_DATA) $(CONF_FILES) "$(RELSYSDIR)/priv/conf/agent" release_docs_spec: diff --git a/lib/snmp/priv/conf/manager/Makefile b/lib/snmp/priv/conf/manager/Makefile index 16355f0ff3..ba5a74c3fe 100644 --- a/lib/snmp/priv/conf/manager/Makefile +++ b/lib/snmp/priv/conf/manager/Makefile @@ -55,9 +55,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/conf/manager - $(INSTALL_DATA) $(CONF_FILES) $(RELSYSDIR)/priv/conf/manager + $(INSTALL_DIR) "$(RELSYSDIR)/priv/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/conf/manager" + $(INSTALL_DATA) $(CONF_FILES) "$(RELSYSDIR)/priv/conf/manager" release_docs_spec: diff --git a/lib/snmp/src/agent/Makefile b/lib/snmp/src/agent/Makefile index a67fe4d17c..9a982edf91 100644 --- a/lib/snmp/src/agent/Makefile +++ b/lib/snmp/src/agent/Makefile @@ -128,14 +128,14 @@ info: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/agent - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/agent - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/agent" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src/agent" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \ - $(RELSYSDIR)/ebin -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include + "$(RELSYSDIR)/ebin" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/snmp/src/agent/depend.mk b/lib/snmp/src/agent/depend.mk index 078ef15821..ea9261e266 100644 --- a/lib/snmp/src/agent/depend.mk +++ b/lib/snmp/src/agent/depend.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2009. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. 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 diff --git a/lib/snmp/src/agent/snmp_community_mib.erl b/lib/snmp/src/agent/snmp_community_mib.erl index 77307aa7ad..7bdd500727 100644 --- a/lib/snmp/src/agent/snmp_community_mib.erl +++ b/lib/snmp/src/agent/snmp_community_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,6 +28,7 @@ -export([add_community/5, add_community/6, delete_community/1]). -export([check_community/1]). +-include("snmpa_internal.hrl"). -include("SNMP-COMMUNITY-MIB.hrl"). -include("SNMP-TARGET-MIB.hrl"). -include("SNMPv2-TC.hrl"). @@ -120,10 +121,17 @@ init_tabs(Comms) -> read_community_config_files(Dir) -> ?vdebug("read community config file",[]), - Gen = fun(_) -> ok end, - Filter = fun(Comms) -> Comms end, - Check = fun(Entry) -> check_community(Entry) end, - [Comms] = + FileName = "community.conf", + Gen = fun(D, Reason) -> + warning_msg("failed reading config file ~s" + "~n Config Dir: ~s" + "~n Reason: ~p", + [FileName, D, Reason]), + ok + end, + Filter = fun(Comms) -> Comms end, + Check = fun(Entry) -> check_community(Entry) end, + [Comms] = snmp_conf:read_files(Dir, [{Gen, Filter, Check, "community.conf"}]), Comms. @@ -601,5 +609,8 @@ set_sname(_) -> %% Keep it, if already set. error(Reason) -> throw({error, Reason}). +warning_msg(F, A) -> + ?snmpa_warning("[COMMUNITY-MIB]: " ++ F, A). + config_err(F, A) -> snmpa_error:config_err("[COMMUNITY-MIB]: " ++ F, A). diff --git a/lib/snmp/src/agent/snmp_framework_mib.erl b/lib/snmp/src/agent/snmp_framework_mib.erl index 0d7866d94d..cc191bd956 100644 --- a/lib/snmp/src/agent/snmp_framework_mib.erl +++ b/lib/snmp/src/agent/snmp_framework_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -115,7 +115,9 @@ do_configure(Dir) -> read_internal_config_files(Dir) -> ?vdebug("read context config file",[]), - Gen = fun(D) -> convert_context(D) end, + Gen = fun(D, Reason) -> + convert_context(D, Reason) + end, Filter = fun(Contexts) -> Contexts end, Check = fun(Entry) -> check_context(Entry) end, [Ctxs] = snmp_conf:read_files(Dir, [{Gen, Filter, Check, "context.conf"}]), @@ -123,10 +125,17 @@ read_internal_config_files(Dir) -> read_agent(Dir) -> - ?vdebug("read agent config file",[]), - Check = fun(Entry) -> check_agent(Entry) end, - File = filename:join(Dir, "agent.conf"), - Agent = snmp_conf:read(File, Check), + ?vdebug("read agent config file", []), + FileName = "agent.conf", + Check = fun(Entry) -> check_agent(Entry) end, + File = filename:join(Dir, FileName), + Agent = + try + snmp_conf:read(File, Check) + catch + throw:{error, Reason} -> + error({failed_reading_config_file, Dir, FileName, Reason}) + end, sort_agent(Agent). @@ -146,9 +155,9 @@ sort_agent(L) -> %%----------------------------------------------------------------- %% Generate a context.conf file. %%----------------------------------------------------------------- -convert_context(Dir) -> +convert_context(Dir, _Reason) -> config_err("missing context.conf file => generating a default file", []), - File = filename:join(Dir,"context.conf"), + File = filename:join(Dir, "context.conf"), case file:open(File, [write]) of {ok, Fid} -> ok = io:format(Fid, "~s\n", [context_header()]), @@ -165,7 +174,7 @@ context_header() -> io_lib:format("%% This file was automatically generated by " "snmp_config v~s ~w-~2.2.0w-~2.2.0w " "~2.2.0w:~2.2.0w:~2.2.0w\n", - [?version,Y,Mo,D,H,Mi,S]). + [?version, Y, Mo, D, H, Mi, S]). %%----------------------------------------------------------------- diff --git a/lib/snmp/src/agent/snmp_generic_mnesia.erl b/lib/snmp/src/agent/snmp_generic_mnesia.erl index ce42af404b..7fd6501977 100644 --- a/lib/snmp/src/agent/snmp_generic_mnesia.erl +++ b/lib/snmp/src/agent/snmp_generic_mnesia.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/snmp/src/agent/snmp_notification_mib.erl b/lib/snmp/src/agent/snmp_notification_mib.erl index 720ac749b8..37e09f5d3e 100644 --- a/lib/snmp/src/agent/snmp_notification_mib.erl +++ b/lib/snmp/src/agent/snmp_notification_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -27,6 +27,7 @@ -export([add_notify/3, delete_notify/1]). -export([check_notify/1]). +-include("snmpa_internal.hrl"). -include("SNMP-NOTIFICATION-MIB.hrl"). -include("SNMPv2-TC.hrl"). -include("snmp_tables.hrl"). @@ -104,7 +105,14 @@ do_reconfigure(Dir) -> read_notify_config_files(Dir) -> ?vdebug("read notify config file",[]), - Gen = fun(_) -> ok end, + FileName = "notify.conf", + Gen = fun(D, Reason) -> + info_msg("failed reading config file ~s" + "~n Config Dir: ~s" + "~n Reason: ~p", + [FileName, D, Reason]), + ok + end, Filter = fun(Notifs) -> Notifs end, Check = fun(Entry) -> check_notify(Entry) end, [Notifs] = @@ -112,7 +120,7 @@ read_notify_config_files(Dir) -> Notifs. check_notify({Name, Tag, Type}) -> - snmp_conf:check_string(Name,{gt,0}), + snmp_conf:check_string(Name, {gt, 0}), snmp_conf:check_string(Tag), {ok, Val} = snmp_conf:check_atom(Type, [{trap, 1}, {inform, 2}]), Notify = {Name, Tag, Val, @@ -451,12 +459,15 @@ set_sname() -> set_sname(get(sname)). set_sname(undefined) -> - put(sname,conf); + put(sname, conf); set_sname(_) -> %% Keep it, if already set. ok. error(Reason) -> throw({error, Reason}). +info_msg(F, A) -> + ?snmpa_info("[NOTIFICATION-MIB]: " ++ F, A). + config_err(F, A) -> snmpa_error:config_err("[NOTIFICATION-MIB]: " ++ F, A). diff --git a/lib/snmp/src/agent/snmp_standard_mib.erl b/lib/snmp/src/agent/snmp_standard_mib.erl index b6834d278c..766b75022b 100644 --- a/lib/snmp/src/agent/snmp_standard_mib.erl +++ b/lib/snmp/src/agent/snmp_standard_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2010. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -143,20 +143,35 @@ do_reconfigure(Dir) -> %% Func: read_standard/1 %% Args: Dir is the directory with trailing dir_separator where %% the configuration files can be found. -%% Purpose: Reads th standard configuration file. +%% Purpose: Reads the standard configuration file. %% Returns: A list of standard variables %% Fails: If an error occurs, the process will die with Reason %% configuration_error. +%% This file is mandatory, as it contains mandatory +%% config options for which there are no default values. %%----------------------------------------------------------------- read_standard(Dir) -> ?vdebug("check standard config file",[]), - Gen = fun(_) -> ok end, - Filter = fun(Standard) -> sort_standard(Standard) end, - Check = fun(Entry) -> check_standard(Entry) end, + FileName = "standard.conf", + Gen = fun(D, Reason) -> + throw({error, {failed_reading_config_file, + D, FileName, + list_dir(Dir), Reason}}) + end, + Filter = fun(Standard) -> sort_standard(Standard) end, + Check = fun(Entry) -> check_standard(Entry) end, [Standard] = - snmp_conf:read_files(Dir, [{Gen, Filter, Check, "standard.conf"}]), + snmp_conf:read_files(Dir, [{Gen, Filter, Check, FileName}]), Standard. +list_dir(Dir) -> + case file:list_dir(Dir) of + {ok, Files} -> + Files; + Error -> + Error + end. + %%----------------------------------------------------------------- %% Make sure that each mandatory standard attribute is present, and @@ -548,6 +563,7 @@ snmp_set_serial_no(set, NewVal) -> snmp_generic:variable_func(set, (NewVal + 1) rem 2147483648, {snmpSetSerialNo, volatile}). + %%----------------------------------------------------------------- %% This is the instrumentation function for sysOrTable %%----------------------------------------------------------------- @@ -588,4 +604,4 @@ error(Reason) -> throw({error, Reason}). config_err(F, A) -> - snmpa_error:config_err("[STANDARD-MIB]: " ++ F, A). + snmpa_error:config_err("[STANDARD-MIB] " ++ F, A). diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl index a45db89c09..b01d536caa 100644 --- a/lib/snmp/src/agent/snmp_target_mib.erl +++ b/lib/snmp/src/agent/snmp_target_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -132,12 +132,12 @@ do_reconfigure(Dir) -> read_target_config_files(Dir) -> - ?vdebug("check target address config file",[]), - TAGen = fun(_D) -> ok end, + ?vdebug("check target address and parameter config file(s)",[]), + TAGen = fun(_D, _Reason) -> ok end, TAFilter = fun(Addr) -> Addr end, TACheck = fun(Entry) -> check_target_addr(Entry) end, - TPGen = fun(_D) -> ok end, + TPGen = fun(_D, _Reason) -> ok end, TPFilter = fun(Params) -> Params end, TPCheck = fun(Entry) -> check_target_params(Entry) end, diff --git a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl index 69cebc858b..3c4ba1af66 100644 --- a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl +++ b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2010. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,6 +54,7 @@ %% Columns not accessible via SNMP -define(usmUserAuthKey, 14). -define(usmUserPrivKey, 15). +-define(is_cloning, 16). %%%----------------------------------------------------------------- @@ -136,7 +137,7 @@ do_reconfigure(Dir) -> read_usm_config_files(Dir) -> ?vdebug("read usm config file",[]), - Gen = fun(D) -> generate_usm(D) end, + Gen = fun(D, Reason) -> generate_usm(D, Reason) end, Filter = fun(Usms) -> Usms end, Check = fun(Entry) -> check_usm(Entry) end, [Usms] = @@ -144,7 +145,7 @@ read_usm_config_files(Dir) -> Usms. -generate_usm(Dir) -> +generate_usm(Dir, _Reason) -> info_msg("Incomplete configuration. Generating empty usm.conf.", []), USMFile = filename:join(Dir, "usm.conf"), ok = file:write_file(USMFile, list_to_binary([])). @@ -564,7 +565,9 @@ usmUserTable(set, RowIndex, Cols0) -> {ok, Cols} -> ?vtrace("usmUserTable(set) -> verified" "~n Cols: ~p", [Cols]), - NCols = pre_set(RowIndex, Cols), + % check whether we're cloning. if so, get cloned params and add a few + % defaults that might be needed. + NCols = pre_set(RowIndex, validate_clone_from(RowIndex, Cols)), ?vtrace("usmUserTable(set) -> pre-set: " "~n NCols: ~p", [NCols]), %% NOTE: The NCols parameter is sent to snmp_generic, but not to @@ -730,30 +733,40 @@ validate_is_set_ok(Error, _RowIndex, _Cols) -> Error. do_validate_is_set_ok(RowIndex, Cols) -> - validate_clone_from(RowIndex, Cols), - validate_auth_protocol(RowIndex, Cols), - validate_auth_key_change(RowIndex, Cols), - validate_own_auth_key_change(RowIndex, Cols), - validate_priv_protocol(RowIndex, Cols), - validate_priv_key_change(RowIndex, Cols), - validate_own_priv_key_change(RowIndex, Cols), + NCols = validate_clone_from(RowIndex, Cols), + validate_auth_protocol(RowIndex, NCols), + validate_auth_key_change(RowIndex, NCols), + validate_own_auth_key_change(RowIndex, NCols), + validate_priv_protocol(RowIndex, NCols), + validate_priv_key_change(RowIndex, NCols), + validate_own_priv_key_change(RowIndex, NCols), ok. pre_set(RowIndex, Cols) -> + %% Remove the ?is_cloning member again; it must no longer be + %% present. + Cols0 = key1delete(?is_cloning, Cols), %% Possibly initialize the usmUserSecurityName and privacy keys case snmp_generic:table_row_exists(db(usmUserTable), RowIndex) of - true -> Cols; + true -> Cols0; false -> SecName = get_user_name(RowIndex), - [{?usmUserSecurityName, SecName} | Cols] ++ - [{?usmUserAuthKey, ""}, - {?usmUserPrivKey, ""}] + Cols1 = [{?usmUserSecurityName, SecName} | Cols0], + case proplists:get_value(?is_cloning, Cols) of + true -> + % the row is just being cloned. the cloned user's + % passwords are already present in Cols and must + % not be overwritten. + Cols1; + _ -> + Cols1 ++ [{?usmUserAuthKey, ""}, + {?usmUserPrivKey, ""}] + end end. validate_set({noError, 0}, RowIndex, Cols) -> %% Now, all is_set_ok validation steps have been executed. So %% everything is ready for the set. - set_clone_from(RowIndex, Cols), set_auth_key_change(RowIndex, Cols), set_own_auth_key_change(RowIndex, Cols), set_priv_key_change(RowIndex, Cols), @@ -769,7 +782,7 @@ validate_set(Error, _RowIndex, _Cols) -> %% no further checks. %%----------------------------------------------------------------- validate_clone_from(RowIndex, Cols) -> - case lists:keysearch(?usmUserCloneFrom, 1, Cols) of + case key1search(?usmUserCloneFrom, Cols) of {value, {_Col, RowPointer}} -> RowIndex2 = extract_row(RowPointer), OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), @@ -778,35 +791,63 @@ validate_clone_from(RowIndex, Cols) -> case OldCloneFrom of {value, Val} when Val /= noinit -> %% This means that the cloning is already done... - ok; + no_cloning(Cols); _ -> - %% Otherwise, we must check the CloneFrom value - case snmp_generic:table_get_element(db(usmUserTable), - RowIndex2, - ?usmUserStatus) of - {value, ?'RowStatus_active'} -> ok; - _ -> inconsistentName(?usmUserCloneFrom) - end + %% Otherwise, we must check the CloneFrom value. It + %% must relate to a usmUserEntry that exists and is active. + case snmp_generic:table_get_row(db(usmUserTable), RowIndex2) of + CloneFromRow when is_tuple(CloneFromRow) -> + case element(?usmUserStatus, CloneFromRow) of + ?'RowStatus_active' -> + get_cloned_cols(CloneFromRow, Cols); + _ -> + inconsistentName(?usmUserCloneFrom) + end; + undefined -> + inconsistentName(?usmUserCloneFrom) + end end; false -> - ok + % no ?usmUserCloneFrom specified, don't modify columns + no_cloning(Cols) end. +get_cloned_cols(CloneFromRow, Cols) -> + % initialize cloned columns with data from CloneFromRow + % and overwrite that again with data found in Cols + AuthP = element(?usmUserAuthProtocol, CloneFromRow), + PrivP = element(?usmUserPrivProtocol, CloneFromRow), + AuthK = element(?usmUserAuthKey, CloneFromRow), + PrivK = element(?usmUserPrivKey, CloneFromRow), + ClonedCols = [{?usmUserAuthProtocol, AuthP}, + {?usmUserPrivProtocol, PrivP}, + {?usmUserAuthKey, AuthK}, + {?usmUserPrivKey, PrivK}, + {?is_cloning, true} + ], + Func = fun({Col, _} = Item, NCols) -> + key1store(Col, NCols, Item) + end, + Cols1 = lists:foldl(Func, ClonedCols, Cols), + key1sort(Cols1). + +no_cloning(Cols0) -> + Cols1 = key1delete(?usmUserCloneFrom, Cols0), + key1delete(?is_cloning, Cols1). + validate_auth_protocol(RowIndex, Cols) -> - case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of + case key1search(?usmUserAuthProtocol, Cols) of {value, {_Col, AuthProtocol}} -> - %% Check if the row has been cloned; we can't check the + %% Check if the row is being cloned; we can't check the %% old value of authProtocol, because if the row was %% createAndWaited, the default value would have been %% written (usmNoAuthProtocol). - OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), - RowIndex, - ?usmUserCloneFrom), - case OldCloneFrom of - {value, Val} when Val /= noinit -> - %% This means that the cloning is already done; set is ok - %% if new protocol is usmNoAuthProtocol + IsCloning = proplists:get_value(?is_cloning, Cols, false), + if + not IsCloning -> + %% This means that the row is not being cloned right + %% now; set is ok if new protocol is usmNoAuthProtocol case AuthProtocol of ?usmNoAuthProtocol -> %% Check that the Priv protocl is noPriv @@ -821,7 +862,7 @@ validate_auth_protocol(RowIndex, Cols) -> _ -> wrongValue(?usmUserAuthProtocol) end; - _ -> + true -> %% Otherwise, check that the new protocol is known, %% and that the system we're running supports the %% hash function. @@ -867,7 +908,7 @@ validate_own_priv_key_change(RowIndex, Cols) -> %% Check that the requesting user is the same as the modified user validate_requester(RowIndex, Cols, KeyChangeCol) -> - case lists:keysearch(KeyChangeCol, 1, Cols) of + case key1search(KeyChangeCol, Cols) of {value, _} -> case get(sec_model) of % Check the securityModel in the request ?SEC_USM -> ok; @@ -890,17 +931,14 @@ validate_requester(RowIndex, Cols, KeyChangeCol) -> end. validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> - case lists:keysearch(KeyChangeCol, 1, Cols) of + case key1search(KeyChangeCol, Cols) of {value, {_Col, KeyC}} -> %% Check if the row has been cloned; or if it is cloned in %% this set-operation. OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), RowIndex, ?usmUserCloneFrom), - IsClonePresent = case lists:keysearch(?usmUserCloneFrom, 1, Cols) of - {value, _} -> true; - false -> false - end, + IsClonePresent = proplists:get_value(?is_cloning, Cols, false), %% Set is ok if 1) the user already is created, 2) this is %% a new user, which has been cloned, or is about to be %% cloned. @@ -912,7 +950,7 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> %% The user is cloned in this operation ok; _ -> - %% The user doen't exist, or hasn't been cloned, + %% The user doesn't exist, or hasn't been cloned, %% and is not cloned in this operation. inconsistentName(KeyChangeCol) end, @@ -939,17 +977,15 @@ validate_key_change(RowIndex, Cols, KeyChangeCol, Type) -> end. validate_priv_protocol(RowIndex, Cols) -> - case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of + case key1search(?usmUserPrivProtocol, Cols) of {value, {_Col, PrivProtocol}} -> %% Check if the row has been cloned; we can't check the %% old value of privhProtocol, because if the row was %% createAndWaited, the default value would have been %% written (usmNoPrivProtocol). - OldCloneFrom = snmp_generic:table_get_element(db(usmUserTable), - RowIndex, - ?usmUserCloneFrom), - case OldCloneFrom of - {value, Val} when Val /= noinit -> + IsCloning = proplists:get_value(?is_cloning, Cols, false), + if + not IsCloning -> %% This means that the cloning is already done; set is ok %% if new protocol is usmNoPrivProtocol case PrivProtocol of @@ -962,7 +998,7 @@ validate_priv_protocol(RowIndex, Cols) -> _ -> wrongValue(?usmUserPrivProtocol) end; - _ -> + true -> %% Otherwise, check that the new protocol is known, %% and that the system we're running supports the %% crypto function. @@ -1005,31 +1041,6 @@ validate_priv_protocol(RowIndex, Cols) -> end. -set_clone_from(RowIndex, Cols) -> - %% If CloneFrom is modified, do the cloning. - case lists:keysearch(?usmUserCloneFrom, 1, Cols) of - {value, {_Col, RowPointer}} -> - RowIndex2 = extract_row(RowPointer), % won't fail - CloneRow = snmp_generic:table_get_row(db(usmUserTable), RowIndex2, - foi(usmUserTable)), - AuthP = element(?usmUserAuthProtocol, CloneRow), - PrivP = element(?usmUserPrivProtocol, CloneRow), - AuthK = element(?usmUserAuthKey, CloneRow), - PrivK = element(?usmUserPrivKey, CloneRow), - SCols = [{?usmUserAuthProtocol, AuthP}, - {?usmUserPrivProtocol, PrivP}, - {?usmUserAuthKey, AuthK}, - {?usmUserPrivKey, PrivK}], - case snmp_generic:table_set_elements(db(usmUserTable), - RowIndex, - SCols) of - true -> ok; - false -> {commitFailed, ?usmUserCloneFrom} - end; - false -> - ok - end. - set_auth_key_change(RowIndex, Cols) -> set_key_change(RowIndex, Cols, ?usmUserAuthKeyChange, auth). @@ -1043,7 +1054,7 @@ set_own_priv_key_change(RowIndex, Cols) -> set_key_change(RowIndex, Cols, ?usmUserOwnPrivKeyChange, priv). set_key_change(RowIndex, Cols, KeyChangeCol, Type) -> - case lists:keysearch(KeyChangeCol, 1, Cols) of + case key1search(KeyChangeCol, Cols) of {value, {_Col, KeyChange}} -> KeyCol = case Type of auth -> ?usmUserAuthKey; @@ -1071,11 +1082,11 @@ extract_row([H | T], [H | T2]) -> extract_row(T, T2); extract_row([], [?usmUserSecurityName | T]) -> T; extract_row(_, _) -> wrongValue(?usmUserCloneFrom). -%% Pre: the user exixt +%% Pre: the user exists or is being cloned in this operation get_auth_proto(RowIndex, Cols) -> - %% The protocol can be chanegd by the request too, otherwise, + %% The protocol can be changed by the request too, otherwise, %% check the stored protocol. - case lists:keysearch(?usmUserAuthProtocol, 1, Cols) of + case key1search(?usmUserAuthProtocol, Cols) of {value, {_, Protocol}} -> Protocol; false -> @@ -1090,11 +1101,11 @@ get_auth_proto(RowIndex, Cols) -> end end. -%% Pre: the user exixt +%% Pre: the user exists or is being cloned in this operation get_priv_proto(RowIndex, Cols) -> - %% The protocol can be chanegd by the request too, otherwise, + %% The protocol can be changed by the request too, otherwise, %% check the stored protocol. - case lists:keysearch(?usmUserPrivProtocol, 1, Cols) of + case key1search(?usmUserPrivProtocol, Cols) of {value, {_, Protocol}} -> Protocol; false -> @@ -1232,6 +1243,27 @@ set_sname(_) -> %% Keep it, if already set. error(Reason) -> throw({error, Reason}). + +%%----------------------------------------------------------------- +%% lists key-function(s) wrappers + +-compile({inline,key1delete/2}). +key1delete(Key, List) -> + lists:keydelete(Key, 1, List). + +-compile({inline,key1search/2}). +key1search(Key, List) -> + lists:keysearch(Key, 1, List). + +-compile({inline,key1store/3}). +key1store(Key, List, Elem) -> + lists:keystore(Key, 1, List, Elem). + +-compile({inline,key1sort/1}). +key1sort(List) -> + lists:keysort(1, List). + + %%----------------------------------------------------------------- info_msg(F, A) -> 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 2cee91b081..436f15eb9c 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -115,7 +115,7 @@ do_reconfigure(Dir) -> read_vacm_config_files(Dir) -> ?vdebug("read vacm config file",[]), - Gen = fun(_) -> ok end, + Gen = fun(_D, _Reason) -> ok end, Filter = fun(Vacms) -> Sec2Group = [X || {vacmSecurityToGroup, X} <- Vacms], Access = [X || {vacmAccess, X} <- Vacms], @@ -565,45 +565,85 @@ vacmAccessTable(is_set_ok, RowIndex, Cols0) -> case (catch verify_vacmAccessTable_cols(Cols0, [])) of {ok, Cols} -> IsValidKey = is_valid_key(RowIndex), - case lists:keysearch(?vacmAccessStatus, 1, Cols) of - %% Ok, if contextMatch is init - {value, {Col, ?'RowStatus_active'}} -> - {ok, Row} = snmpa_vacm:get_row(RowIndex), - case element(?vacmAContextMatch, Row) of - noinit -> {inconsistentValue, Col}; - _ -> {noError, 0} + StatusCol = lists:keyfind(?vacmAccessStatus, 1, Cols), + MaybeRow = snmpa_vacm:get_row(RowIndex), + case {StatusCol, MaybeRow} of + {{Col, ?'RowStatus_active'}, false} -> + %% row does not yet exist => inconsistentValue + %% (see SNMPv2-TC.mib, RowStatus textual convention) + {inconsistentValue, Col}; + {{Col, ?'RowStatus_active'}, {ok, Row}} -> + %% Ok, if contextMatch is init + case element(?vacmAContextMatch, Row) of + noinit -> + %% check whether ContextMatch is being set in + %% the same operation + case proplists:get_value(?vacmAccessContextMatch, + Cols) of + undefined -> + %% no, we can't make this row active yet + {inconsistentValue, Col}; + _ -> + %% ok, activate the row + {noError, 0} + end; + _ -> + {noError, 0} end; - {value, {Col, ?'RowStatus_notInService'}} -> % Ok, if not notReady - {ok, Row} = snmpa_vacm:get_row(RowIndex), + {{Col, ?'RowStatus_notInService'}, false} -> + %% row does not yet exist => inconsistentValue + %% (see SNMPv2-TC.mib, RowStatus textual convention) + {inconsistentValue, Col}; + {{Col, ?'RowStatus_notInService'}, {ok, Row}} -> + %% Ok, if not notReady case element(?vacmAStatus, Row) of - ?'RowStatus_notReady' -> {inconsistentValue, Col}; - _ -> {noError, 0} + ?'RowStatus_notReady' -> + {inconsistentValue, Col}; + _ -> + {noError, 0} end; - {value, {Col, ?'RowStatus_notReady'}} -> % never ok! + {{Col, ?'RowStatus_notReady'}, _} -> + %% never ok! {inconsistentValue, Col}; - {value, {Col, ?'RowStatus_createAndGo'}} -> % ok, if it doesn't exist + {{Col, ?'RowStatus_createAndGo'}, false} -> + %% ok, if it doesn't exist Res = lists:keysearch(?vacmAccessContextMatch, 1, Cols), - case snmpa_vacm:get_row(RowIndex) of - false when (IsValidKey =:= true) andalso - is_tuple(Res) -> {noError, 0}; - false -> {noCreation, Col}; % Bad RowIndex - _ -> {inconsistentValue, Col} + if + IsValidKey =/= true -> + %% bad RowIndex => noCreation + {noCreation, Col}; + is_tuple(Res) -> + %% required field is present => noError + {noError, 0}; + true -> + %% required field is missing => inconsistentValue + {inconsistentValue, Col} end; - {value, {Col, ?'RowStatus_createAndWait'}} -> % ok, if it doesn't exist - case snmpa_vacm:get_row(RowIndex) of - false when (IsValidKey =:= true) -> {noError, 0}; - false -> {noCreation, Col}; % Bad RowIndex - _ -> {inconsistentValue, Col} + {{Col, ?'RowStatus_createAndGo'}, _} -> + %% row already exists => inconsistentValue + {inconsistentValue, Col}; + {{Col, ?'RowStatus_createAndWait'}, false} -> + %% ok, if it doesn't exist + if + IsValidKey =:= true -> + %% RowIndex is valid => noError + {noError, 0}; + true -> + {noCreation, Col} end; - {value, {_Col, ?'RowStatus_destroy'}} -> % always ok! + {{Col, ?'RowStatus_createAndWait'}, _} -> + %% Row already exists => inconsistentValue + {inconsistentValue, Col}; + {value, {_Col, ?'RowStatus_destroy'}} -> + %% always ok! {noError, 0}; - _ -> % otherwise, it's a change; it must exist - case snmpa_vacm:get_row(RowIndex) of - {ok, _} -> - {noError, 0}; - false -> - {inconsistentName, element(1, hd(Cols))} - end + {_, false} -> + %% otherwise, it's a row change; + %% row does not exist => inconsistentName + {inconsistentName, element(1, hd(Cols))}; + _ -> + %% row change and row exists => noError + {noError, 0} end; Error -> Error @@ -734,10 +774,15 @@ do_vacmAccessTable_set(RowIndex, Cols) -> %% Cols are sorted, and all columns are > 3. +do_get_next(_RowIndex, []) -> + % Cols can be empty because we're called for each + % output of split_cols(); and one of that may well + % be empty. + []; do_get_next(RowIndex, Cols) -> case snmpa_vacm:get_next_row(RowIndex) of {NextIndex, Row} -> - F1 = fun(Col) when Col < ?vacmAccessStatus -> + F1 = fun(Col) when Col =< ?vacmAccessStatus -> {[Col | NextIndex], element(Col-3, Row)}; (_) -> endOfTable @@ -745,9 +790,9 @@ do_get_next(RowIndex, Cols) -> lists:map(F1, Cols); false -> case snmpa_vacm:get_next_row([]) of - {_NextIndex, Row} -> + {NextIndex2, Row} -> F2 = fun(Col) when Col < ?vacmAccessStatus -> - {[Col+1 | RowIndex], element(Col-2, Row)}; + {[Col+1 | NextIndex2], element(Col-2, Row)}; (_) -> endOfTable end, diff --git a/lib/snmp/src/agent/snmpa.erl b/lib/snmp/src/agent/snmpa.erl index c400aaddf7..b45a47ec6b 100644 --- a/lib/snmp/src/agent/snmpa.erl +++ b/lib/snmp/src/agent/snmpa.erl @@ -83,8 +83,11 @@ -export([add_agent_caps/2, del_agent_caps/1, get_agent_caps/0]). %% Audit Trail Log functions --export([log_to_txt/2, log_to_txt/3, log_to_txt/4, +-export([log_to_txt/1, + log_to_txt/2, log_to_txt/3, log_to_txt/4, log_to_txt/5, log_to_txt/6, log_to_txt/7, + log_to_io/1, log_to_io/2, log_to_io/3, + log_to_io/4, log_to_io/5, log_to_io/6, log_info/0, change_log_size/1, get_log_type/0, get_log_type/1, @@ -780,6 +783,8 @@ get_agent_caps() -> %%% Audit Trail Log functions %%%----------------------------------------------------------------- +log_to_txt(LogDir) -> + log_to_txt(LogDir, []). log_to_txt(LogDir, Mibs) -> OutFile = "snmpa_log.txt", LogName = ?audit_trail_log_name, @@ -800,6 +805,23 @@ log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop). +log_to_io(LogDir) -> + log_to_io(LogDir, []). +log_to_io(LogDir, Mibs) -> + LogName = ?audit_trail_log_name, + LogFile = ?audit_trail_log_file, + snmp:log_to_io(LogDir, Mibs, LogName, LogFile). +log_to_io(LogDir, Mibs, LogName) -> + LogFile = ?audit_trail_log_file, + snmp:log_to_io(LogDir, Mibs, LogName, LogFile). +log_to_io(LogDir, Mibs, LogName, LogFile) -> + snmp:log_to_io(LogDir, Mibs, LogName, LogFile). +log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> + snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start). +log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> + snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop). + + log_info() -> LogName = ?audit_trail_log_name, snmp_log:info(LogName). diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl index 9cc986cf47..9d30e332f1 100644 --- a/lib/snmp/src/agent/snmpa_agent.erl +++ b/lib/snmp/src/agent/snmpa_agent.erl @@ -1281,7 +1281,7 @@ handle_call({backup, BackupDir}, From, #state{backup = undefined} = S) -> ?vtrace("backup server: ~p", [BackupServer]), {noreply, S#state{backup = {BackupServer, From}}}; -handle_call({backup, _BackupDir}, From, #state{backup = Backup} = S) -> +handle_call({backup, _BackupDir}, _From, #state{backup = Backup} = S) -> ?vinfo("backup already in progress: ~p", [Backup]), {reply, {error, backup_in_progress}, S}; diff --git a/lib/snmp/src/agent/snmpa_internal.hrl b/lib/snmp/src/agent/snmpa_internal.hrl index c435b519d9..6537562d44 100644 --- a/lib/snmp/src/agent/snmpa_internal.hrl +++ b/lib/snmp/src/agent/snmpa_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 diff --git a/lib/snmp/src/agent/snmpa_local_db.erl b/lib/snmp/src/agent/snmpa_local_db.erl index 1ec8dd3874..2c0cad807a 100644 --- a/lib/snmp/src/agent/snmpa_local_db.erl +++ b/lib/snmp/src/agent/snmpa_local_db.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -515,7 +515,7 @@ handle_call({backup, BackupDir}, From, {reply, Error, State} end; -handle_call({backup, _BackupDir}, From, #state{backup = Backup} = S) -> +handle_call({backup, _BackupDir}, _From, #state{backup = Backup} = S) -> ?vinfo("backup already in progress: ~p", [Backup]), {reply, {error, backup_in_progress}, S}; diff --git a/lib/snmp/src/agent/snmpa_mib.erl b/lib/snmp/src/agent/snmpa_mib.erl index 574467d38f..575a018c0c 100644 --- a/lib/snmp/src/agent/snmpa_mib.erl +++ b/lib/snmp/src/agent/snmpa_mib.erl @@ -580,7 +580,7 @@ handle_call({backup, BackupDir}, From, {reply, Error, State} end; -handle_call({backup, _BackupDir}, From, #state{backup = Backup} = S) -> +handle_call({backup, _BackupDir}, _From, #state{backup = Backup} = S) -> ?vinfo("backup already in progress: ~p", [Backup]), {reply, {error, backup_in_progress}, S}; diff --git a/lib/snmp/src/agent/snmpa_mib_lib.erl b/lib/snmp/src/agent/snmpa_mib_lib.erl index 3c94cc8095..529a65a1f1 100644 --- a/lib/snmp/src/agent/snmpa_mib_lib.erl +++ b/lib/snmp/src/agent/snmpa_mib_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 diff --git a/lib/snmp/src/agent/snmpa_supervisor.erl b/lib/snmp/src/agent/snmpa_supervisor.erl index 7a9c214e0d..886fd074bc 100644 --- a/lib/snmp/src/agent/snmpa_supervisor.erl +++ b/lib/snmp/src/agent/snmpa_supervisor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index 994d926224..b9a2496341 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/snmp/src/app/Makefile b/lib/snmp/src/app/Makefile index d89eb4e723..7a16d42c30 100644 --- a/lib/snmp/src/app/Makefile +++ b/lib/snmp/src/app/Makefile @@ -127,14 +127,14 @@ $(APPUP_TARGET): $(APPUP_SRC) ../../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/app - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/app - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/app" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src/app" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \ - $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include + "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/snmp/src/app/snmp.app.src b/lib/snmp/src/app/snmp.app.src index a880a14696..b11c1ef934 100644 --- a/lib/snmp/src/app/snmp.app.src +++ b/lib/snmp/src/app/snmp.app.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -91,6 +91,7 @@ snmpm_mpd, snmpm_net_if, snmpm_net_if_filter, + snmpm_net_if_mt, snmpm_network_interface, snmpm_network_interface_filter, snmpm_server, diff --git a/lib/snmp/src/app/snmp.appup.src b/lib/snmp/src/app/snmp.appup.src index c8e5eec6db..593ddd82bd 100644 --- a/lib/snmp/src/app/snmp.appup.src +++ b/lib/snmp/src/app/snmp.appup.src @@ -22,66 +22,201 @@ %% ----- U p g r a d e ------------------------------------------------------- [ + {"4.22", + [ + {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []} + ] + }, + {"4.21.7", + [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, + {update, snmpa_mib, soft, soft_purge, soft_purge, []}, + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {add_module, snmpm_net_if_mt} + ] + }, {"4.21.6", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {add_module, snmpm_net_if_mt} ] }, {"4.21.5", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, []}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {add_module, snmpm_net_if_mt} ] }, {"4.21.4", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {add_module, snmpm_net_if_mt} ] }, {"4.21.3", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {add_module, snmpm_net_if_mt} ] }, {"4.21.2", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, []}, {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, @@ -89,17 +224,37 @@ {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {add_module, snmpm_net_if_mt} ] }, {"4.21.1", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, []}, {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, @@ -108,158 +263,251 @@ {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, {update, snmpa_agent, soft, soft_purge, soft_purge, []}, - {update, snmp_note_store, soft, soft_purge, soft_purge, []} + {update, snmp_note_store, soft, soft_purge, soft_purge, []}, + + {add_module, snmpm_net_if_mt} ] }, {"4.21", [ - {load_module, snmpa, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, []}, {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, {update, snmpa_agent, soft, soft_purge, soft_purge, []}, - {update, snmp_note_store, soft, soft_purge, soft_purge, []} + {update, snmp_note_store, soft, soft_purge, soft_purge, []}, + + {add_module, snmpm_net_if_mt} ] - }, - {"4.20.1", - [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, - {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, + } + ], - {load_module, snmpa_vacm, soft_purge, soft_purge, []}, - {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, - {load_module, snmpa_trap, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, +%% ------D o w n g r a d e --------------------------------------------------- + + [ + {"4.22", + [ + {load_module, snmpm, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, {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, snmp_note_store, soft, soft_purge, soft_purge, []}, - {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, - {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, - {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {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]} + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, []} ] }, - {"4.20", + {"4.21.7", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, - {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, - {load_module, snmpa_vacm, soft_purge, soft_purge, []}, - {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, - {load_module, snmpa_trap, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, - [snmpa_mib_lib, 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_pdus, 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, snmp_note_store, soft, soft_purge, soft_purge, []}, - {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, - {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, - {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {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]} - ] - } - ], + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]}, -%% ------D o w n g r a d e --------------------------------------------------- + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, - [ + {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, + {update, snmpa_mib, soft, soft_purge, soft_purge, []}, + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} + ] + }, {"4.21.6", [ + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} ] }, {"4.21.5", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, []}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} ] }, {"4.21.4", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} ] }, {"4.21.3", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} ] }, {"4.21.2", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, []}, {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, @@ -267,17 +515,37 @@ {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {update, snmpa_agent, soft, soft_purge, soft_purge, []} + {update, snmpa_agent, soft, soft_purge, soft_purge, []}, + + {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} ] }, {"4.21.1", [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, [snmpa_mib_lib]}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, []}, {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, @@ -286,90 +554,48 @@ {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, {update, snmpa_agent, soft, soft_purge, soft_purge, []}, - {update, snmp_note_store, soft, soft_purge, soft_purge, []} + {update, snmp_note_store, soft, soft_purge, soft_purge, []}, + + {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} ] }, {"4.21", [ - {load_module, snmpa, soft_purge, soft_purge, []}, + {load_module, snmp_pdus, soft_purge, soft_purge, []}, + + {load_module, snmp_config, soft_purge, soft_purge, []}, + {load_module, snmp_conf, soft_purge, soft_purge, []}, + {load_module, snmp_community_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_framework_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_notification_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_standard_mib, soft_purge, soft_purge, [snmp_conf]}, + {load_module, snmp_user_based_sm_mib, soft_purge, soft_purge, [snmp_conf]}, + + {load_module, snmp, soft_purge, soft_purge, [snmp_log]}, + {load_module, snmpm, soft_purge, soft_purge, [snmp]}, + {load_module, snmp_log, soft_purge, soft_purge, []}, + {load_module, snmp_verbosity, soft_purge, soft_purge, []}, + {load_module, snmpm_mpd, soft_purge, soft_purge, []}, + + {load_module, snmpa, soft_purge, soft_purge, [snmp]}, {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, - {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, []}, + {load_module, snmp_view_based_acm_mib, soft_purge, soft_purge, + [snmp_conf]}, {load_module, snmpa_vacm, soft_purge, soft_purge, []}, {load_module, snmpa_mpd, soft_purge, soft_purge, []}, {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, {load_module, snmpa_trap, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, []}, + {load_module, snmp_target_mib, soft_purge, soft_purge, + [snmp_conf, snmpa_mib_lib]}, {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, {update, snmpa_mib, soft, soft_purge, soft_purge, []}, {update, snmpa_agent, soft, soft_purge, soft_purge, []}, - {update, snmp_note_store, soft, soft_purge, soft_purge, []} - ] - }, - {"4.20.1", - [ - {load_module, snmpa, soft_purge, soft_purge, []}, - {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, - {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, - - {load_module, snmpa_vacm, soft_purge, soft_purge, []}, - {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, - {load_module, snmpa_trap, soft_purge, soft_purge, []}, - {load_module, snmp_target_mib, soft_purge, soft_purge, []}, - {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, snmp_note_store, soft, soft_purge, soft_purge, []}, - {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, - {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, - {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {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, snmpa, soft_purge, soft_purge, []}, - {load_module, snmpa_mib_lib, soft_purge, soft_purge, []}, - {update, snmpa_supervisor, soft, soft_purge, soft_purge, []}, - {load_module, snmpa_vacm, soft_purge, soft_purge, []}, - {load_module, snmpa_set_lib, soft_purge, soft_purge, []}, - {load_module, snmpa_trap, soft_purge, soft_purge, []}, - {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, snmp_note_store, soft, soft_purge, soft_purge, []}, - {load_module, snmp_generic_mnesia, soft_purge, soft_purge, []}, - {update, snmpa_local_db, soft, soft_purge, soft_purge, []}, - {update, snmpa_mib, soft, soft_purge, soft_purge, []}, - {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]} + {remove, {snmpm_net_if_mt, soft_purge, soft_purge}} ] } ] diff --git a/lib/snmp/src/app/snmp.erl b/lib/snmp/src/app/snmp.erl index 3e0f05e604..cd3e3a0055 100644 --- a/lib/snmp/src/app/snmp.erl +++ b/lib/snmp/src/app/snmp.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -50,6 +50,7 @@ read_mib/1, log_to_txt/5, log_to_txt/6, log_to_txt/7, + log_to_io/4, log_to_io/5, log_to_io/6, change_log_size/2, octet_string_to_bits/1, bits_to_octet_string/1, @@ -843,6 +844,12 @@ log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start) -> log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> snmp_log:log_to_txt(LogName, LogFile, LogDir, Mibs, OutFile, Start, Stop). +log_to_io(LogDir, Mibs, LogName, LogFile) -> + snmp_log:log_to_io(LogName, LogFile, LogDir, Mibs). +log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> + snmp_log:log_to_io(LogName, LogFile, LogDir, Mibs, Start). +log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> + snmp_log:log_to_io(LogName, LogFile, LogDir, Mibs, Start, Stop). change_log_size(LogName, NewSize) -> snmp_log:change_size(LogName, NewSize). diff --git a/lib/snmp/src/compile/Makefile b/lib/snmp/src/compile/Makefile index 627af6f185..9658ee0005 100644 --- a/lib/snmp/src/compile/Makefile +++ b/lib/snmp/src/compile/Makefile @@ -120,13 +120,13 @@ $(GENERATED_PARSER): $(PARSER_SRC) include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(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) $(EBIN_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/bin - $(INSTALL_SCRIPT) $(ESCRIPT_BIN) $(RELSYSDIR)/bin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(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) $(EBIN_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/bin" + $(INSTALL_SCRIPT) $(ESCRIPT_BIN) "$(RELSYSDIR)/bin" release_docs_spec: diff --git a/lib/snmp/src/compile/snmpc_lib.erl b/lib/snmp/src/compile/snmpc_lib.erl index 38bb8f3ac6..c7eae307e8 100644 --- a/lib/snmp/src/compile/snmpc_lib.erl +++ b/lib/snmp/src/compile/snmpc_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 @@ -704,23 +704,29 @@ check_trap_name(EnterpriseName, Line, MEs) -> %% This information is needed to be able to create default instrumentation %% functions for tables. %%---------------------------------------------------------------------- -make_table_info(Line, _TableName, {augments,SrcTableEntry}, ColumnMEs) -> +make_table_info(Line, TableName, {augments, SrcTableEntry}, ColumnMEs) -> ColMEs = lists:keysort(#me.oid, ColumnMEs), - %% Nbr_of_Cols = length(ColMEs), + Nbr_of_Cols = length(ColMEs), MEs = ColMEs ++ (get(cdata))#cdata.mes, - Aug = case lookup(SrcTableEntry,MEs) of + Aug = case lookup(SrcTableEntry, MEs) of false -> print_error("Cannot AUGMENT the non-existing table entry ~p", - [SrcTableEntry],Line), + [SrcTableEntry], Line), {augments, error}; - {value,ME} -> - {augments, {SrcTableEntry,translate_type(ME#me.asn1_type)}} + {value, ME} -> + {augments, {SrcTableEntry, translate_type(ME#me.asn1_type)}} end, - #table_info{index_types = Aug}; -make_table_info(Line, TableName, {indexes,[]}, _ColumnMEs) -> + FirstNonIdxCol = augments_first_non_index_column(ColMEs), + NoAccs = list_not_accessible(FirstNonIdxCol, ColMEs), + FirstAcc = first_accessible(TableName, ColMEs), + #table_info{nbr_of_cols = Nbr_of_Cols, + first_accessible = FirstAcc, + not_accessible = NoAccs, + index_types = Aug}; +make_table_info(Line, TableName, {indexes, []}, _ColumnMEs) -> print_error("Table ~w lacks indexes.", [TableName],Line), #table_info{}; -make_table_info(Line, TableName, {indexes,Indexes}, ColumnMEs) -> +make_table_info(Line, TableName, {indexes, Indexes}, ColumnMEs) -> ColMEs = lists:keysort(#me.oid, ColumnMEs), NonImpliedIndexes = lists:map(fun non_implied_name/1, Indexes), test_read_create_access(ColMEs, Line, dummy), @@ -860,11 +866,17 @@ get_asn1_type(ColumnName, MEs, Line) -> end. test_index_positions(Line, Indexes, ColMEs) -> - TLI = lists:filter(fun (IndexName) -> - is_table_local_index(IndexName,ColMEs) end, - Indexes), + IsTLI = fun(IndexName) -> is_table_local_index(IndexName, ColMEs) end, + TLI = lists:filter(IsTLI, Indexes), test_index_positions_impl(Line, TLI, ColMEs). +%% An table that augments another cannot conatin any index, +%% so the first non-index column is always the first column. +augments_first_non_index_column([]) -> + none; +augments_first_non_index_column([#me{oid=Col}|_ColMEs]) -> + Col. + %% Returns the first non-index column | none test_index_positions_impl(_Line, [], []) -> none; test_index_positions_impl(_Line, [], [#me{oid=Col}|_ColMEs]) -> diff --git a/lib/snmp/src/manager/Makefile b/lib/snmp/src/manager/Makefile index c1d5703300..1f30669f02 100644 --- a/lib/snmp/src/manager/Makefile +++ b/lib/snmp/src/manager/Makefile @@ -111,13 +111,13 @@ info: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/manager - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/manager - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/manager" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src/manager" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/snmp/src/manager/depend.mk b/lib/snmp/src/manager/depend.mk index 0e7e9e3df7..2e7783c8ed 100644 --- a/lib/snmp/src/manager/depend.mk +++ b/lib/snmp/src/manager/depend.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2009. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. All Rights Reserved. # # The contents of this file are subject to the Erlang Public License, # Version 1.1, (the "License"); you may not use this file except in @@ -49,6 +49,13 @@ $(EBIN)/snmpm_net_if.$(EMULATOR): \ snmpm_net_if.erl \ snmpm_network_interface.erl +$(EBIN)/snmpm_net_if_mt.$(EMULATOR): \ + ../../include/snmp_types.hrl \ + ../misc/snmp_debug.hrl \ + ../misc/snmp_verbosity.hrl \ + snmpm_net_if_mt.erl \ + snmpm_network_interface.erl + $(EBIN)/snmpm_server.$(EMULATOR): \ ../../include/snmp_types.hrl \ ../../include/STANDARD-MIB.hrl \ diff --git a/lib/snmp/src/manager/modules.mk b/lib/snmp/src/manager/modules.mk index 79f3dd65d9..d46545ea3f 100644 --- a/lib/snmp/src/manager/modules.mk +++ b/lib/snmp/src/manager/modules.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2009. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. 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 @@ -31,6 +31,7 @@ MODULES = \ snmpm_mpd \ snmpm_misc_sup \ snmpm_net_if \ + snmpm_net_if_mt \ snmpm_net_if_filter \ snmpm_server \ snmpm_server_sup \ diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index 6d2ac8d747..f590892c66 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -75,8 +75,11 @@ %% %% Logging + log_to_txt/1, log_to_txt/2, log_to_txt/3, log_to_txt/4, log_to_txt/5, log_to_txt/6, log_to_txt/7, + log_to_io/1, log_to_io/2, log_to_io/3, + log_to_io/4, log_to_io/5, log_to_io/6, change_log_size/1, get_log_type/0, set_log_type/1, @@ -1371,6 +1374,9 @@ cancel_async_request(UserId, ReqId) -> %%%----------------------------------------------------------------- %%% Audit Trail Log functions (for backward compatibility) %%%----------------------------------------------------------------- + +log_to_txt(LogDir) -> + log_to_txt(LogDir, []). log_to_txt(LogDir, Mibs) -> OutFile = "snmpm_log.txt", LogName = ?audit_trail_log_name, @@ -1391,6 +1397,23 @@ log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop) -> snmp:log_to_txt(LogDir, Mibs, OutFile, LogName, LogFile, Start, Stop). +log_to_io(LogDir) -> + log_to_io(LogDir, []). +log_to_io(LogDir, Mibs) -> + LogName = ?audit_trail_log_name, + LogFile = ?audit_trail_log_file, + snmp:log_to_io(LogDir, Mibs, LogName, LogFile). +log_to_io(LogDir, Mibs, LogName) -> + LogFile = ?audit_trail_log_file, + snmp:log_to_io(LogDir, Mibs, LogName, LogFile). +log_to_io(LogDir, Mibs, LogName, LogFile) -> + snmp:log_to_io(LogDir, Mibs, LogName, LogFile). +log_to_io(LogDir, Mibs, LogName, LogFile, Start) -> + snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start). +log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop) -> + snmp:log_to_io(LogDir, Mibs, LogName, LogFile, Start, Stop). + + change_log_size(NewSize) -> LogName = ?audit_trail_log_name, snmp:change_log_size(LogName, NewSize). diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl index c2e57abddb..5bbf9e5542 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-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 diff --git a/lib/snmp/src/manager/snmpm_mpd.erl b/lib/snmp/src/manager/snmpm_mpd.erl index 103c87d32b..d7148bb4a4 100644 --- a/lib/snmp/src/manager/snmpm_mpd.erl +++ b/lib/snmp/src/manager/snmpm_mpd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -110,9 +110,9 @@ process_msg(Msg, Domain, Addr, Port, State, NoteStore, Logger) -> #message{version = 'version-2', vsn_hdr = Community, data = Data} when State#state.v2c =:= true -> HS = ?empty_msg_size + length(Community), - (catch process_v1_v2c_msg('version-2', NoteStore, Msg, - Domain, Addr, Port, - Community, Data, HS, Logger)); + 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} diff --git a/lib/snmp/src/manager/snmpm_net_if_mt.erl b/lib/snmp/src/manager/snmpm_net_if_mt.erl new file mode 100644 index 0000000000..3e87f6a7fb --- /dev/null +++ b/lib/snmp/src/manager/snmpm_net_if_mt.erl @@ -0,0 +1,1259 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% + +-module(snmpm_net_if_mt). + +-behaviour(gen_server). +-behaviour(snmpm_network_interface). + + +%% Network Interface callback functions +-export([ + start_link/2, + stop/1, + send_pdu/6, % Backward compatibillity + send_pdu/7, % Backward compatibillity + send_pdu/8, + + inform_response/4, + + note_store/2, + + info/1, + verbosity/2, + %% system_info_updated/2, + get_log_type/1, set_log_type/2, + filter_reset/1 + ]). + +%% gen_server callbacks +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + code_change/3, terminate/2]). + +-define(SNMP_USE_V3, true). +-include("snmp_types.hrl"). +-include("snmpm_internal.hrl"). +-include("snmpm_atl.hrl"). +-include("snmp_debug.hrl"). + +%% -define(VMODULE,"NET_IF"). +-include("snmp_verbosity.hrl"). + +-record(state, + { + server, + note_store, + sock, + mpd_state, + log, + irb = auto, % auto | {user, integer()} + irgc, + filter + }). + + +-define(DEFAULT_FILTER_MODULE, snmpm_net_if_filter). +-define(DEFAULT_FILTER_OPTS, [{module, ?DEFAULT_FILTER_MODULE}]). + +-ifdef(snmp_debug). +-define(GS_START_LINK(Args), + gen_server:start_link(?MODULE, Args, [{debug,[trace]}])). +-else. +-define(GS_START_LINK(Args), + gen_server:start_link(?MODULE, Args, [])). +-endif. + + +-define(IRGC_TIMEOUT, timer:minutes(5)). + +-define(ATL_SEQNO_INITIAL, 1). +-define(ATL_SEQNO_MAX, 2147483647). + + +%%%------------------------------------------------------------------- +%%% API +%%%------------------------------------------------------------------- +start_link(Server, NoteStore) -> + ?d("start_link -> entry with" + "~n Server: ~p" + "~n NoteStore: ~p", [Server, NoteStore]), + Args = [Server, NoteStore], + ?GS_START_LINK(Args). + +stop(Pid) -> + call(Pid, stop). + +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) -> + 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, Domain, Addr, Port]), + cast(Pid, {send_pdu, Pdu, Vsn, MsgData, Domain, Addr, Port, ExtraInfo}). + +note_store(Pid, NoteStore) -> + call(Pid, {note_store, NoteStore}). + +inform_response(Pid, Ref, Addr, Port) -> + cast(Pid, {inform_response, Ref, Addr, Port}). + +info(Pid) -> + call(Pid, info). + +verbosity(Pid, V) -> + call(Pid, {verbosity, V}). + +%% system_info_updated(Pid, What) -> +%% call(Pid, {system_info_updated, What}). + +get_log_type(Pid) -> + call(Pid, get_log_type). + +set_log_type(Pid, NewType) -> + call(Pid, {set_log_type, NewType}). + +filter_reset(Pid) -> + cast(Pid, filter_reset). + + +%%%------------------------------------------------------------------- +%%% Callback functions from gen_server +%%%------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Func: init/1 +%% Returns: {ok, State} | +%% {ok, State, Timeout} | +%% ignore | +%% {stop, Reason} +%%-------------------------------------------------------------------- +init([Server, NoteStore]) -> + ?d("init -> entry with" + "~n Server: ~p" + "~n NoteStore: ~p", [Server, NoteStore]), + case (catch do_init(Server, NoteStore)) of + {error, Reason} -> + {stop, Reason}; + {ok, State} -> + {ok, State} + end. + +do_init(Server, NoteStore) -> + process_flag(trap_exit, true), + + %% -- Prio -- + {ok, Prio} = snmpm_config:system_info(prio), + process_flag(priority, Prio), + + %% -- Create inform request table -- + %% This should really be protected, but it also needs to + %% be writable for the worker processes, so... + ets:new(snmpm_inform_request_table, + [set, public, named_table, {keypos, 1}]), + + %% -- Verbosity -- + {ok, Verbosity} = snmpm_config:system_info(net_if_verbosity), + put(sname, mnif), + put(verbosity, Verbosity), + ?vlog("starting", []), + + %% -- MPD -- + {ok, Vsns} = snmpm_config:system_info(versions), + MpdState = snmpm_mpd:init(Vsns), + ?vdebug("MpdState: ~w", [MpdState]), + + %% -- Module dependent options -- + {ok, Opts} = snmpm_config:system_info(net_if_options), + + %% -- Inform response behaviour -- + {ok, IRB} = snmpm_config:system_info(net_if_irb), + IrGcRef = irgc_start(IRB), + + %% -- Socket -- + SndBuf = get_opt(Opts, sndbuf, default), + RecBuf = get_opt(Opts, recbuf, default), + BindTo = get_opt(Opts, bind_to, false), + NoReuse = get_opt(Opts, no_reuse, false), + {ok, Port} = snmpm_config:system_info(port), + {ok, Sock} = do_open_port(Port, SndBuf, RecBuf, BindTo, NoReuse), + + %% Flow control -- + FilterOpts = get_opt(Opts, filter, []), + FilterMod = create_filter(FilterOpts), + ?vdebug("FilterMod: ~w", [FilterMod]), + + %% -- Audit trail log --- + {ok, ATL} = snmpm_config:system_info(audit_trail_log), + Log = do_init_log(ATL), + ?vdebug("Log: ~w", [Log]), + + %% -- Initiate counters --- + init_counters(), + + %% -- We are done --- + State = #state{server = Server, + note_store = NoteStore, + mpd_state = MpdState, + sock = Sock, + log = Log, + irb = IRB, + irgc = IrGcRef, + filter = FilterMod}, + ?vdebug("started", []), + {ok, State}. + + +%% Open port +do_open_port(Port, SendSz, RecvSz, BindTo, NoReuse) -> + ?vtrace("do_open_port -> entry with" + "~n Port: ~p" + "~n SendSz: ~p" + "~n RecvSz: ~p" + "~n BindTo: ~p" + "~n NoReuse: ~p", [Port, SendSz, RecvSz, BindTo, NoReuse]), + IpOpts1 = bind_to(BindTo), + IpOpts2 = no_reuse(NoReuse), + IpOpts3 = recbuf(RecvSz), + IpOpts4 = sndbuf(SendSz), + IpOpts = [binary | IpOpts1 ++ IpOpts2 ++ IpOpts3 ++ IpOpts4], + OpenRes = + case init:get_argument(snmpm_fd) of + {ok, [[FdStr]]} -> + Fd = list_to_integer(FdStr), + gen_udp:open(0, [{fd, Fd}|IpOpts]); + error -> + gen_udp:open(Port, IpOpts) + end, + case OpenRes of + {error, _} = Error -> + throw(Error); + OK -> + OK + end. + +bind_to(true) -> + case snmpm_config:system_info(address) of + {ok, Addr} when is_list(Addr) -> + [{ip, list_to_tuple(Addr)}]; + {ok, Addr} -> + [{ip, Addr}]; + _ -> + [] + end; +bind_to(_) -> + []. + +no_reuse(false) -> + [{reuseaddr, true}]; +no_reuse(_) -> + []. + +recbuf(default) -> + []; +recbuf(Sz) -> + [{recbuf, Sz}]. + +sndbuf(default) -> + []; +sndbuf(Sz) -> + [{sndbuf, Sz}]. + + +create_filter(Opts) when is_list(Opts) -> + case get_opt(Opts, module, ?DEFAULT_FILTER_MODULE) of + ?DEFAULT_FILTER_MODULE = Mod -> + Mod; + Module -> + snmpm_network_interface_filter:verify(Module), + Module + end; +create_filter(BadOpts) -> + throw({error, {bad_filter_opts, BadOpts}}). + + +%% ---------------------------------------------------------------------- +%% Audit Trail Logger +%% ---------------------------------------------------------------------- + +%% Open log +do_init_log(false) -> + ?vtrace("do_init_log(false) -> entry", []), + undefined; +do_init_log(true) -> + ?vtrace("do_init_log(true) -> entry", []), + {ok, Type} = snmpm_config:system_info(audit_trail_log_type), + {ok, Dir} = snmpm_config:system_info(audit_trail_log_dir), + {ok, Size} = snmpm_config:system_info(audit_trail_log_size), + {ok, Repair} = snmpm_config:system_info(audit_trail_log_repair), + Name = ?audit_trail_log_name, + File = filename:absname(?audit_trail_log_file, Dir), + case snmpm_config:system_info(audit_trail_log_seqno) of + {ok, true} -> + Initial = ?ATL_SEQNO_INITIAL, + Max = ?ATL_SEQNO_MAX, + Module = snmpm_config, + Function = increment_counter, + Args = [atl_seqno, Initial, Max], + SeqNoGen = {Module, Function, Args}, + case snmp_log:create(Name, File, SeqNoGen, Size, Repair, true) of + {ok, Log} -> + ?vdebug("log created: ~w", [Log]), + {Name, Log, Type}; + {error, Reason} -> + throw({error, {failed_create_audit_log, Reason}}) + end; + _ -> + case snmp_log:create(Name, File, Size, Repair, true) of + {ok, Log} -> + ?vdebug("log created: ~w", [Log]), + {Name, Log, Type}; + {error, Reason} -> + throw({error, {failed_create_audit_log, Reason}}) + end + end. + + +%% ---------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +%% Func: handle_call/3 +%% Returns: {reply, Reply, State} | +%% {reply, Reply, State, Timeout} | +%% {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, Reply, State} | (terminate/2 is called) +%% {stop, Reason, State} (terminate/2 is called) +%%-------------------------------------------------------------------- +handle_call({verbosity, Verbosity}, _From, State) -> + ?vlog("received verbosity request", []), + put(verbosity, Verbosity), + {reply, ok, State}; + +%% handle_call({system_info_updated, What}, _From, State) -> +%% ?vlog("received system_info_updated request with What = ~p", [What]), +%% {NewState, Reply} = handle_system_info_updated(State, What), +%% {reply, Reply, NewState}; + +handle_call(get_log_type, _From, State) -> + ?vlog("received get-log-type request", []), + Reply = (catch handle_get_log_type(State)), + {reply, Reply, State}; + +handle_call({set_log_type, NewType}, _From, State) -> + ?vlog("received set-log-type request with NewType = ~p", [NewType]), + {NewState, Reply} = (catch handle_set_log_type(State, NewType)), + {reply, Reply, NewState}; + +handle_call({note_store, Pid}, _From, State) -> + ?vlog("received new note_store: ~w", [Pid]), + {reply, ok, State#state{note_store = Pid}}; + +handle_call(stop, _From, State) -> + ?vlog("received stop request", []), + Reply = ok, + {stop, normal, Reply, State}; + +handle_call(info, _From, State) -> + ?vlog("received info request", []), + Reply = get_info(State), + {reply, Reply, State}; + +handle_call(Req, From, State) -> + warning_msg("received unknown request (from ~p): ~n~p", [Req, From]), + {reply, {error, {invalid_request, Req}}, State}. + + +%%-------------------------------------------------------------------- +%% Func: handle_cast/2 +%% Returns: {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} (terminate/2 is called) +%%-------------------------------------------------------------------- +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, Domain, Addr, Port]), + maybe_process_extra_info(ExtraInfo), + handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, Port, State), + {noreply, State}; + +handle_cast({inform_response, Ref, Addr, Port}, State) -> + ?vlog("received inform_response message with" + "~n Ref: ~p" + "~n Addr: ~p" + "~n Port: ~p", [Ref, Addr, Port]), + handle_inform_response(Ref, Addr, Port, State), + {noreply, State}; + +handle_cast(filter_reset, State) -> + ?vlog("received filter_reset message", []), + reset_counters(), + {noreply, State}; + +handle_cast(Msg, State) -> + warning_msg("received unknown message: ~n~p", [Msg]), + {noreply, State}. + + +%%-------------------------------------------------------------------- +%% Func: handle_info/2 +%% Returns: {noreply, State} | +%% {noreply, State, Timeout} | +%% {stop, Reason, State} (terminate/2 is called) +%%-------------------------------------------------------------------- +handle_info({udp, Sock, Ip, Port, Bytes}, #state{sock = Sock} = State) -> + ?vlog("received ~w bytes from ~p:~p", [size(Bytes), Ip, Port]), + handle_udp(Ip, Port, Bytes, State), + {noreply, State}; + +handle_info(inform_response_gc, State) -> + ?vlog("received inform_response_gc message", []), + State2 = handle_inform_response_gc(State), + {noreply, State2}; + +handle_info({disk_log, _Node, Log, Info}, State) -> + ?vlog("received disk_log message: " + "~n Info: ~p", [Info]), + State2 = handle_disk_log(Log, Info, State), + {noreply, State2}; + +handle_info({'DOWN', _MRef, process, Pid, {net_if_worker, ExitStatus}}, + State) -> + ?vdebug("received DOWN message from net_if-worker: " + "~n ExitStatus: ~p", [ExitStatus]), + handle_worker_exit(Pid, ExitStatus), + {noreply, State}; + +handle_info(Info, State) -> + warning_msg("received unknown info: ~n~p", [Info]), + {noreply, State}. + + +%%-------------------------------------------------------------------- +%% Func: terminate/2 +%% Purpose: Shutdown the server +%% Returns: any (ignored by gen_server) +%%-------------------------------------------------------------------- +terminate(Reason, #state{log = Log, irgc = IrGcRef}) -> + ?vdebug("terminate: ~p", [Reason]), + irgc_stop(IrGcRef), + %% Close logs + do_close_log(Log), + ok. + + +do_close_log({Log, _Type}) -> + (catch snmp_log:sync(Log)), + (catch snmp_log:close(Log)), + ok; +do_close_log(_) -> + ok. + + +%%---------------------------------------------------------------------- +%% Func: code_change/3 +%% Purpose: Convert process state when code is changed +%% Returns: {ok, NewState} +%%---------------------------------------------------------------------- + +code_change(_Vsn, State, _Extra) -> + ?d("code_change -> entry with" + "~n Vsn: ~p" + "~n State: ~p" + "~n Extra: ~p", [_Vsn, State, _Extra]), + {ok, State}. + + +%%%------------------------------------------------------------------- +%%% Internal functions +%%%------------------------------------------------------------------- + +handle_udp(Addr, Port, Bytes, State) -> + Verbosity = get(verbosity), + spawn_opt(fun() -> + Log = worker_init(State, Verbosity), + Res = (catch maybe_handle_recv_msg( + Addr, Port, Bytes, + State#state{log = Log})), + worker_exit(udp, {Addr, Port}, Res) + end, + [monitor]). + + +maybe_handle_recv_msg(Addr, Port, Bytes, #state{filter = FilterMod} = State) -> + case (catch FilterMod:accept_recv(Addr, Port)) of + false -> + %% Drop the received packet + inc(netIfMsgInDrops), + ok; + _ -> + handle_recv_msg(Addr, Port, Bytes, State) + end. + + +handle_recv_msg(Addr, Port, Bytes, #state{server = Pid}) + when is_binary(Bytes) andalso (size(Bytes) =:= 0) -> + Pid ! {snmp_error, {empty_message, Addr, Port}, Addr, Port}, + ok; + +handle_recv_msg(Addr, Port, Bytes, + #state{server = Pid, + note_store = NoteStore, + 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, Domain, Addr, Port, + MpdState, NoteStore, Logger)) of + + {ok, Vsn, Pdu, MS, ACM} -> + maybe_handle_recv_pdu(Addr, Port, Vsn, Pdu, MS, ACM, + Logger, State); + + {discarded, Reason, Report} -> + ?vdebug("discarded: ~p", [Reason]), + ErrorInfo = {failed_processing_message, Reason}, + Pid ! {snmp_error, ErrorInfo, Addr, Port}, + maybe_udp_send(State#state.filter, Sock, Addr, Port, Report), + ok; + + {discarded, Reason} -> + ?vdebug("discarded: ~p", [Reason]), + ErrorInfo = {failed_processing_message, Reason}, + Pid ! {snmp_error, ErrorInfo, Addr, Port}, + ok; + + Error -> + error_msg("processing of received message failed: " + "~n ~p", [Error]), + ok + end. + + +maybe_handle_recv_pdu(Addr, Port, + Vsn, #pdu{type = Type} = Pdu, PduMS, ACM, + Logger, + #state{filter = FilterMod} = State) -> + case (catch FilterMod:accept_recv_pdu(Addr, Port, Type)) of + false -> + inc(netIfPduInDrops), + ok; + _ -> + handle_recv_pdu(Addr, Port, Vsn, Pdu, PduMS, ACM, Logger, State) + end; +maybe_handle_recv_pdu(Addr, Port, Vsn, Trap, PduMS, ACM, Logger, + #state{filter = FilterMod} = State) + when is_record(Trap, trappdu) -> + case (catch FilterMod:accept_recv_pdu(Addr, Port, trappdu)) of + false -> + inc(netIfPduInDrops), + ok; + _ -> + handle_recv_pdu(Addr, Port, Vsn, Trap, PduMS, ACM, Logger, State) + end; +maybe_handle_recv_pdu(Addr, Port, Vsn, Pdu, PduMS, ACM, Logger, State) -> + handle_recv_pdu(Addr, Port, Vsn, Pdu, PduMS, ACM, Logger, State). + + +handle_recv_pdu(Addr, Port, + Vsn, #pdu{type = 'inform-request'} = Pdu, _PduMS, ACM, + Logger, #state{server = Pid, irb = IRB} = State) -> + handle_inform_request(IRB, Pid, Vsn, Pdu, ACM, + Addr, Port, Logger, State); +handle_recv_pdu(Addr, Port, + _Vsn, #pdu{type = report} = Pdu, _PduMS, ok, + _Logger, + #state{server = Pid} = _State) -> + ?vtrace("received report - ok", []), + Pid ! {snmp_report, {ok, Pdu}, Addr, Port}, + ok; +handle_recv_pdu(Addr, Port, + _Vsn, #pdu{type = report} = Pdu, _PduMS, + {error, ReqId, Reason}, + _Logger, + #state{server = Pid} = _State) -> + ?vtrace("received report - error", []), + Pid ! {snmp_report, {error, ReqId, Reason, Pdu}, Addr, Port}, + ok; +handle_recv_pdu(Addr, Port, + _Vsn, #pdu{type = 'snmpv2-trap'} = Pdu, _PduMS, _ACM, + _Logger, + #state{server = Pid} = _State) -> + ?vtrace("received snmpv2-trap", []), + Pid ! {snmp_trap, Pdu, Addr, Port}, + ok; +handle_recv_pdu(Addr, Port, + _Vsn, Trap, _PduMS, _ACM, + _Logger, + #state{server = Pid} = _State) when is_record(Trap, trappdu) -> + ?vtrace("received trappdu", []), + Pid ! {snmp_trap, Trap, Addr, Port}, + ok; +handle_recv_pdu(Addr, Port, + _Vsn, Pdu, _PduMS, _ACM, + _Logger, + #state{server = Pid} = _State) when is_record(Pdu, pdu) -> + ?vtrace("received pdu", []), + Pid ! {snmp_pdu, Pdu, Addr, Port}, + ok; +handle_recv_pdu(_Addr, _Port, _Vsn, Pdu, _PduMS, ACM, _Logger, _State) -> + ?vlog("received unexpected pdu: " + "~n Pdu: ~p" + "~n ACM: ~p", [Pdu, ACM]), + ok. + + +handle_inform_request(auto, Pid, Vsn, Pdu, ACM, Addr, Port, Logger, State) -> + ?vtrace("received inform-request (true)", []), + Pid ! {snmp_inform, ignore, Pdu, Addr, Port}, + RePdu = make_response_pdu(Pdu), + maybe_send_inform_response(RePdu, Vsn, ACM, Addr, Port, Logger, State); +handle_inform_request({user, To}, Pid, Vsn, #pdu{request_id = ReqId} = Pdu, + ACM, Addr, Port, _Logger, _State) -> + ?vtrace("received inform-request (false)", []), + + Pid ! {snmp_inform, ReqId, Pdu, Addr, Port}, + + %% Before we go any further, we need to check that we have not + %% already received this message (possible resend). + + Key = {ReqId, Addr, Port}, + case ets:lookup(snmpm_inform_request_table, Key) of + [_] -> + %% OK, we already know about this. We assume this + %% is a resend. Either the agent is really eager or + %% the user has not answered yet. Bad user! + ok; + [] -> + RePdu = make_response_pdu(Pdu), + Expire = t() + To, + Rec = {Key, Expire, {Vsn, ACM, RePdu}}, + ets:insert(snmpm_inform_request_table, Rec) + end, + ok. + +handle_inform_response(Ref, Addr, Port, State) -> + Verbosity = get(verbosity), + spawn_opt(fun() -> + Log = worker_init(State, Verbosity), + Res = (catch do_handle_inform_response( + Ref, + Addr, Port, + State#state{log = Log})), + worker_exit(inform_reponse, {Addr, Port}, Res) + end, + [monitor]). + + + +do_handle_inform_response(Ref, Addr, Port, State) -> + Key = {Ref, Addr, Port}, + case ets:lookup(snmpm_inform_request_table, Key) of + [{Key, _, {Vsn, ACM, RePdu}}] -> + Logger = logger(State#state.log, read, Addr, Port), + ets:delete(snmpm_inform_request_table, Key), + maybe_send_inform_response(RePdu, Vsn, ACM, Addr, Port, + Logger, State); + [] -> + %% Already acknowledged, or the user was to slow to reply... + ok + end, + ok. + +maybe_send_inform_response(RePdu, Vsn, ACM, Addr, Port, Logger, + #state{server = Pid, + sock = Sock, + filter = FilterMod}) -> + case (catch FilterMod:accept_send_pdu(Addr, Port, pdu_type_of(RePdu))) of + false -> + inc(netIfPduOutDrops), + ok; + _ -> + case snmpm_mpd:generate_response_msg(Vsn, RePdu, ACM, Logger) of + {ok, Msg} -> + maybe_udp_send(FilterMod, Sock, Addr, Port, Msg); + {discarded, Reason} -> + ?vlog("failed generating response message:" + "~n Reason: ~p", [Reason]), + ReqId = RePdu#pdu.request_id, + ErrorInfo = {failed_generating_response, {RePdu, Reason}}, + Pid ! {snmp_error, ReqId, ErrorInfo, Addr, Port}, + ok + end + end. + +handle_inform_response_gc(#state{irb = IRB} = State) -> + ets:safe_fixtable(snmpm_inform_request_table, true), + do_irgc(ets:first(snmpm_inform_request_table), t()), + ets:safe_fixtable(snmpm_inform_request_table, false), + State#state{irgc = irgc_start(IRB)}. + +%% We are deleting at the same time as we are traversing the table!!! +do_irgc('$end_of_table', _) -> + ok; +do_irgc(Key, Now) -> + Next = ets:next(snmpm_inform_request_table, Key), + case ets:lookup(snmpm_inform_request_table, Key) of + [{Key, BestBefore, _}] when BestBefore < Now -> + ets:delete(snmpm_inform_request_table, Key); + _ -> + ok + end, + do_irgc(Next, Now). + +irgc_start(auto) -> + undefined; +irgc_start(_) -> + erlang:send_after(?IRGC_TIMEOUT, self(), inform_response_gc). + +irgc_stop(undefined) -> + ok; +irgc_stop(Ref) -> + (catch erlang:cancel_timer(Ref)). + + +handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, Port, State) -> + Verbosity = get(verbosity), + spawn_opt(fun() -> + Log = worker_init(State, Verbosity), + Res = (catch maybe_handle_send_pdu( + Pdu, Vsn, MsgData, + Domain, Addr, Port, + State#state{log = Log})), + worker_exit(send_pdu, {Domain, Addr, Port}, Res) + end, + [monitor]). + +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; + _ -> + do_handle_send_pdu(Pdu, Vsn, MsgData, Domain, Addr, Port, State) + end. + +do_handle_send_pdu(Pdu, Vsn, MsgData, _Domain, Addr, Port, + #state{server = Pid, + note_store = NoteStore, + sock = Sock, + log = Log, + filter = FilterMod}) -> + Logger = logger(Log, write, Addr, Port), + case (catch snmpm_mpd:generate_msg(Vsn, NoteStore, + Pdu, MsgData, Logger)) of + {ok, Msg} -> + ?vtrace("do_handle_send_pdu -> message generated", []), + maybe_udp_send(FilterMod, Sock, Addr, Port, Msg); + {discarded, Reason} -> + ?vlog("PDU not sent: " + "~n PDU: ~p" + "~n Reason: ~p", [Pdu, Reason]), + Pid ! {snmp_error, Pdu, Reason}, + ok + end. + + +maybe_udp_send(FilterMod, Sock, Addr, Port, Msg) -> + case (catch FilterMod:accept_send(Addr, Port)) of + false -> + inc(netIfMsgOutDrops), + ok; + _ -> + udp_send(Sock, Addr, Port, Msg) + end. + + +udp_send(Sock, Addr, Port, Msg) -> + case (catch gen_udp:send(Sock, Addr, Port, Msg)) of + ok -> + ?vdebug("sent ~w bytes to ~w:~w [~w]", + [sz(Msg), Addr, Port, Sock]), + ok; + {error, Reason} -> + error_msg("failed sending message to ~p:~p: " + "~n ~p",[Addr, Port, Reason]), + ok; + Error -> + error_msg("failed sending message to ~p:~p: " + "~n ~p",[Addr, Port, Error]), + ok + end. + +sz(B) when is_binary(B) -> + size(B); +sz(L) when is_list(L) -> + length(L); +sz(_) -> + undefined. + + +handle_disk_log(_Log, {wrap, NoLostItems}, State) -> + ?vlog("Audit Trail Log - wrapped: ~w previously logged items where lost", + [NoLostItems]), + State; +handle_disk_log(_Log, {truncated, NoLostItems}, State) -> + ?vlog("Audit Trail Log - truncated: ~w items where lost when truncating", + [NoLostItems]), + State; +handle_disk_log(_Log, full, State) -> + error_msg("Failed to write to Audit Trail Log (full)", []), + State; +handle_disk_log(_Log, {error_status, ok}, State) -> + State; +handle_disk_log(_Log, {error_status, {error, Reason}}, State) -> + error_msg("Error status received from Audit Trail Log: " + "~n~p", [Reason]), + State; +handle_disk_log(_Log, _Info, State) -> + State. + + +%% mk_discovery_msg('version-3', Pdu, _VsnHdr, UserName) -> +%% ScopedPDU = #scopedPdu{contextEngineID = "", +%% contextName = "", +%% data = Pdu}, +%% Bytes = snmp_pdus:enc_scoped_pdu(ScopedPDU), +%% MsgID = get(msg_id), +%% put(msg_id,MsgID+1), +%% UsmSecParams = +%% #usmSecurityParameters{msgAuthoritativeEngineID = "", +%% msgAuthoritativeEngineBoots = 0, +%% msgAuthoritativeEngineTime = 0, +%% msgUserName = UserName, +%% msgPrivacyParameters = "", +%% msgAuthenticationParameters = ""}, +%% SecBytes = snmp_pdus:enc_usm_security_parameters(UsmSecParams), +%% PduType = Pdu#pdu.type, +%% Hdr = #v3_hdr{msgID = MsgID, +%% msgMaxSize = 1000, +%% msgFlags = snmp_misc:mk_msg_flags(PduType, 0), +%% msgSecurityModel = ?SEC_USM, +%% msgSecurityParameters = SecBytes}, +%% Msg = #message{version = 'version-3', vsn_hdr = Hdr, data = Bytes}, +%% case (catch snmp_pdus:enc_message_only(Msg)) of +%% {'EXIT', Reason} -> +%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]), +%% error; +%% L when list(L) -> +%% {Msg, L} +%% end; +%% mk_discovery_msg(Version, Pdu, {Com, _, _, _, _}, UserName) -> +%% Msg = #message{version = Version, vsn_hdr = Com, data = Pdu}, +%% case catch snmp_pdus:enc_message(Msg) of +%% {'EXIT', Reason} -> +%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]), +%% error; +%% L when list(L) -> +%% {Msg, L} +%% end. + + +%% mk_msg('version-3', Pdu, {Context, User, EngineID, CtxEngineId, SecLevel}, +%% MsgData) -> +%% %% Code copied from snmp_mpd.erl +%% {MsgId, SecName, SecData} = +%% if +%% tuple(MsgData), Pdu#pdu.type == 'get-response' -> +%% MsgData; +%% true -> +%% Md = get(msg_id), +%% put(msg_id, Md + 1), +%% {Md, User, []} +%% end, +%% ScopedPDU = #scopedPdu{contextEngineID = CtxEngineId, +%% contextName = Context, +%% data = Pdu}, +%% ScopedPDUBytes = snmp_pdus:enc_scoped_pdu(ScopedPDU), + +%% PduType = Pdu#pdu.type, +%% V3Hdr = #v3_hdr{msgID = MsgId, +%% msgMaxSize = 1000, +%% msgFlags = snmp_misc:mk_msg_flags(PduType, SecLevel), +%% msgSecurityModel = ?SEC_USM}, +%% Message = #message{version = 'version-3', vsn_hdr = V3Hdr, +%% data = ScopedPDUBytes}, +%% SecEngineID = case PduType of +%% 'get-response' -> snmp_framework_mib:get_engine_id(); +%% _ -> EngineID +%% end, +%% case catch snmp_usm:generate_outgoing_msg(Message, SecEngineID, +%% SecName, SecData, SecLevel) of +%% {'EXIT', Reason} -> +%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]), +%% error; +%% {error, Reason} -> +%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]), +%% error; +%% Packet -> +%% Packet +%% end; +%% mk_msg(Version, Pdu, {Com, _User, _EngineID, _Ctx, _SecLevel}, _SecData) -> +%% Msg = #message{version = Version, vsn_hdr = Com, data = Pdu}, +%% case catch snmp_pdus:enc_message(Msg) of +%% {'EXIT', Reason} -> +%% error("Encoding error. Pdu: ~w. Reason: ~w",[Pdu, Reason]), +%% error; +%% B when list(B) -> +%% B +%% end. + + +%% handle_system_info_updated(#state{log = {Log, _OldType}} = State, +%% audit_trail_log_type = _What) -> +%% %% Just to make sure, check that ATL is actually enabled +%% case snmpm_config:system_info(audit_trail_log) of +%% {ok, true} -> +%% {ok, Type} = snmpm_config:system_info(audit_trail_log_type), +%% NewState = State#state{log = {Log, Type}}, +%% {NewState, ok}; +%% _ -> +%% {State, {error, {adt_not_enabled}}} +%% end; +%% handle_system_info_updated(_State, _What) -> +%% ok. + +handle_get_log_type(#state{log = {_Log, Value}} = State) -> + %% Just to make sure, check that ATL is actually enabled + case snmpm_config:system_info(audit_trail_log) of + {ok, true} -> + Type = + case {lists:member(read, Value), lists:member(write, Value)} of + {true, true} -> + read_write; + {true, false} -> + read; + {false, true} -> + write; + {false, false} -> + throw({State, {error, {bad_atl_type, Value}}}) + end, + {ok, Type}; + _ -> + {error, not_enabled} + end; +handle_get_log_type(_State) -> + {error, not_enabled}. + +handle_set_log_type(#state{log = {Log, OldValue}} = State, NewType) -> + %% Just to make sure, check that ATL is actually enabled + case snmpm_config:system_info(audit_trail_log) of + {ok, true} -> + NewValue = + case NewType of + read -> + [read]; + write -> + [write]; + read_write -> + [read,write]; + _ -> + throw({State, {error, {bad_atl_type, NewType}}}) + end, + NewState = State#state{log = {Log, NewValue}}, + OldType = + case {lists:member(read, OldValue), + lists:member(write, OldValue)} of + {true, true} -> + read_write; + {true, false} -> + read; + {false, true} -> + write; + {false, false} -> + throw({State, {error, {bad_atl_type, OldValue}}}) + end, + {NewState, {ok, OldType}}; + _ -> + {State, {error, not_enabled}} + end; +handle_set_log_type(State, _NewType) -> + {State, {error, not_enabled}}. + + +%% ------------------------------------------------------------------- + +worker_init(#state{log = undefined = Log}, Verbosity) -> + worker_init2(Log, Verbosity); +worker_init(#state{log = {Name, Log, Type}}, Verbosity) -> + case snmp_log:open(Name, Log) of + {ok, NewLog} -> + worker_init2({Name, NewLog, Type}, Verbosity); + {error, Reason} -> + warning_msg("NetIf worker ~p failed opening ATL: " + "~n ~p", [self(), Reason]), + NewLog = undefined, + worker_init2({Name, NewLog, Type}, Verbosity) + end; +worker_init(State, Verbosity) -> + ?vinfo("worker_init -> entry with invalid data: " + "~n State: ~p" + "~n Verbosity: ~p", [State, Verbosity]), + exit({worker_init, State, Verbosity}). + +worker_init2(Log, Verbosity) -> + put(sname, mnifw), + put(verbosity, Verbosity), + Log. + + +worker_exit(Tag, Info, Result) -> + exit({net_if_worker, {Tag, Info, Result}}). + +handle_worker_exit(_, {_, _, ok}) -> + ok; +handle_worker_exit(Pid, {udp, {Addr, Port}, ExitStatus}) -> + warning_msg("Worker process (~p) terminated " + "while processing (incomming) message from ~w:~w: " + "~n~p", [Pid, Addr, Port, ExitStatus]), + ok; +handle_worker_exit(Pid, {send_pdu, {Domain, Addr, Port}, ExitStatus}) -> + warning_msg("Worker process (~p) terminated " + "while processing (outgoing) pdu for [~w] ~w:~w: " + "~n~p", [Pid, Domain, Addr, Port, ExitStatus]), + ok; +handle_worker_exit(Pid, {inform_response, {Addr, Port}, ExitStatus}) -> + warning_msg("Worker process (~p) terminated " + "while processing (outgoing) inform response for ~w:~w: " + "~n~p", [Pid, Addr, Port, ExitStatus]), + ok; +handle_worker_exit(_, _) -> + ok. + + +%% ------------------------------------------------------------------- + +make_response_pdu(#pdu{request_id = ReqId, varbinds = Vbs}) -> + #pdu{type = 'get-response', + request_id = ReqId, + error_status = noError, + error_index = 0, + varbinds = Vbs}. + + +%% ---------------------------------------------------------------- + +pdu_type_of(#pdu{type = Type}) -> + Type; +pdu_type_of(TrapPdu) when is_record(TrapPdu, trappdu) -> + trap. + + +%% ------------------------------------------------------------------- + +%% At this point this function is used during testing +maybe_process_extra_info(?DEFAULT_EXTRA_INFO) -> + ok; +maybe_process_extra_info({?SNMPM_EXTRA_INFO_TAG, Fun}) + when is_function(Fun, 0) -> + (catch Fun()), + ok; +maybe_process_extra_info(_ExtraInfo) -> + ok. + + +%% ------------------------------------------------------------------- + +t() -> + {A,B,C} = erlang:now(), + A*1000000000+B*1000+(C div 1000). + + +%% ------------------------------------------------------------------- + +logger(undefined, _Type, _Addr, _Port) -> + fun(_) -> + ok + end; +logger({_Name, Log, Types}, Type, Addr, Port) -> + case lists:member(Type, Types) of + true -> + fun(Msg) -> + snmp_log:log(Log, Msg, Addr, Port) + end; + false -> + fun(_) -> + ok + end + end. + + +%% ------------------------------------------------------------------- + +%% info_msg(F, A) -> +%% ?snmpm_info("NET-IF server: " ++ F, A). + +warning_msg(F, A) -> + ?snmpm_warning("NET-IF server: " ++ F, A). + +error_msg(F, A) -> + ?snmpm_error("NET-IF server: " ++ F, A). + + + +%%%------------------------------------------------------------------- + +% get_opt(Key, Opts) -> +% ?vtrace("get option ~w", [Key]), +% snmp_misc:get_option(Key, Opts). + +get_opt(Opts, Key, Def) -> + ?vtrace("get option ~w with default ~p", [Key, Def]), + snmp_misc:get_option(Key, Opts, Def). + + +%% ------------------------------------------------------------------- + +get_info(#state{sock = Id}) -> + ProcSize = proc_mem(self()), + PortInfo = get_port_info(Id), + [{process_memory, ProcSize}, {port_info, PortInfo}]. + +proc_mem(P) when is_pid(P) -> + case (catch erlang:process_info(P, memory)) of + {memory, Sz} when is_integer(Sz) -> + Sz; + _ -> + undefined + end. +%% proc_mem(_) -> +%% undefined. + + +get_port_info(Id) -> + PortInfo = + case (catch erlang:port_info(Id)) of + PI when is_list(PI) -> + [{port_info, PI}]; + _ -> + [] + end, + PortStatus = + case (catch prim_inet:getstatus(Id)) of + {ok, PS} -> + [{port_status, PS}]; + _ -> + [] + end, + PortAct = + case (catch inet:getopts(Id, [active])) of + {ok, PA} -> + [{port_act, PA}]; + _ -> + [] + end, + PortStats = + case (catch inet:getstat(Id)) of + {ok, Stat} -> + [{port_stats, Stat}]; + _ -> + [] + end, + IfList = + case (catch inet:getif(Id)) of + {ok, IFs} -> + [{interfaces, IFs}]; + _ -> + [] + end, + BufSz = + case (catch inet:getopts(Id, [recbuf, sndbuf, buffer])) of + {ok, Sz} -> + [{buffer_size, Sz}]; + _ -> + [] + end, + [{socket, Id}] ++ + IfList ++ + PortStats ++ + PortInfo ++ + PortStatus ++ + PortAct ++ + BufSz. + + +%%----------------------------------------------------------------- +%% Counter functions +%%----------------------------------------------------------------- +init_counters() -> + F = fun(Counter) -> maybe_create_counter(Counter) end, + lists:map(F, counters()). + +reset_counters() -> + F = fun(Counter) -> snmpm_config:reset_stats_counter(Counter) end, + lists:map(F, counters()). + +maybe_create_counter(Counter) -> + snmpm_config:maybe_cre_stats_counter(Counter, 0). + +counters() -> + [ + netIfMsgOutDrops, + netIfMsgInDrops, + netIfPduOutDrops, + netIfPduInDrops + ]. + +inc(Name) -> inc(Name, 1). +inc(Name, N) -> snmpm_config:incr_stats_counter(Name, N). + +%% get_counters() -> +%% Counters = counters(), +%% get_counters(Counters, []). + +%% get_counters([], Acc) -> +%% lists:reverse(Acc); +%% get_counters([Counter|Counters], Acc) -> +%% case snmpm_config:get_stats_counter(Counter) of +%% {ok, CounterVal} -> +%% get_counters(Counters, [{Counter, CounterVal}|Acc]); +%% _ -> +%% get_counters(Counters, Acc) +%% end. + + +%% ---------------------------------------------------------------- + +call(Pid, Req) -> + call(Pid, Req, infinity). + +call(Pid, Req, Timeout) -> + gen_server:call(Pid, Req, Timeout). + +cast(Pid, Msg) -> + gen_server:cast(Pid, Msg). + diff --git a/lib/snmp/src/misc/Makefile b/lib/snmp/src/misc/Makefile index 48d76bdbed..c6a5064d89 100644 --- a/lib/snmp/src/misc/Makefile +++ b/lib/snmp/src/misc/Makefile @@ -109,13 +109,13 @@ info: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/misc - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src/misc - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin -# $(INSTALL_DIR) $(RELSYSDIR)/include -# $(INSTALL_DATA) $(EXT_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/misc" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src/misc" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" +# $(INSTALL_DIR) "$(RELSYSDIR)/include" +# $(INSTALL_DATA) $(EXT_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index 7249def24e..e1e7fab57b 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -71,6 +71,18 @@ %%----------------------------------------------------------------- +%% read_files(Dir, Files) -> Configs +%% Dir - string() - Full path to the config dir. +%% Files - [{Gen, Filter, Check, FileName}] +%% Gen - function/2 - In case of failure when reading the config file, +%% this function is called to either generate a +%% default file or issue the error. +%% Filter - function/1 - Filters all the config entries read from the file +%% Check - function/1 - Check each entry as they are read from the file. +%% FileName - string() - Name of the config file. +%% Configs - [config_entry()] +%% config_entry() - term() + read_files(Dir, Files) when is_list(Dir) andalso is_list(Files) -> read_files(Dir, Files, []). @@ -90,7 +102,7 @@ read_files(Dir, [{Gen, Filter, Check, FileName}|Files], Res) {error, R} -> ?vlog("failed reading file info for ~s: " "~n ~p", [FileName, R]), - Gen(Dir), + Gen(Dir, R), read_files(Dir, Files, [Filter([])|Res]) end. @@ -99,6 +111,7 @@ read_files(Dir, [{Gen, Filter, Check, FileName}|Files], Res) read(File, Check) when is_function(Check) -> ?vdebug("read -> entry with" "~n File: ~p", [File]), + Fd = open_file(File), case loop(Fd, [], Check, 1, File) of diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl index 6ab20e3e48..0bed097b62 100644 --- a/lib/snmp/src/misc/snmp_config.erl +++ b/lib/snmp/src/misc/snmp_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -90,6 +90,17 @@ ]). +-export_type([void/0, + verify_config_entry_function/0, + verify_config_function/0, + write_config_function/0]). + + +%%---------------------------------------------------------------------- + +-type void() :: term(). % Any value - ignored + + %%---------------------------------------------------------------------- %% Handy SNMP configuration %%---------------------------------------------------------------------- @@ -2356,53 +2367,109 @@ write_sys_config_file_manager_atl_opt(Fid, {seqno, SeqNo}) -> header() -> - {Y,Mo,D} = date(), - {H,Mi,S} = time(), + {Y, Mo, D} = date(), + {H, Mi, S} = time(), io_lib:format("%% This file was generated by " - "snmp_config (version-~s) ~w-~2.2.0w-~2.2.0w " + "~w (version-~s) ~w-~2.2.0w-~2.2.0w " "~2.2.0w:~2.2.0w:~2.2.0w\n", - [?version,Y,Mo,D,H,Mi,S]). + [?MODULE, ?version, Y, Mo, D, H, Mi, S]). + +%% *If* these functions are successfull, they successfully return anything +%% (which is ignored), but they fail with either a throw or an exit or +%% something similar. + +%% Verification of one config entry read from a file +-type(verify_config_entry_function() :: + fun((Entry :: term()) -> ok | {error, Reason :: term()})). + +%% Verification of config to be written +-type(verify_config_function() :: + fun(() -> void())). + +%% Write config to file (as defined by Fd) +-type(write_config_function() :: + fun((Fd :: file:io_device()) -> void())). + +-spec write_config_file(Dir :: string(), + FileName :: string(), + Verify :: verify_config_function(), + Write :: write_config_function()) -> + ok | {error, Reason :: term()}. write_config_file(Dir, FileName, Verify, Write) when (is_list(Dir) andalso is_list(FileName) andalso is_function(Verify) andalso is_function(Write)) -> - (catch do_write_config_file(Dir, FileName, Verify, Write)). + try + begin + do_write_config_file(Dir, FileName, Verify, Write) + end + catch + throw:Error -> + Error; + T:E -> + {error, {failed_write, Dir, FileName, T, E}} + end. + do_write_config_file(Dir, FileName, Verify, Write) -> Verify(), case file:open(filename:join(Dir, FileName), [write]) of {ok, Fd} -> - (catch Write(Fd)), - file:close(Fd), - ok; + file_write_and_close(Write, Fd, Dir, FileName); Error -> Error end. - append_config_file(Dir, FileName, Verify, Write) when (is_list(Dir) andalso is_list(FileName) andalso is_function(Verify) andalso is_function(Write)) -> - (catch do_append_config_file(Dir, FileName, Verify, Write)). + try + begin + do_append_config_file(Dir, FileName, Verify, Write) + end + catch + throw:Error -> + Error; + T:E -> + {error, {failed_append, Dir, FileName, T, E}} + end. do_append_config_file(Dir, FileName, Verify, Write) -> Verify(), case file:open(filename:join(Dir, FileName), [read, write]) of {ok, Fd} -> file:position(Fd, eof), - (catch Write(Fd)), - file:close(Fd), - ok; + file_write_and_close(Write, Fd, Dir, FileName); Error -> Error end. +file_write_and_close(Write, Fd, Dir, FileName) -> + ok = Write(Fd), + case file:sync(Fd) of + ok -> + case file:close(Fd) of + ok -> + ok; + {error, Reason} -> + {error, {failed_closing, Dir, FileName, Reason}} + end; + {error, Reason} -> + {error, {failed_syncing, Dir, FileName, Reason}} + end. + + +-spec read_config_file(Dir :: string(), + FileName :: string(), + Verify :: verify_config_entry_function()) -> + {ok, Config :: list()} | {error, Reason :: term()}. + read_config_file(Dir, FileName, Verify) when is_list(Dir) andalso is_list(FileName) andalso is_function(Verify) -> (catch do_read_config_file(Dir, FileName, Verify)). diff --git a/lib/snmp/src/misc/snmp_log.erl b/lib/snmp/src/misc/snmp_log.erl index 2c781810ef..a8c5df0b64 100644 --- a/lib/snmp/src/misc/snmp_log.erl +++ b/lib/snmp/src/misc/snmp_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -21,7 +21,7 @@ -export([ - create/4, create/5, create/6, + create/4, create/5, create/6, open/1, open/2, change_size/2, close/1, sync/1, info/1, log/4, log_to_txt/5, log_to_txt/6, log_to_txt/7, @@ -109,6 +109,24 @@ create(Name, File, SeqNoGen, Size, Repair, Notify) -> {error, {bad_args, Name, File, SeqNoGen, Size, Repair, Notify}}. +%% -- open --- + +%% Open an already existing ( = open ) log + +open(Name) -> + open(Name, #snmp_log{seqno = disabled}). +open(Name, #snmp_log{seqno = SeqNoGen} = _OldLog) -> + %% We include mode in the opts just to be on the safe side + case disk_log:open([{name, Name}, {mode, read_write}]) of + {ok, Log} -> + %% SeqNo must be proprly initiated also + {ok, #snmp_log{id = Log, seqno = SeqNoGen}}; + {repaired, Log, _RecBytes, _BadBytes} -> + {ok, #snmp_log{id = Log, seqno = SeqNoGen}}; + ERROR -> + ERROR + end. + %% -- close --- @@ -394,6 +412,14 @@ log_to_io(Log, FileName, Dir, Mibs, Start) -> log_to_io(Log, FileName, Dir, Mibs, Start, Stop) when is_list(Mibs) -> + ?vtrace("log_to_io -> entry with" + "~n Log: ~p" + "~n FileName: ~p" + "~n Dir: ~p" + "~n Mibs: ~p" + "~n Start: ~p" + "~n Stop: ~p", + [Log, FileName, Dir, Mibs, Start, Stop]), File = filename:join(Dir, FileName), Converter = fun(L) -> do_log_to_io(L, Mibs, Start, Stop) @@ -401,28 +427,10 @@ log_to_io(Log, FileName, Dir, Mibs, Start, Stop) log_convert(Log, File, Converter). -%% -- log_to_plain --- - -%% log_to_plain(Log, FileName, Dir) -> -%% log_to_plain(Log, FileName, Dir, null, null). - -%% log_to_plain(Log, FileName, Dir, Start) -> -%% log_to_plain(Log, FileName, Dir, Start, null). - -%% log_to_plain(Log, FileName, Dir, Start, Stop) -%% when is_list(Mibs) -> -%% File = filename:join(Dir, FileName), -%% Converter = fun(L) -> -%% do_log_to_plain(L, Start, Stop) -%% end, -%% log_convert(Log, File, Converter). - - %% -------------------------------------------------------------------- %% Internal functions %% -------------------------------------------------------------------- - %% -- log_convert --- log_convert(#snmp_log{id = Log}, File, Converter) -> @@ -431,6 +439,26 @@ log_convert(Log, File, Converter) -> do_log_convert(Log, File, Converter). do_log_convert(Log, File, Converter) -> + %% ?vtrace("do_log_converter -> entry with" + %% "~n Log: ~p" + %% "~n File: ~p" + %% "~n disk_log:info(Log): ~p", [Log, File, disk_log:info(Log)]), + {Pid, Ref} = + erlang:spawn_monitor( + fun() -> + Result = do_log_convert2(Log, File, Converter), + exit(Result) + end), + receive + {'DOWN', Ref, process, Pid, Result} -> + %% ?vtrace("do_log_converter -> received result" + %% "~n Result: ~p" + %% "~n disk_log:info(Log): ~p", + %% [Result, disk_log:info(Log)]), + Result + end. + +do_log_convert2(Log, File, Converter) -> %% First check if the caller process has already opened the %% log, because if we close an already open log we will cause %% a runtime error. @@ -439,29 +467,18 @@ do_log_convert(Log, File, Converter) -> Converter(Log); false -> %% Not yet member of the ruling party, apply for membership... - %% If a log is opened as read_write it is not possible to - %% open it as read_only. So, to get around this we open - %% it under a different name... - Log2 = convert_name(Log), - case log_open(Log2, File) of + case log_open(Log, File) of {ok, _} -> - Res = Converter(Log2), - disk_log:close(Log2), + Res = Converter(Log), + disk_log:close(Log), Res; {error, {name_already_open, _}} -> - Converter(Log2); + Converter(Log); {error, Reason} -> {error, {Log, Reason}} end end. -convert_name(Name) when is_list(Name) -> - Name ++ "_tmp"; -convert_name(Name) when is_atom(Name) -> - list_to_atom(atom_to_list(Name) ++ "_tmp"); -convert_name(Name) -> - lists:flatten(io_lib:format("~w_tmp", [Name])). - %% -- do_log_to_text --- @@ -705,21 +722,21 @@ tsf_ge(_Local,Universal,{universal_time,DateTime}) -> tsf_ge(Local,_Universal,DateTime) -> tsf_ge(Local,DateTime). -tsf_ge(TimeStamp,DateTime) -> +tsf_ge(TimeStamp, DateTime) -> T1 = calendar:datetime_to_gregorian_seconds(TimeStamp), T2 = calendar:datetime_to_gregorian_seconds(DateTime), T1 >= T2. -tsf_le(_Local,_Universal,null) -> +tsf_le(_Local, _Universal, null) -> true; -tsf_le(Local,_Universal,{local_time,DateTime}) -> - tsf_le(Local,DateTime); -tsf_le(_Local,Universal,{universal_time,DateTime}) -> - tsf_le(Universal,DateTime); -tsf_le(Local,_Universal,DateTime) -> +tsf_le(Local, _Universal, {local_time, DateTime}) -> + tsf_le(Local, DateTime); +tsf_le(_Local, Universal, {universal_time, DateTime}) -> + tsf_le(Universal, DateTime); +tsf_le(Local, _Universal, DateTime) -> tsf_le(Local,DateTime). -tsf_le(TimeStamp,DateTime) -> +tsf_le(TimeStamp, DateTime) -> T1 = calendar:datetime_to_gregorian_seconds(TimeStamp), T2 = calendar:datetime_to_gregorian_seconds(DateTime), T1 =< T2. @@ -864,11 +881,8 @@ do_std_log_open(Name, File, Size, Repair, Notify) -> log_open(Name, File) -> - Opts = [{name, Name}, - {file, File}, - {type, ?LOG_TYPE}, - {format, ?LOG_FORMAT}, - {mode, read_only}], + Opts = [{name, Name}, + {file, File}], case disk_log:open(Opts) of {error, {badarg, size}} -> {error, no_such_log}; diff --git a/lib/snmp/src/misc/snmp_pdus.erl b/lib/snmp/src/misc/snmp_pdus.erl index 0788d86b2d..5b8be90a71 100644 --- a/lib/snmp/src/misc/snmp_pdus.erl +++ b/lib/snmp/src/misc/snmp_pdus.erl @@ -254,68 +254,95 @@ strip(0, _Tail) -> %%---------------------------------------------------------------------- %% Returns:{Type, Value} %%---------------------------------------------------------------------- + +%% OBJECT IDENTIFIER dec_value([6 | Bytes]) -> {Value, Rest} = dec_oid_notag(Bytes), {{'OBJECT IDENTIFIER', Value}, Rest}; dec_value([5,0 | T]) -> {{'NULL', 'NULL'}, T}; + +%% INTEGER dec_value([2 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), {{'INTEGER', Value}, Rest}; + +%% OCTET STRING dec_value([4 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'OCTET STRING', Value}, Rest}; + +%% IpAddress dec_value([64 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'IpAddress', Value}, Rest}; + +%% Counter32 dec_value([65 | Bytes]) -> %% Counter32 is an unsigned 32 but is actually encoded as %% a signed integer 32 (INTEGER). {Value, Rest} = dec_integer_notag(Bytes), Value2 = - if + if + (Value >= 0) andalso (Value =< 16#ffffffff) -> + %% We accept value above 16#7fffffff + %% in order to be backward bug-compatible + Value; + (Value < 0) -> + 16#ffffffff + Value + 1; + true -> + exit({error, {bad_counter32, Value}}) + end, + {{'Counter32', Value2}, Rest}; + +%% Unsigned32 +dec_value([66 | Bytes]) -> + {Value, Rest} = dec_integer_notag(Bytes), + Value2 = + if (Value >= 0) andalso (Value =< 16#ffffffff) -> - %% We accept value above 16#7fffffff - %% in order to be backward bug-compatible Value; (Value < 0) -> 16#ffffffff + Value + 1; true -> - exit({error, {bad_counter32, Value}}) + exit({error, {bad_unsigned32, Value}}) end, - {{'Counter32', Value2}, Rest}; -dec_value([66 | Bytes]) -> - {Value, Rest} = dec_integer_notag(Bytes), - if - (Value >= 0) andalso (Value =< 4294967295) -> - {{'Unsigned32', Value}, Rest}; - true -> - exit({error, {bad_unsigned32, Value}}) - end; + {{'Unsigned32', Value2}, Rest}; + +%% TimeTicks dec_value([67 | Bytes]) -> {Value, Rest} = dec_integer_notag(Bytes), - if - (Value >= 0) andalso (Value =< 4294967295) -> - {{'TimeTicks', Value}, Rest}; + Value2 = + if + (Value >= 0) andalso (Value =< 16#ffffffff) -> + Value; + (Value < 0) -> + 16#ffffffff + Value + 1; true -> exit({error, {bad_timeticks, Value}}) - end; + end, + {{'TimeTicks', Value2}, Rest}; + +%% Opaque dec_value([68 | Bytes]) -> {Value, Rest} = dec_oct_str_notag(Bytes), {{'Opaque', Value}, Rest}; + +%% Counter64 dec_value([70 | Bytes]) -> %% Counter64 is an unsigned 64 but is actually encoded as %% a signed integer 64. {Value, Rest} = dec_integer_notag(Bytes), Value2 = - if - (Value >= 0) andalso (Value < 16#8000000000000000) -> - Value; - (Value < 0) -> - 18446744073709551615 + Value + 1; - true -> - exit({error, {bad_counter64, Value}}) end, + if + (Value >= 0) andalso (Value < 16#8000000000000000) -> + Value; + (Value < 0) -> + 16#ffffffffffffffff + Value + 1; + true -> + exit({error, {bad_counter64, Value}}) end, {{'Counter64', Value2}, Rest}; + dec_value([128,0|T]) -> {{'NULL', noSuchObject}, T}; dec_value([129,0|T]) -> @@ -629,7 +656,7 @@ enc_VarBind_attributes(#varbind{oid = Oid, variabletype = Type,value = Val}) -> ValueBytes = enc_value(Type, Val), lists:append(OidBytes, ValueBytes). -enc_value('INTEGER',Val) -> +enc_value('INTEGER', Val) -> enc_integer_tag(Val); enc_value('OCTET STRING', Val) -> enc_oct_str_tag(Val); @@ -637,7 +664,7 @@ enc_value('BITS', Val) -> enc_oct_str_tag(bits_to_str(Val)); enc_value('OBJECT IDENTIFIER', Val) -> enc_oid_tag(Val); -enc_value('IpAddress',Val) -> +enc_value('IpAddress', Val) -> Bytes2 = enc_oct_str_notag(Val), Len2 = elength(length(Bytes2)), lists:append([64 | Len2],Bytes2); @@ -668,6 +695,24 @@ enc_value('Counter32', Val) -> Bytes2 = enc_integer_notag(Val2), Len2 = elength(length(Bytes2)), lists:append([65 | Len2],Bytes2); +enc_value('Unsigned32', Val) -> + if + (Val >= 0) andalso (Val =< 4294967295) -> + Bytes2 = enc_integer_notag(Val), + Len2 = elength(length(Bytes2)), + lists:append([66 | Len2], Bytes2); + true -> + exit({error, {bad_counter32, Val}}) + end; +enc_value('TimeTicks', Val) -> + if + (Val >= 0) andalso (Val =< 4294967295) -> + Bytes2 = enc_integer_notag(Val), + Len2 = elength(length(Bytes2)), + lists:append([67 | Len2], Bytes2); + true -> + exit({error, {bad_timeticks, Val}}) + end; enc_value('Counter64', Val) -> Val2 = if @@ -682,18 +727,7 @@ enc_value('Counter64', Val) -> end, Bytes2 = enc_integer_notag(Val2), Len2 = elength(length(Bytes2)), - lists:append([70 | Len2],Bytes2); -enc_value(Type, Val) -> - Bytes2 = enc_integer_notag(Val), - Len2 = elength(length(Bytes2)), - lists:append([enc_val_tag(Type,Val) | Len2],Bytes2). - -enc_val_tag('Counter32',Val) when (Val >= 0) andalso (Val =< 4294967295) -> - 65; -enc_val_tag('Unsigned32', Val) when (Val >= 0) andalso (Val =< 4294967295) -> - 66; -enc_val_tag('TimeTicks', Val) when (Val >= 0) andalso (Val =< 4294967295) -> - 67. + lists:append([70 | Len2],Bytes2). %%---------------------------------------------------------------------- diff --git a/lib/snmp/src/misc/snmp_verbosity.erl b/lib/snmp/src/misc/snmp_verbosity.erl index 85037ba2ae..df5986b7bc 100644 --- a/lib/snmp/src/misc/snmp_verbosity.erl +++ b/lib/snmp/src/misc/snmp_verbosity.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2009. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -76,7 +76,7 @@ format_timestamp({_N1, _N2, N3} = 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", + io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w ~w", [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), lists:flatten(FormatDate). @@ -144,6 +144,8 @@ image_of_sname(mse) -> "M-SERVER"; image_of_sname(msew) -> io_lib:format("M-SERVER-worker(~p)", [self()]); image_of_sname(mns) -> "M-NOTE-STORE"; image_of_sname(mnif) -> "M-NET-IF"; +image_of_sname(mnifl) -> "M-NET-IF-LOGGER"; +image_of_sname(mnifw) -> io_lib:format("M-NET-IF-worker(~p)", [self()]); image_of_sname(mconf) -> "M-CONF"; image_of_sname(mgr) -> "MGR"; diff --git a/lib/snmp/test/Makefile b/lib/snmp/test/Makefile index 78ffb1c255..546d46a002 100644 --- a/lib/snmp/test/Makefile +++ b/lib/snmp/test/Makefile @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2011. All Rights Reserved. +# Copyright Ericsson AB 1997-2012. 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 @@ -79,7 +79,7 @@ COVER_SPEC_FILE = snmp.cover # ---------------------------------------------------- # Release directory specification # ---------------------------------------------------- -RELSYSDIR = $(RELEASE_PATH)/snmp_test +RELSYSDIR = $(RELEASE_PATH)/snmp_test # ---------------------------------------------------- @@ -224,6 +224,9 @@ $(SNMP_BIN_TARGET_DIR)/Klas4.bin: $(SNMP_BIN_TARGET_DIR)/Klas3.bin $(SNMP_BIN_TARGET_DIR)/SA-MIB.bin: $(SNMP_BIN_TARGET_DIR)/OLD-SNMPEA-MIB.bin +$(SNMP_BIN_TARGET_DIR)/Test3.bin: $(SNMP_BIN_TARGET_DIR)/Test2.bin + + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- @@ -232,10 +235,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(RELTEST_FILES) $(COVER_SPEC_FILE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - tar cf - snmp_test_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(RELTEST_FILES) $(COVER_SPEC_FILE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + tar cf - snmp_test_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: @@ -251,7 +254,7 @@ info: @echo "" @echo "ERL_COMPILE_FLAGS = $(ERL_COMPILE_FLAGS)" @echo "" - @echo "RELSYSDIR = $(RELSYSDIR)" + @echo "RELSYSDIR = "$(RELSYSDIR)"" @echo "" @echo "SOURCE = $(SOURCE)" @echo "" diff --git a/lib/snmp/test/exp/.gitignore b/lib/snmp/test/exp/.gitignore new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/lib/snmp/test/exp/.gitignore diff --git a/lib/snmp/test/snmp_agent_bl_test.erl b/lib/snmp/test/exp/snmp_agent_bl_test.erl index b17489a755..a5a6e8260b 100644 --- a/lib/snmp/test/snmp_agent_bl_test.erl +++ b/lib/snmp/test/exp/snmp_agent_bl_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/snmp/test/snmp_agent_ms_test.erl b/lib/snmp/test/exp/snmp_agent_ms_test.erl index 1f34f1c8d1..d5eaea55fa 100644 --- a/lib/snmp/test/snmp_agent_ms_test.erl +++ b/lib/snmp/test/exp/snmp_agent_ms_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/snmp/test/snmp_agent_mt_test.erl b/lib/snmp/test/exp/snmp_agent_mt_test.erl index 4f125c0017..d62bc6c2e7 100644 --- a/lib/snmp/test/snmp_agent_mt_test.erl +++ b/lib/snmp/test/exp/snmp_agent_mt_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/snmp/test/snmp_agent_v1_test.erl b/lib/snmp/test/exp/snmp_agent_v1_test.erl index 737bb25cc3..5379d6d8cf 100644 --- a/lib/snmp/test/snmp_agent_v1_test.erl +++ b/lib/snmp/test/exp/snmp_agent_v1_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/snmp/test/snmp_agent_v2_test.erl b/lib/snmp/test/exp/snmp_agent_v2_test.erl index dc94c18ad9..a86449ca72 100644 --- a/lib/snmp/test/snmp_agent_v2_test.erl +++ b/lib/snmp/test/exp/snmp_agent_v2_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/snmp/test/snmp_agent_v3_test.erl b/lib/snmp/test/exp/snmp_agent_v3_test.erl index 266be72878..c72d845bf2 100644 --- a/lib/snmp/test/snmp_agent_v3_test.erl +++ b/lib/snmp/test/exp/snmp_agent_v3_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2010. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk index eacc749b53..a444cab6d6 100644 --- a/lib/snmp/test/modules.mk +++ b/lib/snmp/test/modules.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2004-2011. All Rights Reserved. +# Copyright Ericsson AB 2004-2012. 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 @@ -76,7 +76,8 @@ MIB_FILES = \ TestTrap.mib \ TestTrapv2.mib \ Test1.mib \ - Test2.mib + Test2.mib \ + Test3.mib SPECS = snmp.spec snmp.spec.vxworks diff --git a/lib/snmp/test/snmp_SUITE.erl b/lib/snmp/test/snmp_SUITE.erl index b6d72da2fa..22b9c64588 100644 --- a/lib/snmp/test/snmp_SUITE.erl +++ b/lib/snmp/test/snmp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -19,6 +19,8 @@ -module(snmp_SUITE). +-include("snmp_test_lib.hrl"). + -export([all/0, suite/0, groups/0, @@ -39,6 +41,21 @@ end_per_testcase(_Case, Config) when is_list(Config) -> Config. +init_per_suite(Config) when is_list(Config) -> + + ?DBG("init_per_suite -> entry with" + "~n Config: ~p", [Config]), + + Config. + +end_per_suite(Config) when is_list(Config) -> + + ?DBG("end_per_suite -> entry with" + "~n Config: ~p", [Config]), + + Config. + + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Top test case @@ -53,7 +70,8 @@ all() -> {group, manager}]. groups() -> - [{app, [], [{group, app_test}, + [ + {app, [], [{group, app_test}, {group, appup_test}]}, {compiler, [], [{group, compiler_test}]}, {misc, [], [{group, conf_test}, @@ -66,6 +84,7 @@ groups() -> {manager, [], [{group, manager_config_test}, {group, manager_user_test}, {group, manager_test}]}, + {app_test, [], [{snmp_app_test, all}]}, {appup_test, [], [{snmp_appup_test, all}]}, {compiler_test, [], [{snmp_compiler_test, all}]}, @@ -78,17 +97,25 @@ groups() -> {agent_test, [], [{snmp_agent_test, all}]}, {manager_config_test, [], [{snmp_manager_config_test, all}]}, {manager_user_test, [], [{snmp_manager_user_test, all}]}, - {manager_test, [], [{snmp_manager_test, all}]}]. + {manager_test, [], [{snmp_manager_test, all}]} + ]. -init_per_suite(Config) -> - Config. -end_per_suite(_Config) -> - ok. +init_per_group(GroupName, Config0) -> -init_per_group(_GroupName, Config) -> - Config. + ?DBG("init_per_group -> entry with" + "~n GroupName: ~p" + "~n Config0: ~p", [GroupName, Config0]), + + %% Group name is not really the suite name + %% (but it is a good enough approximation), + %% but it does not matter since we only need + %% it to be unique. + snmp_test_lib:init_suite_top_dir(GroupName, Config0). + end_per_group(_GroupName, Config) -> - Config. + lists:keydelete(snmp_suite_top_dir, 1, Config). + + diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index e968bc65b1..e1d7f33b3f 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -224,52 +224,82 @@ groups() -> }, {tickets2, [], [otp8395, otp9884]}, {otp_4394, [], [otp_4394_test]}, - {otp_7157, [], [otp_7157_test] - } + {otp_7157, [], [otp_7157_test]} ]. -init_per_group(all_tcs, Config) -> - init_all(Config); -init_per_group(otp_7157, Config) -> - init_otp_7157(Config); -init_per_group(otp_4394, Config) -> - init_otp_4394(Config); -init_per_group(v2_inform, Config) -> - init_v2_inform(Config); -init_per_group(multiple_reqs_2, Config) -> - init_mul(Config); -init_per_group(multiple_reqs, Config) -> - init_mul(Config); -init_per_group(test_multi_threaded, Config) -> - init_mt(Config); -init_per_group(test_v3, Config) -> - init_v3(Config); -init_per_group(test_v1_v2, Config) -> - init_v1_v2(Config); -init_per_group(test_v2, Config) -> - init_v2(Config); -init_per_group(test_v1, Config) -> - init_v1(Config); -init_per_group(misc, Config) -> - init_misc(Config); -init_per_group(mib_storage_varm_mnesia, Config) -> - init_varm_mib_storage_mnesia(Config); -init_per_group(mib_storage_varm_dets, Config) -> - init_varm_mib_storage_dets(Config); -init_per_group(mib_storage_size_check_mnesia, Config) -> - init_size_check_msm(Config); -init_per_group(mib_storage_size_check_dets, Config) -> - init_size_check_msd(Config); -init_per_group(mib_storage_size_check_ets, Config) -> - init_size_check_mse(Config); -init_per_group(mib_storage_mnesia, Config) -> - init_mib_storage_mnesia(Config); -init_per_group(mib_storage_dets, Config) -> - init_mib_storage_dets(Config); -init_per_group(mib_storage_ets, Config) -> - init_mib_storage_ets(Config); -init_per_group(_GroupName, Config) -> - Config. + +init_per_suite(Config0) when is_list(Config0) -> + + ?DBG("init_per_suite -> entry with" + "~n Config0: ~p", [Config0]), + + Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0), + Config2 = snmp_test_lib:fix_data_dir(Config1), + + %% Mib-dirs + MibDir = snmp_test_lib:lookup(data_dir, Config2), + StdMibDir = filename:join([code:priv_dir(snmp), "mibs"]), + + Config3 = [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2], + + ?DBG("init_per_suite -> end with" + "~n Config3: ~p", [Config3]), + + Config3. + +end_per_suite(Config) when is_list(Config) -> + + ?DBG("end_per_suite -> entry with" + "~n Config: ~p", [Config]), + + Config. + + +init_per_group(all_tcs = GroupName, Config) -> + init_all(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(otp_7157 = GroupName, Config) -> + init_otp_7157(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(otp_4394 = GroupName, Config) -> + init_otp_4394(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(v2_inform = GroupName, Config) -> + init_v2_inform(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(multiple_reqs_2 = GroupName, Config) -> + init_mul(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(multiple_reqs = GroupName, Config) -> + init_mul(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(test_multi_threaded = GroupName, Config) -> + init_mt(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(test_v3 = GroupName, Config) -> + init_v3(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(test_v1_v2 = GroupName, Config) -> + init_v1_v2(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(test_v2 = GroupName, Config) -> + init_v2(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(test_v1 = GroupName, Config) -> + init_v1(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(misc = GroupName, Config) -> + init_misc(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(mib_storage_varm_mnesia = GroupName, Config) -> + init_varm_mib_storage_mnesia(snmp_test_lib:init_group_top_dir(GroupName, + Config)); +init_per_group(mib_storage_varm_dets = GroupName, Config) -> + init_varm_mib_storage_dets(snmp_test_lib:init_group_top_dir(GroupName, + Config)); +init_per_group(mib_storage_size_check_mnesia = GroupName, Config) -> + init_size_check_msm(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(mib_storage_size_check_dets = GroupName, Config) -> + init_size_check_msd(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(mib_storage_size_check_ets = GroupName, Config) -> + init_size_check_mse(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(mib_storage_mnesia = GroupName, Config) -> + init_mib_storage_mnesia(snmp_test_lib:init_group_top_dir(GroupName, + Config)); +init_per_group(mib_storage_dets = GroupName, Config) -> + init_mib_storage_dets(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(mib_storage_ets = GroupName, Config) -> + init_mib_storage_ets(snmp_test_lib:init_group_top_dir(GroupName, Config)); +init_per_group(GroupName, Config) -> + snmp_test_lib:init_group_top_dir(GroupName, Config). end_per_group(all_tcs, Config) -> finish_all(Config); @@ -316,115 +346,86 @@ end_per_group(_GroupName, Config) -> -init_per_testcase(otp8395 = Case, Config) when is_list(Config) -> +%% ---- Init Per TestCase ---- + +init_per_testcase(Case, Config) when is_list(Config) -> ?DBG("init_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + p("Agent Info: " + "~n ~p", [snmpa:info()]), + + init_per_testcase1(Case, Config). + +init_per_testcase1(otp8395 = Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [Case, Config]), - Config2 = init_per_testcase2(Case, init_per_suite(Config)), - otp8395({init, Config2}); -init_per_testcase(otp9884 = Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" + otp8395({init, init_per_testcase2(Case, Config)}); +init_per_testcase1(otp9884 = Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [Case, Config]), - Config2 = init_per_testcase2(Case, init_per_suite(Config)), - otp9884({init, Config2}); -init_per_testcase(otp_7157_test = _Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" + otp9884({init, init_per_testcase2(Case, Config)}); +init_per_testcase1(otp_7157_test = _Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(1)), - [{watchdog, Dog}|Config]; -init_per_testcase(v2_inform_i = _Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" + [{watchdog, Dog} | Config ]; +init_per_testcase1(v2_inform_i = _Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(10)), - [{watchdog, Dog}|Config]; -init_per_testcase(v3_inform_i = _Case, Config) when is_list(Config) -> - ?DBG("init_per_testcase -> entry with" + [{watchdog, Dog} | Config ]; +init_per_testcase1(v3_inform_i = _Case, Config) when is_list(Config) -> + ?DBG("init_per_testcase1 -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(10)), - [{watchdog, Dog}|Config]; -init_per_testcase(_Case, Config) when is_list(Config) -> + [{watchdog, Dog} | Config ]; +init_per_testcase1(_Case, Config) when is_list(Config) -> ?DBG("init_per_testcase -> entry with" "~n Case: ~p" "~n Config: ~p", [_Case, Config]), Dog = ?WD_START(?MINS(6)), - [{watchdog, Dog}|Config]. + [{watchdog, Dog}| Config ]. -end_per_testcase(otp8395, Config) when is_list(Config) -> - otp8395({fin, Config}); -end_per_testcase(otp9884, Config) when is_list(Config) -> - otp9884({fin, Config}); -end_per_testcase(_Case, Config) when is_list(Config) -> - ?DBG("end_per_testcase -> entry with" - "~n Case: ~p" - "~n Config: ~p", [_Case, Config]), - Dog = ?config(watchdog, Config), - ?WD_STOP(Dog), - Config. +%% ---- End Per TestCase ---- -init_per_suite(Config) -> - ?DBG("init_per_suite -> entry with" +end_per_testcase(Case, Config) when is_list(Config) -> + ?DBG("end_per_testcase -> entry with" "~n Config: ~p", [Config]), - %% Suite root dir for test suite - PrivDir = ?config(priv_dir, Config), + p("Agent Info: " + "~n ~p", [snmpa:info()]), - %% Create top-directory for this sub-suite - SuiteTopDir = filename:join([PrivDir, ?MODULE]), - case file:make_dir(SuiteTopDir) of - ok -> - ok; - {error, eexist} -> - %% This can happen since this is not really a - %% suite-init function. - ok; - {error, Reason} -> - ?FAIL({failed_creating_suite_top_dir, SuiteTopDir, Reason}) - end, + display_log(Config), + end_per_testcase1(Case, Config). - %% -- - %% Fix config (data-dir is not correct): - %% - - Config1 = fix_data_dir(Config), - %% Config1 = Config, - - %% Mib-dirs - MibDir = ?config(data_dir, Config1), - StdMibDir = filename:join([code:priv_dir(snmp), "mibs"]), - - Config2 = [{suite_top_dir, SuiteTopDir}, - {mib_dir, MibDir}, - {std_mib_dir, StdMibDir} | Config1], - - ?DBG("init_per_suite -> done when" - "~n Config2: ~p", [Config2]), - Config2. - -%% end_per_suite(Config) -> -end_per_suite(Config) -> +end_per_testcase1(otp8395, Config) when is_list(Config) -> + otp8395({fin, Config}); +end_per_testcase1(otp9884, Config) when is_list(Config) -> + otp9884({fin, Config}); +end_per_testcase1(_Case, Config) when is_list(Config) -> + ?DBG("end_per_testcase1 -> entry with" + "~n Case: ~p" + "~n Config: ~p", [_Case, Config]), + Dog = ?config(watchdog, Config), + ?WD_STOP(Dog), Config. -fix_data_dir(Config) -> - DataDir0 = ?config(data_dir, Config), - DataDir1 = filename:split(filename:absname(DataDir0)), - [_|DataDir2] = lists:reverse(DataDir1), - DataDir = filename:join(lists:reverse(DataDir2) ++ [?snmp_test_data]), - Config1 = lists:keydelete(data_dir, 1, Config), - [{data_dir, DataDir} | Config1]. - init_per_testcase2(Case, Config) -> - SuiteToDir = ?config(suite_top_dir, Config), - - %% Create top-directory for this test-case - CaseTopDir = filename:join([SuiteToDir, Case]), - ok = file:make_dir(CaseTopDir), + + ?DBG("end_per_testcase2 -> entry with" + "~n Case: ~p" + "~n Config: ~p", [Case, Config]), + + CaseTopDir = snmp_test_lib:init_testcase_top_dir(Case, Config), %% Create agent top-dir(s) AgentTopDir = filename:join([CaseTopDir, agent]), @@ -623,19 +624,19 @@ mib_storage_mnesia_cases() -> msm_mib_of]. mse_size_check_cases() -> -[mse_size_check]. + [mse_size_check]. msd_size_check_cases() -> -[msd_size_check]. + [msd_size_check]. msm_size_check_cases() -> -[msm_size_check]. + [msm_size_check]. varm_mib_storage_dets_cases() -> -[msd_varm_mib_start]. + [msd_varm_mib_start]. varm_mib_storage_mnesia_cases() -> -[msm_varm_mib_start]. + [msm_varm_mib_start]. init_mib_storage_ets(Config) when is_list(Config) -> ?LOG("init_mib_storage_ets -> entry", []), @@ -644,36 +645,37 @@ init_mib_storage_ets(Config) when is_list(Config) -> init_mib_storage_dets(Config) when is_list(Config) -> ?LOG("init_mib_storage_ets -> entry", []), - ?line AgentDir = ?GCONF(agent_dir, Config), - MibStorage = {snmp_mib_storage,{dets,AgentDir}}, + ?line AgentDbDir = ?GCONF(agent_db_dir, Config), + MibStorage = {snmp_mib_storage, {dets, AgentDbDir}}, init_ms(Config, [MibStorage]). init_mib_storage_mnesia(Config) when is_list(Config) -> ?LOG("init_mib_storage_ets -> entry", []), - MibStorage = {snmp_mib_storage,{mnesia,[]}}, + MibStorage = {snmp_mib_storage, {mnesia,[]}}, init_ms(Config, [MibStorage]). init_ms(Config, Opts) when is_list(Config) -> ?LOG("init_mib_storage_ets -> entry", []), - ?line SaNode = ?GCONF(snmp_sa, Config), + ?line SaNode = ?GCONF(snmp_sa, Config), ?line create_tables(SaNode), - ?line AgentDir = ?GCONF(agent_dir, Config), - ?line MgrDir = ?GCONF(mgr_dir, Config), - ?line Ip = ?GCONF(ip, Config), - ?line config([v1], MgrDir, AgentDir, tuple_to_list(Ip), tuple_to_list(Ip)), + ?line AgentConfDir = ?GCONF(agent_conf_dir, Config), + ?line MgrDir = ?GCONF(mgr_dir, Config), + ?line Ip = ?GCONF(ip, Config), + ?line config([v1], MgrDir, AgentConfDir, + tuple_to_list(Ip), tuple_to_list(Ip)), MasterAgentVerbosity = {snmp_master_agent_verbosity, trace}, MibsVerbosity = {snmp_mibserver_verbosity, trace}, SymStoreVerbosity = {snmp_symbolic_store_verbosity, trace}, - Opts1 = [MasterAgentVerbosity,MibsVerbosity,SymStoreVerbosity|Opts], - [{vsn, v1} | start_v1_agent(Config,Opts1)]. + Opts1 = [MasterAgentVerbosity, MibsVerbosity, SymStoreVerbosity | Opts], + [{vsn, v1} | start_v1_agent(Config, Opts1)]. init_size_check_mse(Config) when is_list(Config) -> MibStorage = {snmp_mib_storage, ets}, init_size_check_ms(Config, [MibStorage]). init_size_check_msd(Config) when is_list(Config) -> - AgentDir = ?GCONF(agent_dir, Config), - MibStorage = {snmp_mib_storage, {dets, AgentDir}}, + AgentDbDir = ?GCONF(agent_db_dir, Config), + MibStorage = {snmp_mib_storage, {dets, AgentDbDir}}, init_size_check_ms(Config, [MibStorage]). init_size_check_msm(Config) when is_list(Config) -> @@ -695,22 +697,24 @@ init_size_check_ms(Config, Opts) when is_list(Config) -> ?SKIP({failed_starting_crypto, Reason}) end, create_tables(SaNode), - AgentDir = ?GCONF(agent_dir, Config), - MgrDir = ?GCONF(mgr_dir, Config), - Ip = ?GCONF(ip, Config), - ?line ok = - config([v3], MgrDir, AgentDir, tuple_to_list(Ip), tuple_to_list(Ip)), + AgentConfDir = ?GCONF(agent_conf_dir, Config), + MgrDir = ?GCONF(mgr_dir, Config), + Ip = ?GCONF(ip, Config), + ?line ok = config([v3], MgrDir, AgentConfDir, + tuple_to_list(Ip), tuple_to_list(Ip)), [{vsn, v3} | start_v3_agent(Config, Opts)]. init_varm_mib_storage_dets(Config) when is_list(Config) -> ?LOG("init_varm_mib_storage_dets -> entry", []), - ?line SaNode = ?GCONF(snmp_sa, Config), + ?line SaNode = ?GCONF(snmp_sa, Config), ?line create_tables(SaNode), - ?line AgentDir = ?GCONF(agent_dir, Config), - ?line MgrDir = ?GCONF(mgr_dir, Config), - ?line Ip = ?GCONF(ip, Config), - ?line config([v1], MgrDir, AgentDir, tuple_to_list(Ip), tuple_to_list(Ip)), - MibStorage = {snmp_mib_storage,{dets,AgentDir}}, + ?line AgentDbDir = ?GCONF(agent_db_dir, Config), + ?line AgentConfDir = ?GCONF(agent_conf_dir, Config), + ?line MgrDir = ?GCONF(mgr_dir, Config), + ?line Ip = ?GCONF(ip, Config), + ?line config([v1], MgrDir, AgentConfDir, + tuple_to_list(Ip), tuple_to_list(Ip)), + MibStorage = {snmp_mib_storage, {dets, AgentDbDir}}, MasterAgentVerbosity = {snmp_master_agent_verbosity, trace}, MibsVerbosity = {snmp_mibserver_verbosity, trace}, SymStoreVerbosity = {snmp_symbolic_store_verbosity, trace}, @@ -719,12 +723,13 @@ init_varm_mib_storage_dets(Config) when is_list(Config) -> init_varm_mib_storage_mnesia(Config) when is_list(Config) -> ?LOG("init_varm_mib_storage_mnesia -> entry", []), - ?line SaNode = ?GCONF(snmp_sa, Config), + ?line SaNode = ?GCONF(snmp_sa, Config), ?line create_tables(SaNode), - ?line AgentDir = ?GCONF(agent_dir, Config), - ?line MgrDir = ?GCONF(mgr_dir, Config), - ?line Ip = ?GCONF(ip, Config), - ?line config([v1], MgrDir, AgentDir, tuple_to_list(Ip), tuple_to_list(Ip)), + ?line AgentConfDir = ?GCONF(agent_conf_dir, Config), + ?line MgrDir = ?GCONF(mgr_dir, Config), + ?line Ip = ?GCONF(ip, Config), + ?line config([v1], MgrDir, AgentConfDir, + tuple_to_list(Ip), tuple_to_list(Ip)), MibStorage = {snmp_mib_storage,{mnesia,[]}}, MasterAgentVerbosity = {snmp_master_agent_verbosity, trace}, MibsVerbosity = {snmp_mibserver_verbosity, trace}, @@ -1189,10 +1194,11 @@ v1_cases() -> init_v1(Config) when is_list(Config) -> ?line SaNode = ?config(snmp_sa, Config), ?line create_tables(SaNode), - ?line AgentDir = ?config(agent_dir, Config), + ?line AgentConfDir = ?config(agent_conf_dir, Config), ?line MgrDir = ?config(mgr_dir, Config), ?line Ip = ?config(ip, Config), - ?line config([v1], MgrDir, AgentDir, tuple_to_list(Ip), tuple_to_list(Ip)), + ?line config([v1], MgrDir, AgentConfDir, + tuple_to_list(Ip), tuple_to_list(Ip)), [{vsn, v1} | start_v1_agent(Config)]. finish_v1(Config) when is_list(Config) -> @@ -1232,10 +1238,11 @@ v2_cases() -> init_v2(Config) when is_list(Config) -> SaNode = ?config(snmp_sa, Config), create_tables(SaNode), - AgentDir = ?config(agent_dir, Config), + AgentConfDir = ?config(agent_conf_dir, Config), MgrDir = ?config(mgr_dir, Config), Ip = ?config(ip, Config), - config([v2], MgrDir, AgentDir, tuple_to_list(Ip), tuple_to_list(Ip)), + config([v2], MgrDir, AgentConfDir, + tuple_to_list(Ip), tuple_to_list(Ip)), [{vsn, v2} | start_v2_agent(Config)]. finish_v2(Config) when is_list(Config) -> @@ -1251,10 +1258,11 @@ v1_v2_cases() -> init_v1_v2(Config) when is_list(Config) -> SaNode = ?config(snmp_sa, Config), create_tables(SaNode), - AgentDir = ?config(agent_dir, Config), + AgentConfDir = ?config(agent_conf_dir, Config), MgrDir = ?config(mgr_dir, Config), Ip = ?config(ip, Config), - config([v1,v2], MgrDir, AgentDir, tuple_to_list(Ip), tuple_to_list(Ip)), + config([v1,v2], MgrDir, AgentConfDir, + tuple_to_list(Ip), tuple_to_list(Ip)), [{vsn, bilingual} | start_bilingual_agent(Config)]. finish_v1_v2(Config) when is_list(Config) -> @@ -1315,10 +1323,10 @@ init_v3(Config) when is_list(Config) -> end, SaNode = ?config(snmp_sa, Config), create_tables(SaNode), - AgentDir = ?config(agent_dir, Config), + AgentConfDir = ?config(agent_conf_dir, Config), MgrDir = ?config(mgr_dir, Config), Ip = ?config(ip, Config), - ?line ok = config([v3], MgrDir, AgentDir, + ?line ok = config([v3], MgrDir, AgentConfDir, tuple_to_list(Ip), tuple_to_list(Ip)), [{vsn, v3} | start_v3_agent(Config)]. @@ -1338,11 +1346,11 @@ mt_cases() -> init_mt(Config) when is_list(Config) -> SaNode = ?config(snmp_sa, Config), create_tables(SaNode), - AgentDir = ?config(agent_dir, Config), + AgentConfDir = ?config(agent_conf_dir, Config), MgrDir = ?config(mgr_dir, Config), Ip = ?config(ip, Config), - ?line ok = - config([v2], MgrDir, AgentDir, tuple_to_list(Ip), tuple_to_list(Ip)), + ?line ok = config([v2], MgrDir, AgentConfDir, + tuple_to_list(Ip), tuple_to_list(Ip)), [{vsn, v2} | start_multi_threaded_agent(Config)]. finish_mt(Config) when is_list(Config) -> @@ -1421,7 +1429,10 @@ simple(Config) when is_list(Config) -> ?P(simple), init_case(Config), - try_test(simple_standard_test). + try_test(simple_standard_test), + + p("done"), + ok. simple_2(X) -> ?P(simple_2), simple(X). @@ -1601,7 +1612,7 @@ change_target_addr_config(Config) when is_list(Config) -> try_test(ma_trap1, [MA]), ?LOG("change_target_addr_config -> set silence verbosity for local_db",[]), - ?line snmpa:verbosity(local_db,silence), + ?line snmpa:verbosity(local_db, silence), %% Start new dummy listener ?LOG("change_target_addr_config -> start dummy manager",[]), @@ -1609,9 +1620,9 @@ change_target_addr_config(Config) when is_list(Config) -> %% Reconfigure ?LOG("change_target_addr_config -> reconfigure",[]), - AgentDir = ?config(agent_dir, Config), - ?line rewrite_target_addr_conf(AgentDir, NewPort), - ?line snmp_target_mib:reconfigure(AgentDir), + AgentConfDir = ?config(agent_conf_dir, Config), + ?line rewrite_target_addr_conf(AgentConfDir, NewPort), + ?line snmp_target_mib:reconfigure(AgentConfDir), %% Send the trap again ?LOG("change_target_addr_config -> send trap again",[]), @@ -1624,7 +1635,7 @@ change_target_addr_config(Config) when is_list(Config) -> ?line ok = dummy_manager_stop(Pid), ?LOG("change_target_addr_config -> reset target address config",[]), - ?line reset_target_addr_conf(AgentDir), + ?line reset_target_addr_conf(AgentConfDir), ?LOG("change_target_addr_config -> unload TestTrap",[]), ?line unload_master("TestTrap"). @@ -2067,7 +2078,7 @@ v3_inform(_X) -> {req, [], {conf, init_v3_inform, [v3_inform_i], finish_v3_inform}}. init_v2_inform(Config) when is_list(Config) -> - _Dir = ?config(agent_dir, Config), + _Dir = ?config(agent_conf_dir, Config), %% snmp_internal_mib:configure(Dir), Config. @@ -2075,7 +2086,7 @@ init_v3_inform(X) -> init_v2_inform(X). finish_v2_inform(Config) when is_list(Config) -> - _Dir = ?config(agent_dir, Config), + _Dir = ?config(agent_conf_dir, Config), %% snmp_internal_mib:configure(Dir), Config. @@ -2377,9 +2388,9 @@ v3_md5_auth(Config) when is_list(Config) -> ?P1("Testing MD5 authentication...takes a few seconds..."), - AgentDir = ?config(agent_dir, Config), - ?line rewrite_target_params_conf(AgentDir, "authMD5", authNoPriv), - ?line snmp_target_mib:reconfigure(AgentDir), + AgentConfDir = ?config(agent_conf_dir, Config), + ?line rewrite_target_params_conf(AgentConfDir, "authMD5", authNoPriv), + ?line snmp_target_mib:reconfigure(AgentConfDir), MA = whereis(snmp_master_agent), @@ -2388,14 +2399,14 @@ v3_md5_auth(Config) when is_list(Config) -> ?line load_master("TestTrapv2"), try_test(v3_sync, [[{v2_proc, []}, - {ma_v2_trap1, [MA]}, - {v3_inform_sync, [MA]}]], - [{sec_level, authNoPriv}, {user, "authMD5"}]), + {ma_v2_trap1, [MA]}, + {v3_inform_sync, [MA]}]], + [{sec_level, authNoPriv}, {user, "authMD5"}]), ?line unload_master("TestTrapv2"), ?line unload_master("TestTrap"), ?line unload_master("Test2"), - ?line reset_target_params_conf(AgentDir). + ?line reset_target_params_conf(AgentConfDir). v3_sha_auth(suite) -> []; v3_sha_auth(Config) when is_list(Config) -> @@ -2404,9 +2415,9 @@ v3_sha_auth(Config) when is_list(Config) -> ?P1("Testing SHA authentication...takes a few seconds..."), - AgentDir = ?config(agent_dir, Config), - ?line rewrite_target_params_conf(AgentDir, "authSHA", authNoPriv), - ?line snmp_target_mib:reconfigure(AgentDir), + AgentConfDir = ?config(agent_conf_dir, Config), + ?line rewrite_target_params_conf(AgentConfDir, "authSHA", authNoPriv), + ?line snmp_target_mib:reconfigure(AgentConfDir), MA = whereis(snmp_master_agent), @@ -2415,14 +2426,14 @@ v3_sha_auth(Config) when is_list(Config) -> ?line load_master("TestTrapv2"), try_test(v3_sync, [[{v2_proc, []}, - {ma_v2_trap1, [MA]}, - {v3_inform_sync, [MA]}]], - [{sec_level, authNoPriv}, {user, "authSHA"}]), + {ma_v2_trap1, [MA]}, + {v3_inform_sync, [MA]}]], + [{sec_level, authNoPriv}, {user, "authSHA"}]), ?line unload_master("TestTrapv2"), ?line unload_master("TestTrap"), ?line unload_master("Test2"), - ?line reset_target_params_conf(AgentDir). + ?line reset_target_params_conf(AgentConfDir). v3_des_priv(suite) -> []; v3_des_priv(Config) when is_list(Config) -> @@ -2431,9 +2442,9 @@ v3_des_priv(Config) when is_list(Config) -> ?P1("Testing DES encryption...takes a few seconds..."), - AgentDir = ?config(agent_dir, Config), - ?line rewrite_target_params_conf(AgentDir, "privDES", authPriv), - ?line snmp_target_mib:reconfigure(AgentDir), + AgentConfDir = ?config(agent_conf_dir, Config), + ?line rewrite_target_params_conf(AgentConfDir, "privDES", authPriv), + ?line snmp_target_mib:reconfigure(AgentConfDir), MA = whereis(snmp_master_agent), @@ -2444,14 +2455,14 @@ v3_des_priv(Config) when is_list(Config) -> ?line load_master("TestTrapv2"), try_test(v3_sync, [[{v2_proc, []}, - {ma_v2_trap1, [MA]}, - {v3_inform_sync, [MA]}]], - [{sec_level, authPriv}, {user, "privDES"}]), + {ma_v2_trap1, [MA]}, + {v3_inform_sync, [MA]}]], + [{sec_level, authPriv}, {user, "privDES"}]), ?line unload_master("TestTrapv2"), ?line unload_master("TestTrap"), ?line unload_master("Test2"), - ?line reset_target_params_conf(AgentDir). + ?line reset_target_params_conf(AgentConfDir). %% -define(usmStatsNotInTimeWindows_instance, [1,3,6,1,6,3,15,1,1,2,0]). @@ -4795,7 +4806,7 @@ snmp_user_based_sm_mib_3(Config) when is_list(Config) -> ?P(snmp_user_based_sm_mib_3), init_case(Config), - _AgentDir = ?config(agent_dir, Config), + _AgentDir = ?config(agent_conf_dir, Config), ?line load_master_std("SNMP-USER-BASED-SM-MIB"), %% The newUser used here already has VACM access. @@ -5727,28 +5738,28 @@ otp_3725_test(MaNode) -> init_otp_4394(Config) when is_list(Config) -> ?DBG("init_otp_4394 -> entry with" "~n Config: ~p", [Config]), - ?line AgentDir = ?config(agent_dir, Config), - ?line MgrDir = ?config(mgr_dir, Config), - ?line Ip = ?config(ip, Config), - ?line otp_4394_config(AgentDir, MgrDir, Ip), + ?line AgentConfDir = ?config(agent_conf_dir, Config), + ?line MgrDir = ?config(mgr_dir, Config), + ?line Ip = ?config(ip, Config), + ?line otp_4394_config(AgentConfDir, MgrDir, Ip), MasterAgentVerbosity = {master_agent_verbosity, trace}, NetIfVerbosity = {net_if_verbosity, trace}, - Opts = [MasterAgentVerbosity,NetIfVerbosity], - [{vsn, v1} | start_v1_agent(Config,Opts)]. + Opts = [MasterAgentVerbosity, NetIfVerbosity], + [{vsn, v1} | start_v1_agent(Config, Opts)]. -otp_4394_config(AgentDir, MgrDir, Ip0) -> +otp_4394_config(AgentConfDir, MgrDir, Ip0) -> ?DBG("otp_4394_config -> entry with" - "~n AgentDir: ~p" - "~n MgrDir: ~p" - "~n Ip0: ~p", [AgentDir, MgrDir, Ip0]), + "~n AgentConfDir: ~p" + "~n MgrDir: ~p" + "~n Ip0: ~p", [AgentConfDir, MgrDir, Ip0]), Vsn = [v1], Ip = tuple_to_list(Ip0), - ?line snmp_config:write_agent_snmp_files(AgentDir, Vsn, Ip, + ?line snmp_config:write_agent_snmp_files(AgentConfDir, Vsn, Ip, ?TRAP_UDP, Ip, 4000, "OTP-4394 test"), - ?line case update_usm(Vsn, AgentDir) of + ?line case update_usm(Vsn, AgentConfDir) of true -> - ?line copy_file(filename:join(AgentDir, "usm.conf"), + ?line copy_file(filename:join(AgentConfDir, "usm.conf"), filename:join(MgrDir, "usm.conf")), ?line update_usm_mgr(Vsn, MgrDir); false -> @@ -5756,8 +5767,8 @@ otp_4394_config(AgentDir, MgrDir, Ip0) -> end, C1 = {"a", "all-rights", "initial", "", "pc"}, C2 = {"c", "secret", "secret_name", "", "secret_tag"}, - ?line write_community_conf(AgentDir, [C1, C2]), - ?line update_vacm(Vsn, AgentDir), + ?line write_community_conf(AgentConfDir, [C1, C2]), + ?line update_vacm(Vsn, AgentConfDir), Ta1 = {"shelob v1", [134,138,177,177], 5000, 1500, 3, %% Anv�nd Ip och modda "pc1", @@ -5771,9 +5782,9 @@ otp_4394_config(AgentDir, MgrDir, Ip0) -> "target_v1", "", %% [255,255,255,255,0,0], [], 2048}, - ?line write_target_addr_conf(AgentDir, [Ta1, Ta2]), - ?line write_target_params_conf(AgentDir, Vsn), - ?line write_notify_conf(AgentDir), + ?line write_target_addr_conf(AgentConfDir, [Ta1, Ta2]), + ?line write_target_params_conf(AgentConfDir, Vsn), + ?line write_notify_conf(AgentConfDir), ok. @@ -5826,10 +5837,11 @@ init_otp_7157(Config) when is_list(Config) -> ?DBG("init_otp_7157 -> entry with" "~n Config: ~p", [Config]), - ?line AgentDir = ?config(agent_dir, Config), + ?line AgentConfDir = ?config(agent_conf_dir, Config), ?line MgrDir = ?config(mgr_dir, Config), ?line Ip = ?config(ip, Config), - ?line config([v2], MgrDir, AgentDir, tuple_to_list(Ip), tuple_to_list(Ip)), + ?line config([v2], MgrDir, AgentConfDir, + tuple_to_list(Ip), tuple_to_list(Ip)), MasterAgentVerbosity = {master_agent_verbosity, trace}, NetIfVerbosity = {net_if_verbosity, trace}, Opts = [MasterAgentVerbosity, NetIfVerbosity], @@ -6562,8 +6574,8 @@ write_community_conf(Dir, Conf) -> write_target_addr_conf(Dir, Conf) -> snmp_agent_test_lib:write_target_addr_conf(Dir, Conf). -write_target_addr_conf(Dir, ManagerIp, UDP, Vsns) -> - snmp_agent_test_lib:write_target_addr_conf(Dir, ManagerIp, UDP, Vsns). +%% write_target_addr_conf(Dir, ManagerIp, UDP, Vsns) -> +%% snmp_agent_test_lib:write_target_addr_conf(Dir, ManagerIp, UDP, Vsns). rewrite_target_addr_conf(Dir, NewPort) -> snmp_agent_test_lib:rewrite_target_addr_conf(Dir, NewPort). @@ -6596,9 +6608,42 @@ copy_file(From, To) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +display_log(Config) -> + case lists:keysearch(agent_log_dir, 1, Config) of + {value, {_, Dir}} -> + case lists:keysearch(snmp_master, 1, Config) of + {value, {_, Node}} -> + LogDir = Dir, + Mibs = [], + OutFile = filename:join(LogDir, "snmpa_log.txt"), + p("~n" + "=========================" + " < Audit Trail Log > " + "=========================" + "~n"), + rcall(Node, snmpa, log_to_txt, [LogDir, Mibs, OutFile]), + rcall(Node, snmpa, log_to_io, [LogDir, Mibs]), + p("~n" + "=========================" + " < / Audit Trail Log > " + "=========================" + "~n", []); + false -> + p("display_log -> no agent node found"), + ok + end; + false -> + p("display_log -> no agent log dir found: " + "~n ~p", [Config]), + ok + end. + + +%% ------ + display_memory_usage() -> Info = snmpa:info(snmp_master_agent), - AMU = display_agent_memory_uasge(Info), + AMU = display_agent_memory_usage(Info), NIMU = display_net_if_memory_usage(Info), NSMU = display_note_store_memory_usage(Info), SSMU = display_symbolic_store_memory_usage(Info), @@ -6608,7 +6653,7 @@ display_memory_usage() -> AMU ++ NIMU ++ NSMU ++ SSMU ++ LDBMU ++ MSMU, []), ok. -display_agent_memory_uasge(Info) -> +display_agent_memory_usage(Info) -> AgentInfo = lists_key1search(agent, Info), ProcMem = lists_key1search([process_memory,master_agent], AgentInfo), @@ -6717,3 +6762,27 @@ lists_key1search(Key, List) when is_atom(Key) -> regs() -> lists:sort(registered()). + +%% ------ + +rcall(Node, Mod, Func, Args) -> + case rpc:call(Node, Mod, Func, Args) of + {badrpc, nodedown} -> + ?FAIL({rpc_failure, Node}); + Else -> + Else + end. + + +%% ------ + +p(F) -> + p(F, []). + +p(F, A) -> + io:format("*** [~s] ***" + "~n" ++ F ++ "~n", [formated_timestamp()|A]). + +formated_timestamp() -> + snmp_test_lib:formated_timestamp(). + diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl index 084b3ee8da..238832b7c1 100644 --- a/lib/snmp/test/snmp_agent_test_lib.erl +++ b/lib/snmp/test/snmp_agent_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 @@ -119,20 +119,10 @@ %%% didn't undo (since it failed). %%%----------------------------------------------------------------- -init_all(Config0) when is_list(Config0) -> - ?LOG("init_all -> entry with" - "~n Config0: ~p",[Config0]), - - %% -- - %% Fix config: - %% +init_all(Config) when is_list(Config) -> - DataDir0 = ?config(data_dir, Config0), - DataDir1 = filename:split(filename:absname(DataDir0)), - [_|DataDir2] = lists:reverse(DataDir1), - DataDir3 = filename:join(lists:reverse(DataDir2) ++ [?snmp_test_data]), - Config1 = lists:keydelete(data_dir, 1, Config0), - Config = [{data_dir, DataDir3 ++ "/"}|Config1], + ?LOG("init_all -> entry with" + "~n Config: ~p",[Config]), %% -- %% Start nodes @@ -143,34 +133,43 @@ init_all(Config0) when is_list(Config0) -> %% -- - %% Create necessary files + %% Create necessary files ( and dirs ) %% - PrivDir = ?config(priv_dir, Config), - ?DBG("init_all -> PrivDir ~p", [PrivDir]), + SuiteTopDir = ?config(snmp_suite_top_dir, Config), + ?DBG("init_all -> SuiteTopDir ~p", [SuiteTopDir]), - TopDir = filename:join(PrivDir, snmp_agent_test), - case file:make_dir(TopDir) of - ok -> - ok; - {error, eexist} -> - ok; - Error -> - ?FAIL({failed_creating_subsuite_top_dir, Error}) - end, + AgentDir = filename:join(SuiteTopDir, "agent/"), + ?line ok = file:make_dir(AgentDir), + ?DBG("init_all -> AgentDir ~p", [AgentDir]), - DataDir = ?config(data_dir, Config), - ?DBG("init_all -> DataDir ~p", [DataDir]), + AgentDbDir = filename:join(AgentDir, "db/"), + ?line ok = file:make_dir(AgentDbDir), + ?DBG("init_all -> AgentDbDir ~p", [AgentDbDir]), - ?line ok = file:make_dir(MgrDir = filename:join(TopDir, "mgr_dir/")), - ?DBG("init_all -> MgrDir ~p", [MgrDir]), + AgentLogDir = filename:join(AgentDir, "log/"), + ?line ok = file:make_dir(AgentLogDir), + ?DBG("init_all -> AgentLogDir ~p", [AgentLogDir]), - ?line ok = file:make_dir(AgentDir = filename:join(TopDir, "agent_dir/")), - ?DBG("init_all -> AgentDir ~p", [AgentDir]), + AgentConfDir = filename:join(AgentDir, "conf/"), + ?line ok = file:make_dir(AgentConfDir), + ?DBG("init_all -> AgentConfDir ~p", [AgentConfDir]), - ?line ok = file:make_dir(SaDir = filename:join(TopDir, "sa_dir/")), + MgrDir = filename:join(SuiteTopDir, "mgr/"), + ?line ok = file:make_dir(MgrDir), + ?DBG("init_all -> MgrDir ~p", [MgrDir]), + + SaDir = filename:join(SuiteTopDir, "sa/"), + ?line ok = file:make_dir(SaDir), ?DBG("init_all -> SaDir ~p", [SaDir]), + SaDbDir = filename:join(SaDir, "db/"), + ?line ok = file:make_dir(SaDbDir), + ?DBG("init_all -> SaDbDir ~p", [SaDbDir]), + + %% MibDir = ?config(mib_dir, Config), + %% ?DBG("init_all -> MibDir ~p", [DataDir]), + %% -- %% Start and initiate mnesia @@ -184,11 +183,11 @@ init_all(Config0) when is_list(Config0) -> ?DBG("init_all -> application mnesia: set_env dir",[]), ?line application_controller:set_env(mnesia, dir, - filename:join(TopDir, "Mnesia1")), + filename:join(AgentDbDir, "Mnesia1")), ?DBG("init_all -> application mnesia: set_env dir on node ~p",[SaNode]), - ?line rpc:call(SaNode, application_controller, set_env, - [mnesia, dir, filename:join(TopDir, "Mnesia2")]), + ?line rpc:call(SaNode, application_controller, set_env, + [mnesia, dir, filename:join(SaDir, "Mnesia2")]), ?DBG("init_all -> create mnesia schema",[]), ?line ok = mnesia:create_schema([SaNode, node()]), @@ -199,13 +198,18 @@ init_all(Config0) when is_list(Config0) -> ?DBG("init_all -> start application mnesia on ~p",[SaNode]), ?line ok = rpc:call(SaNode, application, start, [mnesia]), Ip = ?LOCALHOST(), - [{snmp_sa, SaNode}, - {snmp_mgr, MgrNode}, - {agent_dir, AgentDir ++ "/"}, - {mgr_dir, MgrDir ++ "/"}, - {sa_dir, SaDir ++ "/"}, - {mib_dir, DataDir}, - {ip, Ip} | + [{snmp_sa, SaNode}, + {snmp_mgr, MgrNode}, + {snmp_master, node()}, + {agent_dir, AgentDir ++ "/"}, + {agent_db_dir, AgentDbDir ++ "/"}, + {agent_log_dir, AgentLogDir ++ "/"}, + {agent_conf_dir, AgentConfDir ++ "/"}, + {sa_dir, SaDir ++ "/"}, + {sa_db_dir, SaDbDir ++ "/"}, + {mgr_dir, MgrDir ++ "/"}, + %% {mib_dir, DataDir}, + {ip, Ip} | Config]. @@ -220,11 +224,14 @@ finish_all(Config) when is_list(Config) -> %% --- This one *must* be run first in each case --- init_case(Config) when is_list(Config) -> + ?DBG("init_case -> entry with" - "~n Config: ~p", [Config]), - SaNode = ?config(snmp_sa, Config), - MgrNode = ?config(snmp_mgr, Config), - MasterNode = node(), + "~n Config: ~p", [Config]), + + SaNode = ?config(snmp_sa, Config), + MgrNode = ?config(snmp_mgr, Config), + MasterNode = ?config(snmp_master, Config), + %% MasterNode = node(), SaHost = ?HOSTNAME(SaNode), MgrHost = ?HOSTNAME(MgrNode), @@ -411,7 +418,8 @@ start_v3_agent(Config, Opts) when is_list(Config) andalso is_list(Opts) -> start_bilingual_agent(Config) when is_list(Config) -> start_agent(Config, [v1,v2]). -start_bilingual_agent(Config, Opts) when is_list(Config) andalso is_list(Opts) -> +start_bilingual_agent(Config, Opts) + when is_list(Config) andalso is_list(Opts) -> start_agent(Config, [v1,v2], Opts). start_mt_agent(Config) when is_list(Config) -> @@ -423,57 +431,33 @@ start_mt_agent(Config, Opts) when is_list(Config) andalso is_list(Opts) -> start_agent(Config, Vsns) -> start_agent(Config, Vsns, []). start_agent(Config, Vsns, Opts) -> + ?LOG("start_agent -> entry (~p) with" "~n Config: ~p" "~n Vsns: ~p" "~n Opts: ~p", [node(), Config, Vsns, Opts]), - ?line AgentDir = ?config(agent_dir, Config), - ?line SaNode = ?config(snmp_sa, Config), - -%% AgentConfig = -%% [{agent_type, master}, -%% %% {multi_threaded, MultiT}, -%% %% {priority, Prio}, -%% %% {error_report_mod, ErrorReportMod}, -%% {versions, Vsns}, -%% {db_dir, AgentDir}, -%% %% {db_init_error, DbInitError}, -%% %% {set_mechanism, SetModule}, -%% %% {authentication_service, AuthModule}, -%% {audit_trail_log, [{type, read_write}, -%% {dir, AgentDir}, -%% {size, {10240, 10}}, -%% {repair, true}]}, -%% {config, [{verbosity, info}, -%% {dir, AgentDir}, -%% {force_load, false}]}, -%% {mibs, Mibs}, -%% %% {mib_storage, MibStorage}, -%% {local_db, []}, -%% {mib_server, []}, -%% {symbolic_store, []}, -%% {note_store, []}, -%% {net_if, []}, -%% %% {supervisor, SupOpts} -%% ], - + ?line AgentLogDir = ?config(agent_log_dir, Config), + ?line AgentConfDir = ?config(agent_conf_dir, Config), + ?line AgentDbDir = ?config(agent_db_dir, Config), + ?line SaNode = ?config(snmp_sa, Config), + app_env_init(vsn_init(Vsns) ++ - [{audit_trail_log, read_write_log}, - {audit_trail_log_dir, AgentDir}, - {audit_trail_log_size, {10240, 10}}, - {force_config_reload, false}, - {snmp_agent_type, master}, - {snmp_config_dir, AgentDir}, - {snmp_db_dir, AgentDir}, - {snmp_local_db_auto_repair, true}, - {snmp_local_db_verbosity, log}, - {snmp_master_agent_verbosity, trace}, - {snmp_supervisor_verbosity, trace}, - {snmp_mibserver_verbosity, log}, + [{audit_trail_log, read_write_log}, + {audit_trail_log_dir, AgentLogDir}, + {audit_trail_log_size, {10240, 10}}, + {force_config_reload, false}, + {snmp_agent_type, master}, + {snmp_config_dir, AgentConfDir}, + {snmp_db_dir, AgentDbDir}, + {snmp_local_db_auto_repair, true}, + {snmp_local_db_verbosity, log}, + {snmp_master_agent_verbosity, trace}, + {snmp_supervisor_verbosity, trace}, + {snmp_mibserver_verbosity, log}, {snmp_symbolic_store_verbosity, log}, - {snmp_note_store_verbosity, log}, - {snmp_net_if_verbosity, trace}], + {snmp_note_store_verbosity, log}, + {snmp_net_if_verbosity, trace}], Opts), @@ -1237,30 +1221,44 @@ stop_node(Node) -> %%% Configuration %%%----------------------------------------------------------------- -config(Vsns, MgrDir, AgentDir, MIp, AIp) -> - ?line snmp_config:write_agent_snmp_files(AgentDir, Vsns, MIp, - ?TRAP_UDP, AIp, 4000, +config(Vsns, MgrDir, AgentConfDir, MIp, AIp) -> + ?LOG("config -> entry with" + "~n Vsns: ~p" + "~n MgrDir: ~p" + "~n AgentConfDir: ~p" + "~n MIp: ~p" + "~n AIp: ~p", + [Vsns, MgrDir, AgentConfDir, MIp, AIp]), + ?line snmp_config:write_agent_snmp_files(AgentConfDir, Vsns, + MIp, ?TRAP_UDP, AIp, 4000, "test"), - ?line case update_usm(Vsns, AgentDir) of + ?line case update_usm(Vsns, AgentConfDir) of true -> - ?line copy_file(filename:join(AgentDir, "usm.conf"), + ?line copy_file(filename:join(AgentConfDir, "usm.conf"), filename:join(MgrDir, "usm.conf")), ?line update_usm_mgr(Vsns, MgrDir); false -> ?line ok end, - ?line update_community(Vsns, AgentDir), - ?line update_vacm(Vsns, AgentDir), - ?line write_target_addr_conf(AgentDir, MIp, ?TRAP_UDP, Vsns), - ?line write_target_params_conf(AgentDir, Vsns), - ?line write_notify_conf(AgentDir), + ?line update_community(Vsns, AgentConfDir), + ?line update_vacm(Vsns, AgentConfDir), + ?line write_target_addr_conf(AgentConfDir, MIp, ?TRAP_UDP, Vsns), + ?line write_target_params_conf(AgentConfDir, Vsns), + ?line write_notify_conf(AgentConfDir), ok. delete_files(Config) -> - Dir = ?config(agent_dir, Config), - {ok, List} = file:list_dir(Dir), + AgentDir = ?config(agent_dir, Config), + delete_files(AgentDir, [db, conf]). + +delete_files(_AgentFiles, []) -> + ok; +delete_files(AgentDir, [DirName|DirNames]) -> + Dir = filename:join(AgentDir, DirName), + {ok, Files} = file:list_dir(Dir), lists:foreach(fun(FName) -> file:delete(filename:join(Dir, FName)) end, - List). + Files), + delete_files(AgentDir, DirNames). update_usm(Vsns, Dir) -> case lists:member(v3, Vsns) of @@ -1418,8 +1416,8 @@ rewrite_target_addr_conf2(_NewPort,O) -> O. reset_target_addr_conf(Dir) -> - ?line ok = file:rename(filename:join(Dir,"target_addr.old"), - filename:join(Dir,"target_addr.conf")). + ?line ok = file:rename(filename:join(Dir, "target_addr.old"), + filename:join(Dir, "target_addr.conf")). write_target_params_conf(Dir, Vsns) -> F = fun(v1) -> {"target_v1", v1, v1, "all-rights", noAuthNoPriv}; @@ -1458,7 +1456,6 @@ copy_file(From, To) -> ok = file:write_file(To, Bin). - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% display_memory_usage() -> diff --git a/lib/snmp/test/snmp_app_test.erl b/lib/snmp/test/snmp_app_test.erl index bc62c8d530..9b13e7cf1a 100644 --- a/lib/snmp/test/snmp_app_test.erl +++ b/lib/snmp/test/snmp_app_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2011. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -23,8 +23,9 @@ -module(snmp_app_test). -export([ - all/0,groups/0,init_per_group/2,end_per_group/2, init_per_suite/1, - end_per_suite/1, + all/0, groups/0, + init_per_group/2, end_per_group/2, + init_per_suite/1, end_per_suite/1, init_per_testcase/2, end_per_testcase/2, fields/1, @@ -52,33 +53,47 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% all() -> -Cases = [fields, modules, exportall, app_depend, - undef_funcs, {group, start_and_stop}], - Cases. + Cases = + [ + fields, + modules, + exportall, + app_depend, + undef_funcs, + {group, start_and_stop} + ], + Cases. groups() -> [{start_and_stop, [], - [start_and_stop_empty, start_and_stop_with_agent, - start_and_stop_with_manager, - start_and_stop_with_agent_and_manager, - start_epmty_and_then_agent_and_manager_and_stop, - start_with_agent_and_then_manager_and_stop, - start_with_manager_and_then_agent_and_stop]}]. + [start_and_stop_empty, + start_and_stop_with_agent, + start_and_stop_with_manager, + start_and_stop_with_agent_and_manager, + start_epmty_and_then_agent_and_manager_and_stop, + start_with_agent_and_then_manager_and_stop, + start_with_manager_and_then_agent_and_stop]}]. init_per_group(_GroupName, Config) -> - Config. + Config. end_per_group(_GroupName, Config) -> - Config. + Config. init_per_suite(Config) when is_list(Config) -> ?DISPLAY_SUITE_INFO(), + + %% Note that part of this stuff (the suite top dir creation) + %% may already be done (if we run the entire snmp suite). + PrivDir = ?config(priv_dir, Config), TopDir = filename:join(PrivDir, app), case file:make_dir(TopDir) of ok -> ok; + {error, eexist} -> + ok; Error -> fail({failed_creating_subsuite_top_dir, Error}) end, diff --git a/lib/snmp/test/snmp_compiler_test.erl b/lib/snmp/test/snmp_compiler_test.erl index 0a147130b0..257fc47952 100644 --- a/lib/snmp/test/snmp_compiler_test.erl +++ b/lib/snmp/test/snmp_compiler_test.erl @@ -40,6 +40,7 @@ all/0, groups/0, init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, + init_per_suite/1, end_per_suite/1, description/1, oid_conflicts/1, @@ -48,6 +49,7 @@ agent_capabilities/1, module_compliance/1, warnings_as_errors/1, + augments_extra_info/1, otp_6150/1, otp_8574/1, @@ -58,6 +60,7 @@ %%---------------------------------------------------------------------- %% Internal exports %%---------------------------------------------------------------------- + -export([ ]). @@ -74,19 +77,41 @@ %% External functions %%====================================================================== -init_per_testcase(_Case, Config) when is_list(Config) -> - Dir = ?config(priv_dir, Config), - DataDir = ?config(data_dir, Config), - [_|RL] = lists:reverse(filename:split(DataDir)), - MibDir = join(lists:reverse(["snmp_test_data"|RL])), - CompDir = join(Dir, "comp_dir/"), - ?line ok = file:make_dir(CompDir), - [{comp_dir, CompDir}, {mib_dir, MibDir} | Config]. +init_per_suite(Config0) when is_list(Config0) -> + + ?DBG("init_per_suite -> entry with" + "~n Config0: ~p", [Config0]), + + Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0), + Config2 = snmp_test_lib:fix_data_dir(Config1), + + %% Mib-dirs + %% data_dir is trashed by the test-server / common-test + %% so there is no point in fixing it... + MibDir = snmp_test_lib:lookup(data_dir, Config2), + StdMibDir = filename:join([code:priv_dir(snmp), "mibs"]), + + [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2]. + +end_per_suite(Config) when is_list(Config) -> + + ?DBG("end_per_suite -> entry with" + "~n Config: ~p", [Config]), + + Config. + + +init_per_testcase(Case, Config) when is_list(Config) -> + + ?DBG("init_per_testcase -> entry with" + "~n Config: ~p", [Config]), + + CaseTopDir = snmp_test_lib:init_testcase_top_dir(Case, Config), + + [{case_top_dir, CaseTopDir} | Config]. end_per_testcase(_Case, Config) when is_list(Config) -> - CompDir = ?config(comp_dir, Config), - ?line ok = ?DEL_DIR(CompDir), - lists:keydelete(comp_dir, 1, Config). + Config. %%====================================================================== @@ -102,6 +127,7 @@ all() -> agent_capabilities, module_compliance, warnings_as_errors, + augments_extra_info, {group, tickets} ]. @@ -126,7 +152,7 @@ description(Config) when is_list(Config) -> put(tname,desc), p("starting with Config: ~p~n", [Config]), - Dir = ?config(comp_dir, Config), + Dir = ?config(case_top_dir, Config), Filename = join(Dir,"test"), MibSrcName = Filename ++ ".mib", MibBinName = Filename ++ ".bin", @@ -161,7 +187,7 @@ oid_conflicts(Config) when is_list(Config) -> put(tname,oid_conflicts), p("starting with Config: ~p~n", [Config]), - Dir = ?config(comp_dir, Config), + Dir = ?config(case_top_dir, Config), Mib = join(Dir,"TESTv2.mib"), ?line ok = write_oid_conflict_mib(Mib), ?line {error,compilation_failed} = @@ -278,7 +304,7 @@ warnings_as_errors(suite) -> 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), + Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), MibFile = join(MibDir, "OTP8574-MIB.mib"), OutFile = join(Dir, "OTP8574-MIB.bin"), @@ -303,7 +329,7 @@ otp_6150(Config) when is_list(Config) -> put(tname,otp_6150), p("starting with Config: ~p~n", [Config]), - Dir = ?config(comp_dir, Config), + Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), MibFile = join(MibDir, "ERICSSON-TOP-MIB.mib"), ?line {ok, Mib} = snmpc:compile(MibFile, [{outdir, Dir}, {verbosity, trace}]), @@ -319,7 +345,7 @@ otp_8574(Config) when is_list(Config) -> put(tname,otp_8574), p("starting with Config: ~p~n", [Config]), - Dir = ?config(comp_dir, Config), + Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), MibFile = join(MibDir, "OTP8574-MIB.mib"), @@ -352,7 +378,7 @@ otp_8595(Config) when is_list(Config) -> put(tname,otp_8595), p("starting with Config: ~p~n", [Config]), - Dir = ?config(comp_dir, Config), + Dir = ?config(case_top_dir, Config), MibDir = ?config(mib_dir, Config), MibFile = join(MibDir, "OTP8595-MIB.mib"), ?line {ok, Mib} = @@ -364,6 +390,47 @@ otp_8595(Config) when is_list(Config) -> %%====================================================================== + +augments_extra_info(suite) -> + []; +augments_extra_info(Config) when is_list(Config) -> + put(tname, augments_extra_info), + p("starting with Config: ~p~n", [Config]), + + Dir = ?config(case_top_dir, Config), + MibDir = ?config(mib_dir, Config), + Test2File = join(MibDir, "Test2.mib"), + Test3File = join(MibDir, "Test3.mib"), + ?line {ok, Test2BinFile} = + snmpc:compile(Test2File, [{outdir, Dir}, + {verbosity, silence}, + {group_check, false}]), + io:format("Test2BinFile: ~n~p~n", [Test2BinFile]), + ?line {ok, Test3BinFile} = + snmpc:compile(Test3File, [{i, [MibDir]}, + {outdir, Dir}, + {verbosity, silence}, + {group_check, true}]), + io:format("Test3BinFile: ~n~p~n", [Test3BinFile]), + {ok, Test3Mib} = snmp_misc:read_mib(Test3BinFile), + io:format("Test3Mib: ~n~p~n", [Test3Mib]), + %% There is only one table in this mib + #mib{table_infos = [{TableName, TI}]} = Test3Mib, + io:format("TableName: ~p" + "~n Table Info: ~p" + "~n", [TableName, TI]), + #table_info{nbr_of_cols = 4, + defvals = DefVals, + not_accessible = [2,4], + index_types = {augments, {tEntry, undefined}}, + first_accessible = 1} = TI, + io:format("Table info: ~p" + "~n DefVals: ~p" + "~n", [TableName, DefVals]), + ok. + + +%%====================================================================== %% Internal functions %%====================================================================== @@ -517,11 +584,11 @@ DESCRIPTION \"" ++ Desc ++ "\" ::= { test 1 } END", - Message = file:write_file(Filename ,Binary), + Message = file:write_file(Filename, Binary), case Message of ok -> ok; {error, Reason} -> - exit({failed_writing_mib,Reason}) + exit({failed_writing_mib, Reason}) end. @@ -546,8 +613,8 @@ check_desc(Desc1, Desc2) -> exit({'description not equal', Desc1, Desc2}). -join(Comp) -> - filename:join(Comp). +%% join(Comp) -> +%% filename:join(Comp). join(A,B) -> filename:join(A,B). diff --git a/lib/snmp/test/snmp_log_test.erl b/lib/snmp/test/snmp_log_test.erl index b692017407..aeac4d1f71 100644 --- a/lib/snmp/test/snmp_log_test.erl +++ b/lib/snmp/test/snmp_log_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2010. All Rights Reserved. +%% Copyright Ericsson AB 2003-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -42,7 +42,11 @@ -export([ init_per_testcase/2, end_per_testcase/2, - all/0,groups/0,init_per_group/2,end_per_group/2, + all/0, + groups/0, + init_per_group/2, + end_per_group/2, + open_and_close/1, open_write_and_close1/1, @@ -109,16 +113,21 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %%====================================================================== %% ?SKIP(not_yet_implemented). all() -> -[open_and_close, {group, open_write_and_close}, - {group, log_to_io}, {group, log_to_txt}]. + [ + open_and_close, + {group, open_write_and_close}, + {group, log_to_io}, + {group, log_to_txt}]. groups() -> - [{open_write_and_close, [], - [open_write_and_close1, open_write_and_close2, - open_write_and_close3, open_write_and_close4]}, - {log_to_io, [], [log_to_io1, log_to_io2]}, - {log_to_txt, [], - [log_to_txt1, log_to_txt2, log_to_txt3]}]. + [ + {open_write_and_close, [], + [open_write_and_close1, open_write_and_close2, + open_write_and_close3, open_write_and_close4]}, + {log_to_io, [], [log_to_io1, log_to_io2]}, + {log_to_txt, [], + [log_to_txt1, log_to_txt2, log_to_txt3]} + ]. init_per_group(_GroupName, Config) -> Config. diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 75c9f7b277..c374a2f0a6 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -49,6 +49,8 @@ init_per_group/2, end_per_group/2, init_per_testcase/2, end_per_testcase/2, + init_per_suite/1, end_per_suite/1, + simple_start_and_stop1/1, simple_start_and_stop2/1, @@ -146,36 +148,43 @@ %% External functions %%====================================================================== +init_per_suite(Config0) when is_list(Config0) -> + + ?DBG("init_per_suite -> entry with" + "~n Config0: ~p", [Config0]), + + Config1 = snmp_test_lib:init_suite_top_dir(?MODULE, Config0), + Config2 = snmp_test_lib:fix_data_dir(Config1), + + %% Mib-dirs + %% data_dir is trashed by the test-server / common-test + %% so there is no point in fixing it... + MibDir = snmp_test_lib:lookup(data_dir, Config2), + StdMibDir = filename:join([code:priv_dir(snmp), "mibs"]), + + [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2]. + +end_per_suite(Config) when is_list(Config) -> + + ?DBG("end_per_suite -> entry with" + "~n Config: ~p", [Config]), + + Config. + + init_per_testcase(Case, Config) when is_list(Config) -> io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE,Case]), init_per_testcase2(Case, Config). init_per_testcase2(Case, Config) -> - ?DBG("init [~w] Nodes [1]: ~p", [Case, erlang:nodes()]), - - %% Fix a correct data dir (points to the wrong location): - DataDir0 = ?config(data_dir, Config), - DataDir1 = filename:split(filename:absname(DataDir0)), - [_|DataDir2] = lists:reverse(DataDir1), - DataDir = filename:join(lists:reverse(DataDir2) ++ [?snmp_test_data]), + ?DBG("init_per_testcase2 -> ~p", [erlang:nodes()]), - PrivDir = ?config(priv_dir, Config), + CaseTopDir = snmp_test_lib:init_testcase_top_dir(Case, Config), - TopDir = filename:join(PrivDir, ?MODULE), - case file:make_dir(TopDir) of - ok -> - ok; - {error, eexist} -> - ok; - Error -> - ?FAIL({failed_creating_subsuite_top_dir, Error}) - end, - - CaseTopDir = filename:join(TopDir, Case), - ?line ok = file:make_dir(CaseTopDir), - %% -- Manager dirs -- - MgrTopDir = filename:join(CaseTopDir, "manager/"), + MgrTopDir = filename:join(CaseTopDir, "manager/"), + ?DBG("init_per_testcase2 -> try create manager top dir: ~n~p", + [MgrTopDir]), ?line ok = file:make_dir(MgrTopDir), MgrConfDir = filename:join(MgrTopDir, "conf/"), @@ -200,10 +209,9 @@ init_per_testcase2(Case, Config) -> AgLogDir = filename:join(AgTopDir, "log/"), ?line ok = file:make_dir(AgLogDir), - Conf = [{snmp_data_dir, DataDir}, - {watchdog, ?WD_START(?MINS(5))}, + Conf = [{watchdog, ?WD_START(?MINS(5))}, {ip, ?LOCALHOST()}, - {top_dir, TopDir}, + {case_top_dir, CaseTopDir}, {agent_dir, AgTopDir}, {agent_conf_dir, AgConfDir}, {agent_db_dir, AgDbDir}, @@ -388,9 +396,11 @@ all() -> {group, user_tests}, {group, agent_tests}, {group, request_tests}, + {group, request_tests_mt}, {group, event_tests}, + {group, event_tests_mt}, discovery, - {group, tickets} + {group, tickets} ]. groups() -> @@ -431,6 +441,15 @@ groups() -> {group, misc_request_tests} ] }, + {request_tests_mt, [], + [ + {group, get_tests}, + {group, get_next_tests}, + {group, set_tests}, + {group, bulk_tests}, + {group, misc_request_tests} + ] + }, {get_tests, [], [ simple_sync_get1, @@ -477,18 +496,30 @@ groups() -> misc_async2 ] }, - {event_tests, [], - [ - trap1%% , - %% trap2, - %% inform1, - %% inform2, - %% inform3, - %% inform4, - %% inform_swarm, - %% report - ] - }, + {event_tests, [], + [ + trap1, + trap2, + inform1, + inform2, + inform3, + inform4, + inform_swarm, + report + ] + }, + {event_tests_mt, [], + [ + trap1, + trap2, + inform1, + inform2, + inform3, + inform4, + inform_swarm, + report + ] + }, {tickets, [], [ {group, otp8015}, @@ -507,11 +538,21 @@ groups() -> } ]. -init_per_group(_GroupName, Config) -> - Config. +init_per_group(request_tests_mt = GroupName, Config) -> + snmp_test_lib:init_group_top_dir( + GroupName, + [{manager_net_if_module, snmpm_net_if_mt} | Config]); +init_per_group(event_tests_mt = GroupName, Config) -> + snmp_test_lib:init_group_top_dir( + GroupName, + [{manager_net_if_module, snmpm_net_if_mt} | Config]); +init_per_group(GroupName, Config) -> + snmp_test_lib:init_group_top_dir(GroupName, Config). + end_per_group(_GroupName, Config) -> - Config. + %% Do we really need to do this? + lists:keydelete(snmp_group_top_dir, 1, Config). %%====================================================================== @@ -1571,6 +1612,11 @@ simple_sync_get1(Config) when is_list(Config) -> ?line ok = mgr_user_load_mib(Node, std_mib()), Oids2 = [[sysObjectID, 0], [sysDescr, 0], [sysUpTime, 0]], ?line ok = do_simple_sync_get(Node, Addr, Port, Oids2), + + p("Display log"), + display_log(Config), + + p("done"), ok. do_simple_sync_get(Node, Addr, Port, Oids) -> @@ -1578,7 +1624,7 @@ do_simple_sync_get(Node, Addr, Port, Oids) -> ?DBG("~n Reply: ~p" "~n Rem: ~w", [Reply, Rem]), - + %% verify that the operation actually worked: %% The order should be the same, so no need to seach ?line ok = case Reply of @@ -1612,7 +1658,9 @@ simple_sync_get2(suite) -> []; simple_sync_get2(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, ssg2), - do_simple_sync_get2(Config). + do_simple_sync_get2(Config), + display_log(Config), + ok. do_simple_sync_get2(Config) -> Get = fun(Node, TargetName, Oids) -> @@ -1677,7 +1725,9 @@ simple_sync_get3(suite) -> []; simple_sync_get3(Config) when is_list(Config) -> process_flag(trap_exit, true), put(tname, ssg3), - do_simple_sync_get3(Config). + do_simple_sync_get3(Config), + display_log(Config), + ok. do_simple_sync_get3(Config) -> Self = self(), @@ -1703,8 +1753,8 @@ do_simple_sync_get3(Config) -> %%====================================================================== -simple_async_get1(doc) -> ["Simple (async) get-request - " - "Old style (Addr & Port)"]; +simple_async_get1(doc) -> + ["Simple (async) get-request - Old style (Addr & Port)"]; simple_async_get1(suite) -> []; simple_async_get1(Config) when is_list(Config) -> process_flag(trap_exit, true), @@ -1766,6 +1816,7 @@ simple_async_get1(Config) when is_list(Config) -> p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + display_log(Config), ok. async_g_exec1(Node, Addr, Port, Oids) -> @@ -1816,7 +1867,9 @@ simple_async_get2(Config) when is_list(Config) -> TargetName = ?config(manager_agent_target_name, Config), Get = fun(Oids) -> async_g_exec2(MgrNode, TargetName, Oids) end, PostVerify = fun(Res) -> Res end, - do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify). + do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify), + display_log(Config), + ok. do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify) -> ?line ok = mgr_user_load_mib(MgrNode, std_mib()), @@ -1905,7 +1958,9 @@ simple_async_get3(Config) when is_list(Config) -> PostVerify = fun(ok) -> receive Msg -> ok end; (Error) -> Error end, - do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify). + do_simple_async_sync_get2(Config, MgrNode, AgentNode, Get, PostVerify), + display_log(Config), + ok. async_g_exec3(Node, TargetName, Oids, SendOpts) -> mgr_user_async_get2(Node, TargetName, Oids, SendOpts). @@ -1999,6 +2054,8 @@ simple_sync_get_next1(Config) when is_list(Config) -> end, ?line ok = do_simple_get_next(8, MgrNode, Addr, Port, Oids08, VF08), + + display_log(Config), ok. @@ -2062,7 +2119,10 @@ simple_sync_get_next2(Config) when is_list(Config) -> mgr_user_sync_get_next(Node, TargetName, Oids) end, PostVerify = fun(Res) -> Res end, - do_simple_sync_get_next2(Config, GetNext, PostVerify). + do_simple_sync_get_next2(Config, GetNext, PostVerify), + display_log(Config), + ok. + do_simple_sync_get_next2(Config, GetNext, PostVerify) when is_function(GetNext, 3) andalso is_function(PostVerify, 1) -> @@ -2193,7 +2253,9 @@ simple_sync_get_next3(Config) when is_list(Config) -> PostVerify = fun(ok) -> receive Msg -> ok end; (Error) -> Error end, - do_simple_sync_get_next2(Config, GetNext, PostVerify). + do_simple_sync_get_next2(Config, GetNext, PostVerify), + display_log(Config), + ok. %%====================================================================== @@ -2286,6 +2348,7 @@ simple_async_get_next1(Config) when is_list(Config) -> p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + display_log(Config), ok. @@ -2315,7 +2378,9 @@ simple_async_get_next2(Config) when is_list(Config) -> async_gn_exec2(MgrNode, TargetName, Oids) end, PostVerify = fun(Res) -> Res end, - do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify). + do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify), + display_log(Config), + ok. do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify) when is_function(GetNext, 1) andalso is_function(PostVerify, 1) -> @@ -2437,7 +2502,9 @@ simple_async_get_next3(Config) when is_list(Config) -> (Error) -> Error end, - do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify). + do_simple_async_get_next2(MgrNode, AgentNode, GetNext, PostVerify), + display_log(Config), + ok. async_gn_exec3(Node, TargetName, Oids, SendOpts) -> mgr_user_async_get_next2(Node, TargetName, Oids, SendOpts). @@ -2475,6 +2542,8 @@ simple_sync_set1(Config) when is_list(Config) -> {[sysLocation, 0], Val22} ], ?line ok = do_simple_set1(Node, Addr, Port, VAVs2), + + display_log(Config), ok. do_simple_set1(Node, Addr, Port, VAVs) -> @@ -2527,7 +2596,9 @@ simple_sync_set2(Config) when is_list(Config) -> end, PostVerify = fun() -> ok end, - do_simple_sync_set2(Config, Set, PostVerify). + do_simple_sync_set2(Config, Set, PostVerify), + display_log(Config), + ok. do_simple_sync_set2(Config, Set, PostVerify) when is_function(Set, 3) andalso is_function(PostVerify, 0) -> @@ -2604,7 +2675,9 @@ simple_sync_set3(Config) when is_list(Config) -> end, PostVerify = fun() -> receive Msg -> ok end end, - do_simple_sync_set2(Config, Set, PostVerify). + do_simple_sync_set2(Config, Set, PostVerify), + display_log(Config), + ok. %%====================================================================== @@ -2663,6 +2736,7 @@ simple_async_set1(Config) when is_list(Config) -> p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + display_log(Config), ok. @@ -2724,7 +2798,9 @@ simple_async_set2(Config) when is_list(Config) -> end, PostVerify = fun(Res) -> Res end, - do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify). + do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify), + display_log(Config), + ok. do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify) -> Requests = @@ -2806,7 +2882,9 @@ simple_async_set3(Config) when is_list(Config) -> (Res) -> Res end, - do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify). + do_simple_async_set2(MgrNode, AgentNode, Set, PostVerify), + display_log(Config), + ok. async_s_exec3(Node, TargetName, VAVs, SendOpts) -> mgr_user_async_set2(Node, TargetName, VAVs, SendOpts). @@ -2932,6 +3010,7 @@ simple_sync_get_bulk1(Config) when is_list(Config) -> 0, 2, [[TCnt2, 1]], VF11), + display_log(Config), ok. fl(L) -> @@ -3004,7 +3083,9 @@ simple_sync_get_bulk2(Config) when is_list(Config) -> end, PostVerify = fun(Res) -> Res end, - do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify). + do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify), + display_log(Config), + ok. do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify) -> %% -- 1 -- @@ -3169,7 +3250,9 @@ simple_sync_get_bulk3(Config) when is_list(Config) -> (Res) -> Res end, - do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify). + do_simple_sync_get_bulk2(Config, MgrNode, AgentNode, GetBulk, PostVerify), + display_log(Config), + ok. %%====================================================================== @@ -3308,6 +3391,7 @@ simple_async_get_bulk1(Config) when is_list(Config) -> p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + display_log(Config), ok. @@ -3340,7 +3424,9 @@ simple_async_get_bulk2(Config) when is_list(Config) -> end, PostVerify = fun(Res) -> Res end, - do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify). + do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify), + display_log(Config), + ok. do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify) -> %% We re-use the verification functions from the ssgb test-case @@ -3505,7 +3591,9 @@ simple_async_get_bulk3(Config) when is_list(Config) -> (Res) -> Res end, - do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify). + do_simple_async_get_bulk2(MgrNode, AgentNode, GetBulk, PostVerify), + display_log(Config), + ok. async_gb_exec3(Node, TargetName, {NR, MR, Oids}, SendOpts) -> mgr_user_async_get_bulk2(Node, TargetName, NR, MR, Oids, SendOpts). @@ -3697,6 +3785,7 @@ misc_async1(Config) when is_list(Config) -> p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + display_log(Config), ok. @@ -3885,6 +3974,7 @@ misc_async2(Config) when is_list(Config) -> p("manager info when ending test: ~n~p", [mgr_info(MgrNode)]), p("agent info when ending test: ~n~p", [agent_info(AgentNode)]), + display_log(Config), ok. @@ -4099,7 +4189,9 @@ trap1(Config) when is_list(Config) -> {5, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands). + command_handler(Commands), + display_log(Config), + ok. %%====================================================================== @@ -4290,7 +4382,9 @@ trap2(Config) when is_list(Config) -> {7, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands). + command_handler(Commands), + display_log(Config), + ok. %%====================================================================== @@ -4415,7 +4509,9 @@ inform1(Config) when is_list(Config) -> {6, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands). + command_handler(Commands), + display_log(Config), + ok. %%====================================================================== @@ -4423,7 +4519,7 @@ inform1(Config) when is_list(Config) -> inform2(suite) -> []; inform2(Config) when is_list(Config) -> process_flag(trap_exit, true), - put(tname,i2), + put(tname, i2), p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -4577,7 +4673,7 @@ inform2(Config) when is_list(Config) -> [ {1, "Manager and agent info at start of test", Cmd1}, {2, "Send notifcation [no receiver] from agent", Cmd2}, - {3, "await inform-sent acknowledge from agent", Cmd3}, + {3, "Await inform-sent acknowledge from agent", Cmd3}, {4, "Await first inform to manager - do not reply", Cmd4}, {5, "Await second inform to manager - reply", Cmd5}, {6, "await inform-acknowledge from agent", Cmd6}, @@ -4585,7 +4681,9 @@ inform2(Config) when is_list(Config) -> {8, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands). + command_handler(Commands), + display_log(Config), + ok. %%====================================================================== @@ -4719,7 +4817,9 @@ inform3(Config) when is_list(Config) -> {9, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands). + command_handler(Commands), + display_log(Config), + ok. %%====================================================================== @@ -4835,7 +4935,9 @@ inform4(Config) when is_list(Config) -> {6, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands). + command_handler(Commands), + display_log(Config), + ok. %%====================================================================== @@ -4923,7 +5025,10 @@ inform_swarm(Config) when is_list(Config) -> {5, "Manager and agent info after test completion", Cmd1} ], - command_handler(Commands). + command_handler(Commands), + display_log(Config), + ok. + inform_swarm_collector(N) -> inform_swarm_collector(N, 0, 0, 0, 10000). @@ -5227,8 +5332,8 @@ init_agent(Config) -> %% -- %% Retrieve some dir's %% - Dir = ?config(top_dir, Config), - DataDir = ?config(data_dir, Config), + Dir = ?config(agent_dir, Config), + MibDir = ?config(mib_dir, Config), %% -- %% Start node @@ -5259,7 +5364,7 @@ init_agent(Config) -> ?line ok = write_agent_config(Vsns, Config), Conf = [{agent_node, Node}, - {mib_dir, DataDir} | Config], + {mib_dir, MibDir} | Config], %% %% Start the agent @@ -5720,6 +5825,15 @@ start_manager(Node, Vsns, Conf0, Opts) -> AtlSeqNo = get_opt(manager_atl_seqno, Conf0, false), + NetIfConf = + case get_opt(manager_net_if_module, Conf0, no_module) of + no_module -> + [{verbosity, NetIfVerbosity}]; + NetIfModule -> + [{module, NetIfModule}, + {verbosity, NetIfVerbosity}] + end, + Env = [{versions, Vsns}, {inform_request_behaviour, IRB}, {audit_trail_log, [{type, read_write}, @@ -5732,7 +5846,7 @@ start_manager(Node, Vsns, Conf0, Opts) -> {verbosity, ConfigVerbosity}]}, {note_store, [{verbosity, NoteStoreVerbosity}]}, {server, [{verbosity, ServerVerbosity}]}, - {net_if, [{verbosity, NetIfVerbosity}]}], + {net_if, NetIfConf}], ?line ok = set_mgr_env(Node, Env), ?line ok = start_snmp(Node), @@ -6018,6 +6132,38 @@ write_conf_file(Dir, File, Str) -> %% ------ +display_log(Config) -> + case lists:keysearch(manager_log_dir, 1, Config) of + {value, {_, Dir}} -> + case lists:keysearch(manager_node, 1, Config) of + {value, {_, Node}} -> + LogDir = Dir, + Mibs = [], + OutFile = j(LogDir, "snmpm_log.txt"), + p("~n" + "=========================" + " < Audit Trail Log > " + "=========================" + "~n"), + rcall(Node, snmpm, log_to_txt, [LogDir, Mibs, OutFile]), + rcall(Node, snmpm, log_to_io, [LogDir, Mibs]), + p("~n" + "=========================" + " < / Audit Trail Log > " + "=========================" + "~n"); + false -> + p("display_log -> no manager node found"), + ok + end; + false -> + p("display_log -> no manager log dir found"), + ok + end. + + +%% ------ + test2_mib(Config) -> j(test_mib_dir(Config), "Test2.bin"). @@ -6034,7 +6180,7 @@ snmpv2_mib() -> j(mib_dir(), "SNMPv2-MIB.bin"). test_mib_dir(Config) -> - ?config(snmp_data_dir, Config). + ?config(mib_dir, Config). mib_dir() -> j(code:priv_dir(snmp), "mibs"). diff --git a/lib/snmp/test/snmp_pdus_test.erl b/lib/snmp/test/snmp_pdus_test.erl index 07b6d6b657..0d78749bcb 100644 --- a/lib/snmp/test/snmp_pdus_test.erl +++ b/lib/snmp/test/snmp_pdus_test.erl @@ -39,6 +39,7 @@ otp7575/1, otp8563/1, otp9022/1, + otp10132/1, init_per_testcase/2, end_per_testcase/2 ]). @@ -74,16 +75,16 @@ end_per_testcase(_Case, Config) when is_list(Config) -> %% Test case definitions %%====================================================================== all() -> -[{group, tickets}]. + [{group, tickets}]. groups() -> - [{tickets, [], [otp7575, otp8563, otp9022]}]. + [{tickets, [], [otp7575, otp8563, otp9022, otp10132]}]. init_per_group(_GroupName, Config) -> - Config. + Config. end_per_group(_GroupName, Config) -> - Config. + Config. @@ -94,7 +95,7 @@ end_per_group(_GroupName, Config) -> %%====================================================================== otp7575(suite) -> []; -otp7575(doc) -> ["OTP-7575"]; +otp7575(doc) -> ["OTP-7575 - Message version"]; otp7575(Config) when is_list(Config) -> io:format("attempt to decode message with valid version~n", []), MsgWithOkVersion = <<48,39,2,1,0,4,6,112,117,98,108,105,99,160,26,2,2,1,49,2,1,0,2,1,0,48,14,48,12,6,8,43,6,1,2,1,1,5,0,5,0>>, @@ -127,48 +128,55 @@ otp7575(Config) when is_list(Config) -> otp8563(suite) -> []; -otp8563(doc) -> ["OTP-8563"]; +otp8563(doc) -> ["OTP-8563 - Counter64"]; otp8563(Config) when is_list(Config) -> Val1 = 16#7fffffffffffffff, - io:format("try encode and decode ~w~n", [Val1]), + io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), Enc1 = snmp_pdus:enc_value('Counter64', Val1), + io:format(" => ~w~n", [Enc1]), {{'Counter64', Val1}, []} = snmp_pdus:dec_value(Enc1), Val2 = Val1 + 1, - io:format("try encode and decode ~w~n", [Val2]), + io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), Enc2 = snmp_pdus:enc_value('Counter64', Val2), + io:format(" => ~w~n", [Enc2]), {{'Counter64', Val2}, []} = snmp_pdus:dec_value(Enc2), Val3 = Val2 + 1, - io:format("try encode and decode ~w~n", [Val3]), + io:format("try encode and decode valule 3: ~w (0x~.16b)~n", [Val3, Val3]), Enc3 = snmp_pdus:enc_value('Counter64', Val3), + io:format(" => ~w~n", [Enc3]), {{'Counter64', Val3}, []} = snmp_pdus:dec_value(Enc3), Val4 = 16#fffffffffffffffe, - io:format("try encode and decode ~w~n", [Val4]), + io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), Enc4 = snmp_pdus:enc_value('Counter64', Val4), + io:format(" => ~w~n", [Enc4]), {{'Counter64', Val4}, []} = snmp_pdus:dec_value(Enc4), Val5 = Val4 + 1, - io:format("try encode and decode ~w~n", [Val5]), + io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), Enc5 = snmp_pdus:enc_value('Counter64', Val5), + io:format(" => ~w~n", [Enc5]), {{'Counter64', Val5}, []} = snmp_pdus:dec_value(Enc5), Val6 = 16#ffffffffffffffff + 1, - io:format("try and fail to encode ~w~n", [Val6]), + io:format("try and fail to encode value 6: ~w (0x~.16b)~n", [Val6, Val6]), case (catch snmp_pdus:enc_value('Counter64', Val6)) of {'EXIT', {error, {bad_counter64, Val6}}} -> ok; Unexpected6 -> + io:format(" => ~w~n", [Unexpected6]), exit({unexpected_encode_result, Unexpected6, Val6}) end, Val7 = -1, - io:format("try and fail to encode ~w~n", [Val7]), + io:format("try and fail to encode value 7: ~w~n", [Val7]), case (catch snmp_pdus:enc_value('Counter64', Val7)) of {'EXIT', {error, {bad_counter64, Val7}}} -> ok; Unexpected7 -> + io:format(" => ~w~n", [Unexpected7]), exit({unexpected_encode_result, Unexpected7, Val7}) end, @@ -176,51 +184,151 @@ otp8563(Config) when is_list(Config) -> otp9022(suite) -> []; -otp9022(doc) -> ["OTP-9022"]; +otp9022(doc) -> ["OTP-9022 - Counter32"]; otp9022(Config) when is_list(Config) -> - Val1 = 16#7fffffff, - io:format("try encode and decode ~w~n", [Val1]), + Val0 = 2908389204, + io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]), + Enc0 = snmp_pdus:enc_value('Counter32', Val0), + io:format(" => ~w~n", [Enc0]), + {{'Counter32', Val0}, []} = snmp_pdus:dec_value(Enc0), + + Val1 = 0, + io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), Enc1 = snmp_pdus:enc_value('Counter32', Val1), + io:format(" => ~w~n", [Enc1]), {{'Counter32', Val1}, []} = snmp_pdus:dec_value(Enc1), Val2 = Val1 + 1, - io:format("try encode and decode ~w~n", [Val2]), + io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), Enc2 = snmp_pdus:enc_value('Counter32', Val2), + io:format(" => ~w~n", [Enc2]), {{'Counter32', Val2}, []} = snmp_pdus:dec_value(Enc2), - Val3 = Val2 + 1, - io:format("try encode and decode ~w~n", [Val3]), + Val3 = 16#7ffffffe, + io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]), Enc3 = snmp_pdus:enc_value('Counter32', Val3), + io:format(" => ~w~n", [Enc3]), {{'Counter32', Val3}, []} = snmp_pdus:dec_value(Enc3), - Val4 = 16#fffffffe, - io:format("try encode and decode ~w~n", [Val4]), + Val4 = Val3 + 1, + io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), Enc4 = snmp_pdus:enc_value('Counter32', Val4), + io:format(" => ~w~n", [Enc4]), {{'Counter32', Val4}, []} = snmp_pdus:dec_value(Enc4), Val5 = Val4 + 1, - io:format("try encode and decode ~w~n", [Val5]), + io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), Enc5 = snmp_pdus:enc_value('Counter32', Val5), + io:format(" => ~w~n", [Enc5]), {{'Counter32', Val5}, []} = snmp_pdus:dec_value(Enc5), - Val6 = 16#ffffffff + 1, - io:format("try and fail to encode ~w~n", [Val6]), - case (catch snmp_pdus:enc_value('Counter32', Val6)) of - {'EXIT', {error, {bad_counter32, Val6}}} -> + Val6 = 16#fffffffe, + io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]), + Enc6 = snmp_pdus:enc_value('Counter32', Val6), + io:format(" => ~w~n", [Enc6]), + {{'Counter32', Val6}, []} = snmp_pdus:dec_value(Enc6), + + Val7 = Val6 + 1, + io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]), + Enc7 = snmp_pdus:enc_value('Counter32', Val7), + io:format(" => ~w~n", [Enc7]), + {{'Counter32', Val7}, []} = snmp_pdus:dec_value(Enc7), + + Val8 = 16#ffffffff + 1, + io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]), + case (catch snmp_pdus:enc_value('Counter32', Val8)) of + {'EXIT', {error, {bad_counter32, Val8}}} -> ok; - Unexpected6 -> - exit({unexpected_encode_result, Unexpected6, Val6}) + Unexpected8 -> + io:format(" => ~w~n", [Unexpected8]), + exit({unexpected_encode_result, Unexpected8, Val8}) end, - Val7 = -1, - io:format("try and fail to encode ~w~n", [Val7]), - case (catch snmp_pdus:enc_value('Counter32', Val7)) of - {'EXIT', {error, {bad_counter32, Val7}}} -> + Val9 = -1, + io:format("try and fail to encode value 9: ~w~n", [Val9]), + case (catch snmp_pdus:enc_value('Counter32', Val9)) of + {'EXIT', {error, {bad_counter32, Val9}}} -> ok; - Unexpected7 -> - exit({unexpected_encode_result, Unexpected7, Val7}) + Unexpected9 -> + io:format(" => ~w~n", [Unexpected9]), + exit({unexpected_encode_result, Unexpected9, Val9}) + end, + + ok. + + +otp10132(suite) -> []; +otp10132(doc) -> ["OTP-10132 - TimeTicks"]; +otp10132(Config) when is_list(Config) -> + Val0 = 2159001034, + io:format("try encode and decode value 0: ~w (0x~.16b)~n", [Val0, Val0]), + Enc0 = snmp_pdus:enc_value('TimeTicks', Val0), + io:format(" => ~w~n", [Enc0]), + {{'TimeTicks', Val0}, []} = snmp_pdus:dec_value(Enc0), + + Val1 = 0, + io:format("try encode and decode value 1: ~w (0x~.16b)~n", [Val1, Val1]), + Enc1 = snmp_pdus:enc_value('TimeTicks', Val1), + io:format(" => ~w~n", [Enc1]), + {{'TimeTicks', Val1}, []} = snmp_pdus:dec_value(Enc1), + + Val2 = Val1 + 1, + io:format("try encode and decode value 2: ~w (0x~.16b)~n", [Val2, Val2]), + Enc2 = snmp_pdus:enc_value('TimeTicks', Val2), + io:format(" => ~w~n", [Enc2]), + {{'TimeTicks', Val2}, []} = snmp_pdus:dec_value(Enc2), + + Val3 = 16#7ffffffe, + io:format("try encode and decode value 3: ~w (0x~.16b)~n", [Val3, Val3]), + Enc3 = snmp_pdus:enc_value('TimeTicks', Val3), + io:format(" => ~w~n", [Enc3]), + {{'TimeTicks', Val3}, []} = snmp_pdus:dec_value(Enc3), + + Val4 = Val3 + 1, + io:format("try encode and decode value 4: ~w (0x~.16b)~n", [Val4, Val4]), + Enc4 = snmp_pdus:enc_value('TimeTicks', Val4), + io:format(" => ~w~n", [Enc4]), + {{'TimeTicks', Val4}, []} = snmp_pdus:dec_value(Enc4), + + Val5 = Val4 + 1, + io:format("try encode and decode value 5: ~w (0x~.16b)~n", [Val5, Val5]), + Enc5 = snmp_pdus:enc_value('TimeTicks', Val5), + io:format(" => ~w~n", [Enc5]), + {{'TimeTicks', Val5}, []} = snmp_pdus:dec_value(Enc5), + + Val6 = 16#fffffffe, + io:format("try encode and decode value 6: ~w (0x~.16b)~n", [Val6, Val6]), + Enc6 = snmp_pdus:enc_value('TimeTicks', Val6), + io:format(" => ~w~n", [Enc6]), + {{'TimeTicks', Val6}, []} = snmp_pdus:dec_value(Enc6), + + Val7 = Val6 + 1, + io:format("try encode and decode value 7: ~w (0x~.16b)~n", [Val7, Val7]), + Enc7 = snmp_pdus:enc_value('TimeTicks', Val7), + io:format(" => ~w~n", [Enc7]), + {{'TimeTicks', Val7}, []} = snmp_pdus:dec_value(Enc7), + + Val8 = Val7 + 1, + io:format("try and fail to encode value 8: ~w (0x~.16b)~n", [Val8, Val8]), + case (catch snmp_pdus:enc_value('TimeTicks', Val8)) of + {'EXIT', {error, {bad_timeticks, Val8}}} -> + ok; + Unexpected8 -> + io:format(" => ~w~n", [Unexpected8]), + exit({unexpected_encode_result, Unexpected8, Val8}) end, + Val9 = -1, + io:format("try and fail to encode value 9: ~w~n", [Val9]), + case (catch snmp_pdus:enc_value('TimeTicks', Val9)) of + {'EXIT', {error, {bad_timeticks, Val9}}} -> + ok; + Unexpected9 -> + io:format(" => ~w~n", [Unexpected9]), + exit({unexpected_encode_result, Unexpected9, Val9}) + end, + + io:format("done~n", []), ok. diff --git a/lib/snmp/test/snmp_test_data/Test3.mib b/lib/snmp/test/snmp_test_data/Test3.mib new file mode 100644 index 0000000000..7f76e4dba4 --- /dev/null +++ b/lib/snmp/test/snmp_test_data/Test3.mib @@ -0,0 +1,123 @@ +Test3 DEFINITIONS ::= BEGIN + +IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, Integer32, snmpModules, mib-2 + FROM SNMPv2-SMI + MODULE-COMPLIANCE, OBJECT-GROUP + FROM SNMPv2-CONF + tEntry + FROM Test2; + +t3MIB MODULE-IDENTITY + LAST-UPDATED "1203090000Z" + ORGANIZATION "" + CONTACT-INFO + "" + DESCRIPTION + "Test mib, used to test processing of requests." + ::= { snmpModules 42 } + + +-- Administrative assignments **************************************** + +t3MIBObjects OBJECT IDENTIFIER ::= { t3MIB 1 } +t3MIBConformance OBJECT IDENTIFIER ::= { t3MIB 2 } + + +-- test4 OBJECT IDENTIFIER ::= { mib-2 18 } + +tAugTable OBJECT-TYPE + SYNTAX SEQUENCE OF TAugEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "This table AUGMENTS tTable of the Test2 MIB." + ::= { t3MIBObjects 1 } + +tAugEntry OBJECT-TYPE + SYNTAX TAugEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An entry (conceptual row) in the sysORTable." + AUGMENTS { tEntry } + ::= { tAugTable 1 } + +TAugEntry ::= SEQUENCE { + tFoo1 OCTET STRING, + tFoo2 OCTET STRING, + tBar1 Integer32, + tBar2 Integer32 +} + +tFoo1 OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (0..255)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "A string." + DEFVAL { "foo 1" } + ::= { tAugEntry 1 } + +tFoo2 OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (0..255)) + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A string." + DEFVAL { "foo 2" } + ::= { tAugEntry 2 } + +tBar1 OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "An integer." + DEFVAL { 42 } + ::= { tAugEntry 3 } + +tBar2 OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An integer." + DEFVAL { 42 } + ::= { tAugEntry 4 } + + +-- Conformance Information ******************************************* + +t3MIBCompliances OBJECT IDENTIFIER + ::= { t3MIBConformance 1 } +t3MIBGroups OBJECT IDENTIFIER + ::= { t3MIBConformance 2 } + +-- Compliance statements + +t3MIBCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The compliance statement for SNMP engines which + implement the SNMP-COMMUNITY-MIB." + + MODULE -- this module + MANDATORY-GROUPS { t3Group } + + ::= { t3MIBCompliances 1 } + +t3Group OBJECT-GROUP + OBJECTS { + tFoo1, + tFoo2, + tBar1, + tBar2 + } + STATUS current + DESCRIPTION + "A group." + ::= { t3MIBGroups 1 } + + +END diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl index e4d58a1253..26115a0c74 100644 --- a/lib/snmp/test/snmp_test_lib.erl +++ b/lib/snmp/test/snmp_test_lib.erl @@ -25,7 +25,10 @@ -export([hostname/0, hostname/1, localhost/0, os_type/0, sz/1, display_suite_info/1]). -export([non_pc_tc_maybe_skip/4, os_based_skip/1]). --export([replace_config/3, set_config/3, get_config/2, get_config/3]). +-export([fix_data_dir/1, + init_suite_top_dir/2, init_group_top_dir/2, init_testcase_top_dir/2, + lookup/2, + replace_config/3, set_config/3, get_config/2, get_config/3]). -export([fail/3, skip/3]). -export([millis/0, millis_diff/2, hours/1, minutes/1, seconds/1, sleep/1]). -export([flush_mqueue/0, trap_exit/0, trap_exit/1]). @@ -198,6 +201,97 @@ os_based_skip(_Crap) -> %% Test suite utility functions %% +fix_data_dir(Config) -> + DataDir0 = lookup(data_dir, Config), + DataDir1 = filename:split(filename:absname(DataDir0)), + [_|DataDir2] = lists:reverse(DataDir1), + DataDir = filename:join(lists:reverse(DataDir2) ++ [?snmp_test_data]), + Config1 = lists:keydelete(data_dir, 1, Config), + [{data_dir, DataDir ++ "/"} | Config1]. + + +init_suite_top_dir(Suite, Config0) -> + io:format("~w:init_suite_top_dir -> entry with" + "~n Suite: ~p" + "~n Config0: ~p" + "~n", [?MODULE, Suite, Config0]), + Dir = lookup(priv_dir, Config0), + SuiteTopDir = filename:join(Dir, Suite), + case file:make_dir(SuiteTopDir) of + ok -> + ok; + {error, eexist} -> + ok; + {error, Reason} -> + fail({failed_creating_suite_top_dir, SuiteTopDir, Reason}, + ?MODULE, ?LINE) + end, + + %% This is just in case... + Config1 = lists:keydelete(snmp_group_top_dir, 1, Config0), + Config2 = lists:keydelete(snmp_suite_top_dir, 1, Config1), + [{snmp_suite_top_dir, SuiteTopDir} | Config2]. + + +init_group_top_dir(GroupName, Config) -> + io:format("~w:init_group_top_dir -> entry with" + "~n GroupName: ~p" + "~n Config: ~p" + "~n", [?MODULE, GroupName, Config]), + case lists:keysearch(snmp_group_top_dir, 1, Config) of + {value, {_Key, Dir}} -> + %% This is a sub-group, so create our dir within Dir + GroupTopDir = filename:join(Dir, GroupName), + case file:make_dir(GroupTopDir) of + ok -> + ok; + {error, Reason} -> + fail({failed_creating_group_top_dir, GroupTopDir, Reason}, + ?MODULE, ?LINE) + end, + [{snmp_group_top_dir, GroupTopDir} | Config]; + + _ -> + case lists:keysearch(snmp_suite_top_dir, 1, Config) of + {value, {_Key, Dir}} -> + GroupTopDir = filename:join(Dir, GroupName), + case file:make_dir(GroupTopDir) of + ok -> + ok; + {error, Reason} -> + fail({failed_creating_group_top_dir, + GroupTopDir, Reason}, + ?MODULE, ?LINE) + end, + [{snmp_group_top_dir, GroupTopDir} | Config]; + _ -> + fail(could_not_find_suite_top_dir, ?MODULE, ?LINE) + end + end. + + +init_testcase_top_dir(Case, Config) -> + io:format("~w:init_testcase_top_dir -> entry with" + "~n Case: ~p" + "~n Config: ~p" + "~n", [?MODULE, Case, Config]), + case lists:keysearch(snmp_group_top_dir, 1, Config) of + {value, {_Key, Dir}} -> + CaseTopDir = filename:join(Dir, Case), + ok = file:make_dir(CaseTopDir), + CaseTopDir; + false -> + case lists:keysearch(snmp_suite_top_dir, 1, Config) of + {value, {_Key, Dir}} -> + CaseTopDir = filename:join(Dir, Case), + ok = file:make_dir(CaseTopDir), + CaseTopDir; + false -> + fail(failed_creating_case_top_dir, ?MODULE, ?LINE) + end + end. + + replace_config(Key, Config, NewValue) -> lists:keyreplace(Key, 1, Config, {Key, NewValue}). @@ -220,6 +314,9 @@ get_config(Key,C,Default) -> Default end. +lookup(Key, Config) -> + {value, {Key, Value}} = lists:keysearch(Key, 1, Config), + Value. fail(Reason, Mod, Line) -> exit({suite_failed, Reason, Mod, Line}). diff --git a/lib/snmp/test/test-mibs/RMON-MIB.mib b/lib/snmp/test/test-mibs/RMON-MIB.mib index 0824dbde1d..fa5bc82f4a 100644 --- a/lib/snmp/test/test-mibs/RMON-MIB.mib +++ b/lib/snmp/test/test-mibs/RMON-MIB.mib @@ -1,3826 +1,3958 @@ - RMON-MIB DEFINITIONS ::= BEGIN - - IMPORTS - Counter FROM RFC1155-SMI - mib-2,DisplayString FROM RFC1213-MIB - OBJECT-TYPE FROM RFC-1212 - TRAP-TYPE FROM RFC-1215; - - -- Remote Network Monitoring MIB - - rmon OBJECT IDENTIFIER ::= { mib-2 16 } - - - -- textual conventions - - OwnerString ::= DisplayString - -- This data type is used to model an administratively - -- assigned name of the owner of a resource. This - -- information is taken from the NVT ASCII character - -- set. It is suggested that this name contain one or - - - -- more of the following: IP address, management station - -- name, network manager's name, location, or phone - -- number. - -- In some cases the agent itself will be the owner of - -- an entry. In these cases, this string shall be set - -- to a string starting with 'monitor'. - -- - -- SNMP access control is articulated entirely in terms - -- of the contents of MIB views; access to a particular - -- SNMP object instance depends only upon its presence - -- or absence in a particular MIB view and never upon - -- its value or the value of related object instances. - -- Thus, objects of this type afford resolution of - -- resource contention only among cooperating managers; - -- they realize no access control function with respect - -- to uncooperative parties. - -- - -- By convention, objects with this syntax are declared as - -- having - -- - -- SIZE (0..127) - - EntryStatus ::= INTEGER - { valid(1), - createRequest(2), - underCreation(3), - invalid(4) - } - -- The status of a table entry. - -- - -- Setting this object to the value invalid(4) has the - -- effect of invalidating the corresponding entry. - -- That is, it effectively disassociates the mapping - -- identified with said entry. - -- It is an implementation-specific matter as to whether - -- the agent removes an invalidated entry from the table. - -- Accordingly, management stations must be prepared to - -- receive tabular information from agents that - -- corresponds to entries currently not in use. Proper - -- interpretation of such entries requires examination - -- of the relevant EntryStatus object. - -- - -- An existing instance of this object cannot be set to - -- createRequest(2). This object may only be set to - -- createRequest(2) when this instance is created. When - -- this object is created, the agent may wish to create - -- supplemental object instances with default values - -- to complete a conceptual row in this table. Because - - - -- the creation of these default objects is entirely at - -- the option of the agent, the manager must not assume - -- that any will be created, but may make use of any that - -- are created. Immediately after completing the create - -- operation, the agent must set this object to - -- underCreation(3). - -- - -- When in the underCreation(3) state, an entry is - -- allowed to exist in a possibly incomplete, possibly - -- inconsistent state, usually to allow it to be - -- modified in mutiple PDUs. When in this state, an - -- entry is not fully active. Entries shall exist in - -- the underCreation(3) state until the management - -- station is finished configuring the entry and sets - -- this object to valid(1) or aborts, setting this - -- object to invalid(4). If the agent determines that - -- an entry has been in the underCreation(3) state for - -- an abnormally long time, it may decide that the - -- management station has crashed. If the agent makes - -- this decision, it may set this object to invalid(4) - -- to reclaim the entry. A prudent agent will - -- understand that the management station may need to - -- wait for human input and will allow for that - -- possibility in its determination of this abnormally - -- long period. - -- - -- An entry in the valid(1) state is fully configured and - -- consistent and fully represents the configuration or - -- operation such a row is intended to represent. For - -- example, it could be a statistical function that is - -- configured and active, or a filter that is available - -- in the list of filters processed by the packet capture - -- process. - -- - -- A manager is restricted to changing the state of an - -- entry in the following ways: - -- - -- create under - -- To: valid Request Creation invalid - -- From: - -- valid OK NO OK OK - -- createRequest N/A N/A N/A N/A - -- underCreation OK NO OK OK - -- invalid NO NO NO OK - -- nonExistent NO OK NO OK - -- - -- In the table above, it is not applicable to move the - -- state from the createRequest state to any other - - - -- state because the manager will never find the - -- variable in that state. The nonExistent state is - -- not a value of the enumeration, rather it means that - -- the entryStatus variable does not exist at all. - -- - -- An agent may allow an entryStatus variable to change - -- state in additional ways, so long as the semantics - -- of the states are followed. This allowance is made - -- to ease the implementation of the agent and is made - -- despite the fact that managers should never - -- excercise these additional state transitions. - - - statistics OBJECT IDENTIFIER ::= { rmon 1 } - history OBJECT IDENTIFIER ::= { rmon 2 } - alarm OBJECT IDENTIFIER ::= { rmon 3 } - hosts OBJECT IDENTIFIER ::= { rmon 4 } - hostTopN OBJECT IDENTIFIER ::= { rmon 5 } - matrix OBJECT IDENTIFIER ::= { rmon 6 } - filter OBJECT IDENTIFIER ::= { rmon 7 } - capture OBJECT IDENTIFIER ::= { rmon 8 } - event OBJECT IDENTIFIER ::= { rmon 9 } - - - -- The Ethernet Statistics Group - -- - -- Implementation of the Ethernet Statistics group is - -- optional. - -- - -- The ethernet statistics group contains statistics - -- measured by the probe for each monitored interface on - -- this device. These statistics take the form of free - -- running counters that start from zero when a valid entry - -- is created. - -- - -- This group currently has statistics defined only for - -- Ethernet interfaces. Each etherStatsEntry contains - -- statistics for one Ethernet interface. The probe must - -- create one etherStats entry for each monitored Ethernet - -- interface on the device. - - etherStatsTable OBJECT-TYPE - SYNTAX SEQUENCE OF EtherStatsEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of Ethernet statistics entries." - ::= { statistics 1 } - - - etherStatsEntry OBJECT-TYPE - SYNTAX EtherStatsEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A collection of statistics kept for a particular - Ethernet interface. As an example, an instance of the - etherStatsPkts object might be named etherStatsPkts.1" - INDEX { etherStatsIndex } - ::= { etherStatsTable 1 } - - EtherStatsEntry ::= SEQUENCE { - etherStatsIndex INTEGER (1..65535), - etherStatsDataSource OBJECT IDENTIFIER, - etherStatsDropEvents Counter, - etherStatsOctets Counter, - etherStatsPkts Counter, - etherStatsBroadcastPkts Counter, - etherStatsMulticastPkts Counter, - etherStatsCRCAlignErrors Counter, - etherStatsUndersizePkts Counter, - etherStatsOversizePkts Counter, - etherStatsFragments Counter, - etherStatsJabbers Counter, - etherStatsCollisions Counter, - etherStatsPkts64Octets Counter, - etherStatsPkts65to127Octets Counter, - etherStatsPkts128to255Octets Counter, - etherStatsPkts256to511Octets Counter, - etherStatsPkts512to1023Octets Counter, - etherStatsPkts1024to1518Octets Counter, - etherStatsOwner OwnerString, - etherStatsStatus EntryStatus - } - - etherStatsIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The value of this object uniquely identifies this - etherStats entry." - ::= { etherStatsEntry 1 } - - etherStatsDataSource OBJECT-TYPE - SYNTAX OBJECT IDENTIFIER - ACCESS read-write - STATUS mandatory - - - DESCRIPTION - "This object identifies the source of the data that - this etherStats entry is configured to analyze. This - source can be any ethernet interface on this device. - In order to identify a particular interface, this - object shall identify the instance of the ifIndex - object, defined in RFC 1213 and RFC 1573 [4,6], for - the desired interface. For example, if an entry - were to receive data from interface #1, this object - would be set to ifIndex.1. - - The statistics in this group reflect all packets - on the local network segment attached to the - identified interface. - - An agent may or may not be able to tell if - fundamental changes to the media of the interface - have occurred and necessitate an invalidation of - this entry. For example, a hot-pluggable ethernet - card could be pulled out and replaced by a - token-ring card. In such a case, if the agent has - such knowledge of the change, it is recommended that - it invalidate this entry. - - This object may not be modified if the associated - etherStatsStatus object is equal to valid(1)." - ::= { etherStatsEntry 2 } - - etherStatsDropEvents OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of events in which packets - were dropped by the probe due to lack of resources. - Note that this number is not necessarily the number of - packets dropped; it is just the number of times this - condition has been detected." - ::= { etherStatsEntry 3 } - - etherStatsOctets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of octets of data (including - those in bad packets) received on the - network (excluding framing bits but including - - - FCS octets). - - This object can be used as a reasonable estimate of - ethernet utilization. If greater precision is - desired, the etherStatsPkts and etherStatsOctets - objects should be sampled before and after a common - interval. The differences in the sampled values are - Pkts and Octets, respectively, and the number of - seconds in the interval is Interval. These values - are used to calculate the Utilization as follows: - - Pkts * (9.6 + 6.4) + (Octets * .8) - Utilization = ------------------------------------- - Interval * 10,000 - - The result of this equation is the value Utilization - which is the percent utilization of the ethernet - segment on a scale of 0 to 100 percent." - ::= { etherStatsEntry 4 } - - etherStatsPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets (including bad packets, - broadcast packets, and multicast packets) received." - ::= { etherStatsEntry 5 } - - etherStatsBroadcastPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of good packets received that were - directed to the broadcast address. Note that this - does not include multicast packets." - ::= { etherStatsEntry 6 } - - etherStatsMulticastPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of good packets received that were - directed to a multicast address. Note that this - number does not include packets directed to the - broadcast address." - - - ::= { etherStatsEntry 7 } - - etherStatsCRCAlignErrors OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets received that - had a length (excluding framing bits, but - including FCS octets) of between 64 and 1518 - octets, inclusive, but but had either a bad - Frame Check Sequence (FCS) with an integral - number of octets (FCS Error) or a bad FCS with - a non-integral number of octets (Alignment Error)." - ::= { etherStatsEntry 8 } - - etherStatsUndersizePkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets received that were - less than 64 octets long (excluding framing bits, - but including FCS octets) and were otherwise well - formed." - ::= { etherStatsEntry 9 } - - etherStatsOversizePkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets received that were - longer than 1518 octets (excluding framing bits, - but including FCS octets) and were otherwise - well formed." - ::= { etherStatsEntry 10 } - - etherStatsFragments OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets received that were less - than 64 octets in length (excluding framing bits but - including FCS octets) and had either a bad Frame - Check Sequence (FCS) with an integral number of - octets (FCS Error) or a bad FCS with a non-integral - - - number of octets (Alignment Error). - - Note that it is entirely normal for - etherStatsFragments to increment. This is because - it counts both runts (which are normal occurrences - due to collisions) and noise hits." - ::= { etherStatsEntry 11 } - - etherStatsJabbers OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets received that were - longer than 1518 octets (excluding framing bits, - but including FCS octets), and had either a bad - Frame Check Sequence (FCS) with an integral number - of octets (FCS Error) or a bad FCS with a - non-integral number of octets (Alignment Error). - - Note that this definition of jabber is different - than the definition in IEEE-802.3 section 8.2.1.5 - (10BASE5) and section 10.3.1.4 (10BASE2). These - documents define jabber as the condition where any - packet exceeds 20 ms. The allowed range to detect - jabber is between 20 ms and 150 ms." - ::= { etherStatsEntry 12 } - - etherStatsCollisions OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The best estimate of the total number of collisions - on this Ethernet segment. - - The value returned will depend on the location of - the RMON probe. Section 8.2.1.3 (10BASE-5) and - section 10.3.1.3 (10BASE-2) of IEEE standard 802.3 - states that a station must detect a collision, in - the receive mode, if three or more stations are - transmitting simultaneously. A repeater port must - detect a collision when two or more stations are - transmitting simultaneously. Thus a probe placed on - a repeater port could record more collisions than a - probe connected to a station on the same segment - would. - - - - Probe location plays a much smaller role when - considering 10BASE-T. 14.2.1.4 (10BASE-T) of IEEE - standard 802.3 defines a collision as the - simultaneous presence of signals on the DO and RD - circuits (transmitting and receiving at the same - time). A 10BASE-T station can only detect - collisions when it is transmitting. Thus probes - placed on a station and a repeater, should report - the same number of collisions. - - Note also that an RMON probe inside a repeater - should ideally report collisions between the - repeater and one or more other hosts (transmit - collisions as defined by IEEE 802.3k) plus receiver - collisions observed on any coax segments to which - the repeater is connected." - ::= { etherStatsEntry 13 } - - etherStatsPkts64Octets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets (including bad - packets) received that were 64 octets in length - (excluding framing bits but including FCS octets)." - ::= { etherStatsEntry 14 } - - etherStatsPkts65to127Octets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets (including bad - packets) received that were between - 65 and 127 octets in length inclusive - (excluding framing bits but including FCS octets)." - ::= { etherStatsEntry 15 } - - etherStatsPkts128to255Octets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets (including bad - packets) received that were between - 128 and 255 octets in length inclusive - (excluding framing bits but including FCS octets)." - - - ::= { etherStatsEntry 16 } - - etherStatsPkts256to511Octets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets (including bad - packets) received that were between - 256 and 511 octets in length inclusive - (excluding framing bits but including FCS octets)." - ::= { etherStatsEntry 17 } - - etherStatsPkts512to1023Octets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets (including bad - packets) received that were between - 512 and 1023 octets in length inclusive - (excluding framing bits but including FCS octets)." - ::= { etherStatsEntry 18 } - - etherStatsPkts1024to1518Octets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets (including bad - packets) received that were between - 1024 and 1518 octets in length inclusive - (excluding framing bits but including FCS octets)." - ::= { etherStatsEntry 19 } - - etherStatsOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it." - ::= { etherStatsEntry 20 } - - etherStatsStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - - - DESCRIPTION - "The status of this etherStats entry." - ::= { etherStatsEntry 21 } - - - -- The History Control Group - - -- Implementation of the History Control group is optional. - -- - -- The history control group controls the periodic statistical - -- sampling of data from various types of networks. The - -- historyControlTable stores configuration entries that each - -- define an interface, polling period, and other parameters. - -- Once samples are taken, their data is stored in an entry - -- in a media-specific table. Each such entry defines one - -- sample, and is associated with the historyControlEntry that - -- caused the sample to be taken. Each counter in the - -- etherHistoryEntry counts the same event as its - -- similarly-named counterpart in the etherStatsEntry, - -- except that each value here is a cumulative sum during a - -- sampling period. - -- - -- If the probe keeps track of the time of day, it should - -- start the first sample of the history at a time such that - -- when the next hour of the day begins, a sample is - -- started at that instant. This tends to make more - -- user-friendly reports, and enables comparison of reports - -- from different probes that have relatively accurate time - -- of day. - -- - -- The probe is encouraged to add two history control entries - -- per monitored interface upon initialization that describe - -- a short term and a long term polling period. Suggested - -- parameters are 30 seconds for the short term polling period - -- and 30 minutes for the long term period. - - historyControlTable OBJECT-TYPE - SYNTAX SEQUENCE OF HistoryControlEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of history control entries." - ::= { history 1 } - - historyControlEntry OBJECT-TYPE - SYNTAX HistoryControlEntry - ACCESS not-accessible - STATUS mandatory - - - DESCRIPTION - "A list of parameters that set up a periodic sampling - of statistics. As an example, an instance of the - historyControlInterval object might be named - historyControlInterval.2" - INDEX { historyControlIndex } - ::= { historyControlTable 1 } - - HistoryControlEntry ::= SEQUENCE { - historyControlIndex INTEGER (1..65535), - historyControlDataSource OBJECT IDENTIFIER, - historyControlBucketsRequested INTEGER (1..65535), - historyControlBucketsGranted INTEGER (1..65535), - historyControlInterval INTEGER (1..3600), - historyControlOwner OwnerString, - historyControlStatus EntryStatus - } - - historyControlIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry in the - historyControl table. Each such entry defines a - set of samples at a particular interval for an - interface on the device." - ::= { historyControlEntry 1 } - - historyControlDataSource OBJECT-TYPE - SYNTAX OBJECT IDENTIFIER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "This object identifies the source of the data for - which historical data was collected and - placed in a media-specific table on behalf of this - historyControlEntry. This source can be any - interface on this device. In order to identify - a particular interface, this object shall identify - the instance of the ifIndex object, defined - in RFC 1213 and RFC 1573 [4,6], for the desired - interface. For example, if an entry were to receive - data from interface #1, this object would be set - to ifIndex.1. - - The statistics in this group reflect all packets - on the local network segment attached to the - - - identified interface. - - An agent may or may not be able to tell if fundamental - changes to the media of the interface have occurred - and necessitate an invalidation of this entry. For - example, a hot-pluggable ethernet card could be - pulled out and replaced by a token-ring card. In - such a case, if the agent has such knowledge of the - change, it is recommended that it invalidate this - entry. - - This object may not be modified if the associated - historyControlStatus object is equal to valid(1)." - ::= { historyControlEntry 2 } - - historyControlBucketsRequested OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The requested number of discrete time intervals - over which data is to be saved in the part of the - media-specific table associated with this - historyControlEntry. - - When this object is created or modified, the probe - should set historyControlBucketsGranted as closely to - this object as is possible for the particular probe - implementation and available resources." - DEFVAL { 50 } - ::= { historyControlEntry 3 } - - historyControlBucketsGranted OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of discrete sampling intervals - over which data shall be saved in the part of - the media-specific table associated with this - historyControlEntry. - - When the associated historyControlBucketsRequested - object is created or modified, the probe - should set this object as closely to the requested - value as is possible for the particular - probe implementation and available resources. The - probe must not lower this value except as a result - - - of a modification to the associated - historyControlBucketsRequested object. - - There will be times when the actual number of - buckets associated with this entry is less than - the value of this object. In this case, at the - end of each sampling interval, a new bucket will - be added to the media-specific table. - - When the number of buckets reaches the value of - this object and a new bucket is to be added to the - media-specific table, the oldest bucket associated - with this historyControlEntry shall be deleted by - the agent so that the new bucket can be added. - - When the value of this object changes to a value less - than the current value, entries are deleted - from the media-specific table associated with this - historyControlEntry. Enough of the oldest of these - entries shall be deleted by the agent so that their - number remains less than or equal to the new value of - this object. - - When the value of this object changes to a value - greater than the current value, the number of - associated media- specific entries may be allowed to - grow." - ::= { historyControlEntry 4 } - - historyControlInterval OBJECT-TYPE - SYNTAX INTEGER (1..3600) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The interval in seconds over which the data is - sampled for each bucket in the part of the - media-specific table associated with this - historyControlEntry. This interval can - be set to any number of seconds between 1 and - 3600 (1 hour). - - Because the counters in a bucket may overflow at their - maximum value with no indication, a prudent manager - will take into account the possibility of overflow - in any of the associated counters. It is important - to consider the minimum time in which any counter - could overflow on a particular media type and set - the historyControlInterval object to a value less - - - than this interval. This is typically most - important for the 'octets' counter in any - media-specific table. For example, on an Ethernet - network, the etherHistoryOctets counter could - overflow in about one hour at the Ethernet's maximum - utilization. - - This object may not be modified if the associated - historyControlStatus object is equal to valid(1)." - DEFVAL { 1800 } - ::= { historyControlEntry 5 } - - historyControlOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it." - ::= { historyControlEntry 6 } - - historyControlStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The status of this historyControl entry. - - Each instance of the media-specific table associated - with this historyControlEntry will be deleted by the - agent if this historyControlEntry is not equal to - valid(1)." - ::= { historyControlEntry 7 } - - - -- The Ethernet History Group - - -- Implementation of the Ethernet History group is optional. - -- - -- The Ethernet History group records periodic - -- statistical samples from a network and stores them - -- for later retrieval. Once samples are taken, their - -- data is stored in an entry in a media-specific - -- table. Each such entry defines one sample, and is - -- associated with the historyControlEntry that caused - -- the sample to be taken. This group defines the - -- etherHistoryTable, for Ethernet networks. - -- - - - etherHistoryTable OBJECT-TYPE - SYNTAX SEQUENCE OF EtherHistoryEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of Ethernet history entries." - ::= { history 2 } - - etherHistoryEntry OBJECT-TYPE - SYNTAX EtherHistoryEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "An historical sample of Ethernet statistics on a - particular Ethernet interface. This sample is - associated with the historyControlEntry which set up - the parameters for a regular collection of these - samples. As an example, an instance of the - etherHistoryPkts object might be named - etherHistoryPkts.2.89" - INDEX { etherHistoryIndex , etherHistorySampleIndex } - ::= { etherHistoryTable 1 } - - EtherHistoryEntry ::= SEQUENCE { - etherHistoryIndex INTEGER (1..65535), - etherHistorySampleIndex INTEGER (1..2147483647), - etherHistoryIntervalStart TimeTicks, - etherHistoryDropEvents Counter, - etherHistoryOctets Counter, - etherHistoryPkts Counter, - etherHistoryBroadcastPkts Counter, - etherHistoryMulticastPkts Counter, - etherHistoryCRCAlignErrors Counter, - etherHistoryUndersizePkts Counter, - etherHistoryOversizePkts Counter, - etherHistoryFragments Counter, - etherHistoryJabbers Counter, - etherHistoryCollisions Counter, - etherHistoryUtilization INTEGER (0..10000) - } - - etherHistoryIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The history of which this entry is a part. The - history identified by a particular value of this - - - index is the same history as identified - by the same value of historyControlIndex." - ::= { etherHistoryEntry 1 } - - etherHistorySampleIndex OBJECT-TYPE - SYNTAX INTEGER (1..2147483647) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies the particular - sample this entry represents among all samples - associated with the same historyControlEntry. - This index starts at 1 and increases by one - as each new sample is taken." - ::= { etherHistoryEntry 2 } - - etherHistoryIntervalStart OBJECT-TYPE - SYNTAX TimeTicks - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The value of sysUpTime at the start of the interval - over which this sample was measured. If the probe - keeps track of the time of day, it should start - the first sample of the history at a time such that - when the next hour of the day begins, a sample is - started at that instant. Note that following this - rule may require the probe to delay collecting the - first sample of the history, as each sample must be - of the same interval. Also note that the sample which - is currently being collected is not accessible in this - table until the end of its interval." - ::= { etherHistoryEntry 3 } - - etherHistoryDropEvents OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of events in which packets - were dropped by the probe due to lack of resources - during this sampling interval. Note that this number - is not necessarily the number of packets dropped, it - is just the number of times this condition has been - detected." - ::= { etherHistoryEntry 4 } - - etherHistoryOctets OBJECT-TYPE - - - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of octets of data (including - those in bad packets) received on the - network (excluding framing bits but including - FCS octets)." - ::= { etherHistoryEntry 5 } - - etherHistoryPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of packets (including bad packets) - received during this sampling interval." - ::= { etherHistoryEntry 6 } - - etherHistoryBroadcastPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of good packets received during this - sampling interval that were directed to the - broadcast address." - ::= { etherHistoryEntry 7 } - - etherHistoryMulticastPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of good packets received during this - sampling interval that were directed to a - multicast address. Note that this number does not - include packets addressed to the broadcast address." - ::= { etherHistoryEntry 8 } - - etherHistoryCRCAlignErrors OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of packets received during this sampling - interval that had a length (excluding framing bits - but including FCS octets) between 64 and 1518 - - - octets, inclusive, but had either a bad Frame Check - Sequence (FCS) with an integral number of octets - (FCS Error) or a bad FCS with a non-integral number - of octets (Alignment Error)." - ::= { etherHistoryEntry 9 } - - etherHistoryUndersizePkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of packets received during this - sampling interval that were less than 64 octets - long (excluding framing bits but including FCS - octets) and were otherwise well formed." - ::= { etherHistoryEntry 10 } - - etherHistoryOversizePkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of packets received during this - sampling interval that were longer than 1518 - octets (excluding framing bits but including - FCS octets) but were otherwise well formed." - ::= { etherHistoryEntry 11 } - - etherHistoryFragments OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The total number of packets received during this - sampling interval that were less than 64 octets in - length (excluding framing bits but including FCS - octets) had either a bad Frame Check Sequence (FCS) - with an integral number of octets (FCS Error) or a bad - FCS with a non-integral number of octets (Alignment - Error). - - Note that it is entirely normal for - etherHistoryFragments to increment. This is because - it counts both runts (which are normal occurrences - due to collisions) and noise hits." - ::= { etherHistoryEntry 12 } - - etherHistoryJabbers OBJECT-TYPE - - - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of packets received during this - sampling interval that were longer than 1518 octets - (excluding framing bits but including FCS octets), - and had either a bad Frame Check Sequence (FCS) - with an integral number of octets (FCS Error) or - a bad FCS with a non-integral number of octets - (Alignment Error). - - Note that this definition of jabber is different - than the definition in IEEE-802.3 section 8.2.1.5 - (10BASE5) and section 10.3.1.4 (10BASE2). These - documents define jabber as the condition where any - packet exceeds 20 ms. The allowed range to detect - jabber is between 20 ms and 150 ms." - ::= { etherHistoryEntry 13 } - - etherHistoryCollisions OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The best estimate of the total number of collisions - on this Ethernet segment during this sampling - interval. - - The value returned will depend on the location of - the RMON probe. Section 8.2.1.3 (10BASE-5) and - section 10.3.1.3 (10BASE-2) of IEEE standard 802.3 - states that a station must detect a collision, in - the receive mode, if three or more stations are - transmitting simultaneously. A repeater port must - detect a collision when two or more stations are - transmitting simultaneously. Thus a probe placed on - a repeater port could record more collisions than a - probe connected to a station on the same segment - would. - - Probe location plays a much smaller role when - considering 10BASE-T. 14.2.1.4 (10BASE-T) of IEEE - standard 802.3 defines a collision as the - simultaneous presence of signals on the DO and RD - circuits (transmitting and receiving at the same - time). A 10BASE-T station can only detect - collisions when it is transmitting. Thus probes - - - placed on a station and a repeater, should report - the same number of collisions. - - Note also that an RMON probe inside a repeater - should ideally report collisions between the - repeater and one or more other hosts (transmit - collisions as defined by IEEE 802.3k) plus receiver - collisions observed on any coax segments to which - the repeater is connected." - ::= { etherHistoryEntry 14 } - - etherHistoryUtilization OBJECT-TYPE - SYNTAX INTEGER (0..10000) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The best estimate of the mean physical layer - network utilization on this interface during this - sampling interval, in hundredths of a percent." - ::= { etherHistoryEntry 15 } - - - -- The Alarm Group - - -- Implementation of the Alarm group is optional. - -- - -- The Alarm Group requires the implementation of the Event - -- group. - -- - -- The Alarm group periodically takes - -- statistical samples from variables in the probe and - -- compares them to thresholds that have been - -- configured. The alarm table stores configuration - -- entries that each define a variable, polling period, - -- and threshold parameters. If a sample is found to - -- cross the threshold values, an event is generated. - -- Only variables that resolve to an ASN.1 primitive - -- type of INTEGER (INTEGER, Counter, Gauge, or - -- TimeTicks) may be monitored in this way. - -- - -- This function has a hysteresis mechanism to limit - -- the generation of events. This mechanism generates - -- one event as a threshold is crossed in the - -- appropriate direction. No more events are generated - -- for that threshold until the opposite threshold is - -- crossed. - -- - -- In the case of a sampling a deltaValue, a probe may - - - -- implement this mechanism with more precision if it - -- takes a delta sample twice per period, each time - -- comparing the sum of the latest two samples to the - -- threshold. This allows the detection of threshold - -- crossings that span the sampling boundary. Note - -- that this does not require any special configuration - -- of the threshold value. It is suggested that probes - -- implement this more precise algorithm. - - alarmTable OBJECT-TYPE - SYNTAX SEQUENCE OF AlarmEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of alarm entries." - ::= { alarm 1 } - - alarmEntry OBJECT-TYPE - SYNTAX AlarmEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of parameters that set up a periodic checking - for alarm conditions. For example, an instance of the - alarmValue object might be named alarmValue.8" - INDEX { alarmIndex } - ::= { alarmTable 1 } - - AlarmEntry ::= SEQUENCE { - alarmIndex INTEGER (1..65535), - alarmInterval INTEGER, - alarmVariable OBJECT IDENTIFIER, - alarmSampleType INTEGER, - alarmValue INTEGER, - alarmStartupAlarm INTEGER, - alarmRisingThreshold INTEGER, - alarmFallingThreshold INTEGER, - alarmRisingEventIndex INTEGER (0..65535), - alarmFallingEventIndex INTEGER (0..65535), - alarmOwner OwnerString, - alarmStatus EntryStatus - } - - alarmIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - - - "An index that uniquely identifies an entry in the - alarm table. Each such entry defines a - diagnostic sample at a particular interval - for an object on the device." - ::= { alarmEntry 1 } - - alarmInterval OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The interval in seconds over which the data is - sampled and compared with the rising and falling - thresholds. When setting this variable, care - should be taken in the case of deltaValue - sampling - the interval should be set short enough - that the sampled variable is very unlikely to - increase or decrease by more than 2^31 - 1 during - a single sampling interval. - - This object may not be modified if the associated - alarmStatus object is equal to valid(1)." - ::= { alarmEntry 2 } - - alarmVariable OBJECT-TYPE - SYNTAX OBJECT IDENTIFIER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The object identifier of the particular variable to - be sampled. Only variables that resolve to an ASN.1 - primitive type of INTEGER (INTEGER, Counter, Gauge, - or TimeTicks) may be sampled. - - Because SNMP access control is articulated entirely - in terms of the contents of MIB views, no access - control mechanism exists that can restrict the value - of this object to identify only those objects that - exist in a particular MIB view. Because there is - thus no acceptable means of restricting the read - access that could be obtained through the alarm - mechanism, the probe must only grant write access to - this object in those views that have read access to - all objects on the probe. - - During a set operation, if the supplied variable - name is not available in the selected MIB view, a - badValue error must be returned. If at any time the - - - variable name of an established alarmEntry is no - longer available in the selected MIB view, the probe - must change the status of this alarmEntry to - invalid(4). - - This object may not be modified if the associated - alarmStatus object is equal to valid(1)." - ::= { alarmEntry 3 } - - alarmSampleType OBJECT-TYPE - SYNTAX INTEGER { - absoluteValue(1), - deltaValue(2) - } - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The method of sampling the selected variable and - calculating the value to be compared against the - thresholds. If the value of this object is - absoluteValue(1), the value of the selected variable - will be compared directly with the thresholds at the - end of the sampling interval. If the value of this - object is deltaValue(2), the value of the selected - variable at the last sample will be subtracted from - the current value, and the difference compared with - the thresholds. - - This object may not be modified if the associated - alarmStatus object is equal to valid(1)." - ::= { alarmEntry 4 } - - alarmValue OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The value of the statistic during the last sampling - period. For example, if the sample type is - deltaValue, this value will be the difference - between the samples at the beginning and end of the - period. If the sample type is absoluteValue, this - value will be the sampled value at the end of the - period. - - This is the value that is compared with the rising and - falling thresholds. - - - - The value during the current sampling period is not - made available until the period is completed and will - remain available until the next period completes." - ::= { alarmEntry 5 } - - alarmStartupAlarm OBJECT-TYPE - SYNTAX INTEGER { - risingAlarm(1), - fallingAlarm(2), - risingOrFallingAlarm(3) - } - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The alarm that may be sent when this entry is first - set to valid. If the first sample after this entry - becomes valid is greater than or equal to the - risingThreshold and alarmStartupAlarm is equal to - risingAlarm(1) or risingOrFallingAlarm(3), then a - single rising alarm will be generated. If the first - sample after this entry becomes valid is less than - or equal to the fallingThreshold and - alarmStartupAlarm is equal to fallingAlarm(2) or - risingOrFallingAlarm(3), then a single falling alarm - will be generated. - - This object may not be modified if the associated - alarmStatus object is equal to valid(1)." - ::= { alarmEntry 6 } - - alarmRisingThreshold OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "A threshold for the sampled statistic. When the - current sampled value is greater than or equal to - this threshold, and the value at the last sampling - interval was less than this threshold, a single - event will be generated. A single event will also - be generated if the first sample after this entry - becomes valid is greater than or equal to this - threshold and the associated alarmStartupAlarm is - equal to risingAlarm(1) or risingOrFallingAlarm(3). - - After a rising event is generated, another such event - will not be generated until the sampled value - falls below this threshold and reaches the - - - alarmFallingThreshold. - - This object may not be modified if the associated - alarmStatus object is equal to valid(1)." - ::= { alarmEntry 7 } - - alarmFallingThreshold OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "A threshold for the sampled statistic. When the - current sampled value is less than or equal to this - threshold, and the value at the last sampling - interval was greater than this threshold, a single - event will be generated. A single event will also - be generated if the first sample after this entry - becomes valid is less than or equal to this - threshold and the associated alarmStartupAlarm is - equal to fallingAlarm(2) or risingOrFallingAlarm(3). - - After a falling event is generated, another such event - will not be generated until the sampled value - rises above this threshold and reaches the - alarmRisingThreshold. - - This object may not be modified if the associated - alarmStatus object is equal to valid(1)." - ::= { alarmEntry 8 } - - alarmRisingEventIndex OBJECT-TYPE - SYNTAX INTEGER (0..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The index of the eventEntry that is - used when a rising threshold is crossed. The - eventEntry identified by a particular value of - this index is the same as identified by the same value - of the eventIndex object. If there is no - corresponding entry in the eventTable, then - no association exists. In particular, if this value - is zero, no associated event will be generated, as - zero is not a valid event index. - - This object may not be modified if the associated - alarmStatus object is equal to valid(1)." - ::= { alarmEntry 9 } - - - alarmFallingEventIndex OBJECT-TYPE - SYNTAX INTEGER (0..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The index of the eventEntry that is - used when a falling threshold is crossed. The - eventEntry identified by a particular value of - this index is the same as identified by the same value - of the eventIndex object. If there is no - corresponding entry in the eventTable, then - no association exists. In particular, if this value - is zero, no associated event will be generated, as - zero is not a valid event index. - - This object may not be modified if the associated - alarmStatus object is equal to valid(1)." - ::= { alarmEntry 10 } - - alarmOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it." - ::= { alarmEntry 11 } - - alarmStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The status of this alarm entry." - ::= { alarmEntry 12 } - - - -- The Host Group - - -- Implementation of the Host group is optional. - -- - -- The host group discovers new hosts on the network by - -- keeping a list of source and destination MAC Addresses seen - -- in good packets. For each of these addresses, the host - -- group keeps a set of statistics. The hostControlTable - -- controls which interfaces this function is performed on, - -- and contains some information about the process. On - -- behalf of each hostControlEntry, data is collected on an - - - -- interface and placed in both the hostTable and the - -- hostTimeTable. If the monitoring device finds itself - -- short of resources, it may delete entries as needed. It - -- is suggested that the device delete the least recently - -- used entries first. - - -- The hostTable contains entries for each address - -- discovered on a particular interface. Each entry - -- contains statistical data about that host. This table is - -- indexed by the MAC address of the host, through which a - -- random access may be achieved. - - -- The hostTimeTable contains data in the same format as the - -- hostTable, and must contain the same set of hosts, but is - -- indexed using hostTimeCreationOrder rather than - -- hostAddress. - -- The hostTimeCreationOrder is an integer which reflects - -- the relative order in which a particular entry was - -- discovered and thus inserted into the table. As this - -- order, and thus the index, is among those entries - -- currently in the table, the index for a particular entry - -- may change if an (earlier) entry is deleted. Thus the - -- association between hostTimeCreationOrder and - -- hostTimeEntry may be broken at any time. - - -- The hostTimeTable has two important uses. The first is the - -- fast download of this potentially large table. Because the - -- index of this table runs from 1 to the size of the table, - -- inclusive, its values are predictable. This allows very - -- efficient packing of variables into SNMP PDU's and allows - -- a table transfer to have multiple packets outstanding. - -- These benefits increase transfer rates tremendously. - - -- The second use of the hostTimeTable is the efficient - -- discovery by the management station of new entries added - -- to the table. After the management station has downloaded - -- the entire table, it knows that new entries will be added - -- immediately after the end of the current table. It can - -- thus detect new entries there and retrieve them easily. - - -- Because the association between hostTimeCreationOrder and - -- hostTimeEntry may be broken at any time, the management - -- station must monitor the related hostControlLastDeleteTime - -- object. When the management station thus detects a - -- deletion, it must assume that any such associations have - --- been broken, and invalidate any it has stored locally. - -- This includes restarting any download of the - -- hostTimeTable that may have been in progress, as well as - - - -- rediscovering the end of the hostTimeTable so that it may - -- detect new entries. If the management station does not - -- detect the broken association, it may continue to refer - -- to a particular host by its creationOrder while - -- unwittingly retrieving the data associated with another - -- host entirely. If this happens while downloading the - -- host table, the management station may fail to download - -- all of the entries in the table. - - hostControlTable OBJECT-TYPE - SYNTAX SEQUENCE OF HostControlEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of host table control entries." - ::= { hosts 1 } - - hostControlEntry OBJECT-TYPE - SYNTAX HostControlEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of parameters that set up the discovery of - hosts on a particular interface and the collection - of statistics about these hosts. For example, an - instance of the hostControlTableSize object might be - named hostControlTableSize.1" - INDEX { hostControlIndex } - ::= { hostControlTable 1 } - - HostControlEntry ::= SEQUENCE { - hostControlIndex INTEGER (1..65535), - hostControlDataSource OBJECT IDENTIFIER, - hostControlTableSize INTEGER, - hostControlLastDeleteTime TimeTicks, - hostControlOwner OwnerString, - hostControlStatus EntryStatus - } - - hostControlIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry in the - hostControl table. Each such entry defines - a function that discovers hosts on a particular - interface and places statistics about them in the - - - hostTable and the hostTimeTable on behalf of this - hostControlEntry." - ::= { hostControlEntry 1 } - - hostControlDataSource OBJECT-TYPE - SYNTAX OBJECT IDENTIFIER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "This object identifies the source of the data for - this instance of the host function. This source - can be any interface on this device. In order - to identify a particular interface, this object shall - identify the instance of the ifIndex object, defined - in RFC 1213 and RFC 1573 [4,6], for the desired - interface. For example, if an entry were to receive - data from interface #1, this object would be set to - ifIndex.1. - - The statistics in this group reflect all packets - on the local network segment attached to the - identified interface. - - An agent may or may not be able to tell if - fundamental changes to the media of the interface - have occurred and necessitate an invalidation of - this entry. For example, a hot-pluggable ethernet - card could be pulled out and replaced by a - token-ring card. In such a case, if the agent has - such knowledge of the change, it is recommended that - it invalidate this entry. - - This object may not be modified if the associated - hostControlStatus object is equal to valid(1)." - ::= { hostControlEntry 2 } - - hostControlTableSize OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of hostEntries in the hostTable and the - hostTimeTable associated with this hostControlEntry." - ::= { hostControlEntry 3 } - - hostControlLastDeleteTime OBJECT-TYPE - SYNTAX TimeTicks - ACCESS read-only - - - STATUS mandatory - DESCRIPTION - "The value of sysUpTime when the last entry - was deleted from the portion of the hostTable - associated with this hostControlEntry. If no - deletions have occurred, this value shall be zero." - ::= { hostControlEntry 4 } - - hostControlOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it." - ::= { hostControlEntry 5 } - - hostControlStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The status of this hostControl entry. - - If this object is not equal to valid(1), all - associated entries in the hostTable, hostTimeTable, - and the hostTopNTable shall be deleted by the - agent." - ::= { hostControlEntry 6 } - - hostTable OBJECT-TYPE - SYNTAX SEQUENCE OF HostEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of host entries." - ::= { hosts 2 } - - hostEntry OBJECT-TYPE - SYNTAX HostEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A collection of statistics for a particular host - that has been discovered on an interface of this - device. For example, an instance of the - hostOutBroadcastPkts object might be named - hostOutBroadcastPkts.1.6.8.0.32.27.3.176" - - - INDEX { hostIndex, hostAddress } - ::= { hostTable 1 } - - HostEntry ::= SEQUENCE { - hostAddress OCTET STRING, - hostCreationOrder INTEGER (1..65535), - hostIndex INTEGER (1..65535), - hostInPkts Counter, - hostOutPkts Counter, - hostInOctets Counter, - hostOutOctets Counter, - hostOutErrors Counter, - hostOutBroadcastPkts Counter, - hostOutMulticastPkts Counter - } - - hostAddress OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The physical address of this host." - ::= { hostEntry 1 } - - hostCreationOrder OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that defines the relative ordering of - the creation time of hosts captured for a - particular hostControlEntry. This index shall - be between 1 and N, where N is the value of - the associated hostControlTableSize. The ordering - of the indexes is based on the order of each entry's - insertion into the table, in which entries added - earlier have a lower index value than entries added - later. - - It is important to note that the order for a - particular entry may change as an (earlier) entry - is deleted from the table. Because this order may - change, management stations should make use of the - hostControlLastDeleteTime variable in the - hostControlEntry associated with the relevant - portion of the hostTable. By observing - this variable, the management station may detect - the circumstances where a previous association - - - between a value of hostCreationOrder - and a hostEntry may no longer hold." - ::= { hostEntry 2 } - - hostIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The set of collected host statistics of which - this entry is a part. The set of hosts - identified by a particular value of this - index is associated with the hostControlEntry - as identified by the same value of hostControlIndex." - ::= { hostEntry 3 } - - hostInPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of good packets transmitted to this - address since it was added to the hostTable." - ::= { hostEntry 4 } - - hostOutPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of packets, including bad packets, - transmitted by this address since it was added - to the hostTable." - ::= { hostEntry 5 } - - hostInOctets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of octets transmitted to this address - since it was added to the hostTable (excluding - framing bits but including FCS octets), except for - those octets in bad packets." - ::= { hostEntry 6 } - - hostOutOctets OBJECT-TYPE - SYNTAX Counter - - - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of octets transmitted by this address - since it was added to the hostTable (excluding - framing bits but including FCS octets), including - those octets in bad packets." - ::= { hostEntry 7 } - - hostOutErrors OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of bad packets transmitted by this address - since this host was added to the hostTable." - ::= { hostEntry 8 } - - hostOutBroadcastPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of good packets transmitted by this - address that were directed to the broadcast address - since this host was added to the hostTable." - ::= { hostEntry 9 } - - hostOutMulticastPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of good packets transmitted by this - address that were directed to a multicast address - since this host was added to the hostTable. - Note that this number does not include packets - directed to the broadcast address." - ::= { hostEntry 10 } - - -- host Time Table - - hostTimeTable OBJECT-TYPE - SYNTAX SEQUENCE OF HostTimeEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of time-ordered host table entries." - - - ::= { hosts 3 } - - hostTimeEntry OBJECT-TYPE - SYNTAX HostTimeEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A collection of statistics for a particular host - that has been discovered on an interface of this - device. This collection includes the relative - ordering of the creation time of this object. For - example, an instance of the hostTimeOutBroadcastPkts - object might be named - hostTimeOutBroadcastPkts.1.687" - INDEX { hostTimeIndex, hostTimeCreationOrder } - ::= { hostTimeTable 1 } - - HostTimeEntry ::= SEQUENCE { - hostTimeAddress OCTET STRING, - hostTimeCreationOrder INTEGER (1..65535), - hostTimeIndex INTEGER (1..65535), - hostTimeInPkts Counter, - hostTimeOutPkts Counter, - hostTimeInOctets Counter, - hostTimeOutOctets Counter, - hostTimeOutErrors Counter, - hostTimeOutBroadcastPkts Counter, - hostTimeOutMulticastPkts Counter - } - - hostTimeAddress OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The physical address of this host." - ::= { hostTimeEntry 1 } - - hostTimeCreationOrder OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry in - the hostTime table among those entries associated - with the same hostControlEntry. This index shall - be between 1 and N, where N is the value of - the associated hostControlTableSize. The ordering - - - of the indexes is based on the order of each entry's - insertion into the table, in which entries added - earlier have a lower index value than entries added - later. Thus the management station has the ability to - learn of new entries added to this table without - downloading the entire table. - - It is important to note that the index for a - particular entry may change as an (earlier) entry - is deleted from the table. Because this order may - change, management stations should make use of the - hostControlLastDeleteTime variable in the - hostControlEntry associated with the relevant - portion of the hostTimeTable. By observing - this variable, the management station may detect - the circumstances where a download of the table - may have missed entries, and where a previous - association between a value of hostTimeCreationOrder - and a hostTimeEntry may no longer hold." - ::= { hostTimeEntry 2 } - - hostTimeIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The set of collected host statistics of which - this entry is a part. The set of hosts - identified by a particular value of this - index is associated with the hostControlEntry - as identified by the same value of hostControlIndex." - ::= { hostTimeEntry 3 } - - hostTimeInPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of good packets transmitted to this - address since it was added to the hostTimeTable." - ::= { hostTimeEntry 4 } - - hostTimeOutPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of god packets transmitted by this - - - address since it was added to the hostTimeTable." - ::= { hostTimeEntry 5 } - - hostTimeInOctets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of octets transmitted to this address - since it was added to the hostTimeTable (excluding - framing bits but including FCS octets), except for - those octets in bad packets." - ::= { hostTimeEntry 6 } - - hostTimeOutOctets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of octets transmitted by this address - since it was added to the hostTimeTable (excluding - framing bits but including FCS octets), including - those octets in bad packets." - ::= { hostTimeEntry 7 } - - hostTimeOutErrors OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of bad packets transmitted by this address - since this host was added to the hostTimeTable." - ::= { hostTimeEntry 8 } - - hostTimeOutBroadcastPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of good packets transmitted by this - address that were directed to the broadcast address - since this host was added to the hostTimeTable." - ::= { hostTimeEntry 9 } - - hostTimeOutMulticastPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - - - DESCRIPTION - "The number of good packets transmitted by this - address that were directed to a multicast address - since this host was added to the hostTimeTable. - Note that this number does not include packets - directed to the broadcast address." - ::= { hostTimeEntry 10 } - - - -- The Host Top "N" Group - - -- Implementation of the Host Top N group is optional. - -- - -- The Host Top N group requires the implementation of the - -- host group. - -- - -- The Host Top N group is used to prepare reports that - -- describe the hosts that top a list ordered by one of - -- their statistics. - -- The available statistics are samples of one of their - -- base statistics, over an interval specified by the - -- management station. Thus, these statistics are rate - -- based. The management station also selects how many such - -- hosts are reported. - - -- The hostTopNControlTable is used to initiate the - -- generation of such a report. The management station - -- may select the parameters of such a report, such as - -- which interface, which statistic, how many hosts, - -- and the start and stop times of the sampling. When - -- the report is prepared, entries are created in the - -- hostTopNTable associated with the relevant - -- hostTopNControlEntry. These entries are static for - -- each report after it has been prepared. - - hostTopNControlTable OBJECT-TYPE - SYNTAX SEQUENCE OF HostTopNControlEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of top N host control entries." - ::= { hostTopN 1 } - - hostTopNControlEntry OBJECT-TYPE - SYNTAX HostTopNControlEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - - - "A set of parameters that control the creation of a - report of the top N hosts according to several - metrics. For example, an instance of the - hostTopNDuration object might be named - hostTopNDuration.3" - INDEX { hostTopNControlIndex } - ::= { hostTopNControlTable 1 } - - HostTopNControlEntry ::= SEQUENCE { - hostTopNControlIndex INTEGER (1..65535), - hostTopNHostIndex INTEGER (1..65535), - hostTopNRateBase INTEGER, - hostTopNTimeRemaining INTEGER, - hostTopNDuration INTEGER, - hostTopNRequestedSize INTEGER, - hostTopNGrantedSize INTEGER, - hostTopNStartTime TimeTicks, - hostTopNOwner OwnerString, - hostTopNStatus EntryStatus - } - - hostTopNControlIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry - in the hostTopNControl table. Each such - entry defines one top N report prepared for - one interface." - ::= { hostTopNControlEntry 1 } - - hostTopNHostIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The host table for which a top N report will be - prepared on behalf of this entry. The host table - identified by a particular value of this index is - associated with the same host table as identified by - the same value of hostIndex. - - This object may not be modified if the associated - hostTopNStatus object is equal to valid(1)." - ::= { hostTopNControlEntry 2 } - - hostTopNRateBase OBJECT-TYPE - - - SYNTAX INTEGER { - hostTopNInPkts(1), - hostTopNOutPkts(2), - hostTopNInOctets(3), - hostTopNOutOctets(4), - hostTopNOutErrors(5), - hostTopNOutBroadcastPkts(6), - hostTopNOutMulticastPkts(7) - } - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The variable for each host that the hostTopNRate - variable is based upon. - - This object may not be modified if the associated - hostTopNStatus object is equal to valid(1)." - ::= { hostTopNControlEntry 3 } - - hostTopNTimeRemaining OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The number of seconds left in the report currently - being collected. When this object is modified by - the management station, a new collection is started, - possibly aborting a currently running report. The - new value is used as the requested duration of this - report, which is loaded into the associated - hostTopNDuration object. - - When this object is set to a non-zero value, any - associated hostTopNEntries shall be made - inaccessible by the monitor. While the value of - this object is non-zero, it decrements by one per - second until it reaches zero. During this time, all - associated hostTopNEntries shall remain - inaccessible. At the time that this object - decrements to zero, the report is made accessible in - the hostTopNTable. Thus, the hostTopN table needs - to be created only at the end of the collection - interval." - DEFVAL { 0 } - ::= { hostTopNControlEntry 4 } - - hostTopNDuration OBJECT-TYPE - SYNTAX INTEGER - - - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of seconds that this report has collected - during the last sampling interval, or if this - report is currently being collected, the number - of seconds that this report is being collected - during this sampling interval. - - When the associated hostTopNTimeRemaining object is - set, this object shall be set by the probe to the - same value and shall not be modified until the next - time the hostTopNTimeRemaining is set. - - This value shall be zero if no reports have been - requested for this hostTopNControlEntry." - DEFVAL { 0 } - ::= { hostTopNControlEntry 5 } - - hostTopNRequestedSize OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The maximum number of hosts requested for the top N - table. - - When this object is created or modified, the probe - should set hostTopNGrantedSize as closely to this - object as is possible for the particular probe - implementation and available resources." - DEFVAL { 10 } - ::= { hostTopNControlEntry 6 } - - hostTopNGrantedSize OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The maximum number of hosts in the top N table. - - When the associated hostTopNRequestedSize object is - created or modified, the probe should set this - object as closely to the requested value as is - possible for the particular implementation and - available resources. The probe must not lower this - value except as a result of a set to the associated - hostTopNRequestedSize object. - - - Hosts with the highest value of hostTopNRate shall be - placed in this table in decreasing order of this rate - until there is no more room or until there are no more - hosts." - ::= { hostTopNControlEntry 7 } - - hostTopNStartTime OBJECT-TYPE - SYNTAX TimeTicks - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The value of sysUpTime when this top N report was - last started. In other words, this is the time that - the associated hostTopNTimeRemaining object was - modified to start the requested report." - ::= { hostTopNControlEntry 8 } - - hostTopNOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it." - ::= { hostTopNControlEntry 9 } - - hostTopNStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The status of this hostTopNControl entry. - - If this object is not equal to valid(1), all - associated hostTopNEntries shall be deleted by the - agent." - ::= { hostTopNControlEntry 10 } - - hostTopNTable OBJECT-TYPE - SYNTAX SEQUENCE OF HostTopNEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of top N host entries." - ::= { hostTopN 2 } - - hostTopNEntry OBJECT-TYPE - SYNTAX HostTopNEntry - - - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A set of statistics for a host that is part of a - top N report. For example, an instance of the - hostTopNRate object might be named - hostTopNRate.3.10" - INDEX { hostTopNReport, hostTopNIndex } - ::= { hostTopNTable 1 } - - HostTopNEntry ::= SEQUENCE { - hostTopNReport INTEGER (1..65535), - hostTopNIndex INTEGER (1..65535), - hostTopNAddress OCTET STRING, - hostTopNRate INTEGER - } - - hostTopNReport OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "This object identifies the top N report of which - this entry is a part. The set of hosts - identified by a particular value of this - object is part of the same report as identified - by the same value of the hostTopNControlIndex object." - ::= { hostTopNEntry 1 } - - hostTopNIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry in - the hostTopN table among those in the same report. - This index is between 1 and N, where N is the - number of entries in this table. Increasing values - of hostTopNIndex shall be assigned to entries with - decreasing values of hostTopNRate until index N - is assigned to the entry with the lowest value of - hostTopNRate or there are no more hostTopNEntries." - ::= { hostTopNEntry 2 } - - hostTopNAddress OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-only - STATUS mandatory - - - DESCRIPTION - "The physical address of this host." - ::= { hostTopNEntry 3 } - - hostTopNRate OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The amount of change in the selected variable - during this sampling interval. The selected - variable is this host's instance of the object - selected by hostTopNRateBase." - ::= { hostTopNEntry 4 } - - - -- The Matrix Group - - -- Implementation of the Matrix group is optional. - -- - -- The Matrix group consists of the matrixControlTable, - -- matrixSDTable and the matrixDSTable. These tables - -- store statistics for a particular conversation - -- between two addresses. As the device detects a new - -- conversation, including those to a non-unicast - -- address, it creates a new entry in both of the - -- matrix tables. It must only create new entries - -- based on information received in good packets. If - -- the monitoring device finds itself short of - -- resources, it may delete entries as needed. It is - -- suggested that the device delete the least recently - -- used entries first. - - matrixControlTable OBJECT-TYPE - SYNTAX SEQUENCE OF MatrixControlEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of information entries for the - traffic matrix on each interface." - ::= { matrix 1 } - - matrixControlEntry OBJECT-TYPE - SYNTAX MatrixControlEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "Information about a traffic matrix on a particular - - - interface. For example, an instance of the - matrixControlLastDeleteTime object might be named - matrixControlLastDeleteTime.1" - INDEX { matrixControlIndex } - ::= { matrixControlTable 1 } - - MatrixControlEntry ::= SEQUENCE { - matrixControlIndex INTEGER (1..65535), - matrixControlDataSource OBJECT IDENTIFIER, - matrixControlTableSize INTEGER, - matrixControlLastDeleteTime TimeTicks, - matrixControlOwner OwnerString, - matrixControlStatus EntryStatus - } - - matrixControlIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry in the - matrixControl table. Each such entry defines - a function that discovers conversations on a - particular interface and places statistics about - them in the matrixSDTable and the matrixDSTable on - behalf of this matrixControlEntry." - ::= { matrixControlEntry 1 } - - matrixControlDataSource OBJECT-TYPE - SYNTAX OBJECT IDENTIFIER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "This object identifies the source of - the data from which this entry creates a traffic - matrix. This source can be any interface on this - device. In order to identify a particular - interface, this object shall identify the instance - of the ifIndex object, defined in RFC 1213 and RFC - 1573 [4,6], for the desired interface. For example, - if an entry were to receive data from interface #1, - this object would be set to ifIndex.1. - - The statistics in this group reflect all packets - on the local network segment attached to the - identified interface. - - An agent may or may not be able to tell if - - - fundamental changes to the media of the interface - have occurred and necessitate an invalidation of - this entry. For example, a hot-pluggable ethernet - card could be pulled out and replaced by a - token-ring card. In such a case, if the agent has - such knowledge of the change, it is recommended that - it invalidate this entry. - - This object may not be modified if the associated - matrixControlStatus object is equal to valid(1)." - ::= { matrixControlEntry 2 } - - matrixControlTableSize OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of matrixSDEntries in the matrixSDTable - for this interface. This must also be the value of - the number of entries in the matrixDSTable for this - interface." - ::= { matrixControlEntry 3 } - - matrixControlLastDeleteTime OBJECT-TYPE - SYNTAX TimeTicks - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The value of sysUpTime when the last entry - was deleted from the portion of the matrixSDTable - or matrixDSTable associated with this - matrixControlEntry. If no deletions have occurred, - this value shall be zero." - ::= { matrixControlEntry 4 } - - matrixControlOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it." - ::= { matrixControlEntry 5 } - - matrixControlStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - - - DESCRIPTION - "The status of this matrixControl entry. - - If this object is not equal to valid(1), all - associated entries in the matrixSDTable and the - matrixDSTable shall be deleted by the agent." - ::= { matrixControlEntry 6 } - - matrixSDTable OBJECT-TYPE - SYNTAX SEQUENCE OF MatrixSDEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of traffic matrix entries indexed by - source and destination MAC address." - ::= { matrix 2 } - - matrixSDEntry OBJECT-TYPE - SYNTAX MatrixSDEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A collection of statistics for communications between - two addresses on a particular interface. For example, - an instance of the matrixSDPkts object might be named - matrixSDPkts.1.6.8.0.32.27.3.176.6.8.0.32.10.8.113" - INDEX { matrixSDIndex, - matrixSDSourceAddress, matrixSDDestAddress } - ::= { matrixSDTable 1 } - - MatrixSDEntry ::= SEQUENCE { - matrixSDSourceAddress OCTET STRING, - matrixSDDestAddress OCTET STRING, - matrixSDIndex INTEGER (1..65535), - matrixSDPkts Counter, - matrixSDOctets Counter, - matrixSDErrors Counter - } - - matrixSDSourceAddress OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The source physical address." - ::= { matrixSDEntry 1 } - - matrixSDDestAddress OBJECT-TYPE - - - SYNTAX OCTET STRING - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The destination physical address." - ::= { matrixSDEntry 2 } - - matrixSDIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The set of collected matrix statistics of which - this entry is a part. The set of matrix statistics - identified by a particular value of this index - is associated with the same matrixControlEntry - as identified by the same value of - matrixControlIndex." - ::= { matrixSDEntry 3 } - - matrixSDPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of packets transmitted from the source - address to the destination address (this number - includes bad packets)." - ::= { matrixSDEntry 4 } - - matrixSDOctets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of octets (excluding framing bits but - including FCS octets) contained in all packets - transmitted from the source address to the - destination address." - ::= { matrixSDEntry 5 } - - matrixSDErrors OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of bad packets transmitted from - the source address to the destination address." - - - ::= { matrixSDEntry 6 } - - - -- Traffic matrix tables from destination to source - - matrixDSTable OBJECT-TYPE - SYNTAX SEQUENCE OF MatrixDSEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of traffic matrix entries indexed by - destination and source MAC address." - ::= { matrix 3 } - - matrixDSEntry OBJECT-TYPE - SYNTAX MatrixDSEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A collection of statistics for communications between - two addresses on a particular interface. For example, - an instance of the matrixSDPkts object might be named - matrixSDPkts.1.6.8.0.32.10.8.113.6.8.0.32.27.3.176" - INDEX { matrixDSIndex, - matrixDSDestAddress, matrixDSSourceAddress } - ::= { matrixDSTable 1 } - - MatrixDSEntry ::= SEQUENCE { - matrixDSSourceAddress OCTET STRING, - matrixDSDestAddress OCTET STRING, - matrixDSIndex INTEGER (1..65535), - matrixDSPkts Counter, - matrixDSOctets Counter, - matrixDSErrors Counter - } - - matrixDSSourceAddress OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The source physical address." - ::= { matrixDSEntry 1 } - - matrixDSDestAddress OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-only - STATUS mandatory - - - DESCRIPTION - "The destination physical address." - ::= { matrixDSEntry 2 } - - matrixDSIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The set of collected matrix statistics of which - this entry is a part. The set of matrix statistics - identified by a particular value of this index - is associated with the same matrixControlEntry - as identified by the same value of - matrixControlIndex." - ::= { matrixDSEntry 3 } - - matrixDSPkts OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of packets transmitted from the source - address to the destination address (this number - includes bad packets)." - ::= { matrixDSEntry 4 } - - matrixDSOctets OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of octets (excluding framing bits - but including FCS octets) contained in all packets - transmitted from the source address to the - destination address." - ::= { matrixDSEntry 5 } - - matrixDSErrors OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of bad packets transmitted from - the source address to the destination address." - ::= { matrixDSEntry 6 } - - - - - -- The Filter Group - - -- Implementation of the Filter group is optional. - -- - -- The Filter group allows packets to be captured with an - -- arbitrary filter expression. A logical data and - -- event stream or "channel" is formed by the packets - -- that match the filter expression. - -- - -- This filter mechanism allows the creation of an arbitrary - -- logical expression with which to filter packets. Each - -- filter associated with a channel is OR'ed with the others. - -- Within a filter, any bits checked in the data and status - -- are AND'ed with respect to other bits in the same filter. - -- The NotMask also allows for checking for inequality. - -- Finally, the channelAcceptType object allows for - -- inversion of the whole equation. - -- - -- If a management station wishes to receive a trap to alert - -- it that new packets have been captured and are available - -- for download, it is recommended that it set up an alarm - -- entry that monitors the value of the relevant - -- channelMatches instance. - -- - -- The channel can be turned on or off, and can also - -- generate events when packets pass through it. - - filterTable OBJECT-TYPE - SYNTAX SEQUENCE OF FilterEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of packet filter entries." - ::= { filter 1 } - - filterEntry OBJECT-TYPE - SYNTAX FilterEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A set of parameters for a packet filter applied on a - particular interface. As an example, an instance of - the filterPktData object might be named - filterPktData.12" - INDEX { filterIndex } - ::= { filterTable 1 } - - - - - FilterEntry ::= SEQUENCE { - filterIndex INTEGER (1..65535), - filterChannelIndex INTEGER (1..65535), - filterPktDataOffset INTEGER, - filterPktData OCTET STRING, - filterPktDataMask OCTET STRING, - filterPktDataNotMask OCTET STRING, - filterPktStatus INTEGER, - filterPktStatusMask INTEGER, - filterPktStatusNotMask INTEGER, - filterOwner OwnerString, - filterStatus EntryStatus - } - - filterIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry - in the filter table. Each such entry defines - one filter that is to be applied to every packet - received on an interface." - ::= { filterEntry 1 } - - filterChannelIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "This object identifies the channel of which this - filter is a part. The filters identified by a - particular value of this object are associated with - the same channel as identified by the same value of - the channelIndex object." - ::= { filterEntry 2 } - - filterPktDataOffset OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The offset from the beginning of each packet where - a match of packet data will be attempted. This offset - is measured from the point in the physical layer - packet after the framing bits, if any. For example, - in an Ethernet frame, this point is at the beginning - of the destination MAC address. - - - This object may not be modified if the associated - filterStatus object is equal to valid(1)." - DEFVAL { 0 } - ::= { filterEntry 3 } - - filterPktData OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The data that is to be matched with the input - packet. For each packet received, this filter and - the accompanying filterPktDataMask and - filterPktDataNotMask will be adjusted for the - offset. The only bits relevant to this match - algorithm are those that have the corresponding - filterPktDataMask bit equal to one. The following - three rules are then applied to every packet: - - (1) If the packet is too short and does not have data - corresponding to part of the filterPktData, the - packet will fail this data match. - - (2) For each relevant bit from the packet with the - corresponding filterPktDataNotMask bit set to - zero, if the bit from the packet is not equal to - the corresponding bit from the filterPktData, - then the packet will fail this data match. - - (3) If for every relevant bit from the packet with the - corresponding filterPktDataNotMask bit set to one, - the bit from the packet is equal to the - corresponding bit from the filterPktData, then - the packet will fail this data match. - - Any packets that have not failed any of the three - matches above have passed this data match. In - particular, a zero length filter will match any - packet. - - This object may not be modified if the associated - filterStatus object is equal to valid(1)." - ::= { filterEntry 4 } - - filterPktDataMask OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-write - STATUS mandatory - - - DESCRIPTION - "The mask that is applied to the match process. - After adjusting this mask for the offset, only those - bits in the received packet that correspond to bits - set in this mask are relevant for further processing - by the match algorithm. The offset is applied to - filterPktDataMask in the same way it is applied to the - filter. For the purposes of the matching algorithm, - if the associated filterPktData object is longer - than this mask, this mask is conceptually extended - with '1' bits until it reaches the length of the - filterPktData object. - - This object may not be modified if the associated - filterStatus object is equal to valid(1)." - ::= { filterEntry 5 } - - filterPktDataNotMask OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The inversion mask that is applied to the match - process. After adjusting this mask for the offset, - those relevant bits in the received packet that - correspond to bits cleared in this mask must all be - equal to their corresponding bits in the - filterPktData object for the packet to be accepted. - In addition, at least one of those relevant bits in - the received packet that correspond to bits set in - this mask must be different to its corresponding bit - in the filterPktData object. - - For the purposes of the matching algorithm, if the - associated filterPktData object is longer than this - mask, this mask is conceptually extended with '0' - bits until it reaches the length of the - filterPktData object. - - This object may not be modified if the associated - filterStatus object is equal to valid(1)." - ::= { filterEntry 6 } - - filterPktStatus OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - - - "The status that is to be matched with the input - packet. The only bits relevant to this match - algorithm are those that have the corresponding - filterPktStatusMask bit equal to one. The following - two rules are then applied to every packet: - - (1) For each relevant bit from the packet status - with the corresponding filterPktStatusNotMask bit - set to zero, if the bit from the packet status is - not equal to the corresponding bit from the - filterPktStatus, then the packet will fail this - status match. - - (2) If for every relevant bit from the packet status - with the corresponding filterPktStatusNotMask bit - set to one, the bit from the packet status is - equal to the corresponding bit from the - filterPktStatus, then the packet will fail this - status match. - - Any packets that have not failed either of the two - matches above have passed this status match. In - particular, a zero length status filter will match any - packet's status. - - The value of the packet status is a sum. This sum - initially takes the value zero. Then, for each - error, E, that has been discovered in this packet, - 2 raised to a value representing E is added to the - sum. The errors and the bits that represent them are - dependent on the media type of the interface that - this channel is receiving packets from. - - The errors defined for a packet captured off of an - Ethernet interface are as follows: - - bit # Error - 0 Packet is longer than 1518 octets - 1 Packet is shorter than 64 octets - 2 Packet experienced a CRC or Alignment - error - - For example, an Ethernet fragment would have a - value of 6 (2^1 + 2^2). - - As this MIB is expanded to new media types, this - object will have other media-specific errors - defined. - - - For the purposes of this status matching algorithm, - if the packet status is longer than this - filterPktStatus object, this object is conceptually - extended with '0' bits until it reaches the size of - the packet status. - - This object may not be modified if the associated - filterStatus object is equal to valid(1)." - ::= { filterEntry 7 } - - filterPktStatusMask OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The mask that is applied to the status match - process. Only those bits in the received packet - that correspond to bits set in this mask are - relevant for further processing by the status match - algorithm. For the purposes of the matching - algorithm, if the associated filterPktStatus object - is longer than this mask, this mask is conceptually - extended with '1' bits until it reaches the size of - the filterPktStatus. In addition, if a packet - status is longer than this mask, this mask is - conceptually extended with '0' bits until it reaches - the size of the packet status. - - This object may not be modified if the associated - filterStatus object is equal to valid(1)." - ::= { filterEntry 8 } - - filterPktStatusNotMask OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The inversion mask that is applied to the status - match process. Those relevant bits in the received - packet status that correspond to bits cleared in - this mask must all be equal to their corresponding - bits in the filterPktStatus object for the packet to - be accepted. In addition, at least one of those - relevant bits in the received packet status that - correspond to bits set in this mask must be - different to its corresponding bit in the - filterPktStatus object for the packet to be - accepted. - - - For the purposes of the matching algorithm, if the - associated filterPktStatus object or a packet status - is longer than this mask, this mask is conceptually - extended with '0' bits until it reaches the longer - of the lengths of the filterPktStatus object and the - packet status. - - This object may not be modified if the associated - filterStatus object is equal to valid(1)." - ::= { filterEntry 9 } - - filterOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it." - ::= { filterEntry 10 } - - filterStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The status of this filter entry." - ::= { filterEntry 11 } - - channelTable OBJECT-TYPE - SYNTAX SEQUENCE OF ChannelEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of packet channel entries." - ::= { filter 2 } - - channelEntry OBJECT-TYPE - SYNTAX ChannelEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A set of parameters for a packet channel applied on a - particular interface. As an example, an instance of - the channelMatches object might be named - channelMatches.3" - INDEX { channelIndex } - ::= { channelTable 1 } - - - - ChannelEntry ::= SEQUENCE { - channelIndex INTEGER (1..65535), - channelIfIndex INTEGER (1..65535), - channelAcceptType INTEGER, - channelDataControl INTEGER, - channelTurnOnEventIndex INTEGER (0..65535), - channelTurnOffEventIndex INTEGER (0..65535), - channelEventIndex INTEGER (0..65535), - channelEventStatus INTEGER, - channelMatches Counter, - channelDescription DisplayString (SIZE (0..127)), - channelOwner OwnerString, - channelStatus EntryStatus - } - - channelIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry in the - channel table. Each such entry defines one channel, - a logical data and event stream. - - It is suggested that before creating a channel, an - application should scan all instances of the - filterChannelIndex object to make sure that there - are no pre-existing filters that would be - inadvertently be linked to the channel." - ::= { channelEntry 1 } - - channelIfIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The value of this object uniquely identifies the - interface on this remote network monitoring device - to which the associated filters are applied to allow - data into this channel. The interface identified by - a particular value of this object is the same - interface as identified by the same value of the - ifIndex object, defined in RFC 1213 and RFC 1573 - [4,6]. - - The filters in this group are applied to all packets - on the local network segment attached to the - identified interface. - - - An agent may or may not be able to tell if - fundamental changes to the media of the interface - have occurred and necessitate an invalidation of - this entry. For example, a hot-pluggable ethernet - card could be pulled out and replaced by a - token-ring card. In such a case, if the agent has - such knowledge of the change, it is recommended that - it invalidate this entry. - - This object may not be modified if the associated - channelStatus object is equal to valid(1)." - ::= { channelEntry 2 } - - channelAcceptType OBJECT-TYPE - SYNTAX INTEGER { - acceptMatched(1), - acceptFailed(2) - } - ACCESS read-write - STATUS mandatory - DESCRIPTION - "This object controls the action of the filters - associated with this channel. If this object is equal - to acceptMatched(1), packets will be accepted to this - channel if they are accepted by both the packet data - and packet status matches of an associated filter. If - this object is equal to acceptFailed(2), packets will - be accepted to this channel only if they fail either - the packet data match or the packet status match of - each of the associated filters. - - In particular, a channel with no associated filters - will match no packets if set to acceptMatched(1) - case and will match all packets in the - acceptFailed(2) case. - - This object may not be modified if the associated - channelStatus object is equal to valid(1)." - ::= { channelEntry 3 } - - channelDataControl OBJECT-TYPE - SYNTAX INTEGER { - on(1), - off(2) - } - ACCESS read-write - STATUS mandatory - DESCRIPTION - - - "This object controls the flow of data through this - channel. If this object is on(1), data, status and - events flow through this channel. If this object is - off(2), data, status and events will not flow - through this channel." - DEFVAL { off } - ::= { channelEntry 4 } - - channelTurnOnEventIndex OBJECT-TYPE - SYNTAX INTEGER (0..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The value of this object identifies the event - that is configured to turn the associated - channelDataControl from off to on when the event is - generated. The event identified by a particular value - of this object is the same event as identified by the - same value of the eventIndex object. If there is no - corresponding entry in the eventTable, then no - association exists. In fact, if no event is intended - for this channel, channelTurnOnEventIndex must be - set to zero, a non-existent event index. - - This object may not be modified if the associated - channelStatus object is equal to valid(1)." - ::= { channelEntry 5 } - - channelTurnOffEventIndex OBJECT-TYPE - SYNTAX INTEGER (0..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The value of this object identifies the event - that is configured to turn the associated - channelDataControl from on to off when the event is - generated. The event identified by a particular value - of this object is the same event as identified by the - same value of the eventIndex object. If there is no - corresponding entry in the eventTable, then no - association exists. In fact, if no event is intended - for this channel, channelTurnOffEventIndex must be - set to zero, a non-existent event index. - - This object may not be modified if the associated - channelStatus object is equal to valid(1)." - ::= { channelEntry 6 } - - - - channelEventIndex OBJECT-TYPE - SYNTAX INTEGER (0..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The value of this object identifies the event - that is configured to be generated when the - associated channelDataControl is on and a packet - is matched. The event identified by a particular - value of this object is the same event as identified - by the same value of the eventIndex object. If - there is no corresponding entry in the eventTable, - then no association exists. In fact, if no event is - intended for this channel, channelEventIndex must be - set to zero, a non-existent event index. - - This object may not be modified if the associated - channelStatus object is equal to valid(1)." - ::= { channelEntry 7 } - - channelEventStatus OBJECT-TYPE - SYNTAX INTEGER { - eventReady(1), - eventFired(2), - eventAlwaysReady(3) - } - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The event status of this channel. - - If this channel is configured to generate events - when packets are matched, a means of controlling - the flow of those events is often needed. When - this object is equal to eventReady(1), a single - event may be generated, after which this object - will be set by the probe to eventFired(2). While - in the eventFired(2) state, no events will be - generated until the object is modified to - eventReady(1) (or eventAlwaysReady(3)). The - management station can thus easily respond to a - notification of an event by re-enabling this object. - - If the management station wishes to disable this - flow control and allow events to be generated - at will, this object may be set to - eventAlwaysReady(3). Disabling the flow control - is discouraged as it can result in high network - - - traffic or other performance problems." - DEFVAL { eventReady } - ::= { channelEntry 8 } - - channelMatches OBJECT-TYPE - SYNTAX Counter - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of times this channel has matched a - packet. Note that this object is updated even when - channelDataControl is set to off." - ::= { channelEntry 9 } - - channelDescription OBJECT-TYPE - SYNTAX DisplayString (SIZE (0..127)) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "A comment describing this channel." - ::= { channelEntry 10 } - - channelOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it." - ::= { channelEntry 11 } - - channelStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The status of this channel entry." - ::= { channelEntry 12 } - - - -- The Packet Capture Group - - -- Implementation of the Packet Capture group is optional. - -- - -- The Packet Capture Group requires implementation of the - -- Filter Group. - -- - -- The Packet Capture group allows packets to be captured - - - -- upon a filter match. The bufferControlTable controls - -- the captured packets output from a channel that is - -- associated with it. The captured packets are placed - -- in entries in the captureBufferTable. These entries are - -- associated with the bufferControlEntry on whose behalf they - -- were stored. - - bufferControlTable OBJECT-TYPE - SYNTAX SEQUENCE OF BufferControlEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of buffers control entries." - ::= { capture 1 } - - bufferControlEntry OBJECT-TYPE - SYNTAX BufferControlEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A set of parameters that control the collection of - a stream of packets that have matched filters. As - an example, an instance of the - bufferControlCaptureSliceSize object might be named - bufferControlCaptureSliceSize.3" - INDEX { bufferControlIndex } - ::= { bufferControlTable 1 } - - BufferControlEntry ::= SEQUENCE { - bufferControlIndex INTEGER (1..65535), - bufferControlChannelIndex INTEGER (1..65535), - bufferControlFullStatus INTEGER, - bufferControlFullAction INTEGER, - bufferControlCaptureSliceSize INTEGER, - bufferControlDownloadSliceSize INTEGER, - bufferControlDownloadOffset INTEGER, - bufferControlMaxOctetsRequested INTEGER, - bufferControlMaxOctetsGranted INTEGER, - bufferControlCapturedPackets INTEGER, - bufferControlTurnOnTime TimeTicks, - bufferControlOwner OwnerString, - bufferControlStatus EntryStatus - } - - bufferControlIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - - - DESCRIPTION - "An index that uniquely identifies an entry - in the bufferControl table. The value of this - index shall never be zero. Each such - entry defines one set of packets that is - captured and controlled by one or more filters." - ::= { bufferControlEntry 1 } - - bufferControlChannelIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "An index that identifies the channel that is the - source of packets for this bufferControl table. - The channel identified by a particular value of this - index is the same as identified by the same value of - the channelIndex object. - - This object may not be modified if the associated - bufferControlStatus object is equal to valid(1)." - ::= { bufferControlEntry 2 } - - bufferControlFullStatus OBJECT-TYPE - SYNTAX INTEGER { - spaceAvailable(1), - full(2) - } - ACCESS read-only - STATUS mandatory - DESCRIPTION - "This object shows whether the buffer has room to - accept new packets or if it is full. - - If the status is spaceAvailable(1), the buffer is - accepting new packets normally. If the status is - full(2) and the associated bufferControlFullAction - object is wrapWhenFull, the buffer is accepting new - packets by deleting enough of the oldest packets - to make room for new ones as they arrive. Otherwise, - if the status is full(2) and the - bufferControlFullAction object is lockWhenFull, - then the buffer has stopped collecting packets. - - When this object is set to full(2) the probe must - not later set it to spaceAvailable(1) except in the - case of a significant gain in resources such as - an increase of bufferControlOctetsGranted. In - - - particular, the wrap-mode action of deleting old - packets to make room for newly arrived packets - must not affect the value of this object." - ::= { bufferControlEntry 3 } - - bufferControlFullAction OBJECT-TYPE - SYNTAX INTEGER { - lockWhenFull(1), - wrapWhenFull(2) -- FIFO - } - ACCESS read-write - STATUS mandatory - DESCRIPTION - "Controls the action of the buffer when it - reaches the full status. When in the lockWhenFull(1) - state and a packet is added to the buffer that - fills the buffer, the bufferControlFullStatus will - be set to full(2) and this buffer will stop capturing - packets." - ::= { bufferControlEntry 4 } - - bufferControlCaptureSliceSize OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The maximum number of octets of each packet - that will be saved in this capture buffer. - For example, if a 1500 octet packet is received by - the probe and this object is set to 500, then only - 500 octets of the packet will be stored in the - associated capture buffer. If this variable is set - to 0, the capture buffer will save as many octets - as is possible. - - This object may not be modified if the associated - bufferControlStatus object is equal to valid(1)." - DEFVAL { 100 } - ::= { bufferControlEntry 5 } - - bufferControlDownloadSliceSize OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The maximum number of octets of each packet - in this capture buffer that will be returned in - an SNMP retrieval of that packet. For example, - - - if 500 octets of a packet have been stored in the - associated capture buffer, the associated - bufferControlDownloadOffset is 0, and this - object is set to 100, then the captureBufferPacket - object that contains the packet will contain only - the first 100 octets of the packet. - - A prudent manager will take into account possible - interoperability or fragmentation problems that may - occur if the download slice size is set too large. - In particular, conformant SNMP implementations are not - required to accept messages whose length exceeds 484 - octets, although they are encouraged to support larger - datagrams whenever feasible." - DEFVAL { 100 } - ::= { bufferControlEntry 6 } - - bufferControlDownloadOffset OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The offset of the first octet of each packet - in this capture buffer that will be returned in - an SNMP retrieval of that packet. For example, - if 500 octets of a packet have been stored in the - associated capture buffer and this object is set to - 100, then the captureBufferPacket object that - contains the packet will contain bytes starting - 100 octets into the packet." - DEFVAL { 0 } - ::= { bufferControlEntry 7 } - - bufferControlMaxOctetsRequested OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The requested maximum number of octets to be - saved in this captureBuffer, including any - implementation-specific overhead. If this variable - is set to -1, the capture buffer will save as many - octets as is possible. - - When this object is created or modified, the probe - should set bufferControlMaxOctetsGranted as closely - to this object as is possible for the particular probe - implementation and available resources. However, if - - - the object has the special value of -1, the probe - must set bufferControlMaxOctetsGranted to -1." - DEFVAL { -1 } - ::= { bufferControlEntry 8 } - - bufferControlMaxOctetsGranted OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The maximum number of octets that can be - saved in this captureBuffer, including overhead. - If this variable is -1, the capture buffer will save - as many octets as possible. - - When the bufferControlMaxOctetsRequested object is - created or modified, the probe should set this object - as closely to the requested value as is possible for - the particular probe implementation and available - resources. - However, if the request object has the special value - of -1, the probe must set this object to -1. - The probe must not lower this value except as a result - of a modification to the associated - bufferControlMaxOctetsRequested object. - - When this maximum number of octets is reached - and a new packet is to be added to this - capture buffer and the corresponding - bufferControlFullAction is set to wrapWhenFull(2), - enough of the oldest packets associated with this - capture buffer shall be deleted by the agent so - that the new packet can be added. If the - corresponding bufferControlFullAction is set to - lockWhenFull(1), the new packet shall be discarded. - In either case, the probe must set - bufferControlFullStatus to full(2). - - When the value of this object changes to a value less - than the current value, entries are deleted from - the captureBufferTable associated with this - bufferControlEntry. Enough of the - oldest of these captureBufferEntries shall be - deleted by the agent so that the number of octets - used remains less than or equal to the new value of - this object. - - When the value of this object changes to a value - - - greater than the current value, the number of - associated captureBufferEntries may be allowed to - grow." - ::= { bufferControlEntry 9 } - - bufferControlCapturedPackets OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of packets currently in this - captureBuffer." - ::= { bufferControlEntry 10 } - - bufferControlTurnOnTime OBJECT-TYPE - SYNTAX TimeTicks - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The value of sysUpTime when this capture buffer was - first turned on." - ::= { bufferControlEntry 11 } - - bufferControlOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it." - ::= { bufferControlEntry 12 } - - bufferControlStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The status of this buffer Control Entry." - ::= { bufferControlEntry 13 } - - captureBufferTable OBJECT-TYPE - SYNTAX SEQUENCE OF CaptureBufferEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of packets captured off of a channel." - ::= { capture 2 } - - - - captureBufferEntry OBJECT-TYPE - SYNTAX CaptureBufferEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A packet captured off of an attached network. As an - example, an instance of the captureBufferPacketData - object might be named captureBufferPacketData.3.1783" - INDEX { captureBufferControlIndex, captureBufferIndex } - ::= { captureBufferTable 1 } - - CaptureBufferEntry ::= SEQUENCE { - captureBufferControlIndex INTEGER (1..65535), - captureBufferIndex INTEGER (1..2147483647), - captureBufferPacketID INTEGER, - captureBufferPacketData OCTET STRING, - captureBufferPacketLength INTEGER, - captureBufferPacketTime INTEGER, - captureBufferPacketStatus INTEGER - } - - captureBufferControlIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The index of the bufferControlEntry with which - this packet is associated." - ::= { captureBufferEntry 1 } - - captureBufferIndex OBJECT-TYPE - SYNTAX INTEGER (1..2147483647) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry - in the captureBuffer table associated with a - particular bufferControlEntry. This index will - start at 1 and increase by one for each new packet - added with the same captureBufferControlIndex. - - Should this value reach 2147483647, the next packet - added with the same captureBufferControlIndex shall - cause this value to wrap around to 1." - ::= { captureBufferEntry 2 } - - captureBufferPacketID OBJECT-TYPE - SYNTAX INTEGER - - - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that describes the order of packets - that are received on a particular interface. - The packetID of a packet captured on an - interface is defined to be greater than the - packetID's of all packets captured previously on - the same interface. As the captureBufferPacketID - object has a maximum positive value of 2^31 - 1, - any captureBufferPacketID object shall have the - value of the associated packet's packetID mod 2^31." - ::= { captureBufferEntry 3 } - - captureBufferPacketData OBJECT-TYPE - SYNTAX OCTET STRING - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The data inside the packet, starting at the - beginning of the packet plus any offset specified in - the associated bufferControlDownloadOffset, - including any link level headers. The length of the - data in this object is the minimum of the length of - the captured packet minus the offset, the length of - the associated bufferControlCaptureSliceSize minus - the offset, and the associated - bufferControlDownloadSliceSize. If this minimum is - less than zero, this object shall have a length of - zero." - ::= { captureBufferEntry 4 } - - captureBufferPacketLength OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The actual length (off the wire) of the packet stored - in this entry, including FCS octets." - ::= { captureBufferEntry 5 } - - captureBufferPacketTime OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The number of milliseconds that had passed since - this capture buffer was first turned on when this - - - packet was captured." - ::= { captureBufferEntry 6 } - - captureBufferPacketStatus OBJECT-TYPE - SYNTAX INTEGER - ACCESS read-only - STATUS mandatory - DESCRIPTION - "A value which indicates the error status of this - packet. - - The value of this object is defined in the same way as - filterPktStatus. The value is a sum. This sum - initially takes the value zero. Then, for each - error, E, that has been discovered in this packet, - 2 raised to a value representing E is added to the - sum. - - The errors defined for a packet captured off of an - Ethernet interface are as follows: - - bit # Error - 0 Packet is longer than 1518 octets - 1 Packet is shorter than 64 octets - 2 Packet experienced a CRC or Alignment - error - 3 First packet in this capture buffer after - it was detected that some packets were - not processed correctly. - 4 Packet's order in buffer is only - approximate (May only be set for packets - sent from the probe) - - For example, an Ethernet fragment would have a - value of 6 (2^1 + 2^2). - - As this MIB is expanded to new media types, this - object will have other media-specific errors defined." - ::= { captureBufferEntry 7 } - - - -- The Event Group - - -- Implementation of the Event group is optional. - -- - -- The Event group controls the generation and notification - -- of events from this device. Each entry in the eventTable - -- describes the parameters of the event that can be - - - -- triggered. Each event entry is fired by an associated - -- condition located elsewhere in the MIB. An event entry - -- may also be associated- with a function elsewhere in the - -- MIB that will be executed when the event is generated. For - -- example, a channel may be turned on or off by the firing - -- of an event. - -- - -- Each eventEntry may optionally specify that a log entry - -- be created on its behalf whenever the event occurs. - -- Each entry may also specify that notification should - -- occur by way of SNMP trap messages. In this case, the - -- community for the trap message is given in the associated - -- eventCommunity object. The enterprise and specific trap - -- fields of the trap are determined by the condition that - -- triggered the event. Two traps are defined: risingAlarm - -- and fallingAlarm. If the eventTable is triggered by a - -- condition specified elsewhere, the enterprise and - -- specific trap fields must be specified for traps - -- generated for that condition. - - eventTable OBJECT-TYPE - SYNTAX SEQUENCE OF EventEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A list of events to be generated." - ::= { event 1 } - - eventEntry OBJECT-TYPE - SYNTAX EventEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A set of parameters that describe an event to be - generated when certain conditions are met. As an - example, an instance of the eventLastTimeSent object - might be named eventLastTimeSent.6" - INDEX { eventIndex } - ::= { eventTable 1 } - - EventEntry ::= SEQUENCE { - eventIndex INTEGER (1..65535), - eventDescription DisplayString (SIZE (0..127)), - eventType INTEGER, - eventCommunity OCTET STRING (SIZE (0..127)), - eventLastTimeSent TimeTicks, - eventOwner OwnerString, - eventStatus EntryStatus - - - } - - eventIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry in the - event table. Each such entry defines one event that - is to be generated when the appropriate conditions - occur." - ::= { eventEntry 1 } - - eventDescription OBJECT-TYPE - SYNTAX DisplayString (SIZE (0..127)) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "A comment describing this event entry." - ::= { eventEntry 2 } - - eventType OBJECT-TYPE - SYNTAX INTEGER { - none(1), - log(2), - snmp-trap(3), -- send an SNMP trap - log-and-trap(4) - } - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The type of notification that the probe will make - about this event. In the case of log, an entry is - made in the log table for each event. In the case of - snmp-trap, an SNMP trap is sent to one or more - management stations." - ::= { eventEntry 3 } - - eventCommunity OBJECT-TYPE - SYNTAX OCTET STRING (SIZE (0..127)) - ACCESS read-write - STATUS mandatory - DESCRIPTION - "If an SNMP trap is to be sent, it will be sent to - the SNMP community specified by this octet string. - In the future this table will be extended to include - the party security mechanism. This object shall be - set to a string of length zero if it is intended that - - - that mechanism be used to specify the destination of - the trap." - ::= { eventEntry 4 } - - eventLastTimeSent OBJECT-TYPE - SYNTAX TimeTicks - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The value of sysUpTime at the time this event - entry last generated an event. If this entry has - not generated any events, this value will be - zero." - ::= { eventEntry 5 } - - eventOwner OBJECT-TYPE - SYNTAX OwnerString - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The entity that configured this entry and is - therefore using the resources assigned to it. - - If this object contains a string starting with - 'monitor' and has associated entries in the log - table, all connected management stations should - retrieve those log entries, as they may have - significance to all management stations connected to - this device" - ::= { eventEntry 6 } - - eventStatus OBJECT-TYPE - SYNTAX EntryStatus - ACCESS read-write - STATUS mandatory - DESCRIPTION - "The status of this event entry. - - If this object is not equal to valid(1), all - associated log entries shall be deleted by the - agent." - ::= { eventEntry 7 } - - -- - logTable OBJECT-TYPE - SYNTAX SEQUENCE OF LogEntry - ACCESS not-accessible - STATUS mandatory - - - DESCRIPTION - "A list of events that have been logged." - ::= { event 2 } - - logEntry OBJECT-TYPE - SYNTAX LogEntry - ACCESS not-accessible - STATUS mandatory - DESCRIPTION - "A set of data describing an event that has been - logged. For example, an instance of the - logDescription object might be named - logDescription.6.47" - INDEX { logEventIndex, logIndex } - ::= { logTable 1 } - - LogEntry ::= SEQUENCE { - logEventIndex INTEGER (1..65535), - logIndex INTEGER (1..2147483647), - logTime TimeTicks, - logDescription DisplayString (SIZE (0..255)) - } - - logEventIndex OBJECT-TYPE - SYNTAX INTEGER (1..65535) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The event entry that generated this log - entry. The log identified by a particular - value of this index is associated with the same - eventEntry as identified by the same value - of eventIndex." - ::= { logEntry 1 } - - logIndex OBJECT-TYPE - SYNTAX INTEGER (1..2147483647) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An index that uniquely identifies an entry - in the log table amongst those generated by the - same eventEntries. These indexes are - assigned beginning with 1 and increase by one - with each new log entry. The association - between values of logIndex and logEntries - is fixed for the lifetime of each logEntry. - The agent may choose to delete the oldest - - - instances of logEntry as required because of - lack of memory. It is an implementation-specific - matter as to when this deletion may occur." - ::= { logEntry 2 } - - logTime OBJECT-TYPE - SYNTAX TimeTicks - ACCESS read-only - STATUS mandatory - DESCRIPTION - "The value of sysUpTime when this log entry was - created." - ::= { logEntry 3 } - - logDescription OBJECT-TYPE - SYNTAX DisplayString (SIZE (0..255)) - ACCESS read-only - STATUS mandatory - DESCRIPTION - "An implementation dependent description of the - event that activated this log entry." - ::= { logEntry 4 } - - -- These definitions use the TRAP-TYPE macro as - -- defined in RFC 1215 [10] - - -- Remote Network Monitoring Traps - - risingAlarm TRAP-TYPE - ENTERPRISE rmon - VARIABLES { alarmIndex, alarmVariable, alarmSampleType, - alarmValue, alarmRisingThreshold } - DESCRIPTION - "The SNMP trap that is generated when an alarm - entry crosses its rising threshold and generates - an event that is configured for sending SNMP - traps." - ::= 1 - - fallingAlarm TRAP-TYPE - ENTERPRISE rmon - VARIABLES { alarmIndex, alarmVariable, alarmSampleType, - alarmValue, alarmFallingThreshold } - DESCRIPTION - "The SNMP trap that is generated when an alarm - entry crosses its falling threshold and generates - an event that is configured for sending SNMP - traps." - - - ::= 2 - - END +RMON-MIB DEFINITIONS ::= BEGIN + IMPORTS + MODULE-IDENTITY, OBJECT-TYPE, OBJECT-IDENTITY, + NOTIFICATION-TYPE, mib-2, Counter32, + Integer32, TimeTicks FROM SNMPv2-SMI + + TEXTUAL-CONVENTION, DisplayString FROM SNMPv2-TC + + MODULE-COMPLIANCE, OBJECT-GROUP, + NOTIFICATION-GROUP FROM SNMPv2-CONF; + + +-- Remote Network Monitoring MIB + +rmonMibModule MODULE-IDENTITY + LAST-UPDATED "200005110000Z" -- 11 May, 2000 + ORGANIZATION "IETF RMON MIB Working Group" + CONTACT-INFO + "Steve Waldbusser + Phone: +1-650-948-6500 + Fax: +1-650-745-0671 + Email: [email protected]" + DESCRIPTION + "Remote network monitoring devices, often called + monitors or probes, are instruments that exist for + the purpose of managing a network. This MIB defines + objects for managing remote network monitoring devices." + + REVISION "200005110000Z" -- 11 May, 2000 + DESCRIPTION + "Reformatted into SMIv2 format. + + This version published as RFC 2819." + + REVISION "199502010000Z" -- 1 Feb, 1995 + DESCRIPTION + "Bug fixes, clarifications and minor changes based on + implementation experience, published as RFC1757 [18]. + + Two changes were made to object definitions: + + 1) A new status bit has been defined for the + captureBufferPacketStatus object, indicating that the + packet order within the capture buffer may not be identical to + the packet order as received off the wire. This bit may only + be used for packets transmitted by the probe. Older NMS + applications can safely ignore this status bit, which might be + used by newer agents. + + 2) The packetMatch trap has been removed. This trap was never + actually 'approved' and was not added to this document along + with the risingAlarm and fallingAlarm traps. The packetMatch + trap could not be throttled, which could cause disruption of + normal network traffic under some circumstances. An NMS should + configure a risingAlarm threshold on the appropriate + channelMatches instance if a trap is desired for a packetMatch + event. Note that logging of packetMatch events is still + supported--only trap generation for such events has been + removed. + + In addition, several clarifications to individual object + definitions have been added to assist agent and NMS + implementors: + + - global definition of 'good packets' and 'bad packets' + + - more detailed text governing conceptual row creation and + modification + + - instructions for probes relating to interface changes and + disruptions + + - clarification of some ethernet counter definitions + + - recommended formula for calculating network utilization + + - clarification of channel and captureBuffer behavior for some + unusual conditions + + - examples of proper instance naming for each table" + + REVISION "199111010000Z" -- 1 Nov, 1991 + DESCRIPTION + "The original version of this MIB, published as RFC1271." + ::= { rmonConformance 8 } + + rmon OBJECT IDENTIFIER ::= { mib-2 16 } + + + -- textual conventions + +OwnerString ::= TEXTUAL-CONVENTION + STATUS current + + DESCRIPTION + "This data type is used to model an administratively + assigned name of the owner of a resource. Implementations + must accept values composed of well-formed NVT ASCII + sequences. In addition, implementations should accept + values composed of well-formed UTF-8 sequences. + + It is suggested that this name contain one or more of + the following: IP address, management station name, + network manager's name, location, or phone number. + In some cases the agent itself will be the owner of + an entry. In these cases, this string shall be set + to a string starting with 'monitor'. + + SNMP access control is articulated entirely in terms + of the contents of MIB views; access to a particular + SNMP object instance depends only upon its presence + or absence in a particular MIB view and never upon + its value or the value of related object instances. + Thus, objects of this type afford resolution of + resource contention only among cooperating + managers; they realize no access control function + with respect to uncooperative parties." + SYNTAX OCTET STRING (SIZE (0..127)) + +EntryStatus ::= TEXTUAL-CONVENTION + STATUS current + DESCRIPTION + "The status of a table entry. + + Setting this object to the value invalid(4) has the + effect of invalidating the corresponding entry. + That is, it effectively disassociates the mapping + identified with said entry. + It is an implementation-specific matter as to whether + the agent removes an invalidated entry from the table. + Accordingly, management stations must be prepared to + receive tabular information from agents that corresponds + to entries currently not in use. Proper + interpretation of such entries requires examination + of the relevant EntryStatus object. + + An existing instance of this object cannot be set to + createRequest(2). This object may only be set to + createRequest(2) when this instance is created. When + this object is created, the agent may wish to create + supplemental object instances with default values + to complete a conceptual row in this table. Because the + creation of these default objects is entirely at the option + of the agent, the manager must not assume that any will be + created, but may make use of any that are created. + Immediately after completing the create operation, the agent + must set this object to underCreation(3). + + When in the underCreation(3) state, an entry is allowed to + exist in a possibly incomplete, possibly inconsistent state, + usually to allow it to be modified in multiple PDUs. When in + this state, an entry is not fully active. + Entries shall exist in the underCreation(3) state until + the management station is finished configuring the entry + and sets this object to valid(1) or aborts, setting this + object to invalid(4). If the agent determines that an + entry has been in the underCreation(3) state for an + abnormally long time, it may decide that the management + station has crashed. If the agent makes this decision, + it may set this object to invalid(4) to reclaim the + entry. A prudent agent will understand that the + management station may need to wait for human input + and will allow for that possibility in its + determination of this abnormally long period. + + An entry in the valid(1) state is fully configured and + consistent and fully represents the configuration or + operation such a row is intended to represent. For + example, it could be a statistical function that is + configured and active, or a filter that is available + in the list of filters processed by the packet capture + process. + + A manager is restricted to changing the state of an entry in + the following ways: + + To: valid createRequest underCreation invalid + From: + valid OK NO OK OK + createRequest N/A N/A N/A N/A + underCreation OK NO OK OK + invalid NO NO NO OK + nonExistent NO OK NO OK + + In the table above, it is not applicable to move the state + from the createRequest state to any other state because the + manager will never find the variable in that state. The + nonExistent state is not a value of the enumeration, rather + it means that the entryStatus variable does not exist at all. + + An agent may allow an entryStatus variable to change state in + additional ways, so long as the semantics of the states are + followed. This allowance is made to ease the implementation of + the agent and is made despite the fact that managers should + never exercise these additional state transitions." + SYNTAX INTEGER { + valid(1), + createRequest(2), + underCreation(3), + invalid(4) + } + + statistics OBJECT IDENTIFIER ::= { rmon 1 } + history OBJECT IDENTIFIER ::= { rmon 2 } + alarm OBJECT IDENTIFIER ::= { rmon 3 } + hosts OBJECT IDENTIFIER ::= { rmon 4 } + hostTopN OBJECT IDENTIFIER ::= { rmon 5 } + matrix OBJECT IDENTIFIER ::= { rmon 6 } + filter OBJECT IDENTIFIER ::= { rmon 7 } + capture OBJECT IDENTIFIER ::= { rmon 8 } + event OBJECT IDENTIFIER ::= { rmon 9 } + rmonConformance OBJECT IDENTIFIER ::= { rmon 20 } + +-- The Ethernet Statistics Group +-- +-- Implementation of the Ethernet Statistics group is optional. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The ethernet statistics group contains statistics measured by the +-- probe for each monitored interface on this device. These +-- statistics take the form of free running counters that start from +-- zero when a valid entry is created. +-- +-- This group currently has statistics defined only for +-- Ethernet interfaces. Each etherStatsEntry contains statistics +-- for one Ethernet interface. The probe must create one +-- etherStats entry for each monitored Ethernet interface +-- on the device. + +etherStatsTable OBJECT-TYPE + SYNTAX SEQUENCE OF EtherStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of Ethernet statistics entries." + ::= { statistics 1 } + +etherStatsEntry OBJECT-TYPE + SYNTAX EtherStatsEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A collection of statistics kept for a particular + Ethernet interface. As an example, an instance of the + etherStatsPkts object might be named etherStatsPkts.1" + INDEX { etherStatsIndex } + ::= { etherStatsTable 1 } + +EtherStatsEntry ::= SEQUENCE { + etherStatsIndex Integer32, + etherStatsDataSource OBJECT IDENTIFIER, + etherStatsDropEvents Counter32, + etherStatsOctets Counter32, + etherStatsPkts Counter32, + etherStatsBroadcastPkts Counter32, + etherStatsMulticastPkts Counter32, + etherStatsCRCAlignErrors Counter32, + etherStatsUndersizePkts Counter32, + etherStatsOversizePkts Counter32, + etherStatsFragments Counter32, + etherStatsJabbers Counter32, + etherStatsCollisions Counter32, + etherStatsPkts64Octets Counter32, + etherStatsPkts65to127Octets Counter32, + etherStatsPkts128to255Octets Counter32, + etherStatsPkts256to511Octets Counter32, + etherStatsPkts512to1023Octets Counter32, + etherStatsPkts1024to1518Octets Counter32, + etherStatsOwner OwnerString, + etherStatsStatus EntryStatus +} + +etherStatsIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of this object uniquely identifies this + etherStats entry." + ::= { etherStatsEntry 1 } + +etherStatsDataSource OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-create + STATUS current + + DESCRIPTION + "This object identifies the source of the data that + this etherStats entry is configured to analyze. This + source can be any ethernet interface on this device. + In order to identify a particular interface, this object + shall identify the instance of the ifIndex object, + defined in RFC 2233 [17], for the desired interface. + For example, if an entry were to receive data from + interface #1, this object would be set to ifIndex.1. + + The statistics in this group reflect all packets + on the local network segment attached to the identified + interface. + + An agent may or may not be able to tell if fundamental + changes to the media of the interface have occurred and + necessitate an invalidation of this entry. For example, a + hot-pluggable ethernet card could be pulled out and replaced + by a token-ring card. In such a case, if the agent has such + knowledge of the change, it is recommended that it + invalidate this entry. + + This object may not be modified if the associated + etherStatsStatus object is equal to valid(1)." + ::= { etherStatsEntry 2 } + +etherStatsDropEvents OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of events in which packets + were dropped by the probe due to lack of resources. + Note that this number is not necessarily the number of + packets dropped; it is just the number of times this + condition has been detected." + ::= { etherStatsEntry 3 } + +etherStatsOctets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets of data (including + those in bad packets) received on the + network (excluding framing bits but including + FCS octets). + + This object can be used as a reasonable estimate of + 10-Megabit ethernet utilization. If greater precision is + desired, the etherStatsPkts and etherStatsOctets objects + should be sampled before and after a common interval. The + differences in the sampled values are Pkts and Octets, + respectively, and the number of seconds in the interval is + Interval. These values are used to calculate the Utilization + as follows: + + Pkts * (9.6 + 6.4) + (Octets * .8) + Utilization = ------------------------------------- + Interval * 10,000 + + The result of this equation is the value Utilization which + is the percent utilization of the ethernet segment on a + scale of 0 to 100 percent." + ::= { etherStatsEntry 4 } + +etherStatsPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets (including bad packets, + broadcast packets, and multicast packets) received." + ::= { etherStatsEntry 5 } + +etherStatsBroadcastPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of good packets received that were + directed to the broadcast address. Note that this + does not include multicast packets." + ::= { etherStatsEntry 6 } + +etherStatsMulticastPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of good packets received that were + directed to a multicast address. Note that this number + does not include packets directed to the broadcast + address." + ::= { etherStatsEntry 7 } + +etherStatsCRCAlignErrors OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets received that + had a length (excluding framing bits, but + including FCS octets) of between 64 and 1518 + octets, inclusive, but had either a bad + Frame Check Sequence (FCS) with an integral + number of octets (FCS Error) or a bad FCS with + a non-integral number of octets (Alignment Error)." + ::= { etherStatsEntry 8 } + +etherStatsUndersizePkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets received that were + less than 64 octets long (excluding framing bits, + but including FCS octets) and were otherwise well + formed." + ::= { etherStatsEntry 9 } + +etherStatsOversizePkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets received that were + longer than 1518 octets (excluding framing bits, + but including FCS octets) and were otherwise + well formed." + ::= { etherStatsEntry 10 } + +etherStatsFragments OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets received that were less than + 64 octets in length (excluding framing bits but including + FCS octets) and had either a bad Frame Check Sequence + (FCS) with an integral number of octets (FCS Error) or a + bad FCS with a non-integral number of octets (Alignment + Error). + + Note that it is entirely normal for etherStatsFragments to + increment. This is because it counts both runts (which are + normal occurrences due to collisions) and noise hits." + ::= { etherStatsEntry 11 } + +etherStatsJabbers OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets received that were + longer than 1518 octets (excluding framing bits, + but including FCS octets), and had either a bad + Frame Check Sequence (FCS) with an integral number + of octets (FCS Error) or a bad FCS with a non-integral + number of octets (Alignment Error). + + Note that this definition of jabber is different + than the definition in IEEE-802.3 section 8.2.1.5 + (10BASE5) and section 10.3.1.4 (10BASE2). These + documents define jabber as the condition where any + packet exceeds 20 ms. The allowed range to detect + jabber is between 20 ms and 150 ms." + ::= { etherStatsEntry 12 } + +etherStatsCollisions OBJECT-TYPE + SYNTAX Counter32 + UNITS "Collisions" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The best estimate of the total number of collisions + on this Ethernet segment. + + The value returned will depend on the location of the + RMON probe. Section 8.2.1.3 (10BASE-5) and section + 10.3.1.3 (10BASE-2) of IEEE standard 802.3 states that a + station must detect a collision, in the receive mode, if + three or more stations are transmitting simultaneously. A + repeater port must detect a collision when two or more + stations are transmitting simultaneously. Thus a probe + placed on a repeater port could record more collisions + than a probe connected to a station on the same segment + would. + + Probe location plays a much smaller role when considering + 10BASE-T. 14.2.1.4 (10BASE-T) of IEEE standard 802.3 + defines a collision as the simultaneous presence of signals + on the DO and RD circuits (transmitting and receiving + at the same time). A 10BASE-T station can only detect + collisions when it is transmitting. Thus probes placed on + a station and a repeater, should report the same number of + collisions. + + Note also that an RMON probe inside a repeater should + ideally report collisions between the repeater and one or + more other hosts (transmit collisions as defined by IEEE + 802.3k) plus receiver collisions observed on any coax + segments to which the repeater is connected." + ::= { etherStatsEntry 13 } + +etherStatsPkts64Octets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets (including bad + packets) received that were 64 octets in length + (excluding framing bits but including FCS octets)." + ::= { etherStatsEntry 14 } + +etherStatsPkts65to127Octets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets (including bad + packets) received that were between + 65 and 127 octets in length inclusive + (excluding framing bits but including FCS octets)." + ::= { etherStatsEntry 15 } + +etherStatsPkts128to255Octets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + + STATUS current + DESCRIPTION + "The total number of packets (including bad + packets) received that were between + 128 and 255 octets in length inclusive + (excluding framing bits but including FCS octets)." + ::= { etherStatsEntry 16 } + +etherStatsPkts256to511Octets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets (including bad + packets) received that were between + 256 and 511 octets in length inclusive + (excluding framing bits but including FCS octets)." + ::= { etherStatsEntry 17 } + +etherStatsPkts512to1023Octets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets (including bad + packets) received that were between + 512 and 1023 octets in length inclusive + (excluding framing bits but including FCS octets)." + ::= { etherStatsEntry 18 } + +etherStatsPkts1024to1518Octets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets (including bad + packets) received that were between + 1024 and 1518 octets in length inclusive + (excluding framing bits but including FCS octets)." + ::= { etherStatsEntry 19 } + +etherStatsOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it." + ::= { etherStatsEntry 20 } + +etherStatsStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this etherStats entry." + ::= { etherStatsEntry 21 } + +-- The History Control Group + +-- Implementation of the History Control group is optional. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The history control group controls the periodic statistical +-- sampling of data from various types of networks. The +-- historyControlTable stores configuration entries that each +-- define an interface, polling period, and other parameters. +-- Once samples are taken, their data is stored in an entry +-- in a media-specific table. Each such entry defines one +-- sample, and is associated with the historyControlEntry that +-- caused the sample to be taken. Each counter in the +-- etherHistoryEntry counts the same event as its similarly-named +-- counterpart in the etherStatsEntry, except that each value here +-- is a cumulative sum during a sampling period. +-- +-- If the probe keeps track of the time of day, it should start +-- the first sample of the history at a time such that +-- when the next hour of the day begins, a sample is +-- started at that instant. This tends to make more +-- user-friendly reports, and enables comparison of reports +-- from different probes that have relatively accurate time +-- of day. +-- +-- The probe is encouraged to add two history control entries +-- per monitored interface upon initialization that describe a short +-- term and a long term polling period. Suggested parameters are 30 +-- seconds for the short term polling period and 30 minutes for +-- the long term period. + +historyControlTable OBJECT-TYPE + SYNTAX SEQUENCE OF HistoryControlEntry + MAX-ACCESS not-accessible + + STATUS current + DESCRIPTION + "A list of history control entries." + ::= { history 1 } + +historyControlEntry OBJECT-TYPE + SYNTAX HistoryControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of parameters that set up a periodic sampling of + statistics. As an example, an instance of the + historyControlInterval object might be named + historyControlInterval.2" + INDEX { historyControlIndex } + ::= { historyControlTable 1 } + +HistoryControlEntry ::= SEQUENCE { + historyControlIndex Integer32, + historyControlDataSource OBJECT IDENTIFIER, + historyControlBucketsRequested Integer32, + historyControlBucketsGranted Integer32, + historyControlInterval Integer32, + historyControlOwner OwnerString, + historyControlStatus EntryStatus +} + +historyControlIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry in the + historyControl table. Each such entry defines a + set of samples at a particular interval for an + interface on the device." + ::= { historyControlEntry 1 } + +historyControlDataSource OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object identifies the source of the data for + which historical data was collected and + placed in a media-specific table on behalf of this + historyControlEntry. This source can be any + interface on this device. In order to identify + a particular interface, this object shall identify + the instance of the ifIndex object, defined + in RFC 2233 [17], for the desired interface. + For example, if an entry were to receive data from + interface #1, this object would be set to ifIndex.1. + + The statistics in this group reflect all packets + on the local network segment attached to the identified + interface. + + An agent may or may not be able to tell if fundamental + changes to the media of the interface have occurred and + necessitate an invalidation of this entry. For example, a + hot-pluggable ethernet card could be pulled out and replaced + by a token-ring card. In such a case, if the agent has such + knowledge of the change, it is recommended that it + invalidate this entry. + + This object may not be modified if the associated + historyControlStatus object is equal to valid(1)." + ::= { historyControlEntry 2 } + +historyControlBucketsRequested OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The requested number of discrete time intervals + over which data is to be saved in the part of the + media-specific table associated with this + historyControlEntry. + + When this object is created or modified, the probe + should set historyControlBucketsGranted as closely to + this object as is possible for the particular probe + implementation and available resources." + DEFVAL { 50 } + ::= { historyControlEntry 3 } + +historyControlBucketsGranted OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of discrete sampling intervals + over which data shall be saved in the part of + the media-specific table associated with this + historyControlEntry. + + When the associated historyControlBucketsRequested + object is created or modified, the probe + should set this object as closely to the requested + value as is possible for the particular + probe implementation and available resources. The + probe must not lower this value except as a result + of a modification to the associated + historyControlBucketsRequested object. + + There will be times when the actual number of + buckets associated with this entry is less than + the value of this object. In this case, at the + end of each sampling interval, a new bucket will + be added to the media-specific table. + + When the number of buckets reaches the value of + this object and a new bucket is to be added to the + media-specific table, the oldest bucket associated + with this historyControlEntry shall be deleted by + the agent so that the new bucket can be added. + + When the value of this object changes to a value less + than the current value, entries are deleted + from the media-specific table associated with this + historyControlEntry. Enough of the oldest of these + entries shall be deleted by the agent so that their + number remains less than or equal to the new value of + this object. + + When the value of this object changes to a value greater + than the current value, the number of associated media- + specific entries may be allowed to grow." + ::= { historyControlEntry 4 } + +historyControlInterval OBJECT-TYPE + SYNTAX Integer32 (1..3600) + UNITS "Seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The interval in seconds over which the data is + sampled for each bucket in the part of the + media-specific table associated with this + historyControlEntry. This interval can + be set to any number of seconds between 1 and + 3600 (1 hour). + + Because the counters in a bucket may overflow at their + maximum value with no indication, a prudent manager will + take into account the possibility of overflow in any of + the associated counters. It is important to consider the + minimum time in which any counter could overflow on a + particular media type and set the historyControlInterval + object to a value less than this interval. This is + typically most important for the 'octets' counter in any + media-specific table. For example, on an Ethernet + network, the etherHistoryOctets counter could overflow + in about one hour at the Ethernet's maximum + utilization. + + This object may not be modified if the associated + historyControlStatus object is equal to valid(1)." + DEFVAL { 1800 } + ::= { historyControlEntry 5 } + +historyControlOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it." + ::= { historyControlEntry 6 } + +historyControlStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this historyControl entry. + + Each instance of the media-specific table associated + with this historyControlEntry will be deleted by the agent + if this historyControlEntry is not equal to valid(1)." + ::= { historyControlEntry 7 } + +-- The Ethernet History Group + +-- Implementation of the Ethernet History group is optional. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The Ethernet History group records periodic statistical samples +-- from a network and stores them for later retrieval. +-- Once samples are taken, their data is stored in an entry +-- in a media-specific table. Each such entry defines one +-- sample, and is associated with the historyControlEntry that +-- caused the sample to be taken. This group defines the +-- etherHistoryTable, for Ethernet networks. +-- + +etherHistoryTable OBJECT-TYPE + SYNTAX SEQUENCE OF EtherHistoryEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of Ethernet history entries." + ::= { history 2 } + +etherHistoryEntry OBJECT-TYPE + SYNTAX EtherHistoryEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "An historical sample of Ethernet statistics on a particular + Ethernet interface. This sample is associated with the + historyControlEntry which set up the parameters for + a regular collection of these samples. As an example, an + instance of the etherHistoryPkts object might be named + etherHistoryPkts.2.89" + INDEX { etherHistoryIndex , etherHistorySampleIndex } + ::= { etherHistoryTable 1 } + +EtherHistoryEntry ::= SEQUENCE { + etherHistoryIndex Integer32, + etherHistorySampleIndex Integer32, + etherHistoryIntervalStart TimeTicks, + etherHistoryDropEvents Counter32, + etherHistoryOctets Counter32, + etherHistoryPkts Counter32, + etherHistoryBroadcastPkts Counter32, + etherHistoryMulticastPkts Counter32, + etherHistoryCRCAlignErrors Counter32, + etherHistoryUndersizePkts Counter32, + etherHistoryOversizePkts Counter32, + etherHistoryFragments Counter32, + etherHistoryJabbers Counter32, + etherHistoryCollisions Counter32, + etherHistoryUtilization Integer32 +} + +etherHistoryIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The history of which this entry is a part. The + history identified by a particular value of this + index is the same history as identified + by the same value of historyControlIndex." + ::= { etherHistoryEntry 1 } + +etherHistorySampleIndex OBJECT-TYPE + SYNTAX Integer32 (1..2147483647) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies the particular + sample this entry represents among all samples + associated with the same historyControlEntry. + This index starts at 1 and increases by one + as each new sample is taken." + ::= { etherHistoryEntry 2 } + +etherHistoryIntervalStart OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime at the start of the interval + over which this sample was measured. If the probe + keeps track of the time of day, it should start + the first sample of the history at a time such that + when the next hour of the day begins, a sample is + started at that instant. Note that following this + rule may require the probe to delay collecting the + first sample of the history, as each sample must be + of the same interval. Also note that the sample which + is currently being collected is not accessible in this + table until the end of its interval." + ::= { etherHistoryEntry 3 } + +etherHistoryDropEvents OBJECT-TYPE + SYNTAX Counter32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of events in which packets + were dropped by the probe due to lack of resources + during this sampling interval. Note that this number + is not necessarily the number of packets dropped, it + is just the number of times this condition has been + detected." + ::= { etherHistoryEntry 4 } + +etherHistoryOctets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of octets of data (including + those in bad packets) received on the + network (excluding framing bits but including + FCS octets)." + ::= { etherHistoryEntry 5 } + +etherHistoryPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets (including bad packets) + received during this sampling interval." + ::= { etherHistoryEntry 6 } + +etherHistoryBroadcastPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of good packets received during this + sampling interval that were directed to the + broadcast address." + ::= { etherHistoryEntry 7 } + +etherHistoryMulticastPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of good packets received during this + sampling interval that were directed to a + multicast address. Note that this number does not + include packets addressed to the broadcast address." + ::= { etherHistoryEntry 8 } + +etherHistoryCRCAlignErrors OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets received during this + sampling interval that had a length (excluding + framing bits but including FCS octets) between + 64 and 1518 octets, inclusive, but had either a bad Frame + Check Sequence (FCS) with an integral number of octets + (FCS Error) or a bad FCS with a non-integral number + of octets (Alignment Error)." + ::= { etherHistoryEntry 9 } + +etherHistoryUndersizePkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets received during this + sampling interval that were less than 64 octets + long (excluding framing bits but including FCS + octets) and were otherwise well formed." + ::= { etherHistoryEntry 10 } + +etherHistoryOversizePkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets received during this + sampling interval that were longer than 1518 + octets (excluding framing bits but including + FCS octets) but were otherwise well formed." + ::= { etherHistoryEntry 11 } + +etherHistoryFragments OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The total number of packets received during this + sampling interval that were less than 64 octets in + length (excluding framing bits but including FCS + octets) had either a bad Frame Check Sequence (FCS) + with an integral number of octets (FCS Error) or a bad + FCS with a non-integral number of octets (Alignment + Error). + + Note that it is entirely normal for etherHistoryFragments to + increment. This is because it counts both runts (which are + normal occurrences due to collisions) and noise hits." + ::= { etherHistoryEntry 12 } + +etherHistoryJabbers OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets received during this + sampling interval that were longer than 1518 octets + (excluding framing bits but including FCS octets), + and had either a bad Frame Check Sequence (FCS) + with an integral number of octets (FCS Error) or + a bad FCS with a non-integral number of octets + (Alignment Error). + + Note that this definition of jabber is different + than the definition in IEEE-802.3 section 8.2.1.5 + (10BASE5) and section 10.3.1.4 (10BASE2). These + documents define jabber as the condition where any + packet exceeds 20 ms. The allowed range to detect + jabber is between 20 ms and 150 ms." + ::= { etherHistoryEntry 13 } + +etherHistoryCollisions OBJECT-TYPE + SYNTAX Counter32 + UNITS "Collisions" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The best estimate of the total number of collisions + on this Ethernet segment during this sampling + interval. + + The value returned will depend on the location of the + RMON probe. Section 8.2.1.3 (10BASE-5) and section + 10.3.1.3 (10BASE-2) of IEEE standard 802.3 states that a + station must detect a collision, in the receive mode, if + three or more stations are transmitting simultaneously. A + repeater port must detect a collision when two or more + stations are transmitting simultaneously. Thus a probe + placed on a repeater port could record more collisions + than a probe connected to a station on the same segment + would. + + Probe location plays a much smaller role when considering + 10BASE-T. 14.2.1.4 (10BASE-T) of IEEE standard 802.3 + defines a collision as the simultaneous presence of signals + on the DO and RD circuits (transmitting and receiving + at the same time). A 10BASE-T station can only detect + collisions when it is transmitting. Thus probes placed on + a station and a repeater, should report the same number of + collisions. + + Note also that an RMON probe inside a repeater should + ideally report collisions between the repeater and one or + more other hosts (transmit collisions as defined by IEEE + 802.3k) plus receiver collisions observed on any coax + segments to which the repeater is connected." + ::= { etherHistoryEntry 14 } + +etherHistoryUtilization OBJECT-TYPE + SYNTAX Integer32 (0..10000) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The best estimate of the mean physical layer + network utilization on this interface during this + sampling interval, in hundredths of a percent." + ::= { etherHistoryEntry 15 } + +-- The Alarm Group + +-- Implementation of the Alarm group is optional. The Alarm Group +-- requires the implementation of the Event group. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The Alarm group periodically takes statistical samples from +-- variables in the probe and compares them to thresholds that have +-- been configured. The alarm table stores configuration +-- entries that each define a variable, polling period, and +-- threshold parameters. If a sample is found to cross the +-- threshold values, an event is generated. Only variables that +-- resolve to an ASN.1 primitive type of INTEGER (INTEGER, Integer32, +-- Counter32, Counter64, Gauge32, or TimeTicks) may be monitored in +-- this way. +-- +-- This function has a hysteresis mechanism to limit the generation +-- of events. This mechanism generates one event as a threshold +-- is crossed in the appropriate direction. No more events are +-- generated for that threshold until the opposite threshold is +-- crossed. +-- +-- In the case of a sampling a deltaValue, a probe may implement +-- this mechanism with more precision if it takes a delta sample +-- twice per period, each time comparing the sum of the latest two +-- samples to the threshold. This allows the detection of threshold +-- crossings that span the sampling boundary. Note that this does +-- not require any special configuration of the threshold value. +-- It is suggested that probes implement this more precise algorithm. + +alarmTable OBJECT-TYPE + SYNTAX SEQUENCE OF AlarmEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of alarm entries." + ::= { alarm 1 } + +alarmEntry OBJECT-TYPE + SYNTAX AlarmEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of parameters that set up a periodic checking + for alarm conditions. For example, an instance of the + alarmValue object might be named alarmValue.8" + INDEX { alarmIndex } + ::= { alarmTable 1 } + +AlarmEntry ::= SEQUENCE { + alarmIndex Integer32, + alarmInterval Integer32, + alarmVariable OBJECT IDENTIFIER, + alarmSampleType INTEGER, + alarmValue Integer32, + alarmStartupAlarm INTEGER, + alarmRisingThreshold Integer32, + alarmFallingThreshold Integer32, + alarmRisingEventIndex Integer32, + alarmFallingEventIndex Integer32, + alarmOwner OwnerString, + alarmStatus EntryStatus +} + +alarmIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry in the + alarm table. Each such entry defines a + diagnostic sample at a particular interval + for an object on the device." + ::= { alarmEntry 1 } + +alarmInterval OBJECT-TYPE + SYNTAX Integer32 + UNITS "Seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The interval in seconds over which the data is + sampled and compared with the rising and falling + thresholds. When setting this variable, care + should be taken in the case of deltaValue + sampling - the interval should be set short enough + that the sampled variable is very unlikely to + increase or decrease by more than 2^31 - 1 during + a single sampling interval. + + This object may not be modified if the associated + alarmStatus object is equal to valid(1)." + ::= { alarmEntry 2 } + +alarmVariable OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The object identifier of the particular variable to be + sampled. Only variables that resolve to an ASN.1 primitive + type of INTEGER (INTEGER, Integer32, Counter32, Counter64, + Gauge, or TimeTicks) may be sampled. + + Because SNMP access control is articulated entirely + in terms of the contents of MIB views, no access + control mechanism exists that can restrict the value of + this object to identify only those objects that exist + in a particular MIB view. Because there is thus no + acceptable means of restricting the read access that + could be obtained through the alarm mechanism, the + probe must only grant write access to this object in + those views that have read access to all objects on + the probe. + + During a set operation, if the supplied variable name is + not available in the selected MIB view, a badValue error + must be returned. If at any time the variable name of + an established alarmEntry is no longer available in the + selected MIB view, the probe must change the status of + this alarmEntry to invalid(4). + + This object may not be modified if the associated + alarmStatus object is equal to valid(1)." + ::= { alarmEntry 3 } + +alarmSampleType OBJECT-TYPE + SYNTAX INTEGER { + absoluteValue(1), + deltaValue(2) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The method of sampling the selected variable and + calculating the value to be compared against the + thresholds. If the value of this object is + absoluteValue(1), the value of the selected variable + will be compared directly with the thresholds at the + end of the sampling interval. If the value of this + object is deltaValue(2), the value of the selected + variable at the last sample will be subtracted from + the current value, and the difference compared with + the thresholds. + + This object may not be modified if the associated + alarmStatus object is equal to valid(1)." + ::= { alarmEntry 4 } + +alarmValue OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of the statistic during the last sampling + period. For example, if the sample type is deltaValue, + this value will be the difference between the samples + at the beginning and end of the period. If the sample + type is absoluteValue, this value will be the sampled + value at the end of the period. + This is the value that is compared with the rising and + falling thresholds. + + The value during the current sampling period is not + made available until the period is completed and will + remain available until the next period completes." + ::= { alarmEntry 5 } + +alarmStartupAlarm OBJECT-TYPE + SYNTAX INTEGER { + risingAlarm(1), + fallingAlarm(2), + risingOrFallingAlarm(3) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The alarm that may be sent when this entry is first + set to valid. If the first sample after this entry + becomes valid is greater than or equal to the + risingThreshold and alarmStartupAlarm is equal to + risingAlarm(1) or risingOrFallingAlarm(3), then a single + rising alarm will be generated. If the first sample + after this entry becomes valid is less than or equal + to the fallingThreshold and alarmStartupAlarm is equal + to fallingAlarm(2) or risingOrFallingAlarm(3), then a + single falling alarm will be generated. + + This object may not be modified if the associated + alarmStatus object is equal to valid(1)." + ::= { alarmEntry 6 } + +alarmRisingThreshold OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "A threshold for the sampled statistic. When the current + sampled value is greater than or equal to this threshold, + and the value at the last sampling interval was less than + this threshold, a single event will be generated. + A single event will also be generated if the first + sample after this entry becomes valid is greater than or + equal to this threshold and the associated + alarmStartupAlarm is equal to risingAlarm(1) or + risingOrFallingAlarm(3). + + After a rising event is generated, another such event + will not be generated until the sampled value + falls below this threshold and reaches the + alarmFallingThreshold. + + This object may not be modified if the associated + alarmStatus object is equal to valid(1)." + ::= { alarmEntry 7 } + +alarmFallingThreshold OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "A threshold for the sampled statistic. When the current + sampled value is less than or equal to this threshold, + and the value at the last sampling interval was greater than + this threshold, a single event will be generated. + A single event will also be generated if the first + sample after this entry becomes valid is less than or + equal to this threshold and the associated + alarmStartupAlarm is equal to fallingAlarm(2) or + risingOrFallingAlarm(3). + + After a falling event is generated, another such event + will not be generated until the sampled value + rises above this threshold and reaches the + alarmRisingThreshold. + + This object may not be modified if the associated + alarmStatus object is equal to valid(1)." + ::= { alarmEntry 8 } + +alarmRisingEventIndex OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The index of the eventEntry that is + used when a rising threshold is crossed. The + eventEntry identified by a particular value of + this index is the same as identified by the same value + of the eventIndex object. If there is no + corresponding entry in the eventTable, then + no association exists. In particular, if this value + is zero, no associated event will be generated, as + zero is not a valid event index. + + This object may not be modified if the associated + alarmStatus object is equal to valid(1)." + ::= { alarmEntry 9 } + +alarmFallingEventIndex OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The index of the eventEntry that is + used when a falling threshold is crossed. The + eventEntry identified by a particular value of + this index is the same as identified by the same value + of the eventIndex object. If there is no + corresponding entry in the eventTable, then + no association exists. In particular, if this value + is zero, no associated event will be generated, as + zero is not a valid event index. + + This object may not be modified if the associated + alarmStatus object is equal to valid(1)." + ::= { alarmEntry 10 } + +alarmOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it." + ::= { alarmEntry 11 } + +alarmStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this alarm entry." + ::= { alarmEntry 12 } + +-- The Host Group + +-- Implementation of the Host group is optional. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The host group discovers new hosts on the network by +-- keeping a list of source and destination MAC Addresses seen +-- in good packets. For each of these addresses, the host group +-- keeps a set of statistics. The hostControlTable controls +-- which interfaces this function is performed on, and contains +-- some information about the process. On behalf of each +-- hostControlEntry, data is collected on an interface and placed +-- in both the hostTable and the hostTimeTable. If the +-- monitoring device finds itself short of resources, it may +-- delete entries as needed. It is suggested that the device +-- delete the least recently used entries first. + +-- The hostTable contains entries for each address discovered on +-- a particular interface. Each entry contains statistical +-- data about that host. This table is indexed by the +-- MAC address of the host, through which a random access +-- may be achieved. + +-- The hostTimeTable contains data in the same format as the +-- hostTable, and must contain the same set of hosts, but is +-- indexed using hostTimeCreationOrder rather than hostAddress. +-- The hostTimeCreationOrder is an integer which reflects +-- the relative order in which a particular entry was discovered +-- and thus inserted into the table. As this order, and thus +-- the index, is among those entries currently in the table, +-- the index for a particular entry may change if an +-- (earlier) entry is deleted. Thus the association between +-- hostTimeCreationOrder and hostTimeEntry may be broken at +-- any time. + +-- The hostTimeTable has two important uses. The first is the +-- fast download of this potentially large table. Because the +-- index of this table runs from 1 to the size of the table, +-- inclusive, its values are predictable. This allows very +-- efficient packing of variables into SNMP PDU's and allows +-- a table transfer to have multiple packets outstanding. +-- These benefits increase transfer rates tremendously. + +-- The second use of the hostTimeTable is the efficient discovery +-- by the management station of new entries added to the table. +-- After the management station has downloaded the entire table, +-- it knows that new entries will be added immediately after the +-- end of the current table. It can thus detect new entries there +-- and retrieve them easily. + +-- Because the association between hostTimeCreationOrder and +-- hostTimeEntry may be broken at any time, the management +-- station must monitor the related hostControlLastDeleteTime +-- object. When the management station thus detects a deletion, +-- it must assume that any such associations have been broken, +-- and invalidate any it has stored locally. This includes +-- restarting any download of the hostTimeTable that may have been +-- in progress, as well as rediscovering the end of the +-- hostTimeTable so that it may detect new entries. If the +-- management station does not detect the broken association, +-- it may continue to refer to a particular host by its +-- creationOrder while unwittingly retrieving the data associated +-- with another host entirely. If this happens while downloading +-- the host table, the management station may fail to download +-- all of the entries in the table. + + +hostControlTable OBJECT-TYPE + SYNTAX SEQUENCE OF HostControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of host table control entries." + ::= { hosts 1 } + +hostControlEntry OBJECT-TYPE + SYNTAX HostControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of parameters that set up the discovery of hosts + on a particular interface and the collection of statistics + about these hosts. For example, an instance of the + hostControlTableSize object might be named + hostControlTableSize.1" + INDEX { hostControlIndex } + ::= { hostControlTable 1 } + +HostControlEntry ::= SEQUENCE { + + hostControlIndex Integer32, + hostControlDataSource OBJECT IDENTIFIER, + hostControlTableSize Integer32, + hostControlLastDeleteTime TimeTicks, + hostControlOwner OwnerString, + hostControlStatus EntryStatus +} + +hostControlIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry in the + hostControl table. Each such entry defines + a function that discovers hosts on a particular interface + and places statistics about them in the hostTable and + the hostTimeTable on behalf of this hostControlEntry." + ::= { hostControlEntry 1 } + +hostControlDataSource OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object identifies the source of the data for + this instance of the host function. This source + can be any interface on this device. In order + to identify a particular interface, this object shall + identify the instance of the ifIndex object, defined + in RFC 2233 [17], for the desired interface. + For example, if an entry were to receive data from + interface #1, this object would be set to ifIndex.1. + + The statistics in this group reflect all packets + on the local network segment attached to the identified + interface. + + An agent may or may not be able to tell if fundamental + changes to the media of the interface have occurred and + necessitate an invalidation of this entry. For example, a + hot-pluggable ethernet card could be pulled out and replaced + by a token-ring card. In such a case, if the agent has such + knowledge of the change, it is recommended that it + invalidate this entry. + + This object may not be modified if the associated + hostControlStatus object is equal to valid(1)." + ::= { hostControlEntry 2 } + +hostControlTableSize OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of hostEntries in the hostTable and the + hostTimeTable associated with this hostControlEntry." + ::= { hostControlEntry 3 } + +hostControlLastDeleteTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime when the last entry + was deleted from the portion of the hostTable + associated with this hostControlEntry. If no + deletions have occurred, this value shall be zero." + ::= { hostControlEntry 4 } + +hostControlOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it." + ::= { hostControlEntry 5 } + +hostControlStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this hostControl entry. + + If this object is not equal to valid(1), all associated + entries in the hostTable, hostTimeTable, and the + hostTopNTable shall be deleted by the agent." + ::= { hostControlEntry 6 } + +hostTable OBJECT-TYPE + SYNTAX SEQUENCE OF HostEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of host entries." + ::= { hosts 2 } + +hostEntry OBJECT-TYPE + SYNTAX HostEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A collection of statistics for a particular host that has + been discovered on an interface of this device. For example, + an instance of the hostOutBroadcastPkts object might be + named hostOutBroadcastPkts.1.6.8.0.32.27.3.176" + INDEX { hostIndex, hostAddress } + ::= { hostTable 1 } + +HostEntry ::= SEQUENCE { + hostAddress OCTET STRING, + hostCreationOrder Integer32, + hostIndex Integer32, + hostInPkts Counter32, + hostOutPkts Counter32, + hostInOctets Counter32, + hostOutOctets Counter32, + hostOutErrors Counter32, + hostOutBroadcastPkts Counter32, + hostOutMulticastPkts Counter32 +} + +hostAddress OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The physical address of this host." + ::= { hostEntry 1 } + +hostCreationOrder OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that defines the relative ordering of + the creation time of hosts captured for a + particular hostControlEntry. This index shall + be between 1 and N, where N is the value of + the associated hostControlTableSize. The ordering + of the indexes is based on the order of each entry's + insertion into the table, in which entries added earlier + have a lower index value than entries added later. + + It is important to note that the order for a + particular entry may change as an (earlier) entry + is deleted from the table. Because this order may + change, management stations should make use of the + hostControlLastDeleteTime variable in the + hostControlEntry associated with the relevant + portion of the hostTable. By observing + this variable, the management station may detect + the circumstances where a previous association + between a value of hostCreationOrder + and a hostEntry may no longer hold." + ::= { hostEntry 2 } + +hostIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The set of collected host statistics of which + this entry is a part. The set of hosts + identified by a particular value of this + index is associated with the hostControlEntry + as identified by the same value of hostControlIndex." + ::= { hostEntry 3 } + +hostInPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of good packets transmitted to this + address since it was added to the hostTable." + ::= { hostEntry 4 } + +hostOutPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets, including bad packets, transmitted + by this address since it was added to the hostTable." + ::= { hostEntry 5 } + +hostInOctets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of octets transmitted to this address since + it was added to the hostTable (excluding framing + bits but including FCS octets), except for those + octets in bad packets." + ::= { hostEntry 6 } + +hostOutOctets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of octets transmitted by this address since + it was added to the hostTable (excluding framing + bits but including FCS octets), including those + octets in bad packets." + ::= { hostEntry 7 } + +hostOutErrors OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bad packets transmitted by this address + since this host was added to the hostTable." + ::= { hostEntry 8 } + +hostOutBroadcastPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of good packets transmitted by this + address that were directed to the broadcast address + since this host was added to the hostTable." + ::= { hostEntry 9 } + +hostOutMulticastPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of good packets transmitted by this + address that were directed to a multicast address + since this host was added to the hostTable. + Note that this number does not include packets + directed to the broadcast address." + ::= { hostEntry 10 } + +-- host Time Table + +hostTimeTable OBJECT-TYPE + SYNTAX SEQUENCE OF HostTimeEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of time-ordered host table entries." + ::= { hosts 3 } + +hostTimeEntry OBJECT-TYPE + SYNTAX HostTimeEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A collection of statistics for a particular host that has + been discovered on an interface of this device. This + collection includes the relative ordering of the creation + time of this object. For example, an instance of the + hostTimeOutBroadcastPkts object might be named + hostTimeOutBroadcastPkts.1.687" + INDEX { hostTimeIndex, hostTimeCreationOrder } + ::= { hostTimeTable 1 } + +HostTimeEntry ::= SEQUENCE { + hostTimeAddress OCTET STRING, + hostTimeCreationOrder Integer32, + hostTimeIndex Integer32, + hostTimeInPkts Counter32, + hostTimeOutPkts Counter32, + hostTimeInOctets Counter32, + hostTimeOutOctets Counter32, + hostTimeOutErrors Counter32, + hostTimeOutBroadcastPkts Counter32, + hostTimeOutMulticastPkts Counter32 +} + +hostTimeAddress OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The physical address of this host." + ::= { hostTimeEntry 1 } + +hostTimeCreationOrder OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry in + the hostTime table among those entries associated + with the same hostControlEntry. This index shall + be between 1 and N, where N is the value of + the associated hostControlTableSize. The ordering + of the indexes is based on the order of each entry's + insertion into the table, in which entries added earlier + have a lower index value than entries added later. + Thus the management station has the ability to + learn of new entries added to this table without + downloading the entire table. + + It is important to note that the index for a + particular entry may change as an (earlier) entry + is deleted from the table. Because this order may + change, management stations should make use of the + hostControlLastDeleteTime variable in the + hostControlEntry associated with the relevant + portion of the hostTimeTable. By observing + this variable, the management station may detect + the circumstances where a download of the table + may have missed entries, and where a previous + association between a value of hostTimeCreationOrder + and a hostTimeEntry may no longer hold." + ::= { hostTimeEntry 2 } + +hostTimeIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The set of collected host statistics of which + this entry is a part. The set of hosts + identified by a particular value of this + index is associated with the hostControlEntry + as identified by the same value of hostControlIndex." + ::= { hostTimeEntry 3 } + +hostTimeInPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of good packets transmitted to this + address since it was added to the hostTimeTable." + ::= { hostTimeEntry 4 } + +hostTimeOutPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets, including bad packets, transmitted + by this address since it was added to the hostTimeTable." + ::= { hostTimeEntry 5 } + +hostTimeInOctets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of octets transmitted to this address since + it was added to the hostTimeTable (excluding framing + bits but including FCS octets), except for those + octets in bad packets." + ::= { hostTimeEntry 6 } + +hostTimeOutOctets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of octets transmitted by this address since + it was added to the hostTimeTable (excluding framing + bits but including FCS octets), including those + octets in bad packets." + ::= { hostTimeEntry 7 } + +hostTimeOutErrors OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bad packets transmitted by this address + since this host was added to the hostTimeTable." + ::= { hostTimeEntry 8 } + +hostTimeOutBroadcastPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of good packets transmitted by this + address that were directed to the broadcast address + since this host was added to the hostTimeTable." + ::= { hostTimeEntry 9 } + +hostTimeOutMulticastPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of good packets transmitted by this + address that were directed to a multicast address + since this host was added to the hostTimeTable. + Note that this number does not include packets directed + to the broadcast address." + ::= { hostTimeEntry 10 } + +-- The Host Top "N" Group + +-- Implementation of the Host Top N group is optional. The Host Top N +-- group requires the implementation of the host group. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The Host Top N group is used to prepare reports that describe +-- the hosts that top a list ordered by one of their statistics. +-- The available statistics are samples of one of their +-- base statistics, over an interval specified by the management +-- station. Thus, these statistics are rate based. The management +-- station also selects how many such hosts are reported. + +-- The hostTopNControlTable is used to initiate the generation of +-- such a report. The management station may select the parameters +-- of such a report, such as which interface, which statistic, +-- how many hosts, and the start and stop times of the sampling. +-- When the report is prepared, entries are created in the +-- hostTopNTable associated with the relevant hostTopNControlEntry. +-- These entries are static for each report after it has been +-- prepared. + +hostTopNControlTable OBJECT-TYPE + SYNTAX SEQUENCE OF HostTopNControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of top N host control entries." + ::= { hostTopN 1 } + +hostTopNControlEntry OBJECT-TYPE + SYNTAX HostTopNControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A set of parameters that control the creation of a report + of the top N hosts according to several metrics. For + example, an instance of the hostTopNDuration object might + be named hostTopNDuration.3" + INDEX { hostTopNControlIndex } + ::= { hostTopNControlTable 1 } + +HostTopNControlEntry ::= SEQUENCE { + hostTopNControlIndex Integer32, + hostTopNHostIndex Integer32, + hostTopNRateBase INTEGER, + hostTopNTimeRemaining Integer32, + hostTopNDuration Integer32, + hostTopNRequestedSize Integer32, + hostTopNGrantedSize Integer32, + hostTopNStartTime TimeTicks, + hostTopNOwner OwnerString, + hostTopNStatus EntryStatus +} + +hostTopNControlIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry + in the hostTopNControl table. Each such + entry defines one top N report prepared for + one interface." + ::= { hostTopNControlEntry 1 } + +hostTopNHostIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The host table for which a top N report will be prepared + on behalf of this entry. The host table identified by a + particular value of this index is associated with the same + host table as identified by the same value of + hostIndex. + + This object may not be modified if the associated + hostTopNStatus object is equal to valid(1)." + ::= { hostTopNControlEntry 2 } + +hostTopNRateBase OBJECT-TYPE + SYNTAX INTEGER { + hostTopNInPkts(1), + hostTopNOutPkts(2), + hostTopNInOctets(3), + hostTopNOutOctets(4), + hostTopNOutErrors(5), + hostTopNOutBroadcastPkts(6), + hostTopNOutMulticastPkts(7) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The variable for each host that the hostTopNRate + variable is based upon. + + This object may not be modified if the associated + hostTopNStatus object is equal to valid(1)." + ::= { hostTopNControlEntry 3 } + +hostTopNTimeRemaining OBJECT-TYPE + SYNTAX Integer32 + UNITS "Seconds" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The number of seconds left in the report currently being + collected. When this object is modified by the management + station, a new collection is started, possibly aborting + a currently running report. The new value is used + as the requested duration of this report, which is + loaded into the associated hostTopNDuration object. + + When this object is set to a non-zero value, any + associated hostTopNEntries shall be made + inaccessible by the monitor. While the value of this + object is non-zero, it decrements by one per second until + it reaches zero. During this time, all associated + hostTopNEntries shall remain inaccessible. At the time + that this object decrements to zero, the report is made + accessible in the hostTopNTable. Thus, the hostTopN + table needs to be created only at the end of the collection + interval." + DEFVAL { 0 } + ::= { hostTopNControlEntry 4 } + +hostTopNDuration OBJECT-TYPE + SYNTAX Integer32 + UNITS "Seconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of seconds that this report has collected + during the last sampling interval, or if this + report is currently being collected, the number + of seconds that this report is being collected + during this sampling interval. + + When the associated hostTopNTimeRemaining object is set, + this object shall be set by the probe to the same value + and shall not be modified until the next time + the hostTopNTimeRemaining is set. + + This value shall be zero if no reports have been + requested for this hostTopNControlEntry." + DEFVAL { 0 } + ::= { hostTopNControlEntry 5 } + +hostTopNRequestedSize OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The maximum number of hosts requested for the top N + table. + + When this object is created or modified, the probe + should set hostTopNGrantedSize as closely to this + object as is possible for the particular probe + implementation and available resources." + DEFVAL { 10 } + ::= { hostTopNControlEntry 6 } + +hostTopNGrantedSize OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum number of hosts in the top N table. + + When the associated hostTopNRequestedSize object is + created or modified, the probe should set this + object as closely to the requested value as is possible + for the particular implementation and available + resources. The probe must not lower this value except + as a result of a set to the associated + hostTopNRequestedSize object. + + Hosts with the highest value of hostTopNRate shall be + placed in this table in decreasing order of this rate + until there is no more room or until there are no more + hosts." + ::= { hostTopNControlEntry 7 } + +hostTopNStartTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime when this top N report was + last started. In other words, this is the time that + the associated hostTopNTimeRemaining object was + modified to start the requested report." + ::= { hostTopNControlEntry 8 } + +hostTopNOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it." + ::= { hostTopNControlEntry 9 } + +hostTopNStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this hostTopNControl entry. + + If this object is not equal to valid(1), all associated + hostTopNEntries shall be deleted by the agent." + ::= { hostTopNControlEntry 10 } + +hostTopNTable OBJECT-TYPE + SYNTAX SEQUENCE OF HostTopNEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of top N host entries." + ::= { hostTopN 2 } + +hostTopNEntry OBJECT-TYPE + SYNTAX HostTopNEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A set of statistics for a host that is part of a top N + report. For example, an instance of the hostTopNRate + object might be named hostTopNRate.3.10" + INDEX { hostTopNReport, hostTopNIndex } + ::= { hostTopNTable 1 } + +HostTopNEntry ::= SEQUENCE { + hostTopNReport Integer32, + hostTopNIndex Integer32, + hostTopNAddress OCTET STRING, + hostTopNRate Integer32 +} + +hostTopNReport OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This object identifies the top N report of which + this entry is a part. The set of hosts + identified by a particular value of this + object is part of the same report as identified + by the same value of the hostTopNControlIndex object." + ::= { hostTopNEntry 1 } + +hostTopNIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry in + the hostTopN table among those in the same report. + This index is between 1 and N, where N is the + number of entries in this table. Increasing values + of hostTopNIndex shall be assigned to entries with + decreasing values of hostTopNRate until index N + is assigned to the entry with the lowest value of + hostTopNRate or there are no more hostTopNEntries." + ::= { hostTopNEntry 2 } + +hostTopNAddress OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The physical address of this host." + ::= { hostTopNEntry 3 } + +hostTopNRate OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The amount of change in the selected variable + during this sampling interval. The selected + variable is this host's instance of the object + selected by hostTopNRateBase." + ::= { hostTopNEntry 4 } + +-- The Matrix Group + +-- Implementation of the Matrix group is optional. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The Matrix group consists of the matrixControlTable, matrixSDTable +-- and the matrixDSTable. These tables store statistics for a +-- particular conversation between two addresses. As the device +-- detects a new conversation, including those to a non-unicast +-- address, it creates a new entry in both of the matrix tables. +-- It must only create new entries based on information +-- received in good packets. If the monitoring device finds +-- itself short of resources, it may delete entries as needed. +-- It is suggested that the device delete the least recently used +-- entries first. + +matrixControlTable OBJECT-TYPE + SYNTAX SEQUENCE OF MatrixControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of information entries for the + traffic matrix on each interface." + ::= { matrix 1 } + +matrixControlEntry OBJECT-TYPE + SYNTAX MatrixControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "Information about a traffic matrix on a particular + interface. For example, an instance of the + matrixControlLastDeleteTime object might be named + matrixControlLastDeleteTime.1" + INDEX { matrixControlIndex } + ::= { matrixControlTable 1 } + +MatrixControlEntry ::= SEQUENCE { + matrixControlIndex Integer32, + matrixControlDataSource OBJECT IDENTIFIER, + matrixControlTableSize Integer32, + matrixControlLastDeleteTime TimeTicks, + matrixControlOwner OwnerString, + matrixControlStatus EntryStatus +} + +matrixControlIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry in the + matrixControl table. Each such entry defines + a function that discovers conversations on a particular + interface and places statistics about them in the + matrixSDTable and the matrixDSTable on behalf of this + matrixControlEntry." + ::= { matrixControlEntry 1 } + +matrixControlDataSource OBJECT-TYPE + SYNTAX OBJECT IDENTIFIER + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object identifies the source of + the data from which this entry creates a traffic matrix. + This source can be any interface on this device. In + order to identify a particular interface, this object + shall identify the instance of the ifIndex object, + defined in RFC 2233 [17], for the desired + interface. For example, if an entry were to receive data + from interface #1, this object would be set to ifIndex.1. + + The statistics in this group reflect all packets + on the local network segment attached to the identified + interface. + + An agent may or may not be able to tell if fundamental + changes to the media of the interface have occurred and + necessitate an invalidation of this entry. For example, a + hot-pluggable ethernet card could be pulled out and replaced + by a token-ring card. In such a case, if the agent has such + knowledge of the change, it is recommended that it + invalidate this entry. + + This object may not be modified if the associated + matrixControlStatus object is equal to valid(1)." + ::= { matrixControlEntry 2 } + +matrixControlTableSize OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of matrixSDEntries in the matrixSDTable + for this interface. This must also be the value of + the number of entries in the matrixDSTable for this + interface." + ::= { matrixControlEntry 3 } + +matrixControlLastDeleteTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime when the last entry + was deleted from the portion of the matrixSDTable + or matrixDSTable associated with this matrixControlEntry. + If no deletions have occurred, this value shall be + zero." + ::= { matrixControlEntry 4 } + +matrixControlOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it." + ::= { matrixControlEntry 5 } + +matrixControlStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this matrixControl entry. + If this object is not equal to valid(1), all associated + entries in the matrixSDTable and the matrixDSTable + shall be deleted by the agent." + ::= { matrixControlEntry 6 } + +matrixSDTable OBJECT-TYPE + SYNTAX SEQUENCE OF MatrixSDEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of traffic matrix entries indexed by + source and destination MAC address." + ::= { matrix 2 } + +matrixSDEntry OBJECT-TYPE + SYNTAX MatrixSDEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A collection of statistics for communications between + two addresses on a particular interface. For example, + an instance of the matrixSDPkts object might be named + matrixSDPkts.1.6.8.0.32.27.3.176.6.8.0.32.10.8.113" + INDEX { matrixSDIndex, + matrixSDSourceAddress, matrixSDDestAddress } + ::= { matrixSDTable 1 } + +MatrixSDEntry ::= SEQUENCE { + matrixSDSourceAddress OCTET STRING, + matrixSDDestAddress OCTET STRING, + matrixSDIndex Integer32, + matrixSDPkts Counter32, + matrixSDOctets Counter32, + matrixSDErrors Counter32 +} + +matrixSDSourceAddress OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The source physical address." + ::= { matrixSDEntry 1 } + +matrixSDDestAddress OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The destination physical address." + ::= { matrixSDEntry 2 } + +matrixSDIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The set of collected matrix statistics of which + this entry is a part. The set of matrix statistics + identified by a particular value of this index + is associated with the same matrixControlEntry + as identified by the same value of matrixControlIndex." + ::= { matrixSDEntry 3 } + +matrixSDPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets transmitted from the source + address to the destination address (this number includes + bad packets)." + ::= { matrixSDEntry 4 } + +matrixSDOctets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of octets (excluding framing bits but + including FCS octets) contained in all packets + transmitted from the source address to the + destination address." + ::= { matrixSDEntry 5 } + +matrixSDErrors OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bad packets transmitted from + the source address to the destination address." + ::= { matrixSDEntry 6 } + +-- Traffic matrix tables from destination to source + +matrixDSTable OBJECT-TYPE + SYNTAX SEQUENCE OF MatrixDSEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of traffic matrix entries indexed by + destination and source MAC address." + ::= { matrix 3 } + +matrixDSEntry OBJECT-TYPE + SYNTAX MatrixDSEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A collection of statistics for communications between + two addresses on a particular interface. For example, + an instance of the matrixSDPkts object might be named + matrixSDPkts.1.6.8.0.32.10.8.113.6.8.0.32.27.3.176" + INDEX { matrixDSIndex, + matrixDSDestAddress, matrixDSSourceAddress } + ::= { matrixDSTable 1 } + +MatrixDSEntry ::= SEQUENCE { + matrixDSSourceAddress OCTET STRING, + matrixDSDestAddress OCTET STRING, + matrixDSIndex Integer32, + matrixDSPkts Counter32, + matrixDSOctets Counter32, + matrixDSErrors Counter32 +} + +matrixDSSourceAddress OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The source physical address." + ::= { matrixDSEntry 1 } + +matrixDSDestAddress OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The destination physical address." + ::= { matrixDSEntry 2 } + +matrixDSIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The set of collected matrix statistics of which + this entry is a part. The set of matrix statistics + identified by a particular value of this index + is associated with the same matrixControlEntry + as identified by the same value of matrixControlIndex." + ::= { matrixDSEntry 3 } + +matrixDSPkts OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets transmitted from the source + address to the destination address (this number includes + bad packets)." + ::= { matrixDSEntry 4 } + +matrixDSOctets OBJECT-TYPE + SYNTAX Counter32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of octets (excluding framing bits + but including FCS octets) contained in all packets + transmitted from the source address to the + destination address." + ::= { matrixDSEntry 5 } + +matrixDSErrors OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of bad packets transmitted from + the source address to the destination address." + ::= { matrixDSEntry 6 } + +-- The Filter Group + +-- Implementation of the Filter group is optional. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The Filter group allows packets to be captured with an +-- arbitrary filter expression. A logical data and +-- event stream or "channel" is formed by the packets +-- that match the filter expression. +-- +-- This filter mechanism allows the creation of an arbitrary +-- logical expression with which to filter packets. Each +-- filter associated with a channel is OR'ed with the others. +-- Within a filter, any bits checked in the data and status are +-- AND'ed with respect to other bits in the same filter. The +-- NotMask also allows for checking for inequality. Finally, +-- the channelAcceptType object allows for inversion of the +-- whole equation. +-- +-- If a management station wishes to receive a trap to alert it +-- that new packets have been captured and are available for +-- download, it is recommended that it set up an alarm entry that +-- monitors the value of the relevant channelMatches instance. +-- +-- The channel can be turned on or off, and can also +-- generate events when packets pass through it. + +filterTable OBJECT-TYPE + SYNTAX SEQUENCE OF FilterEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of packet filter entries." + ::= { filter 1 } + +filterEntry OBJECT-TYPE + SYNTAX FilterEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A set of parameters for a packet filter applied on a + particular interface. As an example, an instance of the + filterPktData object might be named filterPktData.12" + INDEX { filterIndex } + ::= { filterTable 1 } + +FilterEntry ::= SEQUENCE { + filterIndex Integer32, + filterChannelIndex Integer32, + filterPktDataOffset Integer32, + filterPktData OCTET STRING, + filterPktDataMask OCTET STRING, + filterPktDataNotMask OCTET STRING, + filterPktStatus Integer32, + filterPktStatusMask Integer32, + filterPktStatusNotMask Integer32, + filterOwner OwnerString, + filterStatus EntryStatus +} + +filterIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry + in the filter table. Each such entry defines + one filter that is to be applied to every packet + received on an interface." + ::= { filterEntry 1 } + +filterChannelIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object identifies the channel of which this filter + is a part. The filters identified by a particular value + of this object are associated with the same channel as + identified by the same value of the channelIndex object." + ::= { filterEntry 2 } + +filterPktDataOffset OBJECT-TYPE + SYNTAX Integer32 + UNITS "Octets" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The offset from the beginning of each packet where + a match of packet data will be attempted. This offset + is measured from the point in the physical layer + packet after the framing bits, if any. For example, + in an Ethernet frame, this point is at the beginning of + the destination MAC address. + + This object may not be modified if the associated + filterStatus object is equal to valid(1)." + DEFVAL { 0 } + + ::= { filterEntry 3 } + +filterPktData OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The data that is to be matched with the input packet. + For each packet received, this filter and the accompanying + filterPktDataMask and filterPktDataNotMask will be + adjusted for the offset. The only bits relevant to this + match algorithm are those that have the corresponding + filterPktDataMask bit equal to one. The following three + rules are then applied to every packet: + + (1) If the packet is too short and does not have data + corresponding to part of the filterPktData, the packet + will fail this data match. + + (2) For each relevant bit from the packet with the + corresponding filterPktDataNotMask bit set to zero, if + the bit from the packet is not equal to the corresponding + bit from the filterPktData, then the packet will fail + this data match. + + (3) If for every relevant bit from the packet with the + corresponding filterPktDataNotMask bit set to one, the + bit from the packet is equal to the corresponding bit + from the filterPktData, then the packet will fail this + data match. + + Any packets that have not failed any of the three matches + above have passed this data match. In particular, a zero + length filter will match any packet. + + This object may not be modified if the associated + filterStatus object is equal to valid(1)." + ::= { filterEntry 4 } + +filterPktDataMask OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The mask that is applied to the match process. + After adjusting this mask for the offset, only those + bits in the received packet that correspond to bits set + in this mask are relevant for further processing by the + match algorithm. The offset is applied to filterPktDataMask + in the same way it is applied to the filter. For the + purposes of the matching algorithm, if the associated + filterPktData object is longer than this mask, this mask is + conceptually extended with '1' bits until it reaches the + length of the filterPktData object. + + This object may not be modified if the associated + filterStatus object is equal to valid(1)." + ::= { filterEntry 5 } + +filterPktDataNotMask OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The inversion mask that is applied to the match + process. After adjusting this mask for the offset, + those relevant bits in the received packet that correspond + to bits cleared in this mask must all be equal to their + corresponding bits in the filterPktData object for the packet + to be accepted. In addition, at least one of those relevant + bits in the received packet that correspond to bits set in + this mask must be different to its corresponding bit in the + filterPktData object. + + For the purposes of the matching algorithm, if the associated + filterPktData object is longer than this mask, this mask is + conceptually extended with '0' bits until it reaches the + length of the filterPktData object. + + This object may not be modified if the associated + filterStatus object is equal to valid(1)." + ::= { filterEntry 6 } + +filterPktStatus OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status that is to be matched with the input packet. + The only bits relevant to this match algorithm are those that + have the corresponding filterPktStatusMask bit equal to one. + The following two rules are then applied to every packet: + + (1) For each relevant bit from the packet status with the + corresponding filterPktStatusNotMask bit set to zero, if + the bit from the packet status is not equal to the + corresponding bit from the filterPktStatus, then the + packet will fail this status match. + + (2) If for every relevant bit from the packet status with the + corresponding filterPktStatusNotMask bit set to one, the + bit from the packet status is equal to the corresponding + bit from the filterPktStatus, then the packet will fail + this status match. + + Any packets that have not failed either of the two matches + above have passed this status match. In particular, a zero + length status filter will match any packet's status. + + The value of the packet status is a sum. This sum + initially takes the value zero. Then, for each + error, E, that has been discovered in this packet, + 2 raised to a value representing E is added to the sum. + The errors and the bits that represent them are dependent + on the media type of the interface that this channel + is receiving packets from. + + The errors defined for a packet captured off of an + Ethernet interface are as follows: + + bit # Error + 0 Packet is longer than 1518 octets + 1 Packet is shorter than 64 octets + 2 Packet experienced a CRC or Alignment error + + For example, an Ethernet fragment would have a + value of 6 (2^1 + 2^2). + + As this MIB is expanded to new media types, this object + will have other media-specific errors defined. + + For the purposes of this status matching algorithm, if the + packet status is longer than this filterPktStatus object, + this object is conceptually extended with '0' bits until it + reaches the size of the packet status. + + This object may not be modified if the associated + filterStatus object is equal to valid(1)." + ::= { filterEntry 7 } + +filterPktStatusMask OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The mask that is applied to the status match process. + Only those bits in the received packet that correspond to + bits set in this mask are relevant for further processing + by the status match algorithm. For the purposes + of the matching algorithm, if the associated filterPktStatus + object is longer than this mask, this mask is conceptually + extended with '1' bits until it reaches the size of the + filterPktStatus. In addition, if a packet status is longer + than this mask, this mask is conceptually extended with '0' + bits until it reaches the size of the packet status. + + This object may not be modified if the associated + filterStatus object is equal to valid(1)." + ::= { filterEntry 8 } + +filterPktStatusNotMask OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The inversion mask that is applied to the status match + process. Those relevant bits in the received packet status + that correspond to bits cleared in this mask must all be + equal to their corresponding bits in the filterPktStatus + object for the packet to be accepted. In addition, at least + one of those relevant bits in the received packet status + that correspond to bits set in this mask must be different + to its corresponding bit in the filterPktStatus object for + the packet to be accepted. + + For the purposes of the matching algorithm, if the associated + filterPktStatus object or a packet status is longer than this + mask, this mask is conceptually extended with '0' bits until + it reaches the longer of the lengths of the filterPktStatus + object and the packet status. + + This object may not be modified if the associated + filterStatus object is equal to valid(1)." + ::= { filterEntry 9 } + +filterOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it." + ::= { filterEntry 10 } + +filterStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this filter entry." + ::= { filterEntry 11 } + +channelTable OBJECT-TYPE + SYNTAX SEQUENCE OF ChannelEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of packet channel entries." + ::= { filter 2 } + +channelEntry OBJECT-TYPE + SYNTAX ChannelEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A set of parameters for a packet channel applied on a + particular interface. As an example, an instance of the + channelMatches object might be named channelMatches.3" + INDEX { channelIndex } + ::= { channelTable 1 } + +ChannelEntry ::= SEQUENCE { + channelIndex Integer32, + channelIfIndex Integer32, + channelAcceptType INTEGER, + channelDataControl INTEGER, + channelTurnOnEventIndex Integer32, + channelTurnOffEventIndex Integer32, + channelEventIndex Integer32, + channelEventStatus INTEGER, + channelMatches Counter32, + channelDescription DisplayString, + channelOwner OwnerString, + channelStatus EntryStatus +} + +channelIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry in the channel + table. Each such entry defines one channel, a logical + data and event stream. + + It is suggested that before creating a channel, an + application should scan all instances of the + filterChannelIndex object to make sure that there are no + pre-existing filters that would be inadvertently be linked + to the channel." + ::= { channelEntry 1 } + +channelIfIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The value of this object uniquely identifies the + interface on this remote network monitoring device to which + the associated filters are applied to allow data into this + channel. The interface identified by a particular value + of this object is the same interface as identified by the + same value of the ifIndex object, defined in RFC 2233 [17]. + + The filters in this group are applied to all packets on + the local network segment attached to the identified + interface. + + An agent may or may not be able to tell if fundamental + changes to the media of the interface have occurred and + necessitate an invalidation of this entry. For example, a + hot-pluggable ethernet card could be pulled out and replaced + by a token-ring card. In such a case, if the agent has such + knowledge of the change, it is recommended that it + invalidate this entry. + + This object may not be modified if the associated + channelStatus object is equal to valid(1)." + ::= { channelEntry 2 } + +channelAcceptType OBJECT-TYPE + SYNTAX INTEGER { + acceptMatched(1), + acceptFailed(2) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object controls the action of the filters + associated with this channel. If this object is equal + to acceptMatched(1), packets will be accepted to this + channel if they are accepted by both the packet data and + packet status matches of an associated filter. If + this object is equal to acceptFailed(2), packets will + be accepted to this channel only if they fail either + the packet data match or the packet status match of + each of the associated filters. + + In particular, a channel with no associated filters will + match no packets if set to acceptMatched(1) case and will + match all packets in the acceptFailed(2) case. + + This object may not be modified if the associated + channelStatus object is equal to valid(1)." + ::= { channelEntry 3 } + +channelDataControl OBJECT-TYPE + SYNTAX INTEGER { + on(1), + off(2) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "This object controls the flow of data through this channel. + If this object is on(1), data, status and events flow + through this channel. If this object is off(2), data, + status and events will not flow through this channel." + DEFVAL { off } + ::= { channelEntry 4 } + +channelTurnOnEventIndex OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The value of this object identifies the event + that is configured to turn the associated + channelDataControl from off to on when the event is + generated. The event identified by a particular value + of this object is the same event as identified by the + same value of the eventIndex object. If there is no + corresponding entry in the eventTable, then no + association exists. In fact, if no event is intended + for this channel, channelTurnOnEventIndex must be + set to zero, a non-existent event index. + + This object may not be modified if the associated + channelStatus object is equal to valid(1)." + ::= { channelEntry 5 } + +channelTurnOffEventIndex OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The value of this object identifies the event + that is configured to turn the associated + channelDataControl from on to off when the event is + generated. The event identified by a particular value + of this object is the same event as identified by the + same value of the eventIndex object. If there is no + corresponding entry in the eventTable, then no + association exists. In fact, if no event is intended + for this channel, channelTurnOffEventIndex must be + set to zero, a non-existent event index. + + This object may not be modified if the associated + channelStatus object is equal to valid(1)." + ::= { channelEntry 6 } + +channelEventIndex OBJECT-TYPE + SYNTAX Integer32 (0..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The value of this object identifies the event + that is configured to be generated when the + associated channelDataControl is on and a packet + is matched. The event identified by a particular value + of this object is the same event as identified by the + same value of the eventIndex object. If there is no + corresponding entry in the eventTable, then no + association exists. In fact, if no event is intended + for this channel, channelEventIndex must be + set to zero, a non-existent event index. + + This object may not be modified if the associated + channelStatus object is equal to valid(1)." + ::= { channelEntry 7 } + +channelEventStatus OBJECT-TYPE + SYNTAX INTEGER { + eventReady(1), + eventFired(2), + eventAlwaysReady(3) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The event status of this channel. + + If this channel is configured to generate events + when packets are matched, a means of controlling + the flow of those events is often needed. When + this object is equal to eventReady(1), a single + event may be generated, after which this object + will be set by the probe to eventFired(2). While + in the eventFired(2) state, no events will be + generated until the object is modified to + eventReady(1) (or eventAlwaysReady(3)). The + management station can thus easily respond to a + notification of an event by re-enabling this object. + + If the management station wishes to disable this + flow control and allow events to be generated + at will, this object may be set to + eventAlwaysReady(3). Disabling the flow control + is discouraged as it can result in high network + traffic or other performance problems." + DEFVAL { eventReady } + ::= { channelEntry 8 } + +channelMatches OBJECT-TYPE + SYNTAX Counter32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of times this channel has matched a packet. + Note that this object is updated even when + channelDataControl is set to off." + ::= { channelEntry 9 } + +channelDescription OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..127)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "A comment describing this channel." + ::= { channelEntry 10 } + +channelOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it." + ::= { channelEntry 11 } + +channelStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this channel entry." + ::= { channelEntry 12 } + +-- The Packet Capture Group + +-- Implementation of the Packet Capture group is optional. The Packet +-- Capture Group requires implementation of the Filter Group. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The Packet Capture group allows packets to be captured +-- upon a filter match. The bufferControlTable controls +-- the captured packets output from a channel that is +-- associated with it. The captured packets are placed +-- in entries in the captureBufferTable. These entries are +-- associated with the bufferControlEntry on whose behalf they +-- were stored. + +bufferControlTable OBJECT-TYPE + SYNTAX SEQUENCE OF BufferControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of buffers control entries." + ::= { capture 1 } + +bufferControlEntry OBJECT-TYPE + SYNTAX BufferControlEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A set of parameters that control the collection of a stream + of packets that have matched filters. As an example, an + instance of the bufferControlCaptureSliceSize object might + be named bufferControlCaptureSliceSize.3" + + INDEX { bufferControlIndex } + ::= { bufferControlTable 1 } + +BufferControlEntry ::= SEQUENCE { + bufferControlIndex Integer32, + bufferControlChannelIndex Integer32, + bufferControlFullStatus INTEGER, + bufferControlFullAction INTEGER, + bufferControlCaptureSliceSize Integer32, + bufferControlDownloadSliceSize Integer32, + bufferControlDownloadOffset Integer32, + bufferControlMaxOctetsRequested Integer32, + bufferControlMaxOctetsGranted Integer32, + bufferControlCapturedPackets Integer32, + bufferControlTurnOnTime TimeTicks, + bufferControlOwner OwnerString, + bufferControlStatus EntryStatus +} + +bufferControlIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry + in the bufferControl table. The value of this + index shall never be zero. Each such + entry defines one set of packets that is + captured and controlled by one or more filters." + ::= { bufferControlEntry 1 } + +bufferControlChannelIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "An index that identifies the channel that is the + source of packets for this bufferControl table. + The channel identified by a particular value of this + index is the same as identified by the same value of + the channelIndex object. + + This object may not be modified if the associated + bufferControlStatus object is equal to valid(1)." + ::= { bufferControlEntry 2 } + +bufferControlFullStatus OBJECT-TYPE + SYNTAX INTEGER { + spaceAvailable(1), + full(2) + } + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "This object shows whether the buffer has room to + accept new packets or if it is full. + + If the status is spaceAvailable(1), the buffer is + accepting new packets normally. If the status is + full(2) and the associated bufferControlFullAction + object is wrapWhenFull, the buffer is accepting new + packets by deleting enough of the oldest packets + to make room for new ones as they arrive. Otherwise, + if the status is full(2) and the + bufferControlFullAction object is lockWhenFull, + then the buffer has stopped collecting packets. + + When this object is set to full(2) the probe must + not later set it to spaceAvailable(1) except in the + case of a significant gain in resources such as + an increase of bufferControlOctetsGranted. In + particular, the wrap-mode action of deleting old + packets to make room for newly arrived packets + must not affect the value of this object." + ::= { bufferControlEntry 3 } + +bufferControlFullAction OBJECT-TYPE + SYNTAX INTEGER { + lockWhenFull(1), + wrapWhenFull(2) -- FIFO + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "Controls the action of the buffer when it + reaches the full status. When in the lockWhenFull(1) + state and a packet is added to the buffer that + fills the buffer, the bufferControlFullStatus will + be set to full(2) and this buffer will stop capturing + packets." + ::= { bufferControlEntry 4 } + +bufferControlCaptureSliceSize OBJECT-TYPE + SYNTAX Integer32 + UNITS "Octets" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The maximum number of octets of each packet + that will be saved in this capture buffer. + For example, if a 1500 octet packet is received by + the probe and this object is set to 500, then only + 500 octets of the packet will be stored in the + associated capture buffer. If this variable is set + to 0, the capture buffer will save as many octets + as is possible. + + This object may not be modified if the associated + bufferControlStatus object is equal to valid(1)." + DEFVAL { 100 } + ::= { bufferControlEntry 5 } + +bufferControlDownloadSliceSize OBJECT-TYPE + SYNTAX Integer32 + UNITS "Octets" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The maximum number of octets of each packet + in this capture buffer that will be returned in + an SNMP retrieval of that packet. For example, + if 500 octets of a packet have been stored in the + associated capture buffer, the associated + bufferControlDownloadOffset is 0, and this + object is set to 100, then the captureBufferPacket + object that contains the packet will contain only + the first 100 octets of the packet. + + A prudent manager will take into account possible + interoperability or fragmentation problems that may + occur if the download slice size is set too large. + In particular, conformant SNMP implementations are not + required to accept messages whose length exceeds 484 + octets, although they are encouraged to support larger + datagrams whenever feasible." + DEFVAL { 100 } + ::= { bufferControlEntry 6 } + +bufferControlDownloadOffset OBJECT-TYPE + SYNTAX Integer32 + UNITS "Octets" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The offset of the first octet of each packet + in this capture buffer that will be returned in + an SNMP retrieval of that packet. For example, + if 500 octets of a packet have been stored in the + associated capture buffer and this object is set to + 100, then the captureBufferPacket object that + contains the packet will contain bytes starting + 100 octets into the packet." + DEFVAL { 0 } + ::= { bufferControlEntry 7 } + +bufferControlMaxOctetsRequested OBJECT-TYPE + SYNTAX Integer32 + UNITS "Octets" + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The requested maximum number of octets to be + saved in this captureBuffer, including any + implementation-specific overhead. If this variable + is set to -1, the capture buffer will save as many + octets as is possible. + + When this object is created or modified, the probe + should set bufferControlMaxOctetsGranted as closely + to this object as is possible for the particular probe + implementation and available resources. However, if + the object has the special value of -1, the probe + must set bufferControlMaxOctetsGranted to -1." + DEFVAL { -1 } + ::= { bufferControlEntry 8 } + +bufferControlMaxOctetsGranted OBJECT-TYPE + SYNTAX Integer32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The maximum number of octets that can be + saved in this captureBuffer, including overhead. + If this variable is -1, the capture buffer will save + as many octets as possible. + + When the bufferControlMaxOctetsRequested object is + created or modified, the probe should set this object + as closely to the requested value as is possible for the + particular probe implementation and available resources. + However, if the request object has the special value + of -1, the probe must set this object to -1. + + The probe must not lower this value except as a result of + a modification to the associated + bufferControlMaxOctetsRequested object. + + When this maximum number of octets is reached + and a new packet is to be added to this + capture buffer and the corresponding + bufferControlFullAction is set to wrapWhenFull(2), + enough of the oldest packets associated with this + capture buffer shall be deleted by the agent so + that the new packet can be added. If the corresponding + bufferControlFullAction is set to lockWhenFull(1), + the new packet shall be discarded. In either case, + the probe must set bufferControlFullStatus to + full(2). + + When the value of this object changes to a value less + than the current value, entries are deleted from + the captureBufferTable associated with this + bufferControlEntry. Enough of the + oldest of these captureBufferEntries shall be + deleted by the agent so that the number of octets + used remains less than or equal to the new value of + this object. + + When the value of this object changes to a value greater + than the current value, the number of associated + captureBufferEntries may be allowed to grow." + ::= { bufferControlEntry 9 } + +bufferControlCapturedPackets OBJECT-TYPE + SYNTAX Integer32 + UNITS "Packets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of packets currently in this captureBuffer." + ::= { bufferControlEntry 10 } + +bufferControlTurnOnTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime when this capture buffer was + first turned on." + ::= { bufferControlEntry 11 } + +bufferControlOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it." + ::= { bufferControlEntry 12 } + +bufferControlStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this buffer Control Entry." + ::= { bufferControlEntry 13 } + +captureBufferTable OBJECT-TYPE + SYNTAX SEQUENCE OF CaptureBufferEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of packets captured off of a channel." + ::= { capture 2 } + +captureBufferEntry OBJECT-TYPE + SYNTAX CaptureBufferEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A packet captured off of an attached network. As an + example, an instance of the captureBufferPacketData + object might be named captureBufferPacketData.3.1783" + INDEX { captureBufferControlIndex, captureBufferIndex } + ::= { captureBufferTable 1 } + +CaptureBufferEntry ::= SEQUENCE { + captureBufferControlIndex Integer32, + captureBufferIndex Integer32, + captureBufferPacketID Integer32, + captureBufferPacketData OCTET STRING, + captureBufferPacketLength Integer32, + captureBufferPacketTime Integer32, + captureBufferPacketStatus Integer32 +} + +captureBufferControlIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The index of the bufferControlEntry with which + this packet is associated." + ::= { captureBufferEntry 1 } + +captureBufferIndex OBJECT-TYPE + SYNTAX Integer32 (1..2147483647) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry + in the captureBuffer table associated with a + particular bufferControlEntry. This index will + start at 1 and increase by one for each new packet + added with the same captureBufferControlIndex. + + Should this value reach 2147483647, the next packet + added with the same captureBufferControlIndex shall + cause this value to wrap around to 1." + ::= { captureBufferEntry 2 } + +captureBufferPacketID OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that describes the order of packets + that are received on a particular interface. + The packetID of a packet captured on an + interface is defined to be greater than the + packetID's of all packets captured previously on + the same interface. As the captureBufferPacketID + object has a maximum positive value of 2^31 - 1, + any captureBufferPacketID object shall have the + value of the associated packet's packetID mod 2^31." + ::= { captureBufferEntry 3 } + +captureBufferPacketData OBJECT-TYPE + SYNTAX OCTET STRING + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The data inside the packet, starting at the beginning + of the packet plus any offset specified in the + associated bufferControlDownloadOffset, including any + link level headers. The length of the data in this object + is the minimum of the length of the captured packet minus + the offset, the length of the associated + bufferControlCaptureSliceSize minus the offset, and the + associated bufferControlDownloadSliceSize. If this minimum + is less than zero, this object shall have a length of zero." + ::= { captureBufferEntry 4 } + +captureBufferPacketLength OBJECT-TYPE + SYNTAX Integer32 + UNITS "Octets" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The actual length (off the wire) of the packet stored + in this entry, including FCS octets." + ::= { captureBufferEntry 5 } + +captureBufferPacketTime OBJECT-TYPE + SYNTAX Integer32 + UNITS "Milliseconds" + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The number of milliseconds that had passed since + this capture buffer was first turned on when this + packet was captured." + ::= { captureBufferEntry 6 } + +captureBufferPacketStatus OBJECT-TYPE + SYNTAX Integer32 + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "A value which indicates the error status of this packet. + + The value of this object is defined in the same way as + filterPktStatus. The value is a sum. This sum + initially takes the value zero. Then, for each + error, E, that has been discovered in this packet, + 2 raised to a value representing E is added to the sum. + + The errors defined for a packet captured off of an + Ethernet interface are as follows: + + bit # Error + 0 Packet is longer than 1518 octets + 1 Packet is shorter than 64 octets + 2 Packet experienced a CRC or Alignment error + 3 First packet in this capture buffer after + it was detected that some packets were + not processed correctly. + 4 Packet's order in buffer is only approximate + (May only be set for packets sent from + the probe) + + For example, an Ethernet fragment would have a + value of 6 (2^1 + 2^2). + + As this MIB is expanded to new media types, this object + will have other media-specific errors defined." + ::= { captureBufferEntry 7 } + +-- The Event Group + +-- Implementation of the Event group is optional. +-- Consult the MODULE-COMPLIANCE macro for the authoritative +-- conformance information for this MIB. +-- +-- The Event group controls the generation and notification +-- of events from this device. Each entry in the eventTable +-- describes the parameters of the event that can be triggered. +-- Each event entry is fired by an associated condition located +-- elsewhere in the MIB. An event entry may also be associated +-- with a function elsewhere in the MIB that will be executed +-- when the event is generated. For example, a channel may +-- be turned on or off by the firing of an event. +-- +-- Each eventEntry may optionally specify that a log entry +-- be created on its behalf whenever the event occurs. +-- Each entry may also specify that notification should +-- occur by way of SNMP trap messages. In this case, the +-- community for the trap message is given in the associated +-- eventCommunity object. The enterprise and specific trap +-- fields of the trap are determined by the condition that +-- triggered the event. Two traps are defined: risingAlarm and +-- fallingAlarm. If the eventTable is triggered by a condition +-- specified elsewhere, the enterprise and specific trap fields +-- must be specified for traps generated for that condition. + +eventTable OBJECT-TYPE + SYNTAX SEQUENCE OF EventEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of events to be generated." + ::= { event 1 } + +eventEntry OBJECT-TYPE + SYNTAX EventEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A set of parameters that describe an event to be generated + when certain conditions are met. As an example, an instance + of the eventLastTimeSent object might be named + eventLastTimeSent.6" + INDEX { eventIndex } + ::= { eventTable 1 } + +EventEntry ::= SEQUENCE { + eventIndex Integer32, + eventDescription DisplayString, + eventType INTEGER, + eventCommunity OCTET STRING, + eventLastTimeSent TimeTicks, + eventOwner OwnerString, + eventStatus EntryStatus +} + +eventIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry in the + event table. Each such entry defines one event that + is to be generated when the appropriate conditions + occur." + ::= { eventEntry 1 } + +eventDescription OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..127)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "A comment describing this event entry." + ::= { eventEntry 2 } + +eventType OBJECT-TYPE + SYNTAX INTEGER { + none(1), + log(2), + snmptrap(3), -- send an SNMP trap + logandtrap(4) + } + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The type of notification that the probe will make + about this event. In the case of log, an entry is + made in the log table for each event. In the case of + snmp-trap, an SNMP trap is sent to one or more + management stations." + ::= { eventEntry 3 } + +eventCommunity OBJECT-TYPE + SYNTAX OCTET STRING (SIZE (0..127)) + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "If an SNMP trap is to be sent, it will be sent to + the SNMP community specified by this octet string." + ::= { eventEntry 4 } + +eventLastTimeSent OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime at the time this event + entry last generated an event. If this entry has + not generated any events, this value will be + zero." + ::= { eventEntry 5 } + +eventOwner OBJECT-TYPE + SYNTAX OwnerString + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The entity that configured this entry and is therefore + using the resources assigned to it. + + If this object contains a string starting with 'monitor' + and has associated entries in the log table, all connected + management stations should retrieve those log entries, + as they may have significance to all management stations + connected to this device" + ::= { eventEntry 6 } + +eventStatus OBJECT-TYPE + SYNTAX EntryStatus + MAX-ACCESS read-create + STATUS current + DESCRIPTION + "The status of this event entry. + + If this object is not equal to valid(1), all associated + log entries shall be deleted by the agent." + ::= { eventEntry 7 } + +-- +logTable OBJECT-TYPE + SYNTAX SEQUENCE OF LogEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A list of events that have been logged." + ::= { event 2 } + +logEntry OBJECT-TYPE + SYNTAX LogEntry + MAX-ACCESS not-accessible + STATUS current + DESCRIPTION + "A set of data describing an event that has been + logged. For example, an instance of the logDescription + object might be named logDescription.6.47" + INDEX { logEventIndex, logIndex } + ::= { logTable 1 } + +LogEntry ::= SEQUENCE { + logEventIndex Integer32, + logIndex Integer32, + logTime TimeTicks, + logDescription DisplayString +} + +logEventIndex OBJECT-TYPE + SYNTAX Integer32 (1..65535) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The event entry that generated this log + entry. The log identified by a particular + value of this index is associated with the same + eventEntry as identified by the same value + of eventIndex." + ::= { logEntry 1 } + +logIndex OBJECT-TYPE + SYNTAX Integer32 (1..2147483647) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An index that uniquely identifies an entry + in the log table amongst those generated by the + same eventEntries. These indexes are + assigned beginning with 1 and increase by one + with each new log entry. The association + between values of logIndex and logEntries + is fixed for the lifetime of each logEntry. + The agent may choose to delete the oldest + instances of logEntry as required because of + lack of memory. It is an implementation-specific + matter as to when this deletion may occur." + ::= { logEntry 2 } + +logTime OBJECT-TYPE + SYNTAX TimeTicks + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "The value of sysUpTime when this log entry was created." + ::= { logEntry 3 } + +logDescription OBJECT-TYPE + SYNTAX DisplayString (SIZE (0..255)) + MAX-ACCESS read-only + STATUS current + DESCRIPTION + "An implementation dependent description of the + event that activated this log entry." + ::= { logEntry 4 } + +-- Remote Network Monitoring Traps + +rmonEventsV2 OBJECT-IDENTITY + STATUS current + DESCRIPTION "Definition point for RMON notifications." + ::= { rmon 0 } + +risingAlarm NOTIFICATION-TYPE + OBJECTS { alarmIndex, alarmVariable, alarmSampleType, + alarmValue, alarmRisingThreshold } + STATUS current + DESCRIPTION + "The SNMP trap that is generated when an alarm + entry crosses its rising threshold and generates + an event that is configured for sending SNMP + traps." + ::= { rmonEventsV2 1 } + +fallingAlarm NOTIFICATION-TYPE + OBJECTS { alarmIndex, alarmVariable, alarmSampleType, + alarmValue, alarmFallingThreshold } + STATUS current + DESCRIPTION + "The SNMP trap that is generated when an alarm + entry crosses its falling threshold and generates + an event that is configured for sending SNMP + traps." + ::= { rmonEventsV2 2 } + +-- Conformance information + +rmonCompliances OBJECT IDENTIFIER ::= { rmonConformance 9 } +rmonGroups OBJECT IDENTIFIER ::= { rmonConformance 10 } + +-- Compliance Statements +rmonCompliance MODULE-COMPLIANCE + STATUS current + DESCRIPTION + "The requirements for conformance to the RMON MIB. At least + one of the groups in this module must be implemented to + conform to the RMON MIB. Implementations of this MIB + must also implement the system group of MIB-II [16] and the + IF-MIB [17]." + MODULE -- this module + + GROUP rmonEtherStatsGroup + DESCRIPTION + "The RMON Ethernet Statistics Group is optional." + + GROUP rmonHistoryControlGroup + DESCRIPTION + "The RMON History Control Group is optional." + + GROUP rmonEthernetHistoryGroup + DESCRIPTION + "The RMON Ethernet History Group is optional." + + GROUP rmonAlarmGroup + DESCRIPTION + "The RMON Alarm Group is optional." + + GROUP rmonHostGroup + DESCRIPTION + "The RMON Host Group is mandatory when the + rmonHostTopNGroup is implemented." + + GROUP rmonHostTopNGroup + DESCRIPTION + "The RMON Host Top N Group is optional." + + GROUP rmonMatrixGroup + DESCRIPTION + "The RMON Matrix Group is optional." + + GROUP rmonFilterGroup + DESCRIPTION + "The RMON Filter Group is mandatory when the + rmonPacketCaptureGroup is implemented." + + GROUP rmonPacketCaptureGroup + DESCRIPTION + "The RMON Packet Capture Group is optional." + + GROUP rmonEventGroup + DESCRIPTION + "The RMON Event Group is mandatory when the + rmonAlarmGroup is implemented." + ::= { rmonCompliances 1 } + + rmonEtherStatsGroup OBJECT-GROUP + OBJECTS { + etherStatsIndex, etherStatsDataSource, + etherStatsDropEvents, etherStatsOctets, etherStatsPkts, + etherStatsBroadcastPkts, etherStatsMulticastPkts, + etherStatsCRCAlignErrors, etherStatsUndersizePkts, + etherStatsOversizePkts, etherStatsFragments, + etherStatsJabbers, etherStatsCollisions, + etherStatsPkts64Octets, etherStatsPkts65to127Octets, + etherStatsPkts128to255Octets, + etherStatsPkts256to511Octets, + etherStatsPkts512to1023Octets, + etherStatsPkts1024to1518Octets, + etherStatsOwner, etherStatsStatus + } + STATUS current + DESCRIPTION + "The RMON Ethernet Statistics Group." + ::= { rmonGroups 1 } + + rmonHistoryControlGroup OBJECT-GROUP + OBJECTS { + historyControlIndex, historyControlDataSource, + historyControlBucketsRequested, + historyControlBucketsGranted, historyControlInterval, + historyControlOwner, historyControlStatus + } + STATUS current + DESCRIPTION + "The RMON History Control Group." + ::= { rmonGroups 2 } + + rmonEthernetHistoryGroup OBJECT-GROUP + OBJECTS { + etherHistoryIndex, etherHistorySampleIndex, + etherHistoryIntervalStart, etherHistoryDropEvents, + etherHistoryOctets, etherHistoryPkts, + etherHistoryBroadcastPkts, etherHistoryMulticastPkts, + etherHistoryCRCAlignErrors, etherHistoryUndersizePkts, + etherHistoryOversizePkts, etherHistoryFragments, + etherHistoryJabbers, etherHistoryCollisions, + etherHistoryUtilization + } + STATUS current + DESCRIPTION + "The RMON Ethernet History Group." + ::= { rmonGroups 3 } + + rmonAlarmGroup OBJECT-GROUP + OBJECTS { + alarmIndex, alarmInterval, alarmVariable, + alarmSampleType, alarmValue, alarmStartupAlarm, + alarmRisingThreshold, alarmFallingThreshold, + alarmRisingEventIndex, alarmFallingEventIndex, + alarmOwner, alarmStatus + } + STATUS current + DESCRIPTION + "The RMON Alarm Group." + ::= { rmonGroups 4 } + + rmonHostGroup OBJECT-GROUP + OBJECTS { + hostControlIndex, hostControlDataSource, + hostControlTableSize, hostControlLastDeleteTime, + hostControlOwner, hostControlStatus, + hostAddress, hostCreationOrder, hostIndex, + hostInPkts, hostOutPkts, hostInOctets, + hostOutOctets, hostOutErrors, hostOutBroadcastPkts, + hostOutMulticastPkts, hostTimeAddress, + hostTimeCreationOrder, hostTimeIndex, + hostTimeInPkts, hostTimeOutPkts, hostTimeInOctets, + hostTimeOutOctets, hostTimeOutErrors, + hostTimeOutBroadcastPkts, hostTimeOutMulticastPkts + } + STATUS current + DESCRIPTION + "The RMON Host Group." + ::= { rmonGroups 5 } + + rmonHostTopNGroup OBJECT-GROUP + OBJECTS { + hostTopNControlIndex, hostTopNHostIndex, + hostTopNRateBase, hostTopNTimeRemaining, + hostTopNDuration, hostTopNRequestedSize, + hostTopNGrantedSize, hostTopNStartTime, + hostTopNOwner, hostTopNStatus, + hostTopNReport, hostTopNIndex, + hostTopNAddress, hostTopNRate + } + STATUS current + DESCRIPTION + "The RMON Host Top 'N' Group." + ::= { rmonGroups 6 } + + rmonMatrixGroup OBJECT-GROUP + OBJECTS { + matrixControlIndex, matrixControlDataSource, + matrixControlTableSize, matrixControlLastDeleteTime, + matrixControlOwner, matrixControlStatus, + matrixSDSourceAddress, matrixSDDestAddress, + matrixSDIndex, matrixSDPkts, + matrixSDOctets, matrixSDErrors, + matrixDSSourceAddress, matrixDSDestAddress, + matrixDSIndex, matrixDSPkts, + matrixDSOctets, matrixDSErrors + } + STATUS current + DESCRIPTION + "The RMON Matrix Group." + ::= { rmonGroups 7 } + + rmonFilterGroup OBJECT-GROUP + OBJECTS { + filterIndex, filterChannelIndex, filterPktDataOffset, + filterPktData, filterPktDataMask, + filterPktDataNotMask, filterPktStatus, + filterPktStatusMask, filterPktStatusNotMask, + filterOwner, filterStatus, + channelIndex, channelIfIndex, channelAcceptType, + channelDataControl, channelTurnOnEventIndex, + channelTurnOffEventIndex, channelEventIndex, + channelEventStatus, channelMatches, + channelDescription, channelOwner, channelStatus + } + STATUS current + DESCRIPTION + "The RMON Filter Group." + ::= { rmonGroups 8 } + + rmonPacketCaptureGroup OBJECT-GROUP + OBJECTS { + bufferControlIndex, bufferControlChannelIndex, + bufferControlFullStatus, bufferControlFullAction, + bufferControlCaptureSliceSize, + bufferControlDownloadSliceSize, + bufferControlDownloadOffset, + bufferControlMaxOctetsRequested, + bufferControlMaxOctetsGranted, + bufferControlCapturedPackets, + bufferControlTurnOnTime, + bufferControlOwner, bufferControlStatus, + captureBufferControlIndex, captureBufferIndex, + captureBufferPacketID, captureBufferPacketData, + captureBufferPacketLength, captureBufferPacketTime, + captureBufferPacketStatus + } + STATUS current + DESCRIPTION + "The RMON Packet Capture Group." + ::= { rmonGroups 9 } + + rmonEventGroup OBJECT-GROUP + OBJECTS { + eventIndex, eventDescription, eventType, + eventCommunity, eventLastTimeSent, + eventOwner, eventStatus, + logEventIndex, logIndex, logTime, + logDescription + } + STATUS current + DESCRIPTION + "The RMON Event Group." + ::= { rmonGroups 10 } + + rmonNotificationGroup NOTIFICATION-GROUP + NOTIFICATIONS { risingAlarm, fallingAlarm } + STATUS current + DESCRIPTION + "The RMON Notification Group." + ::= { rmonGroups 11 } +END diff --git a/lib/snmp/vsn.mk b/lib/snmp/vsn.mk index fb1dcb6c41..b90dbe4eef 100644 --- a/lib/snmp/vsn.mk +++ b/lib/snmp/vsn.mk @@ -18,6 +18,6 @@ # %CopyrightEnd% APPLICATION = snmp -SNMP_VSN = 4.21.7 +SNMP_VSN = 4.22.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(SNMP_VSN)$(PRE_VSN)" diff --git a/lib/ssh/doc/src/Makefile b/lib/ssh/doc/src/Makefile index 125dcf8775..38782a3b00 100644 --- a/lib/ssh/doc/src/Makefile +++ b/lib/ssh/doc/src/Makefile @@ -109,13 +109,13 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index c6c634212f..a85cada732 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -29,6 +29,108 @@ <file>notes.xml</file> </header> +<section><title>Ssh 2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + All keys in authorized_keys are considerd, wrongly only + the first one was before.</p> + <p> + Own Id: OTP-7235</p> + </item> + <item> + <p> + ssh daemon now properly handles ras host keys, in + previous versions only dsa host keys sufficed to set up a + connection.</p> + <p> + Own Id: OTP-7677</p> + </item> + <item> + <p> + ssh:shell/3 and ssh:connect/3 does not hang anymore if + connection negotiation fails</p> + <p> + Own Id: OTP-8111</p> + </item> + <item> + <p> + Improve check so that we will not try to read ssh packet + length indicator if not sure we have enough data.</p> + <p> + Own Id: OTP-8380</p> + </item> + <item> + <p> + Do not try to use user interaction when it is disabled.</p> + <p> + Own Id: OTP-9466 Aux Id: seq11886 </p> + </item> + <item> + <p> + Improved error handling of internal errors i the ssh + connection handling process</p> + <p> + Own Id: OTP-9905</p> + </item> + <item> + <p> + sftp daemon generates file handles correct</p> + <p> + Own Id: OTP-9948</p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + Document supported algorithms</p> + <p> + Own Id: OTP-8109</p> + </item> + <item> + <p> + Graceful handling of premature close from an sftp client.</p> + <p> + Own Id: OTP-9391 Aux Id: seq11838 </p> + </item> + <item> + <p> + Changed ssh implementation to use the public_key + application for all public key handling. This is also a + first step for enabling a callback API for supplying + public keys and handling keys protected with password + phrases. </p> + <p> + Additionally the test suites where improved so that they + do not copy the users keys to test server directories as + this is a security liability. Also ipv6 and file access + issues found in the process has been fixed.</p> + <p> + This change also solves OTP-7677 and OTP-7235</p> + <p> + This changes also involves some updates to public_keys + ssh-functions.</p> + <p> + Own Id: OTP-9911</p> + </item> + <item> + <p> + Added options for the ssh client to support user keys + files that are password protected.</p> + <p> + Own Id: OTP-10036 Aux Id: OTP-6400, Seq10595 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 2.0.9</title> <section><title>Improvements and New Features</title> diff --git a/lib/ssh/examples/Makefile b/lib/ssh/examples/Makefile index 5f17542fb8..84c7edac8c 100644 --- a/lib/ssh/examples/Makefile +++ b/lib/ssh/examples/Makefile @@ -69,8 +69,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 1734ae4df4..b8eecd3fa2 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -38,15 +38,17 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssh-$(VSN) # Behaviour (api) modules are first so they are compiled when # the compiler reaches a callback module using them. -MODULES= \ +BEHAVIOUR_MODULES= \ ssh_sftpd_file_api \ - ssh_key_api \ + ssh_channel \ + ssh_key_api + +MODULES= \ ssh \ ssh_sup \ sshc_sup \ sshd_sup \ ssh_connection_sup \ - ssh_channel \ ssh_connection \ ssh_connection_handler \ ssh_connection_manager \ @@ -73,11 +75,14 @@ MODULES= \ PUBLIC_HRL_FILES= ssh.hrl ssh_userauth.hrl ssh_xfer.hrl -ERL_FILES= $(MODULES:%=%.erl) +ERL_FILES= \ + $(MODULES:%=%.erl) \ + $(BEHAVIOUR_MODULES:%=%.erl) -ALL_MODULES= $(MODULES) -TARGET_FILES= $(ALL_MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET) +TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) + +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) APP_FILE= ssh.app APPUP_FILE= ssh.appup @@ -93,29 +98,29 @@ INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- -ERL_COMPILE_FLAGS += -pa$(EBIN)\ - -pz $(ERL_TOP)/lib/public_key/ebin +EXTRA_ERLC_FLAGS = +warn_unused_vars +ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ + -pz $(EBIN) \ + -pz $(ERL_TOP)/lib/public_key/ebin \ + $(EXTRA_ERLC_FLAGS) + + # ---------------------------------------------------- # Targets # ---------------------------------------------------- -debug opt: $(TARGET_FILES) +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) -debug: ERLC_FLAGS += -Ddebug +debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) clean: - rm -f $(TARGET_FILES) + rm -f $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) rm -f errs core *~ -$(TARGET_FILES): ssh.hrl - -# $(EBIN)/ssh_sftpd_file.$(EMULATOR): ERLC_FLAGS += -pa$(EBIN) -# $(EBIN)/ssh_sftpd_file.$(EMULATOR): $(EBIN)/ssh_sftpd_file_api.$(EMULATOR) - -$(APP_TARGET): $(APP_SRC) ../vsn.mk +$(APP_TARGET): $(APP_SRC) ../vsn.mk sed -e 's;%VSN%;$(VSN);' $< > $@ -$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk +$(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk sed -e 's;%VSN%;$(VSN);' $< > $@ @@ -127,12 +132,13 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(PUBLIC_HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) \ + $(APPUP_TARGET) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(PUBLIC_HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/ssh/src/ssh.appup.src b/lib/ssh/src/ssh.appup.src index 21a0582c06..0542054596 100644 --- a/lib/ssh/src/ssh.appup.src +++ b/lib/ssh/src/ssh.appup.src @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -18,26 +18,12 @@ %% {"%VSN%", - [ - {"2.0.8", [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, - {load_module, ssh_channel, 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\\.*">>, [{restart_application, ssh}]}, + {<<"1\\.*">>, [{restart_application, ssh}]} ], [ - {"2.0.8", [{load_module, ssh_sftpd_file_api, soft_purge, soft_purge, []}, - {load_module, ssh_channel, 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\\.*">>, [{restart_application, ssh}]}, + {<<"1\\.*">>, [{restart_application, ssh}]} ] }. - - diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 39c7fe329e..f4a40c81a4 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -346,8 +346,9 @@ handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{compression, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); -handle_option([{allow_user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) -> - handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +%%Backwards compatibility +handle_option([{allow_user_interaction, Value} | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option({user_interaction, Value}) | SshOptions]); handle_option([{infofun, _} = Opt | Rest],SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{connectfun, _} = Opt | Rest], SocketOptions, SshOptions) -> @@ -366,6 +367,8 @@ handle_option([{ssh_cli, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([{shell, _} = Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); +handle_option([{exec, _} = Opt | Rest], SocketOptions, SshOptions) -> + handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]); handle_option([Opt | Rest], SocketOptions, SshOptions) -> handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions). @@ -401,8 +404,9 @@ handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) -> Opt; handle_ssh_option({compression, Value} = Opt) when is_atom(Value) -> Opt; -handle_ssh_option({allow_user_interaction, Value} = Opt) when Value == true; - Value == false -> +handle_ssh_option({exec, {Module, Function, _}} = Opt) when is_atom(Module), + is_atom(Function) -> + Opt; handle_ssh_option({infofun, Value} = Opt) when is_function(Value) -> Opt; @@ -412,11 +416,12 @@ handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) -> Opt; handle_ssh_option({failfun, Value} = Opt) when is_function(Value) -> Opt; -handle_ssh_option({ip_v6_disabled, Value} = Opt) when is_function(Value) -> +handle_ssh_option({ip_v6_disabled, Value} = Opt) when Value == true; + Value == false -> Opt; handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol), - is_atom(Cb), - is_atom(ClosTag) -> + is_atom(Cb), + is_atom(ClosTag) -> Opt; handle_ssh_option({subsystems, Value} = Opt) when is_list(Value) -> Opt; @@ -495,4 +500,3 @@ verify_data(Data, Signature, Algorithm) when is_binary(Data), is_binary(Signatur Error -> Error end. - diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl index 1a4517c689..aa452a8e09 100644 --- a/lib/ssh/src/ssh_auth.erl +++ b/lib/ssh/src/ssh_auth.erl @@ -71,7 +71,7 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb, ssh_bits:install_messages(userauth_passwd_messages()), Password = case proplists:get_value(password, Opts) of undefined -> - user_interaction(Opts, IoCb); + user_interaction(IoCb); PW -> PW end, @@ -89,13 +89,10 @@ password_msg([#ssh{opts = Opts, io_cb = IoCb, Ssh) end. -user_interaction(Opts, IoCb) -> - case proplists:get_value(allow_user_interaction, Opts, true) of - true -> - IoCb:read_password("ssh password: "); - false -> - not_ok - end. +user_interaction(ssh_no_io) -> + not_ok; +user_interaction(IoCb) -> + IoCb:read_password("ssh password: "). %% See RFC 4256 for info on keyboard-interactive @@ -124,8 +121,7 @@ init_userauth_request_msg(#ssh{opts = Opts} = Ssh) -> FirstAlg = algorithm(proplists:get_value(public_key_alg, Opts, ?PREFERRED_PK_ALG)), SecondAlg = other_alg(FirstAlg), - AllowUserInt = proplists:get_value(allow_user_interaction, Opts, - true), + AllowUserInt = proplists:get_value(user_interaction, Opts, true), Prefs = method_preference(FirstAlg, SecondAlg, AllowUserInt), ssh_transport:ssh_packet(Msg, Ssh#ssh{user = User, userauth_preference = Prefs, diff --git a/lib/ssh/src/ssh_channel.erl b/lib/ssh/src/ssh_channel.erl index 7b600ed8b2..1938858420 100644 --- a/lib/ssh/src/ssh_channel.erl +++ b/lib/ssh/src/ssh_channel.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -215,7 +215,7 @@ handle_info({ssh_cm, ConnectionManager, {closed, ChannelId}}, close_sent = false} = State) -> %% To be on the safe side, i.e. the manager has already been terminated. (catch ssh_connection:close(ConnectionManager, ChannelId)), - {stop, normal, State}; + {stop, normal, State#state{close_sent = true}}; handle_info({ssh_cm, _, _} = Msg, #state{cm = ConnectionManager, channel_cb = Module, diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index 46f0c7e688..c46f799b6d 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -720,12 +720,17 @@ handle_msg(#ssh_msg_channel_request{request_type = "env"}, handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, request_type = _Other, - want_reply = WantReply}, Connection, + want_reply = WantReply}, #connection{channel_cache = Cache} = Connection, ConnectionPid, _) -> if WantReply == true -> - FailMsg = channel_failure_msg(ChannelId), - {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, - Connection}; + case ssh_channel:cache_lookup(Cache, ChannelId) of + #channel{remote_id = RemoteId} -> + FailMsg = channel_failure_msg(RemoteId), + {{replies, [{connection_reply, ConnectionPid, FailMsg}]}, + Connection}; + undefined -> %% Chanel has been closed + {noreply, Connection} + end; true -> {noreply, Connection} end; diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl index 406a042d72..e53cd4f4f7 100644 --- a/lib/ssh/src/ssh_connection_manager.erl +++ b/lib/ssh/src/ssh_connection_manager.erl @@ -144,27 +144,21 @@ adjust_window(ConnectionManager, Channel, Bytes) -> cast(ConnectionManager, {adjust_window, Channel, Bytes}). close(ConnectionManager, ChannelId) -> - try call(ConnectionManager, {close, ChannelId}) of - ok -> + case call(ConnectionManager, {close, ChannelId}) of + ok -> ok; - {error, channel_closed} -> - ok - catch - exit:{noproc, _} -> + {error, channel_closed} -> ok - end. + end. stop(ConnectionManager) -> - try call(ConnectionManager, stop) of + case call(ConnectionManager, stop) of ok -> ok; {error, channel_closed} -> ok - catch - exit:{noproc, _} -> - ok end. - + send(ConnectionManager, ChannelId, Type, Data, Timeout) -> call(ConnectionManager, {data, ChannelId, Type, Data}, Timeout). @@ -273,7 +267,7 @@ handle_call({ssh_msg, Pid, Msg}, From, disconnect_fun(Reason, SSHOpts), {stop, {shutdown, normal}, State#state{connection_state = Connection}} catch - _:Reason -> + _:Error -> {disconnect, Reason, {{replies, Replies}, Connection}} = ssh_connection:handle_msg( #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION, @@ -283,7 +277,7 @@ handle_call({ssh_msg, Pid, Msg}, From, lists:foreach(fun send_msg/1, Replies), SSHOpts = proplists:get_value(ssh_opts, Opts), disconnect_fun(Reason, SSHOpts), - {stop, {shutdown, Reason}, State#state{connection_state = Connection}} + {stop, {shutdown, Error}, State#state{connection_state = Connection}} end; handle_call({global_request, Pid, _, _, _} = Request, From, @@ -390,9 +384,10 @@ handle_call({close, ChannelId}, _, #state{connection = Pid, connection_state = #connection{channel_cache = Cache}} = State) -> case ssh_channel:cache_lookup(Cache, ChannelId) of - #channel{remote_id = Id} -> + #channel{remote_id = Id} = Channel -> send_msg({connection_reply, Pid, ssh_connection:channel_close_msg(Id)}), + ssh_channel:cache_update(Cache, Channel#channel{sent_close = true}), {reply, ok, State}; undefined -> {reply, ok, State} @@ -593,6 +588,8 @@ call(Pid, Msg, Timeout) -> {error, timeout}; exit:{normal, _} -> {error, channel_closed}; + exit:{{shutdown, _}, _} -> + {error, channel_closed}; exit:{noproc,_} -> {error, channel_closed} end. diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl index 23480f5fe3..36daf3b1ac 100644 --- a/lib/ssh/src/ssh_system_sup.erl +++ b/lib/ssh/src/ssh_system_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile index 145d5d2ad6..25072688ad 100644 --- a/lib/ssh/test/Makefile +++ b/lib/ssh/test/Makefile @@ -110,11 +110,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) ssh.spec ssh.cover $(RELSYSDIR) - $(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) ssh.spec ssh.cover "$(RELSYSDIR)" + $(INSTALL_DATA) $(HRL_FILES_NEEDED_IN_TEST) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index 012367a6df..c5019425cd 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -110,7 +110,8 @@ all() -> {group, rsa_pass_key}, {group, internal_error}, daemon_already_started, - server_password_option, server_userpassword_option]. + server_password_option, server_userpassword_option, + close]. groups() -> [{dsa_key, [], [exec, exec_compressed, shell, known_hosts]}, @@ -182,7 +183,29 @@ app_test(doc) -> app_test(Config) when is_list(Config) -> ?t:app_test(ssh), ok. +%%-------------------------------------------------------------------- +misc_ssh_options(doc) -> + ["Test that we can set some misc options not tested elsewhere, " + "some options not yet present are not decided if we should support or " + "if they need thier own test case."]; +misc_ssh_options(suite) -> + []; +misc_ssh_options(Config) when is_list(Config) -> + SystemDir = filename:join(?config(priv_dir, Config), system), + UserDir = ?config(priv_dir, Config), + + CMiscOpt0 = [{connecect_timeout, 1000}, {ip_v6_disable, false}, {user_dir, UserDir}], + CMiscOpt1 = [{connecect_timeout, infinity}, {ip_v6_disable, true}, {user_dir, UserDir}], + SMiscOpt0 = [{ip_v6_disable, false}, {user_dir, UserDir}, {system_dir, SystemDir}], + SMiscOpt1 = [{ip_v6_disable, true}, {user_dir, UserDir}, {system_dir, SystemDir}], + + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + basic_test([{client_opts, CMiscOpt0 ++ ClientOpts}, {server_opts, SMiscOpt0 ++ ServerOpts}]), + basic_test([{client_opts, CMiscOpt1 ++ ClientOpts}, {server_opts, SMiscOpt1 ++ ServerOpts}]). +%%-------------------------------------------------------------------- exec(doc) -> ["Test api function ssh_connection:exec"]; @@ -499,15 +522,52 @@ internal_error(Config) when is_list(Config) -> SystemDir = filename:join(?config(priv_dir, Config), system), UserDir = ?config(priv_dir, Config), - {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, {user_dir, UserDir}, {failfun, fun ssh_test_lib:failfun/2}]), {error,"Internal error"} = ssh:connect(Host, Port, [{silently_accept_hosts, true}, {user_dir, UserDir}, - {user_interaction, false}]). + {user_interaction, false}]), + ssh:stop_daemon(Pid). + +%%-------------------------------------------------------------------- +close(doc) -> + ["Simulate that we try to close an already closed connection"]; + +close(suite) -> + []; +close(Config) when is_list(Config) -> + SystemDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + + {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir}, + {user_dir, UserDir}, + {user_passwords, [{"vego", "morot"}]}, + {failfun, fun ssh_test_lib:failfun/2}]), + {ok, CM} = ssh:connect(Host, Port, [{silently_accept_hosts, true}, + {user_dir, UserDir}, + {user, "vego"}, + {password, "morot"}, + {user_interaction, false}]), + exit(CM, {shutdown, normal}), + ok = ssh:close(CM). + + + %%-------------------------------------------------------------------- %% Internal functions %%-------------------------------------------------------------------- + +basic_test(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + + {Pid, Host, Port} = ssh_test_lib:daemon(ServerOpts), + {ok, CM} = ssh:connect(Host, Port, ClientOpts), + ok = ssh:close(CM), + ssh:stop_daemon(Pid). diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 42f860d6ae..bff73a1b40 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.9 +SSH_VSN = 2.1 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/Makefile b/lib/ssl/doc/src/Makefile index 5d808d6727..8736913c72 100644 --- a/lib/ssl/doc/src/Makefile +++ b/lib/ssl/doc/src/Makefile @@ -117,15 +117,15 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 1e1fe0d119..a4da939d3e 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -30,7 +30,53 @@ </header> <p>This document describes the changes made to the SSL application.</p> - <section><title>SSL 5.0</title> + <section><title>SSL 5.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Robustness and improvement to distribution over SSL</p> + <p> + Fix a bug where ssl_tls_dist_proxy would crash at caller + timeout. Fix a bug where a timeout from the SSL layer + would block the distribution indefinately. Run the proxy + exclusively on the loopback interface. (Thanks to Paul + Guyot)</p> + <p> + Own Id: OTP-9915</p> + </item> + <item> + <p> + Fix setup loop of SSL TLS dist proxy</p> + <p> + Fix potential leak of processes waiting indefinately for + data from closed sockets during socket setup phase. + (Thanks to Paul Guyot)</p> + <p> + Own Id: OTP-9916</p> + </item> + <item> + <p> + Correct spelling of registered (Thanks to Richard + Carlsson)</p> + <p> + Own Id: OTP-9925</p> + </item> + <item> + <p> + Added TLS PRF function to the SSL API for generation of + additional key material from a TLS session. (Thanks to + Andreas Schultz)</p> + <p> + Own Id: OTP-10024</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 5.0</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 4910a6f1b8..5098d26a3a 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1999</year><year>2011</year> + <year>1999</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -36,12 +36,16 @@ <list type="bulleted"> <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>Supported SSL/TLS-versions are SSL-3.0 and TLS-1.0, experimental + support for TLS-1.1 and TLS-1.2 is also available (no support for elliptic curve cipher suites yet).</item> <item>For security reasons sslv2 is not supported.</item> <item>Ephemeral Diffie-Hellman cipher suites are supported but not Diffie Hellman Certificates cipher suites.</item> <item>Export cipher suites are not supported as the U.S. lifted its export restrictions in early 2000.</item> + <item>IDEA cipher suites are not supported as they have + become deprecated by the latest TLS spec so there is not any + real motivation to implement them.</item> <item>CRL and policy certificate extensions are not supported yet. </item> </list> @@ -75,7 +79,7 @@ {keyfile, path()} | {password, string()} | {cacerts, [der_encoded()]} | {cacertfile, path()} | |{dh, der_encoded()} | {dhfile, path()} | {ciphers, ciphers()} | - {ssl_imp, ssl_imp()} | {reuse_sessions, boolean()} | {reuse_session, fun()} + {ssl_imp, ssl_imp()}| {reuse_sessions, boolean()} | {reuse_session, fun()} </c></p> <p><c>transportoption() = {CallbackModule, DataTag, ClosedTag} @@ -106,7 +110,7 @@ <p><c>sslsocket() - opaque to the user. </c></p> - <p><c>protocol() = sslv3 | tlsv1 </c></p> + <p><c>protocol() = sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2' </c></p> <p><c>ciphers() = [ciphersuite()] | string() (according to old API)</c></p> @@ -122,6 +126,9 @@ <p> <c>hash() = md5 | sha </c></p> + <p><c>prf_random() = client_random | server_random + </c></p> + </section> <section> @@ -190,13 +197,13 @@ </item> <tag>{depth, integer()}</tag> - <item>Specifies the maximum - verification depth, i.e. how far in a chain of certificates the - verification process can proceed before the verification is - considered to fail. Peer certificate = 0, CA certificate = 1, - higher level CA certificate = 2, etc. The value 2 thus means - that a chain can at most contain peer cert, CA cert, next CA - cert, and an additional CA cert. The default value is 1. + <item> + The depth is the maximum number of non-self-issued + intermediate certificates that may follow the peer certificate + in a valid certification path. So if depth is 0 the PEER must + be signed by the trusted ROOT-CA directly, if 1 the path can + be PEER, CA, ROOT-CA, if it is 2 PEER, CA, CA, ROOT-CA and so + on. The default value is 1. </item> <tag>{verify_fun, {Verifyfun :: fun(), InitialUserState :: term()}}</tag> @@ -561,6 +568,26 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} | </func> <func> + <name>prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name> + <fsummary>Use a sessions pseudo random function to generate key material.</fsummary> + <type> + <v>Socket = sslsocket()</v> + <v>Secret = binary() | master_secret</v> + <v>Label = binary()</v> + <v>Seed = [binary() | prf_random()]</v> + <v>WantedLength = non_neg_integer()</v> + </type> + <desc> + <p>Use the pseudo random function (PRF) of a TLS session to generate + additional key material. It either takes user generated values for + <c>Secret</c> and <c>Seed</c> or atoms directing it use a specific + value from the session security parameters.</p> + <p>This function can only be used with TLS connections, <c>{error, undefined}</c> + is returned for SSLv3 connections.</p> + </desc> + </func> + + <func> <name>renegotiate(Socket) -> ok | {error, Reason}</name> <fsummary> Initiates a new handshake.</fsummary> <type> diff --git a/lib/ssl/examples/certs/Makefile b/lib/ssl/examples/certs/Makefile index a4f067ade6..549f11d709 100644 --- a/lib/ssl/examples/certs/Makefile +++ b/lib/ssl/examples/certs/Makefile @@ -54,8 +54,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/certs + $(INSTALL_DIR) "$(RELSYSDIR)/examples/certs" tar cf - etc | \ - (cd $(RELSYSDIR)/examples/certs; tar xf -) - chmod -R ug+rw $(RELSYSDIR)/examples + (cd "$(RELSYSDIR)/examples/certs"; tar xf -) + chmod -R ug+rw "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/ssl/examples/src/Makefile b/lib/ssl/examples/src/Makefile index c5f31b689c..420634c97e 100644 --- a/lib/ssl/examples/src/Makefile +++ b/lib/ssl/examples/src/Makefile @@ -63,10 +63,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples/src - $(INSTALL_DIR) $(RELSYSDIR)/examples/ebin - (cd ..; tar cf - src ebin | (cd $(RELSYSDIR)/examples; tar xf -)) - chmod -R ug+w $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples/src" + $(INSTALL_DIR) "$(RELSYSDIR)/examples/ebin" + (cd ..; tar cf - src ebin | (cd "$(RELSYSDIR)/examples"; tar xf -)) + chmod -R ug+w "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile index dc69b53b28..a08444b8dd 100644 --- a/lib/ssl/src/Makefile +++ b/lib/ssl/src/Makefile @@ -37,6 +37,9 @@ RELSYSDIR = $(RELEASE_PATH)/lib/ssl-$(VSN) # Common Macros # ---------------------------------------------------- +BEHAVIOUR_MODULES= \ + ssl_session_cache_api + MODULES= \ ssl \ ssl_alert \ @@ -53,7 +56,6 @@ MODULES= \ ssl_handshake \ ssl_manager \ ssl_session \ - ssl_session_cache_api \ ssl_session_cache \ ssl_record \ ssl_ssl2 \ @@ -66,10 +68,15 @@ INTERNAL_HRL_FILES = \ ssl_alert.hrl ssl_cipher.hrl ssl_handshake.hrl ssl_internal.hrl \ ssl_record.hrl -ERL_FILES= $(MODULES:%=%.erl) +ERL_FILES= \ + $(MODULES:%=%.erl) \ + $(BEHAVIOUR_MODULES:%=%.erl) + TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) +BEHAVIOUR_TARGET_FILES= $(BEHAVIOUR_MODULES:%=$(EBIN)/%.$(EMULATOR)) + APP_FILE= ssl.app APPUP_FILE= ssl.appup @@ -83,6 +90,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE) # ---------------------------------------------------- EXTRA_ERLC_FLAGS = +warn_unused_vars ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ + -pz $(EBIN) \ -pz $(ERL_TOP)/lib/public_key/ebin \ $(EXTRA_ERLC_FLAGS) -DVSN=\"$(VSN)\" @@ -91,6 +99,8 @@ ERL_COMPILE_FLAGS += -I$(ERL_TOP)/lib/kernel/src \ # Targets # ---------------------------------------------------- +$(TARGET_FILES): $(BEHAVIOUR_TARGET_FILES) + debug opt: $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) clean: @@ -105,23 +115,18 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk docs: + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) \ - $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(BEHAVIOUR_TARGET_FILES) $(TARGET_FILES) $(APP_TARGET) \ + $(APPUP_TARGET) "$(RELSYSDIR)/ebin" release_docs_spec: - - - - - - diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl index bc395cb6d5..57c859bf24 100644 --- a/lib/ssl/src/inet_tls_dist.erl +++ b/lib/ssl/src/inet_tls_dist.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 diff --git a/lib/ssl/src/ssl.appup.src b/lib/ssl/src/ssl.appup.src index 1b07e76d6a..76550fa04b 100644 --- a/lib/ssl/src/ssl.appup.src +++ b/lib/ssl/src/ssl.appup.src @@ -1,23 +1,15 @@ %% -*- erlang -*- {"%VSN%", [ - {"4.1.6", [{restart_application, ssl}]}, - {"4.1.5", [{restart_application, ssl}]}, - {"4.1.4", [{restart_application, ssl}]}, - {"4.1.3", [{restart_application, ssl}]}, - {"4.1.2", [{restart_application, ssl}]}, - {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, - {"4.0.1", [{restart_application, ssl}]} + {"5.0.1", [{restart_application, ssl}]}, + {"5.0", [{restart_application, ssl}]}, + {<<"4\\.*">>, [{restart_application, ssl}]}, + {<<"3\\.*">>, [{restart_application, ssl}]} ], [ - {"4.1.6", [{restart_application, ssl}]}, - {"4.1.5", [{restart_application, ssl}]}, - {"4.1.4", [{restart_application, ssl}]}, - {"4.1.3", [{restart_application, ssl}]}, - {"4.1.2", [{restart_application, ssl}]}, - {"4.1.1", [{restart_application, ssl}]}, - {"4.1", [{restart_application, ssl}]}, - {"4.0.1", [{restart_application, ssl}]} + {"5.0.1", [{restart_application, ssl}]}, + {"5.0", [{restart_application, ssl}]}, + {<<"4\\.*">>, [{restart_application, ssl}]}, + {<<"3\\.*">>, [{restart_application, ssl}]} ]}. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index d0693445e0..40d933a256 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -25,12 +25,13 @@ -export([start/0, start/1, stop/0, transport_accept/1, transport_accept/2, ssl_accept/1, ssl_accept/2, ssl_accept/3, - cipher_suites/0, cipher_suites/1, close/1, shutdown/2, + cipher_suites/0, cipher_suites/1, suite_definition/1, + close/1, shutdown/2, connect/3, connect/2, connect/4, connection_info/1, controlling_process/2, listen/2, pid/1, peername/1, peercert/1, recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1, versions/0, session_info/1, format_error/1, - renegotiate/1]). + renegotiate/1, prf/5, clear_pem_cache/0, random_bytes/1]). -deprecated({pid, 1, next_major_release}). @@ -40,6 +41,12 @@ -include_lib("public_key/include/public_key.hrl"). +%% Visible in API +-export_type([connect_option/0, listen_option/0, ssl_option/0, transport_option/0, + erl_cipher_suite/0, %% From ssl_cipher.hrl + tls_atom_version/0, %% From ssl_internal.hrl + prf_random/0]). + -record(config, {ssl, %% SSL parameters inet_user, %% User set inet options emulated, %% #socket_option{} emulated @@ -67,7 +74,7 @@ -type ssl_imp() :: new | old. -type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}}. - +-type prf_random() :: client_random | server_random. %%-------------------------------------------------------------------- -spec start() -> ok | {error, reason()}. @@ -298,6 +305,15 @@ peercert(#sslsocket{pid = Pid}) -> end. %%-------------------------------------------------------------------- +-spec suite_definition(cipher_suite()) -> erl_cipher_suite(). +%% +%% Description: Return erlang cipher suite definition. +%%-------------------------------------------------------------------- +suite_definition(S) -> + {KeyExchange, Cipher, Hash, _} = ssl_cipher:suite_definition(S), + {KeyExchange, Cipher, Hash}. + +%%-------------------------------------------------------------------- -spec cipher_suites() -> [erl_cipher_suite()]. -spec cipher_suites(erlang | openssl) -> [erl_cipher_suite()] | [string()]. @@ -308,7 +324,7 @@ cipher_suites() -> cipher_suites(erlang) -> Version = ssl_record:highest_protocol_version([]), - [ssl_cipher:suite_definition(S) || S <- ssl_cipher:suites(Version)]; + [suite_definition(S) || S <- ssl_cipher:suites(Version)]; cipher_suites(openssl) -> Version = ssl_record:highest_protocol_version([]), @@ -402,7 +418,7 @@ session_info(#sslsocket{pid = Pid, fd = new_ssl}) -> versions() -> Vsns = ssl_record:supported_protocol_versions(), SupportedVsns = [ssl_record:protocol_version(Vsn) || Vsn <- Vsns], - AvailableVsns = ?DEFAULT_SUPPORTED_VERSIONS, + AvailableVsns = ?ALL_SUPPORTED_VERSIONS, [{ssl_app, ?VSN}, {supported, SupportedVsns}, {available, AvailableVsns}]. @@ -414,6 +430,26 @@ versions() -> renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> ssl_connection:renegotiation(Pid). +%%-------------------------------------------------------------------- +-spec prf(#sslsocket{}, binary() | 'master_secret', binary(), + binary() | prf_random(), non_neg_integer()) -> + {ok, binary()} | {error, reason()}. +%% +%% Description: use a ssl sessions TLS PRF to generate key material +%%-------------------------------------------------------------------- +prf(#sslsocket{pid = Pid, fd = new_ssl}, + Secret, Label, Seed, WantedLength) -> + ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength). + + +%%-------------------------------------------------------------------- +-spec clear_pem_cache() -> ok. +%% +%% Description: Clear the PEM cache +%%-------------------------------------------------------------------- +clear_pem_cache() -> + ssl_manager:clear_pem_cache(). + %%--------------------------------------------------------------- -spec format_error({error, term()}) -> list(). %% @@ -448,6 +484,23 @@ format_error(Error) -> Other end. +%%-------------------------------------------------------------------- +-spec random_bytes(integer()) -> binary(). + +%% +%% Description: Generates cryptographically secure random sequence if possible +%% fallbacks on pseudo random function +%%-------------------------------------------------------------------- +random_bytes(N) -> + try crypto:strong_rand_bytes(N) of + RandBytes -> + RandBytes + catch + error:low_entropy -> + crypto:rand_bytes(N) + end. + + %%%-------------------------------------------------------------- %%% Internal functions %%%-------------------------------------------------------------------- @@ -515,7 +568,7 @@ handle_options(Opts0, _Role) -> throw({error, {eoptions, {verify, Value}}}) end, - CertFile = handle_option(certfile, Opts, ""), + CertFile = handle_option(certfile, Opts, <<>>), SSLOptions = #ssl_options{ versions = handle_option(versions, Opts, []), @@ -602,8 +655,12 @@ validate_option(depth, Value) when is_integer(Value), validate_option(cert, Value) when Value == undefined; is_binary(Value) -> Value; -validate_option(certfile, Value) when Value == undefined; is_list(Value) -> +validate_option(certfile, undefined = Value) -> + Value; +validate_option(certfile, Value) when is_binary(Value) -> Value; +validate_option(certfile, Value) when is_list(Value) -> + list_to_binary(Value); validate_option(key, undefined) -> undefined; @@ -614,8 +671,13 @@ validate_option(key, {KeyType, Value}) when is_binary(Value), KeyType == 'DSAPrivateKey'; KeyType == 'PrivateKeyInfo' -> {KeyType, Value}; -validate_option(keyfile, Value) when is_list(Value) -> + +validate_option(keyfile, undefined) -> + <<>>; +validate_option(keyfile, Value) when is_binary(Value) -> Value; +validate_option(keyfile, Value) when is_list(Value), Value =/= "" -> + list_to_binary(Value); validate_option(password, Value) when is_list(Value) -> Value; @@ -625,16 +687,20 @@ validate_option(cacerts, Value) when Value == undefined; %% certfile must be present in some cases otherwhise it can be set %% to the empty string. validate_option(cacertfile, undefined) -> - ""; -validate_option(cacertfile, Value) when is_list(Value), Value =/= "" -> + <<>>; +validate_option(cacertfile, Value) when is_binary(Value) -> Value; +validate_option(cacertfile, Value) when is_list(Value), Value =/= ""-> + list_to_binary(Value); validate_option(dh, Value) when Value == undefined; is_binary(Value) -> Value; validate_option(dhfile, undefined = Value) -> Value; -validate_option(dhfile, Value) when is_list(Value), Value =/= "" -> +validate_option(dhfile, Value) when is_binary(Value) -> Value; +validate_option(dhfile, Value) when is_list(Value), Value =/= "" -> + list_to_binary(Value); validate_option(ciphers, Value) when is_list(Value) -> Version = ssl_record:highest_protocol_version([]), try cipher_suites(Version, Value) @@ -670,7 +736,8 @@ validate_option(Opt, Value) -> validate_versions([], Versions) -> Versions; -validate_versions([Version | Rest], Versions) when Version == 'tlsv1.1'; +validate_versions([Version | Rest], Versions) when Version == 'tlsv1.2'; + Version == 'tlsv1.1'; Version == tlsv1; Version == sslv3 -> validate_versions(Rest, Versions); diff --git a/lib/ssl/src/ssl_alert.erl b/lib/ssl/src/ssl_alert.erl index eb1228afa4..222b3f1ad7 100644 --- a/lib/ssl/src/ssl_alert.erl +++ b/lib/ssl/src/ssl_alert.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -84,6 +84,8 @@ description_txt(?DECOMPRESSION_FAILURE) -> "decompression failure"; description_txt(?HANDSHAKE_FAILURE) -> "handshake failure"; +description_txt(?NO_CERTIFICATE_RESERVED) -> + "No certificate reserved"; description_txt(?BAD_CERTIFICATE) -> "bad certificate"; description_txt(?UNSUPPORTED_CERTIFICATE) -> diff --git a/lib/ssl/src/ssl_alert.hrl b/lib/ssl/src/ssl_alert.hrl index 6470b82d50..92548edab7 100644 --- a/lib/ssl/src/ssl_alert.hrl +++ b/lib/ssl/src/ssl_alert.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2009. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -43,6 +43,7 @@ %% record_overflow(22), %% decompression_failure(30), %% handshake_failure(40), +%% no_certificate_RESERVED(41), %% Only sslv3 %% bad_certificate(42), %% unsupported_certificate(43), %% certificate_revoked(44), @@ -69,6 +70,7 @@ -define(RECORD_OVERFLOW, 22). -define(DECOMPRESSION_FAILURE, 30). -define(HANDSHAKE_FAILURE, 40). +-define(NO_CERTIFICATE_RESERVED, 41). -define(BAD_CERTIFICATE, 42). -define(UNSUPPORTED_CERTIFICATE, 43). -define(CERTIFICATE_REVOKED, 44). diff --git a/lib/ssl/src/ssl_certificate.erl b/lib/ssl/src/ssl_certificate.erl index 61876e1158..86f5617b54 100644 --- a/lib/ssl/src/ssl_certificate.erl +++ b/lib/ssl/src/ssl_certificate.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -103,7 +103,7 @@ certificate_chain(OwnCert, CertDbHandle, CertsDbRef) -> ErlCert = public_key:pkix_decode_cert(OwnCert, otp), certificate_chain(ErlCert, OwnCert, CertDbHandle, CertsDbRef, [OwnCert]). %%-------------------------------------------------------------------- --spec file_to_certificats(string(), term()) -> [der_cert()]. +-spec file_to_certificats(binary(), term()) -> [der_cert()]. %% %% Description: Return list of DER encoded certificates. %%-------------------------------------------------------------------- @@ -111,7 +111,7 @@ 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, +-spec validate_extension(term(), {extension, #'Extension'{}} | {bad_cert, atom()} | valid, term()) -> {valid, term()} | {fail, tuple()} | {unknown, term()}. @@ -172,7 +172,12 @@ extensions_list(Extensions) -> %% Description: %%-------------------------------------------------------------------- signature_type(RSA) when RSA == ?sha1WithRSAEncryption; - RSA == ?md5WithRSAEncryption -> + RSA == ?md5WithRSAEncryption; + RSA == ?sha224WithRSAEncryption; + RSA == ?sha256WithRSAEncryption; + RSA == ?sha384WithRSAEncryption; + RSA == ?sha512WithRSAEncryption + -> rsa; signature_type(?'id-dsa-with-sha1') -> dsa. diff --git a/lib/ssl/src/ssl_certificate_db.erl b/lib/ssl/src/ssl_certificate_db.erl index cb2473576a..67d00f0da7 100644 --- a/lib/ssl/src/ssl_certificate_db.erl +++ b/lib/ssl/src/ssl_certificate_db.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -24,12 +24,13 @@ -module(ssl_certificate_db). -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). +-include_lib("kernel/include/file.hrl"). -export([create/0, remove/1, add_trusted_certs/3, - remove_trusted_certs/2, lookup_trusted_cert/4, foldl/3, - 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()}. + remove_trusted_certs/2, insert/3, remove/2, clear/1, db_size/1, + ref_count/3, lookup_trusted_cert/4, foldl/3, + lookup_cached_pem/2, cache_pem_file/2, cache_pem_file/3, + lookup/2]). %%==================================================================== %% Internal application API @@ -43,9 +44,14 @@ %% the process that called create may call the other functions. %%-------------------------------------------------------------------- create() -> - [ets:new(ssl_otp_certificate_db, [set, protected]), - ets:new(ssl_file_to_ref, [set, protected]), - ets:new(ssl_pid_to_file, [bag, private])]. + [%% Let connection process delete trusted certs + %% that can only belong to one connection. (Supplied directly + %% on DER format to ssl:connect/listen.) + ets:new(ssl_otp_cacertificate_db, [set, public]), + %% Let connection processes call ref_count/3 directly + ets:new(ssl_otp_ca_file_ref, [set, public]), + ets:new(ssl_otp_pem_cache, [set, protected]) + ]. %%-------------------------------------------------------------------- -spec remove([db_handle()]) -> term(). @@ -53,7 +59,9 @@ create() -> %% Description: Removes database db %%-------------------------------------------------------------------- remove(Dbs) -> - lists:foreach(fun(Db) -> true = ets:delete(Db) end, Dbs). + lists:foreach(fun(Db) -> + true = ets:delete(Db) + end, Dbs). %%-------------------------------------------------------------------- -spec lookup_trusted_cert(db_handle(), certdb_ref(), serialnumber(), issuer()) -> @@ -72,11 +80,14 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> {ok, Certs} end. -lookup_cached_certs(DbHandle, File) -> - ets:lookup(DbHandle, {file, File}). +lookup_cached_pem([_, _, PemChache], MD5) -> + lookup_cached_pem(PemChache, MD5); +lookup_cached_pem(PemChache, MD5) -> + lookup(MD5, PemChache). %%-------------------------------------------------------------------- --spec add_trusted_certs(pid(), string() | {der, list()}, [db_handle()]) -> {ok, [db_handle()]}. +-spec add_trusted_certs(pid(), {erlang:timestamp(), 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 @@ -86,82 +97,55 @@ add_trusted_certs(_Pid, {der, DerList}, [CerDb, _,_]) -> NewRef = make_ref(), add_certs_from_der(DerList, NewRef, CerDb), {ok, NewRef}; -add_trusted_certs(Pid, File, [CertsDb, FileToRefDb, PidToFileDb]) -> - Ref = case lookup(File, FileToRefDb) of - undefined -> - NewRef = make_ref(), - add_certs_from_file(File, NewRef, CertsDb), - insert(File, NewRef, 1, FileToRefDb), - NewRef; - [OldRef] -> - ref_count(File,FileToRefDb,1), - OldRef - end, - insert(Pid, File, PidToFileDb), - {ok, Ref}. + +add_trusted_certs(_Pid, File, [CertsDb, RefDb, PemChache] = Db) -> + MD5 = crypto:md5(File), + case lookup_cached_pem(Db, MD5) of + [{_Content, Ref}] -> + ref_count(Ref, RefDb, 1), + {ok, Ref}; + [Content] -> + Ref = make_ref(), + update_counter(Ref, 1, RefDb), + insert(MD5, {Content, Ref}, PemChache), + add_certs_from_pem(Content, Ref, CertsDb), + {ok, Ref}; + undefined -> + new_trusted_cert_entry({MD5, File}, Db) + end. %%-------------------------------------------------------------------- --spec cache_pem_file(pid(), string(), time(), [db_handle()]) -> term(). +-spec cache_pem_file({binary(), binary()}, [db_handle()]) -> term(). +-spec cache_pem_file(reference(), {binary(), binary()}, [db_handle()]) -> term(). %% %% Description: Cache file as binary in DB %%-------------------------------------------------------------------- -cache_pem_file(Pid, File, Time, [CertsDb, _FileToRefDb, PidToFileDb]) -> - {ok, PemBin} = file:read_file(File), +cache_pem_file({MD5, File}, [_CertsDb, _RefDb, PemChache]) -> + {ok, PemBin} = file:read_file(File), Content = public_key:pem_decode(PemBin), - insert({file, File}, {Time, Content}, CertsDb), - insert(Pid, File, PidToFileDb), + insert(MD5, Content, PemChache), {ok, Content}. -%-------------------------------------------------------------------- --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 -%% when those processes are finish the cache will be cleaned. It is -%% a rare but possible case a new ssl client/server is started with -%% a filename with the same name as previously started client/server -%% but with different content. -%% -------------------------------------------------------------------- -uncache_pem_file(File, [_CertsDb, _FileToRefDb, PidToFileDb]) -> - Pids = select(PidToFileDb, [{{'$1', File},[],['$$']}]), - lists:foreach(fun([Pid]) -> - exit(Pid, shutdown) - end, Pids). - -%%-------------------------------------------------------------------- --spec remove_trusted_certs(pid(), [db_handle()]) -> term(). - -%% -%% Description: Removes trusted certs originating from -%% the file associated to Pid from the runtime database. -%%-------------------------------------------------------------------- -remove_trusted_certs(Pid, [CertsDb, FileToRefDb, PidToFileDb]) -> - Files = lookup(Pid, PidToFileDb), - delete(Pid, PidToFileDb), - Clear = fun(File) -> - delete({file,File}, CertsDb), - try - 0 = ref_count(File, FileToRefDb, -1), - case lookup(File, FileToRefDb) of - [Ref] when is_reference(Ref) -> - remove_certs(Ref, CertsDb); - _ -> ok - end, - delete(File, FileToRefDb) - catch _:_ -> - ok - end - end, - case Files of - undefined -> ok; - _ -> - [Clear(File) || File <- Files], - ok - end. +cache_pem_file(Ref, {MD5, File}, [_CertsDb, _RefDb, PemChache]) -> + {ok, PemBin} = file:read_file(File), + Content = public_key:pem_decode(PemBin), + insert(MD5, {Content, Ref}, PemChache), + {ok, Content}. + +remove_trusted_certs(Ref, CertsDb) -> + remove_certs(Ref, CertsDb). + +%%-------------------------------------------------------------------- +-spec remove(term(), db_handle()) -> term(). +%% +%% Description: Removes an element in a <Db>. +%%-------------------------------------------------------------------- +remove(Key, Db) -> + _ = ets:delete(Db, Key). %%-------------------------------------------------------------------- -spec lookup(term(), db_handle()) -> term() | undefined. %% -%% Description: Looks up an element in a certificat <Db>. +%% Description: Looks up an element in a <Db>. %%-------------------------------------------------------------------- lookup(Key, Db) -> case ets:lookup(Db, Key) of @@ -184,24 +168,44 @@ lookup(Key, Db) -> %%-------------------------------------------------------------------- foldl(Fun, Acc0, Cache) -> ets:foldl(Fun, Acc0, Cache). - + %%-------------------------------------------------------------------- -%%% Internal functions +-spec ref_count(term(), db_handle(), integer()) -> integer(). +%% +%% Description: Updates a reference counter in a <Db>. %%-------------------------------------------------------------------- -insert(Key, Data, Db) -> - true = ets:insert(Db, {Key, Data}). +ref_count(Key, Db, N) -> + ets:update_counter(Db,Key,N). -insert(Key, Data, Count, Db) -> - true = ets:insert(Db, {Key, Count, Data}). +%%-------------------------------------------------------------------- +-spec clear(db_handle()) -> term(). +%% +%% Description: Clears the cache +%%-------------------------------------------------------------------- +clear(Db) -> + ets:delete_all_objects(Db). -ref_count(Key, Db,N) -> - ets:update_counter(Db,Key,N). +%%-------------------------------------------------------------------- +-spec db_size(db_handle()) -> integer(). +%% +%% Description: Returns the size of the db +%%-------------------------------------------------------------------- +db_size(Db) -> + ets:info(Db, size). -delete(Key, Db) -> - _ = ets:delete(Db, Key). +%%-------------------------------------------------------------------- +%%-spec insert(Key::term(), Data::term(), Db::db_handle()) -> no_return(). +%% +%% Description: Inserts data into <Db> +%%-------------------------------------------------------------------- +insert(Key, Data, Db) -> + true = ets:insert(Db, {Key, Data}). -select(Db, MatchSpec)-> - ets:select(Db, MatchSpec). +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- +update_counter(Key, Count, Db) -> + true = ets:insert(Db, {Key, Count}). remove_certs(Ref, CertsDb) -> ets:match_delete(CertsDb, {{Ref, '_', '_'}, '_'}). @@ -210,10 +214,8 @@ add_certs_from_der(DerList, Ref, CertsDb) -> Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, [Add(Cert) || Cert <- DerList]. -add_certs_from_file(File, Ref, CertsDb) -> +add_certs_from_pem(PemEntries, Ref, CertsDb) -> Add = fun(Cert) -> add_certs(Cert, Ref, CertsDb) end, - {ok, PemBin} = file:read_file(File), - PemEntries = public_key:pem_decode(PemBin), [Add(Cert) || {'Certificate', Cert, not_encrypted} <- PemEntries]. add_certs(Cert, Ref, CertsDb) -> @@ -229,3 +231,10 @@ add_certs(Cert, Ref, CertsDb) -> "it could not be correctly decoded.~n", []), error_logger:info_report(Report) end. + +new_trusted_cert_entry(FileRef, [CertsDb, RefDb, _] = Db) -> + Ref = make_ref(), + update_counter(Ref, 1, RefDb), + {ok, Content} = cache_pem_file(Ref, FileRef, Db), + add_certs_from_pem(Content, Ref, CertsDb), + {ok, Ref}. diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index d43d312be8..567690a413 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,25 +28,27 @@ -include("ssl_internal.hrl"). -include("ssl_record.hrl"). -include("ssl_cipher.hrl"). +-include("ssl_handshake.hrl"). -include("ssl_alert.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([security_parameters/2, suite_definition/1, - decipher/5, cipher/4, +-export([security_parameters/3, suite_definition/1, + decipher/5, cipher/5, suite/1, suites/1, anonymous_suites/0, - openssl_suite/1, openssl_suite_name/1, filter/2]). + openssl_suite/1, openssl_suite_name/1, filter/2, + hash_algorithm/1, sign_algorithm/1]). -compile(inline). %%-------------------------------------------------------------------- --spec security_parameters(cipher_suite(), #security_parameters{}) -> +-spec security_parameters(tls_version(), cipher_suite(), #security_parameters{}) -> #security_parameters{}. %% %% Description: Returns a security parameters record where the %% cipher values has been updated according to <CipherSuite> %%------------------------------------------------------------------- -security_parameters(CipherSuite, SecParams) -> - { _, Cipher, Hash} = suite_definition(CipherSuite), +security_parameters(Version, CipherSuite, SecParams) -> + { _, Cipher, Hash, PrfHashAlg} = suite_definition(CipherSuite), SecParams#security_parameters{ cipher_suite = CipherSuite, bulk_cipher_algorithm = bulk_cipher_algorithm(Cipher), @@ -55,20 +57,21 @@ security_parameters(CipherSuite, SecParams) -> expanded_key_material_length = expanded_key_material(Cipher), key_material_length = key_material(Cipher), iv_size = iv_size(Cipher), - mac_algorithm = mac_algorithm(Hash), + mac_algorithm = hash_algorithm(Hash), + prf_algorithm = prf_algorithm(PrfHashAlg, Version), hash_size = hash_size(Hash)}. %%-------------------------------------------------------------------- --spec cipher(cipher_enum(), #cipher_state{}, binary(), binary()) -> +-spec cipher(cipher_enum(), #cipher_state{}, binary(), binary(), tls_version()) -> {binary(), #cipher_state{}}. %% %% Description: Encrypts the data and the MAC using chipher described %% by cipher_enum() and updating the cipher state %%------------------------------------------------------------------- -cipher(?NULL, CipherState, <<>>, Fragment) -> +cipher(?NULL, CipherState, <<>>, Fragment, _Version) -> GenStreamCipherList = [Fragment, <<>>], {GenStreamCipherList, CipherState}; -cipher(?RC4, CipherState, Mac, Fragment) -> +cipher(?RC4, CipherState, Mac, Fragment, _Version) -> State0 = case CipherState#cipher_state.state of undefined -> crypto:rc4_set_key(CipherState#cipher_state.key); S -> S @@ -76,32 +79,41 @@ cipher(?RC4, CipherState, Mac, Fragment) -> GenStreamCipherList = [Fragment, Mac], {State1, T} = crypto:rc4_encrypt_with_state(State0, GenStreamCipherList), {T, CipherState#cipher_state{state = State1}}; -cipher(?DES, CipherState, Mac, Fragment) -> +cipher(?DES, CipherState, Mac, Fragment, Version) -> block_cipher(fun(Key, IV, T) -> crypto:des_cbc_encrypt(Key, IV, T) - end, block_size(des_cbc), CipherState, Mac, Fragment); -cipher(?'3DES', CipherState, Mac, Fragment) -> + end, block_size(des_cbc), CipherState, Mac, Fragment, Version); +cipher(?'3DES', CipherState, Mac, Fragment, Version) -> block_cipher(fun(<<K1:8/binary, K2:8/binary, K3:8/binary>>, IV, T) -> crypto:des3_cbc_encrypt(K1, K2, K3, IV, T) - end, block_size(des_cbc), CipherState, Mac, Fragment); -cipher(?AES, CipherState, Mac, Fragment) -> + end, block_size(des_cbc), CipherState, Mac, Fragment, Version); +cipher(?AES, CipherState, Mac, Fragment, Version) -> block_cipher(fun(Key, IV, T) when byte_size(Key) =:= 16 -> crypto:aes_cbc_128_encrypt(Key, IV, T); (Key, IV, T) when byte_size(Key) =:= 32 -> crypto:aes_cbc_256_encrypt(Key, IV, T) - end, block_size(aes_128_cbc), CipherState, Mac, Fragment). -%% cipher(?IDEA, CipherState, Mac, Fragment) -> -%% block_cipher(fun(Key, IV, T) -> -%% crypto:idea_cbc_encrypt(Key, IV, T) -%% end, block_size(idea_cbc), CipherState, Mac, Fragment); - -block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, - Mac, Fragment) -> + end, block_size(aes_128_cbc), CipherState, Mac, Fragment, Version). + +build_cipher_block(BlockSz, Mac, Fragment) -> TotSz = byte_size(Mac) + erlang:iolist_size(Fragment) + 1, {PaddingLength, Padding} = get_padding(TotSz, BlockSz), - L = [Fragment, Mac, PaddingLength, Padding], + [Fragment, Mac, PaddingLength, Padding]. + +block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, + Mac, Fragment, {3, N}) + when N == 0; N == 1 -> + L = build_cipher_block(BlockSz, Mac, Fragment), T = Fun(Key, IV, L), NextIV = next_iv(T, IV), + {T, CS0#cipher_state{iv=NextIV}}; + +block_cipher(Fun, BlockSz, #cipher_state{key=Key, iv=IV} = CS0, + Mac, Fragment, {3, N}) + when N == 2; N == 3 -> + NextIV = random_iv(IV), + L0 = build_cipher_block(BlockSz, Mac, Fragment), + L = [NextIV|L0], + T = Fun(Key, IV, L), {T, CS0#cipher_state{iv=NextIV}}. %%-------------------------------------------------------------------- @@ -147,19 +159,16 @@ decipher(?AES, HashSz, CipherState, Fragment, Version) -> (Key, IV, T) when byte_size(Key) =:= 32 -> crypto:aes_cbc_256_decrypt(Key, IV, T) end, CipherState, HashSz, Fragment, Version). -%% decipher(?IDEA, HashSz, CipherState, Fragment, Version) -> -%% block_decipher(fun(Key, IV, T) -> -%% crypto:idea_cbc_decrypt(Key, IV, T) -%% end, CipherState, HashSz, Fragment, Version); block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, HashSz, Fragment, Version) -> try Text = Fun(Key, IV, Fragment), - GBC = generic_block_cipher_from_bin(Text, HashSz), + NextIV = next_iv(Fragment, IV), + GBC = generic_block_cipher_from_bin(Version, Text, NextIV, HashSz), Content = GBC#generic_block_cipher.content, Mac = GBC#generic_block_cipher.mac, - CipherState1 = CipherState0#cipher_state{iv=next_iv(Fragment, IV)}, + CipherState1 = CipherState0#cipher_state{iv=GBC#generic_block_cipher.next_iv}, case is_correct_padding(GBC, Version) of true -> {Content, Mac, CipherState1}; @@ -187,8 +196,8 @@ block_decipher(Fun, #cipher_state{key=Key, iv=IV} = CipherState0, %%-------------------------------------------------------------------- suites({3, 0}) -> ssl_ssl3:suites(); -suites({3, N}) when N == 1; N == 2 -> - ssl_tls1:suites(). +suites({3, N}) -> + ssl_tls1:suites(N). %%-------------------------------------------------------------------- -spec anonymous_suites() -> [cipher_suite()]. @@ -201,10 +210,12 @@ anonymous_suites() -> ?TLS_DH_anon_WITH_DES_CBC_SHA, ?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA, ?TLS_DH_anon_WITH_AES_128_CBC_SHA, - ?TLS_DH_anon_WITH_AES_256_CBC_SHA]. + ?TLS_DH_anon_WITH_AES_256_CBC_SHA, + ?TLS_DH_anon_WITH_AES_128_CBC_SHA256, + ?TLS_DH_anon_WITH_AES_256_CBC_SHA256]. %%-------------------------------------------------------------------- --spec suite_definition(cipher_suite()) -> erl_cipher_suite(). +-spec suite_definition(cipher_suite()) -> int_cipher_suite(). %% %% Description: Return erlang cipher suite definition. %% Note: Currently not supported suites are commented away. @@ -212,56 +223,81 @@ anonymous_suites() -> %%------------------------------------------------------------------- %% TLS v1.1 suites suite_definition(?TLS_NULL_WITH_NULL_NULL) -> - {null, null, null}; + {null, null, null, null}; %% suite_definition(?TLS_RSA_WITH_NULL_MD5) -> -%% {rsa, null, md5}; +%% {rsa, null, md5, default_prf}; %% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> -%% {rsa, null, sha}; +%% {rsa, null, sha, default_prf}; suite_definition(?TLS_RSA_WITH_RC4_128_MD5) -> - {rsa, rc4_128, md5}; -suite_definition(?TLS_RSA_WITH_RC4_128_SHA) -> - {rsa, rc4_128, sha}; -%% suite_definition(?TLS_RSA_WITH_IDEA_CBC_SHA) -> -%% {rsa, idea_cbc, sha}; -suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) -> - {rsa, des_cbc, sha}; + {rsa, rc4_128, md5, default_prf}; +suite_definition(?TLS_RSA_WITH_RC4_128_SHA) -> + {rsa, rc4_128, sha, default_prf}; +suite_definition(?TLS_RSA_WITH_DES_CBC_SHA) -> + {rsa, des_cbc, sha, default_prf}; suite_definition(?TLS_RSA_WITH_3DES_EDE_CBC_SHA) -> - {rsa, '3des_ede_cbc', sha}; + {rsa, '3des_ede_cbc', sha, default_prf}; suite_definition(?TLS_DHE_DSS_WITH_DES_CBC_SHA) -> - {dhe_dss, des_cbc, sha}; + {dhe_dss, des_cbc, sha, default_prf}; suite_definition(?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) -> - {dhe_dss, '3des_ede_cbc', sha}; + {dhe_dss, '3des_ede_cbc', sha, default_prf}; suite_definition(?TLS_DHE_RSA_WITH_DES_CBC_SHA) -> - {dhe_rsa, des_cbc, sha}; + {dhe_rsa, des_cbc, sha, default_prf}; suite_definition(?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA) -> - {dhe_rsa, '3des_ede_cbc', sha}; + {dhe_rsa, '3des_ede_cbc', sha, default_prf}; %%% TSL V1.1 AES suites suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA) -> - {rsa, aes_128_cbc, sha}; + {rsa, aes_128_cbc, sha, default_prf}; suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) -> - {dhe_dss, aes_128_cbc, sha}; + {dhe_dss, aes_128_cbc, sha, default_prf}; suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA) -> - {dhe_rsa, aes_128_cbc, sha}; + {dhe_rsa, aes_128_cbc, sha, default_prf}; suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA) -> - {rsa, aes_256_cbc, sha}; + {rsa, aes_256_cbc, sha, default_prf}; suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA) -> - {dhe_dss, aes_256_cbc, sha}; + {dhe_dss, aes_256_cbc, sha, default_prf}; suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA) -> - {dhe_rsa, aes_256_cbc, sha}; + {dhe_rsa, aes_256_cbc, sha, default_prf}; + +%% TLS v1.2 suites + +%% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> +%% {rsa, null, sha, default_prf}; +suite_definition(?TLS_RSA_WITH_AES_128_CBC_SHA256) -> + {rsa, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_RSA_WITH_AES_256_CBC_SHA256) -> + {rsa, aes_256_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) -> + {dhe_dss, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) -> + {dhe_rsa, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) -> + {dhe_dss, aes_256_cbc, sha256, default_prf}; +suite_definition(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> + {dhe_rsa, aes_256_cbc, sha256, default_prf}; + +%% not defined YET: +%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 DH_DSS AES_128_CBC SHA256 +%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 DH_RSA AES_128_CBC SHA256 +%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 DH_DSS AES_256_CBC SHA256 +%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 DH_RSA AES_256_CBC SHA256 %%% DH-ANON deprecated by TLS spec and not available %%% by default, but good for testing purposes. suite_definition(?TLS_DH_anon_WITH_RC4_128_MD5) -> - {dh_anon, rc4_128, md5}; + {dh_anon, rc4_128, md5, default_prf}; suite_definition(?TLS_DH_anon_WITH_DES_CBC_SHA) -> - {dh_anon, des_cbc, sha}; + {dh_anon, des_cbc, sha, default_prf}; suite_definition(?TLS_DH_anon_WITH_3DES_EDE_CBC_SHA) -> - {dh_anon, '3des_ede_cbc', sha}; + {dh_anon, '3des_ede_cbc', sha, default_prf}; suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA) -> - {dh_anon, aes_128_cbc, sha}; + {dh_anon, aes_128_cbc, sha, default_prf}; suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA) -> - {dh_anon, aes_256_cbc, sha}. + {dh_anon, aes_256_cbc, sha, default_prf}; +suite_definition(?TLS_DH_anon_WITH_AES_128_CBC_SHA256) -> + {dh_anon, aes_128_cbc, sha256, default_prf}; +suite_definition(?TLS_DH_anon_WITH_AES_256_CBC_SHA256) -> + {dh_anon, aes_256_cbc, sha256, default_prf}. %%-------------------------------------------------------------------- -spec suite(erl_cipher_suite()) -> cipher_suite(). @@ -278,8 +314,6 @@ suite({rsa, rc4_128, md5}) -> ?TLS_RSA_WITH_RC4_128_MD5; suite({rsa, rc4_128, sha}) -> ?TLS_RSA_WITH_RC4_128_SHA; -%% suite({rsa, idea_cbc, sha}) -> -%% ?TLS_RSA_WITH_IDEA_CBC_SHA; suite({rsa, des_cbc, sha}) -> ?TLS_RSA_WITH_DES_CBC_SHA; suite({rsa, '3des_ede_cbc', sha}) -> @@ -315,7 +349,28 @@ suite({dhe_dss, aes_256_cbc, sha}) -> suite({dhe_rsa, aes_256_cbc, sha}) -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; suite({dh_anon, aes_256_cbc, sha}) -> - ?TLS_DH_anon_WITH_AES_256_CBC_SHA. + ?TLS_DH_anon_WITH_AES_256_CBC_SHA; + +%% TLS v1.2 suites + +%% suite_definition(?TLS_RSA_WITH_NULL_SHA) -> +%% {rsa, null, sha, sha256}; +suite({rsa, aes_128_cbc, sha256}) -> + ?TLS_RSA_WITH_AES_128_CBC_SHA256; +suite({rsa, aes_256_cbc, sha256}) -> + ?TLS_RSA_WITH_AES_256_CBC_SHA256; +suite({dhe_dss, aes_128_cbc, sha256}) -> + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256; +suite({dhe_rsa, aes_128_cbc, sha256}) -> + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; +suite({dhe_dss, aes_256_cbc, sha256}) -> + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256; +suite({dhe_rsa, aes_256_cbc, sha256}) -> + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; +suite({dh_anon, aes_128_cbc, sha256}) -> + ?TLS_DH_anon_WITH_AES_128_CBC_SHA256; +suite({dh_anon, aes_256_cbc, sha256}) -> + ?TLS_DH_anon_WITH_AES_256_CBC_SHA256. %%-------------------------------------------------------------------- -spec openssl_suite(openssl_cipher_suite()) -> cipher_suite(). @@ -323,6 +378,18 @@ suite({dh_anon, aes_256_cbc, sha}) -> %% Description: Return TLS cipher suite definition. %%-------------------------------------------------------------------- %% translate constants <-> openssl-strings +openssl_suite("DHE-RSA-AES256-SHA256") -> + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256; +openssl_suite("DHE-DSS-AES256-SHA256") -> + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256; +openssl_suite("AES256-SHA256") -> + ?TLS_RSA_WITH_AES_256_CBC_SHA256; +openssl_suite("DHE-RSA-AES128-SHA256") -> + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256; +openssl_suite("DHE-DSS-AES128-SHA256") -> + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256; +openssl_suite("AES128-SHA256") -> + ?TLS_RSA_WITH_AES_128_CBC_SHA256; openssl_suite("DHE-RSA-AES256-SHA") -> ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA; openssl_suite("DHE-DSS-AES256-SHA") -> @@ -341,8 +408,6 @@ openssl_suite("DHE-DSS-AES128-SHA") -> ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA; openssl_suite("AES128-SHA") -> ?TLS_RSA_WITH_AES_128_CBC_SHA; -%%openssl_suite("IDEA-CBC-SHA") -> -%% ?TLS_RSA_WITH_IDEA_CBC_SHA; openssl_suite("RC4-SHA") -> ?TLS_RSA_WITH_RC4_128_SHA; openssl_suite("RC4-MD5") -> @@ -374,8 +439,6 @@ openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA) -> "DHE-DSS-AES128-SHA"; openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA) -> "AES128-SHA"; -%% openssl_suite_name(?TLS_RSA_WITH_IDEA_CBC_SHA) -> -%% "IDEA-CBC-SHA"; openssl_suite_name(?TLS_RSA_WITH_RC4_128_SHA) -> "RC4-SHA"; openssl_suite_name(?TLS_RSA_WITH_RC4_128_MD5) -> @@ -384,6 +447,28 @@ openssl_suite_name(?TLS_DHE_RSA_WITH_DES_CBC_SHA) -> "EDH-RSA-DES-CBC-SHA"; openssl_suite_name(?TLS_RSA_WITH_DES_CBC_SHA) -> "DES-CBC-SHA"; +openssl_suite_name(?TLS_RSA_WITH_NULL_SHA256) -> + "NULL-SHA256"; +openssl_suite_name(?TLS_RSA_WITH_AES_128_CBC_SHA256) -> + "AES128-SHA256"; +openssl_suite_name(?TLS_RSA_WITH_AES_256_CBC_SHA256) -> + "AES256-SHA256"; +openssl_suite_name(?TLS_DH_DSS_WITH_AES_128_CBC_SHA256) -> + "DH-DSS-AES128-SHA256"; +openssl_suite_name(?TLS_DH_RSA_WITH_AES_128_CBC_SHA256) -> + "DH-RSA-AES128-SHA256"; +openssl_suite_name(?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256) -> + "DHE-DSS-AES128-SHA256"; +openssl_suite_name(?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256) -> + "DHE-RSA-AES128-SHA256"; +openssl_suite_name(?TLS_DH_DSS_WITH_AES_256_CBC_SHA256) -> + "DH-DSS-AES256-SHA256"; +openssl_suite_name(?TLS_DH_RSA_WITH_AES_256_CBC_SHA256) -> + "DH-RSA-AES256-SHA256"; +openssl_suite_name(?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256) -> + "DHE-DSS-AES256-SHA256"; +openssl_suite_name(?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256) -> + "DHE-RSA-AES256-SHA256"; %% No oppenssl name openssl_suite_name(Cipher) -> suite_definition(Cipher). @@ -411,9 +496,6 @@ filter(DerCert, Ciphers) -> bulk_cipher_algorithm(null) -> ?NULL; -%% Not supported yet -%% bulk_cipher_algorithm(idea_cbc) -> -%% ?IDEA; bulk_cipher_algorithm(rc4_128) -> ?RC4; bulk_cipher_algorithm(des_cbc) -> @@ -428,8 +510,7 @@ type(Cipher) when Cipher == null; Cipher == rc4_128 -> ?STREAM; -type(Cipher) when Cipher == idea_cbc; - Cipher == des_cbc; +type(Cipher) when Cipher == des_cbc; Cipher == '3des_ede_cbc'; Cipher == aes_128_cbc; Cipher == aes_256_cbc -> @@ -437,8 +518,7 @@ type(Cipher) when Cipher == idea_cbc; key_material(null) -> 0; -key_material(Cipher) when Cipher == idea_cbc; - Cipher == rc4_128 -> +key_material(rc4_128) -> 16; key_material(des_cbc) -> 8; @@ -451,8 +531,7 @@ key_material(aes_256_cbc) -> expanded_key_material(null) -> 0; -expanded_key_material(Cipher) when Cipher == idea_cbc; - Cipher == rc4_128 -> +expanded_key_material(rc4_128) -> 16; expanded_key_material(Cipher) when Cipher == des_cbc -> 8; @@ -467,8 +546,7 @@ effective_key_bits(null) -> 0; effective_key_bits(des_cbc) -> 56; -effective_key_bits(Cipher) when Cipher == idea_cbc; - Cipher == rc4_128; +effective_key_bits(Cipher) when Cipher == rc4_128; Cipher == aes_128_cbc -> 128; effective_key_bits('3des_ede_cbc') -> @@ -482,8 +560,7 @@ iv_size(Cipher) when Cipher == null; iv_size(Cipher) -> block_size(Cipher). -block_size(Cipher) when Cipher == idea_cbc; - Cipher == des_cbc; +block_size(Cipher) when Cipher == des_cbc; Cipher == '3des_ede_cbc' -> 8; @@ -491,19 +568,51 @@ block_size(Cipher) when Cipher == aes_128_cbc; Cipher == aes_256_cbc -> 16. -mac_algorithm(null) -> - ?NULL; -mac_algorithm(md5) -> - ?MD5; -mac_algorithm(sha) -> - ?SHA. +prf_algorithm(default_prf, {3, N}) when N >= 3 -> + ?SHA256; +prf_algorithm(default_prf, {3, _}) -> + ?MD5SHA; +prf_algorithm(Algo, _) -> + hash_algorithm(Algo). + +hash_algorithm(null) -> ?NULL; +hash_algorithm(md5) -> ?MD5; +hash_algorithm(sha) -> ?SHA; %% Only sha always refers to "SHA-1" +hash_algorithm(sha224) -> ?SHA224; +hash_algorithm(sha256) -> ?SHA256; +hash_algorithm(sha384) -> ?SHA384; +hash_algorithm(sha512) -> ?SHA512; +hash_algorithm(?NULL) -> null; +hash_algorithm(?MD5) -> md5; +hash_algorithm(?SHA) -> sha; +hash_algorithm(?SHA224) -> sha224; +hash_algorithm(?SHA256) -> sha256; +hash_algorithm(?SHA384) -> sha384; +hash_algorithm(?SHA512) -> sha512. + +sign_algorithm(anon) -> ?ANON; +sign_algorithm(rsa) -> ?RSA; +sign_algorithm(dsa) -> ?DSA; +sign_algorithm(ecdsa) -> ?ECDSA; +sign_algorithm(?ANON) -> anon; +sign_algorithm(?RSA) -> rsa; +sign_algorithm(?DSA) -> dsa; +sign_algorithm(?ECDSA) -> ecdsa. hash_size(null) -> 0; hash_size(md5) -> 16; hash_size(sha) -> - 20. + 20; +hash_size(sha256) -> + 32. +%% Currently no supported cipher suites defaults to sha384 or sha512 +%% so these clauses are not needed at the moment. +%% hash_size(sha384) -> +%% 48; +%% hash_size(sha512) -> +%% 64. %% RFC 5246: 6.2.3.2. CBC Block Cipher %% @@ -525,7 +634,8 @@ hash_size(sha) -> %% We return the original (possibly invalid) PadLength in any case. %% An invalid PadLength will be caught by is_correct_padding/2 %% -generic_block_cipher_from_bin(T, HashSize) -> +generic_block_cipher_from_bin({3, N}, T, IV, HashSize) + when N == 0; N == 1 -> Sz1 = byte_size(T) - 1, <<_:Sz1/binary, ?BYTE(PadLength0)>> = T, PadLength = if @@ -536,7 +646,20 @@ generic_block_cipher_from_bin(T, HashSize) -> <<Content:CompressedLength/binary, Mac:HashSize/binary, Padding:PadLength/binary, ?BYTE(PadLength0)>> = T, #generic_block_cipher{content=Content, mac=Mac, - padding=Padding, padding_length=PadLength0}. + padding=Padding, padding_length=PadLength0, + next_iv = IV}; + +generic_block_cipher_from_bin({3, N}, T, IV, HashSize) + when N == 2; N == 3 -> + Sz1 = byte_size(T) - 1, + <<_:Sz1/binary, ?BYTE(PadLength)>> = T, + IVLength = byte_size(IV), + CompressedLength = byte_size(T) - IVLength - PadLength - 1 - HashSize, + <<NextIV:IVLength/binary, Content:CompressedLength/binary, Mac:HashSize/binary, + Padding:PadLength/binary, ?BYTE(PadLength)>> = T, + #generic_block_cipher{content=Content, mac=Mac, + padding=Padding, padding_length=PadLength, + next_iv = NextIV}. generic_stream_cipher_from_bin(T, HashSz) -> Sz = byte_size(T), @@ -567,6 +690,10 @@ get_padding_aux(BlockSize, PadLength) -> N = BlockSize - PadLength, {N, list_to_binary(lists:duplicate(N, N))}. +random_iv(IV) -> + IVSz = byte_size(IV), + ssl:random_bytes(IVSz). + next_iv(Bin, IV) -> BinSz = byte_size(Bin), IVSz = byte_size(IV), @@ -578,16 +705,19 @@ rsa_signed_suites() -> dhe_rsa_suites() ++ rsa_suites(). dhe_rsa_suites() -> - [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + [?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA, ?TLS_DHE_RSA_WITH_DES_CBC_SHA]. rsa_suites() -> - [?TLS_RSA_WITH_AES_256_CBC_SHA, + [?TLS_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_RSA_WITH_AES_256_CBC_SHA, ?TLS_RSA_WITH_3DES_EDE_CBC_SHA, + ?TLS_RSA_WITH_AES_128_CBC_SHA256, ?TLS_RSA_WITH_AES_128_CBC_SHA, - %%?TLS_RSA_WITH_IDEA_CBC_SHA, ?TLS_RSA_WITH_RC4_128_SHA, ?TLS_RSA_WITH_RC4_128_MD5, ?TLS_RSA_WITH_DES_CBC_SHA]. @@ -596,8 +726,10 @@ dsa_signed_suites() -> dhe_dss_suites(). dhe_dss_suites() -> - [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, + [?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA, ?TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA]. diff --git a/lib/ssl/src/ssl_cipher.hrl b/lib/ssl/src/ssl_cipher.hrl index 8bd68cc190..0f439f8ed5 100644 --- a/lib/ssl/src/ssl_cipher.hrl +++ b/lib/ssl/src/ssl_cipher.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,8 +28,9 @@ -type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc' | aes_128_cbc | aes_256_cbc. --type hash() :: null | sha | md5. +-type hash() :: null | sha | md5 | sha256 | sha384 | sha512. -type erl_cipher_suite() :: {key_algo(), cipher(), hash()}. +-type int_cipher_suite() :: {key_algo(), cipher(), hash(), hash()}. -type cipher_suite() :: binary(). -type cipher_enum() :: integer(). -type openssl_cipher_suite() :: string(). @@ -177,6 +178,47 @@ %% TLS_DH_anon_WITH_AES_256_CBC_SHA = { 0x00, 0x3A }; -define(TLS_DH_anon_WITH_AES_256_CBC_SHA, <<?BYTE(16#00), ?BYTE(16#3A)>>). +%%% TLS 1.2 Cipher Suites RFC 5246 + +%% TLS_RSA_WITH_NULL_SHA256 = { 0x00,0x3B }; +-define(TLS_RSA_WITH_NULL_SHA256, <<?BYTE(16#00), ?BYTE(16#3B)>>). + +%% TLS_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3C }; +-define(TLS_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3C)>>). + +%% TLS_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x3D }; +-define(TLS_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3D)>>). + +%% TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x3E }; +-define(TLS_DH_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3E)>>). + +%% TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x3F }; +-define(TLS_DH_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#3F)>>). + +%% TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = { 0x00,0x40 }; +-define(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#40)>>). + +%% TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = { 0x00,0x67 }; +-define(TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#67)>>). + +%% TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x68 }; +-define(TLS_DH_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#68)>>). + +%% TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x69 }; +-define(TLS_DH_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#69)>>). + +%% TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = { 0x00,0x6A }; +-define(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6A)>>). + +%% TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = { 0x00,0x6B }; +-define(TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6B)>>). + +%% TLS_DH_anon_WITH_AES_128_CBC_SHA256 = { 0x00,0x6C }; +-define(TLS_DH_anon_WITH_AES_128_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6C)>>). + +%% TLS_DH_anon_WITH_AES_256_CBC_SHA256 = { 0x00,0x6D }; +-define(TLS_DH_anon_WITH_AES_256_CBC_SHA256, <<?BYTE(16#00), ?BYTE(16#6D)>>). + %%% Kerberos Cipher Suites %% TLS_KRB5_WITH_DES_CBC_SHA = { 0x00,0x1E }; diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 28dd0c85d0..ff2556c488 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -40,14 +40,15 @@ -export([send/2, recv/3, connect/7, ssl_accept/6, handshake/2, socket_control/3, close/1, shutdown/2, new_user/2, get_opts/2, set_opts/2, info/1, session_info/1, - peer_certificate/1, sockname/1, peername/1, renegotiation/1]). + peer_certificate/1, sockname/1, peername/1, renegotiation/1, + prf/5]). %% Called by ssl_connection_sup -export([start_link/7]). %% gen_fsm callbacks --export([init/1, hello/2, certify/2, cipher/2, connection/2, - abbreviated/2, handle_event/3, +-export([init/1, hello/2, certify/2, cipher/2, + abbreviated/2, connection/2, handle_event/3, handle_sync_event/4, handle_info/3, terminate/3, code_change/4]). -record(state, { @@ -66,8 +67,7 @@ tls_packets = [], % Not yet handled decode ssl/tls packets. tls_record_buffer, % binary() buffer of incomplete records tls_handshake_buffer, % binary() buffer of incomplete handshakes - %% {{md5_hash, sha_hash}, {prev_md5, prev_sha}} (binary()) - tls_handshake_hashes, % see above + tls_handshake_history, % tls_handshake_history() tls_cipher_texts, % list() received but not deciphered yet cert_db, % session, % #session{} from ssl_handshake.hrl @@ -77,18 +77,19 @@ supported_protocol_versions, % [atom()] client_certificate_requested = false, key_algorithm, % atom as defined by cipher_suite + hashsign_algorithm, % atom as defined by cipher_suite public_key_info, % PKIX: {Algorithm, PublicKey, PublicKeyParams} private_key, % PKIX: #'RSAPrivateKey'{} diffie_hellman_params, % PKIX: #'DHParameter'{} relevant for server side diffie_hellman_keys, % {PublicKey, PrivateKey} premaster_secret, % - cert_db_ref, % ets_table() - from, % term(), where to reply + file_ref_db, % ets() + cert_db_ref, % ref() bytes_to_read, % integer(), # bytes to read in passive mode user_data_buffer, % binary() log_alert, % boolean() renegotiation, % {boolean(), From | internal | peer} - recv_from, % + start_or_recv_from, % "gen_fsm From" send_queue, % queue() terminated = false, % allow_renegotiate = true @@ -273,6 +274,16 @@ peer_certificate(ConnectionPid) -> renegotiation(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, renegotiate). +%%-------------------------------------------------------------------- +-spec prf(pid(), binary() | 'master_secret', binary(), + binary() | ssl:prf_random(), non_neg_integer()) -> + {ok, binary()} | {error, reason()} | {'EXIT', term()}. +%% +%% Description: use a ssl sessions TLS PRF to generate key material +%%-------------------------------------------------------------------- +prf(ConnectionPid, Secret, Label, Seed, WantedLength) -> + sync_send_all_state_event(ConnectionPid, {prf, Secret, Label, Seed, WantedLength}). + %%==================================================================== %% ssl_connection_sup API %%==================================================================== @@ -286,39 +297,31 @@ renegotiation(ConnectionPid) -> %% does not return until Module:init/1 has returned. %%-------------------------------------------------------------------- start_link(Role, Host, Port, Socket, Options, User, CbInfo) -> - gen_fsm:start_link(?MODULE, [Role, Host, Port, Socket, Options, - User, CbInfo], []). + {ok, proc_lib:spawn_link(?MODULE, init, [[Role, Host, Port, Socket, Options, User, CbInfo]])}. -%%==================================================================== -%% gen_fsm callbacks -%%==================================================================== -%%-------------------------------------------------------------------- -%% Description:Whenever a gen_fsm is started using gen_fsm:start/[3,4] or -%% gen_fsm:start_link/3,4, this function is called by the new process to -%% initialize. -%%-------------------------------------------------------------------- -init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, - User, CbInfo]) -> +init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, User, CbInfo]) -> State0 = initial_state(Role, Host, Port, Socket, Options, User, CbInfo), - Hashes0 = ssl_handshake:init_hashes(), + Handshake = ssl_handshake:init_handshake_history(), TimeStamp = calendar:datetime_to_gregorian_seconds({date(), time()}), try ssl_init(SSLOpts0, Role) of - {ok, Ref, CertDbHandle, CacheHandle, OwnCert, Key, DHParams} -> + {ok, Ref, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, Key, DHParams} -> Session = State0#state.session, - State = State0#state{tls_handshake_hashes = Hashes0, + State = State0#state{ + tls_handshake_history = Handshake, session = Session#session{own_certificate = OwnCert, time_stamp = TimeStamp}, + file_ref_db = FileRefHandle, cert_db_ref = Ref, cert_db = CertDbHandle, session_cache = CacheHandle, private_key = Key, diffie_hellman_params = DHParams}, - {ok, hello, State, get_timeout(State)} - catch + gen_fsm:enter_loop(?MODULE, [], hello, State, get_timeout(State)) + catch throw:Error -> - {stop, Error} + gen_fsm:enter_loop(?MODULE, [], error, {Error,State0}, get_timeout(State0)) end. - + %%-------------------------------------------------------------------- %% Description:There should be one instance of this function for each %% possible state name. Whenever a gen_fsm receives an event sent @@ -326,6 +329,7 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, %% same name as the current state name StateName is called to handle %% the event. It is also called if a timeout occurs. %% + %%-------------------------------------------------------------------- -spec hello(start | #hello_request{} | #client_hello{} | #server_hello{} | term(), #state{}) -> gen_fsm_state_return(). @@ -333,23 +337,23 @@ init([Role, Host, Port, Socket, {SSLOpts0, _} = Options, hello(start, #state{host = Host, port = Port, role = client, ssl_options = SslOpts, session = #session{own_certificate = Cert} = Session0, + session_cache = Cache, session_cache_cb = CacheCb, transport_cb = Transport, socket = Socket, - connection_states = ConnectionStates, + connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, - ConnectionStates, - SslOpts, Renegotiation, Cert), + Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Cache, CacheCb, Renegotiation, Cert), Version = Hello#client_hello.client_version, - Hashes0 = ssl_handshake:init_hashes(), - {BinMsg, CS2, Hashes1} = - encode_handshake(Hello, Version, ConnectionStates, Hashes0), + Handshake0 = ssl_handshake:init_handshake_history(), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Hello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), - State1 = State0#state{connection_states = CS2, - negotiated_version = Version, %% Requested version + State1 = State0#state{connection_states = ConnectionStates, + negotiated_version = Version, %% Requested version session = Session0#session{session_id = Hello#client_hello.session_id}, - tls_handshake_hashes = Hashes1}, + tls_handshake_history = Handshake}, {Record, State} = next_record(State1), next_state(hello, hello, Record, State); @@ -371,19 +375,21 @@ hello(#server_hello{cipher_suite = CipherSuite, ssl_options = SslOptions} = State0) -> case ssl_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of {Version, NewId, ConnectionStates} -> - {KeyAlgorithm, _, _} = + {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), PremasterSecret = make_premaster_secret(ReqVersion, KeyAlgorithm), State = State0#state{key_algorithm = KeyAlgorithm, + hashsign_algorithm = default_hashsign(Version, KeyAlgorithm), negotiated_version = Version, connection_states = ConnectionStates, premaster_secret = PremasterSecret}, case ssl_session:is_new(OldId, NewId) of true -> - handle_new_session(NewId, CipherSuite, Compression, State); + handle_new_session(NewId, CipherSuite, Compression, + State#state{connection_states = ConnectionStates}); false -> handle_resumed_session(NewId, State#state{connection_states = ConnectionStates}) end; @@ -427,12 +433,13 @@ abbreviated(#hello_request{}, State0) -> abbreviated(#finished{verify_data = Data} = Finished, #state{role = server, negotiated_version = Version, - tls_handshake_hashes = Hashes, + tls_handshake_history = Handshake, session = #session{master_secret = MasterSecret}, connection_states = ConnectionStates0} = State) -> case ssl_handshake:verify_connection(Version, Finished, client, - MasterSecret, Hashes) of + get_current_connection_state_prf(ConnectionStates0, write), + MasterSecret, Handshake) of verified -> ConnectionStates = ssl_record:set_client_verify_data(current_both, Data, ConnectionStates0), next_state_connection(abbreviated, @@ -443,18 +450,19 @@ abbreviated(#finished{verify_data = Data} = Finished, end; abbreviated(#finished{verify_data = Data} = Finished, - #state{role = client, tls_handshake_hashes = Hashes0, + #state{role = client, tls_handshake_history = Handshake0, session = #session{master_secret = MasterSecret}, negotiated_version = Version, connection_states = ConnectionStates0} = State) -> case ssl_handshake:verify_connection(Version, Finished, server, - MasterSecret, Hashes0) of + get_pending_connection_state_prf(ConnectionStates0, write), + MasterSecret, Handshake0) of verified -> ConnectionStates1 = ssl_record:set_server_verify_data(current_read, Data, ConnectionStates0), - {ConnectionStates, Hashes} = + {ConnectionStates, Handshake} = finalize_handshake(State#state{connection_states = ConnectionStates1}, abbreviated), next_state_connection(abbreviated, - ack_connection(State#state{tls_handshake_hashes = Hashes, + ack_connection(State#state{tls_handshake_history = Handshake, connection_states = ConnectionStates})); #alert{} = Alert -> @@ -541,8 +549,8 @@ certify(#server_hello_done{}, role = client} = State0) -> case ssl_handshake:master_secret(Version, Session, ConnectionStates0, client) of - {MasterSecret, ConnectionStates1} -> - State = State0#state{connection_states = ConnectionStates1}, + {MasterSecret, ConnectionStates} -> + State = State0#state{connection_states = ConnectionStates}, client_certify_and_key_exchange(State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify, State0), @@ -558,10 +566,10 @@ certify(#server_hello_done{}, role = client} = State0) -> case ssl_handshake:master_secret(Version, PremasterSecret, ConnectionStates0, client) of - {MasterSecret, ConnectionStates1} -> + {MasterSecret, ConnectionStates} -> Session = Session0#session{master_secret = MasterSecret}, - State = State0#state{connection_states = ConnectionStates1, - session = Session}, + State = State0#state{connection_states = ConnectionStates, + session = Session}, client_certify_and_key_exchange(State); #alert{} = Alert -> handle_own_alert(Alert, Version, certify, State0), @@ -632,15 +640,20 @@ cipher(#hello_request{}, State0) -> {Record, State} = next_record(State0), next_state(cipher, hello, Record, State); -cipher(#certificate_verify{signature = Signature}, +cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign}, #state{role = server, public_key_info = PublicKeyInfo, negotiated_version = Version, session = #session{master_secret = MasterSecret}, - tls_handshake_hashes = Hashes + hashsign_algorithm = ConnectionHashSign, + tls_handshake_history = Handshake } = State0) -> + HashSign = case CertHashSign of + {_, _} -> CertHashSign; + _ -> ConnectionHashSign + end, case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, - Version, MasterSecret, Hashes) of + Version, HashSign, MasterSecret, Handshake) of valid -> {Record, State} = next_record(State0), next_state(cipher, cipher, Record, State); @@ -656,10 +669,12 @@ cipher(#finished{verify_data = Data} = Finished, role = Role, session = #session{master_secret = MasterSecret} = Session0, - tls_handshake_hashes = Hashes0} = State) -> + connection_states = ConnectionStates0, + tls_handshake_history = Handshake0} = State) -> case ssl_handshake:verify_connection(Version, Finished, opposite_role(Role), - MasterSecret, Hashes0) of + get_current_connection_state_prf(ConnectionStates0, read), + MasterSecret, Handshake0) of verified -> Session = register_session(Role, Host, Port, Session0), cipher_role(Role, Data, Session, State); @@ -680,22 +695,24 @@ cipher(Msg, State) -> %%-------------------------------------------------------------------- connection(#hello_request{}, #state{host = Host, port = Port, socket = Socket, - session = #session{own_certificate = Cert}, + session = #session{own_certificate = Cert} = Session0, + session_cache = Cache, session_cache_cb = CacheCb, ssl_options = SslOpts, negotiated_version = Version, transport_cb = Transport, connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}, - tls_handshake_hashes = Hashes0} = State0) -> - Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, - SslOpts, Renegotiation, Cert), + tls_handshake_history = Handshake0} = State0) -> + Hello = ssl_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Cache, CacheCb, Renegotiation, Cert), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Hello, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Hello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), {Record, State} = next_record(State0#state{connection_states = - ConnectionStates1, - tls_handshake_hashes = Hashes1}), + ConnectionStates, + session = Session0#session{session_id = Hello#client_hello.session_id}, + tls_handshake_history = Handshake}), next_state(connection, hello, Record, State); connection(#client_hello{} = Hello, #state{role = server, allow_renegotiate = true} = State) -> %% Mitigate Computational DoS attack @@ -721,6 +738,7 @@ connection(timeout, State) -> connection(Msg, State) -> handle_unexpected_message(Msg, connection, State). + %%-------------------------------------------------------------------- %% Description: Whenever a gen_fsm receives an event sent using %% gen_fsm:send_all_state_event/2, this function is called to handle @@ -750,8 +768,8 @@ handle_sync_event({application_data, Data}, From, StateName, State#state{send_queue = queue:in({From, Data}, Queue)}, get_timeout(State)}; -handle_sync_event(start, From, hello, State) -> - hello(start, State#state{from = From}); +handle_sync_event(start, StartFrom, hello, State) -> + hello(start, State#state{start_or_recv_from = StartFrom}); %% The two clauses below could happen if a server upgrades a socket in %% active mode. Note that in this case we are lucky that @@ -763,8 +781,10 @@ handle_sync_event(start, From, hello, State) -> %% they upgrade a active socket. handle_sync_event(start, _, connection, State) -> {reply, connected, connection, State, get_timeout(State)}; -handle_sync_event(start, From, StateName, State) -> - {next_state, StateName, State#state{from = From}, get_timeout(State)}; +handle_sync_event(start, _From, error, {Error, State = #state{}}) -> + {stop, {shutdown, Error}, {error, Error}, State}; +handle_sync_event(start, StartFrom, StateName, State) -> + {next_state, StateName, State#state{start_or_recv_from = StartFrom}, get_timeout(State)}; handle_sync_event(close, _, StateName, State) -> %% Run terminate before returning @@ -795,13 +815,13 @@ handle_sync_event({shutdown, How0}, _, StateName, {stop, normal, Error, State} end; -handle_sync_event({recv, N}, From, connection = StateName, State0) -> - passive_receive(State0#state{bytes_to_read = N, recv_from = From}, StateName); +handle_sync_event({recv, N}, RecvFrom, connection = StateName, State0) -> + passive_receive(State0#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, StateName); %% Doing renegotiate wait with handling request until renegotiate is %% finished. Will be handled by next_state_is_connection/2. -handle_sync_event({recv, N}, From, StateName, State) -> - {next_state, StateName, State#state{bytes_to_read = N, recv_from = From}, +handle_sync_event({recv, N}, RecvFrom, StateName, State) -> + {next_state, StateName, State#state{bytes_to_read = N, start_or_recv_from = RecvFrom}, get_timeout(State)}; handle_sync_event({new_user, User}, _From, StateName, @@ -868,19 +888,45 @@ handle_sync_event(renegotiate, From, connection, State) -> handle_sync_event(renegotiate, _, StateName, State) -> {reply, {error, already_renegotiating}, StateName, State, get_timeout(State)}; +handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName, + #state{connection_states = ConnectionStates, + negotiated_version = Version} = State) -> + ConnectionState = + ssl_record:current_connection_state(ConnectionStates, read), + SecParams = ConnectionState#connection_state.security_parameters, + #security_parameters{master_secret = MasterSecret, + client_random = ClientRandom, + server_random = ServerRandom} = SecParams, + Reply = try + SecretToUse = case Secret of + _ when is_binary(Secret) -> Secret; + master_secret -> MasterSecret + end, + SeedToUse = lists:reverse( + lists:foldl(fun(X, Acc) when is_binary(X) -> [X|Acc]; + (client_random, Acc) -> [ClientRandom|Acc]; + (server_random, Acc) -> [ServerRandom|Acc] + end, [], Seed)), + ssl_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength) + catch + exit:_ -> {error, badarg}; + error:Reason -> {error, Reason} + end, + {reply, Reply, StateName, State, get_timeout(State)}; + handle_sync_event(info, _, StateName, #state{negotiated_version = Version, session = #session{cipher_suite = Suite}} = State) -> AtomVersion = ssl_record:protocol_version(Version), - {reply, {ok, {AtomVersion, ssl_cipher:suite_definition(Suite)}}, + {reply, {ok, {AtomVersion, ssl:suite_definition(Suite)}}, StateName, State, get_timeout(State)}; handle_sync_event(session_info, _, StateName, #state{session = #session{session_id = Id, cipher_suite = Suite}} = State) -> {reply, [{session_id, Id}, - {cipher_suite, ssl_cipher:suite_definition(Suite)}], + {cipher_suite, ssl:suite_definition(Suite)}], StateName, State, get_timeout(State)}; handle_sync_event(peer_certificate, _, StateName, @@ -926,9 +972,9 @@ handle_info({CloseTag, Socket}, StateName, {stop, normal, State}; handle_info({ErrorTag, Socket, econnaborted}, StateName, - #state{socket = Socket, from = User, role = Role, + #state{socket = Socket, start_or_recv_from = StartFrom, role = Role, error_tag = ErrorTag} = State) when StateName =/= connection -> - alert_user(User, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), + alert_user(StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), {stop, normal, State}; handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket, @@ -965,16 +1011,19 @@ terminate(Reason, connection, #state{negotiated_version = Version, connection_states = ConnectionStates, transport_cb = Transport, socket = Socket, send_queue = SendQueue, - renegotiation = Renegotiate}) -> + renegotiation = Renegotiate} = State) -> + handle_trusted_certs_db(State), notify_senders(SendQueue), notify_renegotiater(Renegotiate), BinAlert = terminate_alert(Reason, Version, ConnectionStates), Transport:send(Socket, BinAlert), workaround_transport_delivery_problems(Socket, Transport, Reason), Transport:close(Socket); + terminate(Reason, _StateName, #state{transport_cb = Transport, socket = Socket, send_queue = SendQueue, - renegotiation = Renegotiate}) -> + renegotiation = Renegotiate} = State) -> + handle_trusted_certs_db(State), notify_senders(SendQueue), notify_renegotiater(Renegotiate), workaround_transport_delivery_problems(Socket, Transport, Reason), @@ -1022,12 +1071,12 @@ ssl_init(SslOpts, Role) -> init_manager_name(SslOpts#ssl_options.erl_dist), - {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} = init_certificates(SslOpts, Role), + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert} = init_certificates(SslOpts, Role), PrivateKey = - init_private_key(CertDbHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, + init_private_key(PemCacheHandle, SslOpts#ssl_options.key, SslOpts#ssl_options.keyfile, SslOpts#ssl_options.password, Role), - DHParams = init_diffie_hellman(CertDbHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), - {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert, PrivateKey, DHParams}. + DHParams = init_diffie_hellman(PemCacheHandle, SslOpts#ssl_options.dh, SslOpts#ssl_options.dhfile, Role), + {ok, CertDbRef, CertDbHandle, FileRefHandle, CacheHandle, OwnCert, PrivateKey, DHParams}. init_manager_name(false) -> put(ssl_manager, ssl_manager); @@ -1038,7 +1087,7 @@ init_certificates(#ssl_options{cacerts = CaCerts, cacertfile = CACertFile, certfile = CertFile, cert = Cert}, Role) -> - {ok, CertDbRef, CertDbHandle, CacheHandle} = + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle} = try Certs = case CaCerts of undefined -> @@ -1046,38 +1095,38 @@ 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, CertDbHandle, CacheHandle, CertFile, Role). + init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, Role). -init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, "", _) -> - {ok, CertDbRef, CertDbHandle, CacheHandle, undefined}; +init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, <<>>, _) -> + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined}; -init_certificates(undefined, CertDbRef, CertDbHandle, CacheHandle, CertFile, client) -> +init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, CertFile, client) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle), - {ok, CertDbRef, CertDbHandle, CacheHandle, OwnCert} + [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle), + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, OwnCert} catch _Error:_Reason -> - {ok, CertDbRef, CertDbHandle, CacheHandle, undefined} + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheHandle, undefined} end; -init_certificates(undefined, CertDbRef, CertDbHandle, CacheRef, CertFile, server) -> +init_certificates(undefined, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, CertFile, server) -> try - [OwnCert] = ssl_certificate:file_to_certificats(CertFile, CertDbHandle), - {ok, CertDbRef, CertDbHandle, CacheRef, OwnCert} + [OwnCert] = ssl_certificate:file_to_certificats(CertFile, PemCacheHandle), + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, OwnCert} catch Error:Reason -> handle_file_error(?LINE, Error, Reason, CertFile, ecertfile, erlang:get_stacktrace()) end; -init_certificates(Cert, CertDbRef, CertDbHandle, CacheRef, _, _) -> - {ok, CertDbRef, CertDbHandle, CacheRef, Cert}. +init_certificates(Cert, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, _, _) -> + {ok, CertDbRef, CertDbHandle, FileRefHandle, PemCacheHandle, CacheRef, Cert}. -init_private_key(_, undefined, "", _Password, _Client) -> +init_private_key(_, undefined, <<>>, _Password, _Client) -> undefined; init_private_key(DbHandle, undefined, KeyFile, Password, _) -> try @@ -1186,13 +1235,13 @@ certify_client(#state{client_certificate_requested = true, role = client, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}, socket = Socket, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> Certificate = ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, client), - {BinCert, ConnectionStates1, Hashes1} = - encode_handshake(Certificate, Version, ConnectionStates0, Hashes0), + {BinCert, ConnectionStates, Handshake} = + encode_handshake(Certificate, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinCert), - State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; certify_client(#state{client_certificate_requested = false} = State) -> State. @@ -1204,17 +1253,19 @@ verify_client_cert(#state{client_certificate_requested = true, role = client, private_key = PrivateKey, session = #session{master_secret = MasterSecret, own_certificate = OwnCert}, - tls_handshake_hashes = Hashes0} = State) -> + hashsign_algorithm = HashSign, + tls_handshake_history = Handshake0} = State) -> + %%TODO: for TLS 1.2 we can choose a different/stronger HashSign combination for this. case ssl_handshake:client_certificate_verify(OwnCert, MasterSecret, - Version, PrivateKey, Hashes0) of + Version, HashSign, PrivateKey, Handshake0) of #certificate_verify{} = Verified -> - {BinVerified, ConnectionStates1, Hashes1} = + {BinVerified, ConnectionStates, Handshake} = encode_handshake(Verified, Version, - ConnectionStates0, Hashes0), + ConnectionStates0, Handshake0), Transport:send(Socket, BinVerified), - State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; ignore -> State; #alert{} = Alert -> @@ -1224,7 +1275,7 @@ verify_client_cert(#state{client_certificate_requested = false} = State) -> State. do_server_hello(Type, #state{negotiated_version = Version, - session = #session{session_id = SessId} = Session, + session = #session{session_id = SessId}, connection_states = ConnectionStates0, renegotiation = {Renegotiation, _}} = State0) when is_atom(Type) -> @@ -1232,29 +1283,13 @@ do_server_hello(Type, #state{negotiated_version = Version, ServerHello = ssl_handshake:server_hello(SessId, Version, ConnectionStates0, Renegotiation), - State1 = server_hello(ServerHello, State0), + State = server_hello(ServerHello, State0), case Type of new -> - new_server_hello(ServerHello, State1); + new_server_hello(ServerHello, State); resumed -> - ConnectionStates1 = State1#state.connection_states, - case ssl_handshake:master_secret(Version, Session, - ConnectionStates1, server) of - {_, ConnectionStates2} -> - State2 = State1#state{connection_states=ConnectionStates2, - session = Session}, - {ConnectionStates, Hashes} = - finalize_handshake(State2, abbreviated), - State3 = State2#state{connection_states = - ConnectionStates, - tls_handshake_hashes = Hashes}, - {Record, State} = next_record(State3), - next_state(hello, abbreviated, Record, State); - #alert{} = Alert -> - handle_own_alert(Alert, Version, hello, State1), - {stop, normal, State1} - end + resumed_server_hello(State) end. new_server_hello(#server_hello{cipher_suite = CipherSuite, @@ -1277,6 +1312,27 @@ new_server_hello(#server_hello{cipher_suite = CipherSuite, {stop, normal, State0} end. +resumed_server_hello(#state{session = Session, + connection_states = ConnectionStates0, + negotiated_version = Version} = State0) -> + + case ssl_handshake:master_secret(Version, Session, + ConnectionStates0, server) of + {_, ConnectionStates1} -> + State1 = State0#state{connection_states = ConnectionStates1, + session = Session}, + {ConnectionStates, Handshake} = + finalize_handshake(State1, abbreviated), + State2 = State1#state{connection_states = + ConnectionStates, + tls_handshake_history = Handshake}, + {Record, State} = next_record(State2), + next_state(hello, abbreviated, Record, State); + #alert{} = Alert -> + handle_own_alert(Alert, Version, hello, State0), + {stop, normal, State0} + end. + handle_new_session(NewId, CipherSuite, Compression, #state{session = Session0} = State0) -> Session = Session0#session{session_id = NewId, cipher_suite = CipherSuite, @@ -1292,10 +1348,10 @@ handle_resumed_session(SessId, #state{connection_states = ConnectionStates0, Session = CacheCb:lookup(Cache, {{Host, Port}, SessId}), case ssl_handshake:master_secret(Version, Session, ConnectionStates0, client) of - {_, ConnectionStates1} -> + {_, ConnectionStates} -> {Record, State} = next_record(State0#state{ - connection_states = ConnectionStates1, + connection_states = ConnectionStates, session = Session}), next_state(hello, abbreviated, Record, State); #alert{} = Alert -> @@ -1308,11 +1364,11 @@ client_certify_and_key_exchange(#state{negotiated_version = Version} = State0) -> try do_client_certify_and_key_exchange(State0) of State1 = #state{} -> - {ConnectionStates, Hashes} = finalize_handshake(State1, certify), + {ConnectionStates, Handshake} = finalize_handshake(State1, certify), State2 = State1#state{connection_states = ConnectionStates, %% Reinitialize client_certificate_requested = false, - tls_handshake_hashes = Hashes}, + tls_handshake_history = Handshake}, {Record, State} = next_record(State2), next_state(certify, cipher, Record, State) catch @@ -1335,29 +1391,30 @@ server_hello(ServerHello, #state{transport_cb = Transport, socket = Socket, negotiated_version = Version, connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0} = State) -> + tls_handshake_history = Handshake0} = State) -> CipherSuite = ServerHello#server_hello.cipher_suite, - {KeyAlgorithm, _, _} = ssl_cipher:suite_definition(CipherSuite), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(ServerHello, Version, ConnectionStates0, Hashes0), + {KeyAlgorithm, _, _, _} = ssl_cipher:suite_definition(CipherSuite), + {BinMsg, ConnectionStates1, Handshake1} = + encode_handshake(ServerHello, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1, - key_algorithm = KeyAlgorithm}. + tls_handshake_history = Handshake1, + key_algorithm = KeyAlgorithm, + hashsign_algorithm = default_hashsign(Version, KeyAlgorithm)}. server_hello_done(#state{transport_cb = Transport, socket = Socket, negotiated_version = Version, - connection_states = ConnectionStates, - tls_handshake_hashes = Hashes} = State) -> + connection_states = ConnectionStates0, + tls_handshake_history = Handshake0} = State) -> HelloDone = ssl_handshake:server_hello_done(), - {BinHelloDone, NewConnectionStates, NewHashes} = - encode_handshake(HelloDone, Version, ConnectionStates, Hashes), + {BinHelloDone, ConnectionStates, Handshake} = + encode_handshake(HelloDone, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinHelloDone), - State#state{connection_states = NewConnectionStates, - tls_handshake_hashes = NewHashes}. + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}. certify_server(#state{key_algorithm = dh_anon} = State) -> State; @@ -1365,18 +1422,18 @@ certify_server(#state{key_algorithm = dh_anon} = State) -> certify_server(#state{transport_cb = Transport, socket = Socket, negotiated_version = Version, - connection_states = ConnectionStates, - tls_handshake_hashes = Hashes, + connection_states = ConnectionStates0, + tls_handshake_history = Handshake0, cert_db = CertDbHandle, cert_db_ref = CertDbRef, session = #session{own_certificate = OwnCert}} = State) -> case ssl_handshake:certificate(OwnCert, CertDbHandle, CertDbRef, server) of CertMsg = #certificate{} -> - {BinCertMsg, NewConnectionStates, NewHashes} = - encode_handshake(CertMsg, Version, ConnectionStates, Hashes), + {BinCertMsg, ConnectionStates, Handshake} = + encode_handshake(CertMsg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinCertMsg), - State#state{connection_states = NewConnectionStates, - tls_handshake_hashes = NewHashes + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake }; Alert = #alert{} -> throw(Alert) @@ -1385,11 +1442,12 @@ certify_server(#state{transport_cb = Transport, key_exchange(#state{role = server, key_algorithm = rsa} = State) -> State; key_exchange(#state{role = server, key_algorithm = Algo, + hashsign_algorithm = HashSignAlgo, diffie_hellman_params = #'DHParameter'{prime = P, base = G} = Params, private_key = PrivateKey, connection_states = ConnectionStates0, negotiated_version = Version, - tls_handshake_hashes = Hashes0, + tls_handshake_history = Handshake0, socket = Socket, transport_cb = Transport } = State) @@ -1402,16 +1460,16 @@ key_exchange(#state{role = server, key_algorithm = Algo, SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Msg = ssl_handshake:key_exchange(server, {dh, Keys, Params, - Algo, ClientRandom, + Msg = ssl_handshake:key_exchange(server, Version, {dh, Keys, Params, + HashSignAlgo, ClientRandom, ServerRandom, PrivateKey}), - {BinMsg, ConnectionStates, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{connection_states = ConnectionStates, diffie_hellman_keys = Keys, - tls_handshake_hashes = Hashes1}; + tls_handshake_history = Handshake}; key_exchange(#state{role = client, connection_states = ConnectionStates0, @@ -1420,56 +1478,61 @@ key_exchange(#state{role = client, negotiated_version = Version, premaster_secret = PremasterSecret, socket = Socket, transport_cb = Transport, - tls_handshake_hashes = Hashes0} = State) -> - Msg = rsa_key_exchange(PremasterSecret, PublicKeyInfo), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + tls_handshake_history = Handshake0} = State) -> + Msg = rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), - State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}; key_exchange(#state{role = client, connection_states = ConnectionStates0, key_algorithm = Algorithm, negotiated_version = Version, diffie_hellman_keys = {DhPubKey, _}, socket = Socket, transport_cb = Transport, - tls_handshake_hashes = Hashes0} = State) + tls_handshake_history = Handshake0} = State) when Algorithm == dhe_dss; Algorithm == dhe_rsa; Algorithm == dh_anon -> - Msg = ssl_handshake:key_exchange(client, {dh, DhPubKey}), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + Msg = ssl_handshake:key_exchange(client, Version, {dh, DhPubKey}), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), - State#state{connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}. + State#state{connection_states = ConnectionStates, + tls_handshake_history = Handshake}. -rsa_key_exchange(PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) +rsa_key_exchange(Version, PremasterSecret, PublicKeyInfo = {Algorithm, _, _}) when Algorithm == ?rsaEncryption; Algorithm == ?md2WithRSAEncryption; Algorithm == ?md5WithRSAEncryption; - Algorithm == ?sha1WithRSAEncryption -> - ssl_handshake:key_exchange(client, + Algorithm == ?sha1WithRSAEncryption; + Algorithm == ?sha224WithRSAEncryption; + Algorithm == ?sha256WithRSAEncryption; + Algorithm == ?sha384WithRSAEncryption; + Algorithm == ?sha512WithRSAEncryption + -> + ssl_handshake:key_exchange(client, Version, {premaster_secret, PremasterSecret, PublicKeyInfo}); -rsa_key_exchange(_, _) -> +rsa_key_exchange(_, _, _) -> throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)). 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, + tls_handshake_history = Handshake0, negotiated_version = Version, socket = Socket, transport_cb = Transport} = State) -> Msg = ssl_handshake:certificate_request(ConnectionStates0, CertDbHandle, CertDbRef), - {BinMsg, ConnectionStates1, Hashes1} = - encode_handshake(Msg, Version, ConnectionStates0, Hashes0), + {BinMsg, ConnectionStates, Handshake} = + encode_handshake(Msg, Version, ConnectionStates0, Handshake0), Transport:send(Socket, BinMsg), State#state{client_certificate_requested = true, - connection_states = ConnectionStates1, - tls_handshake_hashes = Hashes1}; + connection_states = ConnectionStates, + tls_handshake_history = Handshake}; request_client_cert(#state{ssl_options = #ssl_options{verify = verify_none}} = State) -> State. @@ -1495,14 +1558,16 @@ finished(#state{role = Role, socket = Socket, negotiated_version = Version, transport_cb = Transport, session = Session, connection_states = ConnectionStates0, - tls_handshake_hashes = Hashes0}, StateName) -> + tls_handshake_history = Handshake0}, StateName) -> MasterSecret = Session#session.master_secret, - Finished = ssl_handshake:finished(Version, Role, MasterSecret, Hashes0), + Finished = ssl_handshake:finished(Version, Role, + get_current_connection_state_prf(ConnectionStates0, write), + MasterSecret, Handshake0), ConnectionStates1 = save_verify_data(Role, Finished, ConnectionStates0, StateName), - {BinFinished, ConnectionStates, Hashes} = - encode_handshake(Finished, Version, ConnectionStates1, Hashes0), + {BinFinished, ConnectionStates, Handshake} = + encode_handshake(Finished, Version, ConnectionStates1, Handshake0), Transport:send(Socket, BinFinished), - {ConnectionStates, Hashes}. + {ConnectionStates, Handshake}. save_verify_data(client, #finished{verify_data = Data}, ConnectionStates, certify) -> ssl_record:set_client_verify_data(current_write, Data, ConnectionStates); @@ -1526,36 +1591,41 @@ handle_server_key( #server_dh_params{dh_p = P, dh_g = G, dh_y = ServerPublicDhKey}, - signed_params = Signed}, - #state{public_key_info = PubKeyInfo, - key_algorithm = KeyAlgo, + signed_params = Signed, + hashsign = HashSign}, + #state{negotiated_version = Version, + public_key_info = PubKeyInfo, connection_states = ConnectionStates} = State) -> PLen = size(P), GLen = size(G), YLen = size(ServerPublicDhKey), + HashAlgo = connection_hash_algo(HashSign, State), ConnectionState = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, #security_parameters{client_random = ClientRandom, server_random = ServerRandom} = SecParams, - Hash = ssl_handshake:server_key_exchange_hash(KeyAlgo, + Hash = ssl_handshake:server_key_exchange_hash(HashAlgo, <<ClientRandom/binary, ServerRandom/binary, ?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), ServerPublicDhKey/binary>>), - - case verify_dh_params(Signed, Hash, PubKeyInfo) of + + case verify_dh_params(Version, Signed, Hash, HashAlgo, PubKeyInfo) of true -> dh_master_secret(P, G, ServerPublicDhKey, undefined, State); false -> ?ALERT_REC(?FATAL, ?DECRYPT_ERROR) end. -verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) -> +verify_dh_params({3, Minor}, Signed, Hashes, HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) + when Minor >= 3 -> + public_key:verify({digest, Hashes}, HashAlgo, Signed, PubKey); +verify_dh_params(_Version, Signed, Hashes, _HashAlgo, {?rsaEncryption, PubKey, _PubKeyParams}) -> case public_key:decrypt_public(Signed, PubKey, [{rsa_pad, rsa_pkcs1_padding}]) of Hashes -> @@ -1563,8 +1633,8 @@ verify_dh_params(Signed, Hashes, {?rsaEncryption, PubKey, _PubKeyParams}) -> _ -> false end; -verify_dh_params(Signed, Hash, {?'id-dsa', PublicKey, PublicKeyParams}) -> - public_key:verify(Hash, none, Signed, {PublicKey, PublicKeyParams}). +verify_dh_params(_Version, Signed, Hash, HashAlgo, {?'id-dsa', PublicKey, PublicKeyParams}) -> + public_key:verify({digest, Hash}, HashAlgo, Signed, {PublicKey, PublicKeyParams}). dh_master_secret(Prime, Base, PublicDhKey, undefined, State) -> PMpint = mpint_binary(Prime), @@ -1598,26 +1668,26 @@ cipher_role(client, Data, Session, #state{connection_states = ConnectionStates0} cipher_role(server, Data, Session, #state{connection_states = ConnectionStates0} = State) -> ConnectionStates1 = ssl_record:set_client_verify_data(current_read, Data, ConnectionStates0), - {ConnectionStates, Hashes} = + {ConnectionStates, Handshake} = finalize_handshake(State#state{connection_states = ConnectionStates1, session = Session}, cipher), next_state_connection(cipher, ack_connection(State#state{connection_states = ConnectionStates, session = Session, - tls_handshake_hashes = - Hashes})). + tls_handshake_history = + Handshake})). encode_alert(#alert{} = Alert, Version, ConnectionStates) -> ssl_record:encode_alert_record(Alert, Version, ConnectionStates). encode_change_cipher(#change_cipher_spec{}, Version, ConnectionStates) -> ssl_record:encode_change_cipher_spec(Version, ConnectionStates). -encode_handshake(HandshakeRec, Version, ConnectionStates0, Hashes0) -> +encode_handshake(HandshakeRec, Version, ConnectionStates0, Handshake0) -> Frag = ssl_handshake:encode_handshake(HandshakeRec, Version), - Hashes1 = ssl_handshake:update_hashes(Hashes0, Frag), + Handshake1 = ssl_handshake:update_handshake_history(Handshake0, Frag), {E, ConnectionStates1} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), - {E, ConnectionStates1, Hashes1}. + {E, ConnectionStates1, Handshake1}. encode_packet(Data, #socket_options{packet=Packet}) -> case Packet of @@ -1660,7 +1730,7 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> read_application_data(Data, #state{user_application = {_Mon, Pid}, socket_options = SOpts, bytes_to_read = BytesToRead, - recv_from = From, + start_or_recv_from = RecvFrom, user_data_buffer = Buffer0} = State0) -> Buffer1 = if Buffer0 =:= <<>> -> Data; @@ -1669,9 +1739,9 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, end, case get_data(SOpts, BytesToRead, Buffer1) of {ok, ClientData, Buffer} -> % Send data - SocketOpt = deliver_app_data(SOpts, ClientData, Pid, From), + SocketOpt = deliver_app_data(SOpts, ClientData, Pid, RecvFrom), State = State0#state{user_data_buffer = Buffer, - recv_from = undefined, + start_or_recv_from = undefined, bytes_to_read = 0, socket_options = SocketOpt }, @@ -1686,7 +1756,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, {more, Buffer} -> % no reply, we need more data next_record(State0#state{user_data_buffer = Buffer}); {error,_Reason} -> %% Invalid packet in packet mode - deliver_packet_error(SOpts, Buffer1, Pid, From), + deliver_packet_error(SOpts, Buffer1, Pid, RecvFrom), {stop, normal, State0} end. @@ -1806,14 +1876,15 @@ format_reply(binary, _, N, Data) when N > 0 -> % Header mode format_reply(binary, _, _, Data) -> Data; format_reply(list, Packet, _, Data) - when Packet == http; Packet == {http, headers}; Packet == http_bin; Packet == {http_bin, headers}; Packet == httph; - Packet == httph_bin-> + when Packet == http; Packet == {http, headers}; + Packet == http_bin; Packet == {http_bin, headers}; + Packet == httph; Packet == httph_bin -> Data; format_reply(list, _,_, Data) -> binary_to_list(Data). header(0, <<>>) -> - <<>>; + []; header(_, <<>>) -> []; header(0, Binary) -> @@ -1869,25 +1940,26 @@ next_state(Current, Next, #ssl_tls{type = ?HANDSHAKE, fragment = Data}, fun({#hello_request{} = Packet, _}, {next_state, connection = SName, State}) -> %% This message should not be included in handshake %% message hashes. Starts new handshake (renegotiation) - Hs0 = ssl_handshake:init_hashes(), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs0, + Hs0 = ssl_handshake:init_handshake_history(), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs0, renegotiation = {true, peer}}); ({#hello_request{} = Packet, _}, {next_state, SName, State}) -> %% This message should not be included in handshake %% message hashes. Already in negotiation so it will be ignored! ?MODULE:SName(Packet, State); ({#client_hello{} = Packet, Raw}, {next_state, connection = SName, State}) -> - Hs0 = ssl_handshake:init_hashes(), - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1, + Version = Packet#client_hello.client_version, + Hs0 = ssl_handshake:init_handshake_history(), + Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1, renegotiation = {true, peer}}); - ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_hashes=Hs0}}) -> - Hs1 = ssl_handshake:update_hashes(Hs0, Raw), - ?MODULE:SName(Packet, State#state{tls_handshake_hashes=Hs1}); + ({Packet, Raw}, {next_state, SName, State = #state{tls_handshake_history=Hs0}}) -> + Hs1 = ssl_handshake:update_handshake_history(Hs0, Raw), + ?MODULE:SName(Packet, State#state{tls_handshake_history=Hs1}); (_, StopState) -> StopState end, try - {Packets, Buf} = ssl_handshake:get_tls_handshake(Data,Buf0), + {Packets, Buf} = ssl_handshake:get_tls_handshake(Version,Data,Buf0), State = State0#state{tls_packets = Packets, tls_handshake_buffer = Buf}, handle_tls_handshake(Handle, Next, State) catch throw:#alert{} = Alert -> @@ -1967,22 +2039,22 @@ next_state_connection(StateName, #state{send_queue = Queue0, next_state_is_connection(StateName, State) end. -%% In next_state_is_connection/1: clear tls_handshake_hashes, +%% In next_state_is_connection/1: clear tls_handshake, %% premaster_secret and public_key_info (only needed during handshake) %% to reduce memory foot print of a connection. next_state_is_connection(_, State = - #state{recv_from = From, + #state{start_or_recv_from = RecvFrom, socket_options = - #socket_options{active = false}}) when From =/= undefined -> + #socket_options{active = false}}) when RecvFrom =/= undefined -> passive_receive(State#state{premaster_secret = undefined, public_key_info = undefined, - tls_handshake_hashes = {<<>>, <<>>}}, connection); + tls_handshake_history = ssl_handshake:init_handshake_history()}, connection); next_state_is_connection(StateName, State0) -> {Record, State} = next_record_if_active(State0), next_state(StateName, connection, Record, State#state{premaster_secret = undefined, public_key_info = undefined, - tls_handshake_hashes = {<<>>, <<>>}}). + tls_handshake_history = ssl_handshake:init_handshake_history()}). register_session(client, Host, Port, #session{is_resumable = new} = Session0) -> Session = Session0#session{is_resumable = true}, @@ -2036,7 +2108,7 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, log_alert = true, session_cache_cb = SessionCacheCb, renegotiation = {false, first}, - recv_from = undefined, + start_or_recv_from = undefined, send_queue = queue:new() }. @@ -2140,7 +2212,7 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) -> handle_alerts(Alerts, handle_alert(Alert, StateName, State)). handle_alert(#alert{level = ?FATAL} = Alert, StateName, - #state{from = From, host = Host, port = Port, session = Session, + #state{start_or_recv_from = From, host = Host, port = Port, session = Session, user_application = {_Mon, Pid}, log_alert = Log, role = Role, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), @@ -2222,13 +2294,13 @@ handle_own_alert(Alert, Version, StateName, ok end. -handle_normal_shutdown(Alert, _, #state{from = User, role = Role, renegotiation = {false, first}}) -> - alert_user(User, Alert, Role); +handle_normal_shutdown(Alert, _, #state{start_or_recv_from = StartFrom, role = Role, renegotiation = {false, first}}) -> + alert_user(StartFrom, Alert, Role); handle_normal_shutdown(Alert, StateName, #state{socket_options = Opts, user_application = {_Mon, Pid}, - from = User, role = Role}) -> - alert_user(StateName, Opts, Pid, User, Alert, Role). + start_or_recv_from = RecvFrom, role = Role}) -> + alert_user(StateName, Opts, Pid, RecvFrom, Alert, Role). handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), @@ -2236,7 +2308,7 @@ handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = Stat {stop, normal, State}. make_premaster_secret({MajVer, MinVer}, rsa) -> - Rand = crypto:rand_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), + Rand = ssl:random_bytes(?NUM_OF_PREMASTERSECRET_BYTES-2), <<?BYTE(MajVer), ?BYTE(MinVer), Rand/binary>>; make_premaster_secret(_, _) -> undefined. @@ -2254,17 +2326,17 @@ ack_connection(#state{renegotiation = {true, From}} = State) -> gen_fsm:reply(From, ok), State#state{renegotiation = undefined}; ack_connection(#state{renegotiation = {false, first}, - from = From} = State) when From =/= undefined -> - gen_fsm:reply(From, connected), - State#state{renegotiation = undefined}; + start_or_recv_from = StartFrom} = State) when StartFrom =/= undefined -> + gen_fsm:reply(StartFrom, connected), + State#state{renegotiation = undefined, start_or_recv_from = undefined}; ack_connection(State) -> State. renegotiate(#state{role = client} = State) -> %% Handle same way as if server requested %% the renegotiation - Hs0 = ssl_handshake:init_hashes(), - connection(#hello_request{}, State#state{tls_handshake_hashes = Hs0}); + Hs0 = ssl_handshake:init_handshake_history(), + connection(#hello_request{}, State#state{tls_handshake_history = Hs0}); renegotiate(#state{role = server, socket = Socket, transport_cb = Transport, @@ -2272,13 +2344,13 @@ renegotiate(#state{role = server, connection_states = ConnectionStates0} = State0) -> HelloRequest = ssl_handshake:hello_request(), Frag = ssl_handshake:encode_handshake(HelloRequest, Version), - Hs0 = ssl_handshake:init_hashes(), + Hs0 = ssl_handshake:init_handshake_history(), {BinMsg, ConnectionStates} = ssl_record:encode_handshake(Frag, Version, ConnectionStates0), Transport:send(Socket, BinMsg), {Record, State} = next_record(State0#state{connection_states = ConnectionStates, - tls_handshake_hashes = Hs0}), + tls_handshake_history = Hs0}), next_state(connection, hello, Record, State#state{allow_renegotiate = true}). notify_senders(SendQueue) -> @@ -2322,7 +2394,74 @@ linux_workaround_transport_delivery_problems(#alert{level = ?FATAL}, Socket) -> linux_workaround_transport_delivery_problems(_, _) -> ok. -get_timeout(#state{ssl_options=#ssl_options{hibernate_after=undefined}}) -> +get_timeout(#state{ssl_options=#ssl_options{hibernate_after = undefined}}) -> infinity; -get_timeout(#state{ssl_options=#ssl_options{hibernate_after=HibernateAfter}}) -> +get_timeout(#state{ssl_options=#ssl_options{hibernate_after = HibernateAfter}}) -> HibernateAfter. + +handle_trusted_certs_db(#state{ssl_options = #ssl_options{cacertfile = <<>>}}) -> + %% No trusted certs specified + ok; +handle_trusted_certs_db(#state{cert_db_ref = Ref, + cert_db = CertDb, + ssl_options = #ssl_options{cacertfile = undefined}}) -> + %% Certs provided as DER directly can not be shared + %% with other connections and it is safe to delete them when the connection ends. + ssl_certificate_db:remove_trusted_certs(Ref, CertDb); +handle_trusted_certs_db(#state{file_ref_db = undefined}) -> + %% Something went wrong early (typically cacertfile does not exist) so there is nothing to handle + ok; +handle_trusted_certs_db(#state{cert_db_ref = Ref, + file_ref_db = RefDb, + ssl_options = #ssl_options{cacertfile = File}}) -> + case ssl_certificate_db:ref_count(Ref, RefDb, -1) of + 0 -> + ssl_manager:clean_cert_db(Ref, File); + _ -> + ok + end. + +get_current_connection_state_prf(CStates, Direction) -> + CS = ssl_record:current_connection_state(CStates, Direction), + CS#connection_state.security_parameters#security_parameters.prf_algorithm. +get_pending_connection_state_prf(CStates, Direction) -> + CS = ssl_record:pending_connection_state(CStates, Direction), + CS#connection_state.security_parameters#security_parameters.prf_algorithm. + +connection_hash_algo({HashAlgo, _}, _State) -> + HashAlgo; +connection_hash_algo(_, #state{hashsign_algorithm = {HashAlgo, _}}) -> + HashAlgo. + +%% RFC 5246, Sect. 7.4.1.4.1. Signature Algorithms +%% If the client does not send the signature_algorithms extension, the +%% server MUST do the following: +%% +%% - If the negotiated key exchange algorithm is one of (RSA, DHE_RSA, +%% DH_RSA, RSA_PSK, ECDH_RSA, ECDHE_RSA), behave as if client had +%% sent the value {sha1,rsa}. +%% +%% - If the negotiated key exchange algorithm is one of (DHE_DSS, +%% DH_DSS), behave as if the client had sent the value {sha1,dsa}. +%% +%% - If the negotiated key exchange algorithm is one of (ECDH_ECDSA, +%% ECDHE_ECDSA), behave as if the client had sent value {sha1,ecdsa}. + +default_hashsign(_Version = {Major, Minor}, KeyExchange) + when Major == 3 andalso Minor >= 3 andalso + (KeyExchange == rsa orelse + KeyExchange == dhe_rsa orelse + KeyExchange == dh_rsa) -> + {sha, rsa}; +default_hashsign(_Version, KeyExchange) + when KeyExchange == rsa; + KeyExchange == dhe_rsa; + KeyExchange == dh_rsa -> + {md5sha, rsa}; +default_hashsign(_Version, KeyExchange) + when KeyExchange == dhe_dss; + KeyExchange == dh_dss -> + {sha, dsa}; +default_hashsign(_Version, KeyExchange) + when KeyExchange == dh_anon -> + {null, anon}. diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 542033e6ce..28469dfa5f 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -30,14 +30,14 @@ -include("ssl_internal.hrl"). -include_lib("public_key/include/public_key.hrl"). --export([master_secret/4, client_hello/6, server_hello/4, hello/4, +-export([master_secret/4, client_hello/8, server_hello/4, hello/4, hello_request/0, certify/7, certificate/4, - client_certificate_verify/5, certificate_verify/5, - certificate_request/3, key_exchange/2, server_key_exchange_hash/2, - finished/4, verify_connection/5, get_tls_handshake/2, + client_certificate_verify/6, certificate_verify/6, + certificate_request/3, key_exchange/3, server_key_exchange_hash/2, + finished/5, verify_connection/6, get_tls_handshake/3, decode_client_key/3, server_hello_done/0, - encode_handshake/2, init_hashes/0, update_hashes/2, - decrypt_premaster_secret/2]). + encode_handshake/2, init_handshake_history/0, update_handshake_history/2, + decrypt_premaster_secret/2, prf/5]). -export([dec_hello_extensions/2]). @@ -51,14 +51,17 @@ %%==================================================================== %%-------------------------------------------------------------------- -spec client_hello(host(), inet:port_number(), #connection_states{}, - #ssl_options{}, boolean(), der_cert()) -> #client_hello{}. + #ssl_options{}, integer(), atom(), boolean(), der_cert()) -> + #client_hello{}. %% %% Description: Creates a client hello message. %%-------------------------------------------------------------------- -client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, - ciphers = UserSuites} - = SslOpts, Renegotiation, OwnCert) -> - +client_hello(Host, Port, ConnectionStates, + #ssl_options{versions = Versions, + ciphers = UserSuites + } = SslOpts, + Cache, CacheCb, Renegotiation, OwnCert) -> + Fun = fun(Version) -> ssl_record:protocol_version(Version) end, @@ -67,15 +70,16 @@ client_hello(Host, Port, ConnectionStates, #ssl_options{versions = Versions, SecParams = Pending#connection_state.security_parameters, Ciphers = available_suites(UserSuites, Version), - Id = ssl_manager:client_session_id(Host, Port, SslOpts, OwnCert), + Id = ssl_session:client_id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), - #client_hello{session_id = Id, + #client_hello{session_id = Id, client_version = Version, cipher_suites = cipher_suites(Ciphers, Renegotiation), compression_methods = ssl_record:compressions(), random = SecParams#security_parameters.client_random, - renegotiation_info = - renegotiation_info(client, ConnectionStates, Renegotiation) + renegotiation_info = + renegotiation_info(client, ConnectionStates, Renegotiation), + hash_signs = default_hash_signs() }. %%-------------------------------------------------------------------- @@ -118,17 +122,18 @@ hello_request() -> %%-------------------------------------------------------------------- hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, compression_method = Compression, random = Random, - session_id = SessionId, renegotiation_info = Info}, + session_id = SessionId, renegotiation_info = Info, + hash_signs = _HashSigns}, #ssl_options{secure_renegotiate = SecureRenegotation}, ConnectionStates0, Renegotiation) -> - +%%TODO: select hash and signature algorigthm case ssl_record:is_acceptable_version(Version) of true -> case handle_renegotiation_info(client, Info, ConnectionStates0, Renegotiation, SecureRenegotation, []) of {ok, ConnectionStates1} -> ConnectionStates = - hello_pending_connection_states(client, CipherSuite, Random, + hello_pending_connection_states(client, Version, CipherSuite, Random, Compression, ConnectionStates1), {Version, SessionId, ConnectionStates}; #alert{} = Alert -> @@ -140,10 +145,12 @@ hello(#server_hello{cipher_suite = CipherSuite, server_version = Version, hello(#client_hello{client_version = ClientVersion, random = Random, cipher_suites = CipherSuites, - renegotiation_info = Info} = Hello, + renegotiation_info = Info, + hash_signs = _HashSigns} = Hello, #ssl_options{versions = Versions, secure_renegotiate = SecureRenegotation} = SslOpts, {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> +%% TODO: select hash and signature algorithm Version = select_version(ClientVersion, Versions), case ssl_record:is_acceptable_version(Version) of true -> @@ -161,6 +168,7 @@ hello(#client_hello{client_version = ClientVersion, random = Random, {ok, ConnectionStates1} -> ConnectionStates = hello_pending_connection_states(server, + Version, CipherSuite, Random, Compression, @@ -254,54 +262,51 @@ certificate(OwnCert, CertDbHandle, CertDbRef, server) -> %%-------------------------------------------------------------------- -spec client_certificate_verify(undefined | der_cert(), binary(), - tls_version(), private_key(), - {{binary(), binary()},{binary(), binary()}}) -> + tls_version(), term(), private_key(), + tls_handshake_history()) -> #certificate_verify{} | ignore | #alert{}. %% %% Description: Creates a certificate_verify message, called by the client. %%-------------------------------------------------------------------- -client_certificate_verify(undefined, _, _, _, _) -> +client_certificate_verify(undefined, _, _, _, _, _) -> ignore; -client_certificate_verify(_, _, _, undefined, _) -> +client_certificate_verify(_, _, _, _, undefined, _) -> ignore; client_certificate_verify(OwnCert, MasterSecret, Version, - PrivateKey, {Hashes0, _}) -> + {HashAlgo, SignAlgo}, + PrivateKey, {Handshake, _}) -> case public_key:pkix_is_fixed_dh_cert(OwnCert) of true -> ?ALERT_REC(?FATAL, ?UNSUPPORTED_CERTIFICATE); - false -> - Hashes = - calc_certificate_verify(Version, MasterSecret, - alg_oid(PrivateKey), Hashes0), - Signed = digitally_signed(Hashes, PrivateKey), - #certificate_verify{signature = Signed} + false -> + Hashes = + calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + Signed = digitally_signed(Version, Hashes, HashAlgo, PrivateKey), + #certificate_verify{signature = Signed, hashsign_algorithm = {HashAlgo, SignAlgo}} end. %%-------------------------------------------------------------------- --spec certificate_verify(binary(), public_key_info(), tls_version(), - binary(), {_, {binary(), binary()}}) -> valid | #alert{}. +-spec certificate_verify(binary(), public_key_info(), tls_version(), term(), + binary(), tls_handshake_history()) -> valid | #alert{}. %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- -certificate_verify(Signature, {?'rsaEncryption'= Algorithm, PublicKey, _}, Version, - MasterSecret, {_, Hashes0}) -> - Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Hashes0), - case public_key:decrypt_public(Signature, PublicKey, - [{rsa_pad, rsa_pkcs1_padding}]) of - Hashes -> +certificate_verify(Signature, {?'rsaEncryption', PublicKey, _}, Version, + {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) -> + Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + case certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, Version) of + true -> valid; _ -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end; -certificate_verify(Signature, {?'id-dsa' = Algorithm, PublicKey, PublicKeyParams}, Version, - MasterSecret, {_, Hashes0}) -> - Hashes = calc_certificate_verify(Version, MasterSecret, - Algorithm, Hashes0), - case public_key:verify(Hashes, none, Signature, {PublicKey, PublicKeyParams}) of - true -> - valid; - false -> +certificate_verify(Signature, {?'id-dsa', PublicKey, PublicKeyParams}, Version, + {HashAlgo, _SignAlgo}, MasterSecret, {_, Handshake}) -> + Hashes = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), + case public_key:verify({digest, Hashes}, sha, Signature, {PublicKey, PublicKeyParams}) of + true -> + valid; + false -> ?ALERT_REC(?FATAL, ?BAD_CERTIFICATE) end. @@ -317,36 +322,38 @@ certificate_request(ConnectionStates, CertDbHandle, CertDbRef) -> #security_parameters{cipher_suite = CipherSuite}} = ssl_record:pending_connection_state(ConnectionStates, read), Types = certificate_types(CipherSuite), + HashSigns = default_hash_signs(), Authorities = certificate_authorities(CertDbHandle, CertDbRef), #certificate_request{ certificate_types = Types, + hashsign_algorithms = HashSigns, certificate_authorities = Authorities }. %%-------------------------------------------------------------------- --spec key_exchange(client | server, +-spec key_exchange(client | server, tls_version(), {premaster_secret, binary(), public_key_info()} | {dh, binary()} | - {dh, {binary(), binary()}, #'DHParameter'{}, key_algo(), + {dh, {binary(), binary()}, #'DHParameter'{}, {HashAlgo::atom(), SignAlgo::atom()}, binary(), binary(), private_key()}) -> #client_key_exchange{} | #server_key_exchange{}. %% %% Description: Creates a keyexchange message. %%-------------------------------------------------------------------- -key_exchange(client, {premaster_secret, Secret, {_, PublicKey, _}}) -> +key_exchange(client, _Version, {premaster_secret, Secret, {_, PublicKey, _}}) -> EncPremasterSecret = encrypted_premaster_secret(Secret, PublicKey), #client_key_exchange{exchange_keys = EncPremasterSecret}; -key_exchange(client, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> +key_exchange(client, _Version, {dh, <<?UINT32(Len), PublicKey:Len/binary>>}) -> #client_key_exchange{ exchange_keys = #client_diffie_hellman_public{ dh_public = PublicKey} }; -key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, +key_exchange(server, Version, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, #'DHParameter'{prime = P, base = G}, - KeyAlgo, ClientRandom, ServerRandom, PrivateKey}) -> + {HashAlgo, SignAlgo}, ClientRandom, ServerRandom, PrivateKey}) -> <<?UINT32(_), PBin/binary>> = crypto:mpint(P), <<?UINT32(_), GBin/binary>> = crypto:mpint(G), PLen = byte_size(PBin), @@ -355,20 +362,22 @@ key_exchange(server, {dh, {<<?UINT32(Len), PublicKey:Len/binary>>, _}, ServerDHParams = #server_dh_params{dh_p = PBin, dh_g = GBin, dh_y = PublicKey}, - case KeyAlgo of - dh_anon -> + case HashAlgo of + null -> #server_key_exchange{params = ServerDHParams, - signed_params = <<>>}; + signed_params = <<>>, + hashsign = {null, anon}}; _ -> Hash = - server_key_exchange_hash(KeyAlgo, <<ClientRandom/binary, + server_key_exchange_hash(HashAlgo, <<ClientRandom/binary, ServerRandom/binary, ?UINT16(PLen), PBin/binary, ?UINT16(GLen), GBin/binary, ?UINT16(YLen), PublicKey/binary>>), - Signed = digitally_signed(Hash, PrivateKey), + Signed = digitally_signed(Version, Hash, HashAlgo, PrivateKey), #server_key_exchange{params = ServerDHParams, - signed_params = Signed} + signed_params = Signed, + hashsign = {HashAlgo, SignAlgo}} end. %%-------------------------------------------------------------------- @@ -398,10 +407,11 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> ConnectionState = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = ConnectionState#connection_state.security_parameters, - #security_parameters{client_random = ClientRandom, + #security_parameters{prf_algorithm = PrfAlgo, + client_random = ClientRandom, server_random = ServerRandom} = SecParams, try master_secret(Version, - calc_master_secret(Version,PremasterSecret, + calc_master_secret(Version,PrfAlgo,PremasterSecret, ClientRandom, ServerRandom), SecParams, ConnectionStates, Role) catch @@ -413,26 +423,26 @@ master_secret(Version, PremasterSecret, ConnectionStates, Role) -> end. %%-------------------------------------------------------------------- --spec finished(tls_version(), client | server, binary(), {{binary(), binary()},_}) -> +-spec finished(tls_version(), client | server, integer(), binary(), tls_handshake_history()) -> #finished{}. %% %% Description: Creates a handshake finished message %%------------------------------------------------------------------- -finished(Version, Role, MasterSecret, {Hashes, _}) -> % use the current hashes +finished(Version, Role, PrfAlgo, MasterSecret, {Handshake, _}) -> % use the current handshake #finished{verify_data = - calc_finished(Version, Role, MasterSecret, Hashes)}. + calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake)}. %%-------------------------------------------------------------------- --spec verify_connection(tls_version(), #finished{}, client | server, binary(), - {_, {binary(), binary()}}) -> verified | #alert{}. +-spec verify_connection(tls_version(), #finished{}, client | server, integer(), binary(), + tls_handshake_history()) -> verified | #alert{}. %% %% Description: Checks the ssl handshake finished message to verify %% the connection. %%------------------------------------------------------------------- verify_connection(Version, #finished{verify_data = Data}, - Role, MasterSecret, {_, {MD5, SHA}}) -> + Role, PrfAlgo, MasterSecret, {_, Handshake}) -> %% use the previous hashes - case calc_finished(Version, Role, MasterSecret, {MD5, SHA}) of + case calc_finished(Version, Role, PrfAlgo, MasterSecret, Handshake) of Data -> verified; _ -> @@ -457,17 +467,17 @@ encode_handshake(Package, Version) -> [MsgType, ?uint24(Len), Bin]. %%-------------------------------------------------------------------- --spec get_tls_handshake(binary(), binary() | iolist()) -> +-spec get_tls_handshake(tls_version(), binary(), binary() | iolist()) -> {[tls_handshake()], binary()}. %% %% Description: Given buffered and new data from ssl_record, collects %% and returns it as a list of handshake messages, also returns leftover %% data. %%-------------------------------------------------------------------- -get_tls_handshake(Data, <<>>) -> - get_tls_handshake_aux(Data, []); -get_tls_handshake(Data, Buffer) -> - get_tls_handshake_aux(list_to_binary([Buffer, Data]), []). +get_tls_handshake(Version, Data, <<>>) -> + get_tls_handshake_aux(Version, Data, []); +get_tls_handshake(Version, Data, Buffer) -> + get_tls_handshake_aux(Version, list_to_binary([Buffer, Data]), []). %%-------------------------------------------------------------------- -spec decode_client_key(binary(), key_algo(), tls_version()) -> @@ -479,39 +489,34 @@ decode_client_key(ClientKey, Type, Version) -> dec_client_key(ClientKey, key_exchange_alg(Type), Version). %%-------------------------------------------------------------------- --spec init_hashes() ->{{binary(), binary()}, {binary(), binary()}}. +-spec init_handshake_history() -> tls_handshake_history(). %% -%% Description: Calls crypto hash (md5 and sha) init functions to -%% initalize the hash context. +%% Description: Initialize the empty handshake history buffer. %%-------------------------------------------------------------------- -init_hashes() -> - T = {crypto:md5_init(), crypto:sha_init()}, - {T, T}. +init_handshake_history() -> + {[], []}. %%-------------------------------------------------------------------- --spec update_hashes({{binary(), binary()}, {binary(), binary()}}, Data ::term()) -> - {{binary(), binary()}, {binary(), binary()}}. +-spec update_handshake_history(tls_handshake_history(), Data ::term()) -> + tls_handshake_history(). %% -%% Description: Calls crypto hash (md5 and sha) update functions to -%% update the hash context with Data. +%% Description: Update the handshake history buffer with Data. %%-------------------------------------------------------------------- -update_hashes(Hashes, % special-case SSL2 client hello - <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor), - ?UINT16(CSLength), ?UINT16(0), - ?UINT16(CDLength), - CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>) -> - update_hashes(Hashes, - <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor), - ?UINT16(CSLength), ?UINT16(0), - ?UINT16(CDLength), - CipherSuites:CSLength/binary, - ChallengeData:CDLength/binary>>); -update_hashes({{MD50, SHA0}, _Prev}, Data) -> - {MD51, SHA1} = {crypto:md5_update(MD50, Data), - crypto:sha_update(SHA0, Data)}, - {{MD51, SHA1}, {MD50, SHA0}}. +update_handshake_history(Handshake, % special-case SSL2 client hello + <<?CLIENT_HELLO, ?UINT24(_), ?BYTE(Major), ?BYTE(Minor), + ?UINT16(CSLength), ?UINT16(0), + ?UINT16(CDLength), + CipherSuites:CSLength/binary, + ChallengeData:CDLength/binary>>) -> + update_handshake_history(Handshake, + <<?CLIENT_HELLO, ?BYTE(Major), ?BYTE(Minor), + ?UINT16(CSLength), ?UINT16(0), + ?UINT16(CDLength), + CipherSuites:CSLength/binary, + ChallengeData:CDLength/binary>>); +update_handshake_history({Handshake0, _Prev}, Data) -> + {[Data|Handshake0], Handshake0}. %%-------------------------------------------------------------------- -spec decrypt_premaster_secret(binary(), #'RSAPrivateKey'{}) -> binary(). @@ -524,33 +529,45 @@ decrypt_premaster_secret(Secret, RSAPrivateKey) -> [{rsa_pad, rsa_pkcs1_padding}]) catch _:_ -> + io:format("decrypt_premaster_secret error"), throw(?ALERT_REC(?FATAL, ?DECRYPT_ERROR)) end. %%-------------------------------------------------------------------- --spec server_key_exchange_hash(rsa | dhe_rsa| dhe_dss | dh_anon, binary()) -> binary(). - +-spec server_key_exchange_hash(md5sha | md5 | sha | sha224 |sha256 | sha384 | sha512, binary()) -> binary(). %% %% Description: Calculate server key exchange hash %%-------------------------------------------------------------------- -server_key_exchange_hash(Algorithm, Value) when Algorithm == rsa; - Algorithm == dhe_rsa -> +server_key_exchange_hash(md5sha, Value) -> MD5 = crypto:md5(Value), - SHA = crypto:sha(Value), + SHA = crypto:sha(Value), <<MD5/binary, SHA/binary>>; -server_key_exchange_hash(dhe_dss, Value) -> - crypto:sha(Value). +server_key_exchange_hash(Hash, Value) -> + crypto:hash(Hash, Value). + +%%-------------------------------------------------------------------- +-spec prf(tls_version(), binary(), binary(), [binary()], non_neg_integer()) -> + {ok, binary()} | {error, undefined}. +%% +%% Description: use the TLS PRF to generate key material +%%-------------------------------------------------------------------- +prf({3,0}, _, _, _, _) -> + {error, undefined}; +prf({3,1}, Secret, Label, Seed, WantedLength) -> + {ok, ssl_tls1:prf(?MD5SHA, Secret, Label, Seed, WantedLength)}; +prf({3,_N}, Secret, Label, Seed, WantedLength) -> + {ok, ssl_tls1:prf(?SHA256, Secret, Label, Seed, WantedLength)}. %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length), +get_tls_handshake_aux(Version, <<?BYTE(Type), ?UINT24(Length), Body:Length/binary,Rest/binary>>, Acc) -> Raw = <<?BYTE(Type), ?UINT24(Length), Body/binary>>, - H = dec_hs(Type, Body), - get_tls_handshake_aux(Rest, [{H,Raw} | Acc]); -get_tls_handshake_aux(Data, Acc) -> + H = dec_hs(Version, Type, Body), + get_tls_handshake_aux(Version, Rest, [{H,Raw} | Acc]); +get_tls_handshake_aux(_Version, Data, Acc) -> {lists:reverse(Acc), Data}. path_validation_alert({bad_cert, cert_expired}) -> @@ -572,24 +589,23 @@ path_validation_alert({bad_cert, unknown_ca}) -> path_validation_alert(_) -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE). -select_session(Hello, Port, Session, Version, +select_session(Hello, Port, Session, Version, #ssl_options{ciphers = UserSuites} = SslOpts, Cache, CacheCb, Cert) -> SuggestedSessionId = Hello#client_hello.session_id, - SessionId = ssl_manager:server_session_id(Port, SuggestedSessionId, - SslOpts, Cert), - - Suites = available_suites(Cert, UserSuites, Version), - case ssl_session:is_new(SuggestedSessionId, SessionId) of - true -> - CipherSuite = - select_cipher_suite(Hello#client_hello.cipher_suites, Suites), + {SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId, + SslOpts, Cert, + Cache, CacheCb), + Suites = available_suites(Cert, UserSuites, Version), + case Resumed of + undefined -> + CipherSuite = select_cipher_suite(Hello#client_hello.cipher_suites, Suites), Compressions = Hello#client_hello.compression_methods, Compression = select_compression(Compressions), {new, Session#session{session_id = SessionId, cipher_suite = CipherSuite, compression_method = Compression}}; - false -> - {resumed, CacheCb:lookup(Cache, {Port, SessionId})} + _ -> + {resumed, Resumed} end. available_suites(UserSuites, Version) -> @@ -708,7 +724,7 @@ handle_renegotiation_info(ConnectionStates, SecureRenegotation) -> %% hello messages %% NOTE : Role is the role of the receiver of the hello message %% currently being processed. -hello_pending_connection_states(Role, CipherSuite, Random, Compression, +hello_pending_connection_states(Role, Version, CipherSuite, Random, Compression, ConnectionStates) -> ReadState = ssl_record:pending_connection_state(ConnectionStates, read), @@ -716,30 +732,30 @@ hello_pending_connection_states(Role, CipherSuite, Random, Compression, ssl_record:pending_connection_state(ConnectionStates, write), NewReadSecParams = - hello_security_parameters(Role, ReadState, CipherSuite, + hello_security_parameters(Role, Version, ReadState, CipherSuite, Random, Compression), NewWriteSecParams = - hello_security_parameters(Role, WriteState, CipherSuite, + hello_security_parameters(Role, Version, WriteState, CipherSuite, Random, Compression), ssl_record:update_security_params(NewReadSecParams, NewWriteSecParams, ConnectionStates). -hello_security_parameters(client, ConnectionState, CipherSuite, Random, +hello_security_parameters(client, Version, ConnectionState, CipherSuite, Random, Compression) -> SecParams = ConnectionState#connection_state.security_parameters, - NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams), + NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams), NewSecParams#security_parameters{ server_random = Random, compression_algorithm = Compression }; -hello_security_parameters(server, ConnectionState, CipherSuite, Random, +hello_security_parameters(server, Version, ConnectionState, CipherSuite, Random, Compression) -> SecParams = ConnectionState#connection_state.security_parameters, - NewSecParams = ssl_cipher:security_parameters(CipherSuite, SecParams), + NewSecParams = ssl_cipher:security_parameters(Version, CipherSuite, SecParams), NewSecParams#security_parameters{ client_random = Random, compression_algorithm = Compression @@ -773,13 +789,14 @@ master_secret(Version, MasterSecret, #security_parameters{ client_random = ClientRandom, server_random = ServerRandom, hash_size = HashSize, + prf_algorithm = PrfAlgo, key_material_length = KML, expanded_key_material_length = EKML, iv_size = IVS}, ConnectionStates, Role) -> {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, ServerWriteKey, ClientIV, ServerIV} = - setup_keys(Version, MasterSecret, ServerRandom, + setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS), ConnStates1 = ssl_record:set_master_secret(MasterSecret, ConnectionStates), @@ -794,13 +811,13 @@ master_secret(Version, MasterSecret, #security_parameters{ ServerCipherState, Role)}. -dec_hs(?HELLO_REQUEST, <<>>) -> +dec_hs(_Version, ?HELLO_REQUEST, <<>>) -> #hello_request{}; %% Client hello v2. %% 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. -dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), +dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), ?UINT16(CSLength), ?UINT16(0), ?UINT16(CDLength), CipherSuites:CSLength/binary, @@ -812,24 +829,27 @@ dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), compression_methods = [?NULL], renegotiation_info = undefined }; -dec_hs(?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, +dec_hs(_Version, ?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, ?UINT16(Cs_length), CipherSuites:Cs_length/binary, ?BYTE(Cm_length), Comp_methods:Cm_length/binary, Extensions/binary>>) -> - - RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions), - undefined), + HelloExtensions = dec_hello_extensions(Extensions), + RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions, + undefined), + HashSigns = proplists:get_value(hash_signs, HelloExtensions, + undefined), #client_hello{ client_version = {Major,Minor}, random = Random, session_id = Session_ID, cipher_suites = from_2bytes(CipherSuites), compression_methods = Comp_methods, - renegotiation_info = RenegotiationInfo + renegotiation_info = RenegotiationInfo, + hash_signs = HashSigns }; -dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, +dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, Cipher_suite:2/binary, ?BYTE(Comp_method)>>) -> #server_hello{ @@ -838,53 +858,81 @@ dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, session_id = Session_ID, cipher_suite = Cipher_suite, compression_method = Comp_method, - renegotiation_info = undefined}; + renegotiation_info = undefined, + hash_signs = undefined}; -dec_hs(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, +dec_hs(_Version, ?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID:SID_length/binary, Cipher_suite:2/binary, ?BYTE(Comp_method), ?UINT16(ExtLen), Extensions:ExtLen/binary>>) -> - RenegotiationInfo = proplists:get_value(renegotiation_info, dec_hello_extensions(Extensions, []), - undefined), + HelloExtensions = dec_hello_extensions(Extensions, []), + RenegotiationInfo = proplists:get_value(renegotiation_info, HelloExtensions, + undefined), + HashSigns = proplists:get_value(hash_signs, HelloExtensions, + undefined), #server_hello{ server_version = {Major,Minor}, random = Random, session_id = Session_ID, cipher_suite = Cipher_suite, compression_method = Comp_method, - renegotiation_info = RenegotiationInfo}; -dec_hs(?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) -> + renegotiation_info = RenegotiationInfo, + hash_signs = HashSigns}; +dec_hs(_Version, ?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>) -> #certificate{asn1_certificates = certs_to_list(ASN1Certs)}; -dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, +dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, ?UINT16(GLen), G:GLen/binary, ?UINT16(YLen), Y:YLen/binary, ?UINT16(0)>>) -> %% May happen if key_algorithm is dh_anon #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y}, - signed_params = <<>>}; -dec_hs(?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, + signed_params = <<>>, hashsign = {null, anon}}; +dec_hs({Major, Minor}, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, + ?UINT16(GLen), G:GLen/binary, + ?UINT16(YLen), Y:YLen/binary, + ?BYTE(HashAlgo), ?BYTE(SignAlgo), + ?UINT16(Len), Sig:Len/binary>>) + when Major == 3, Minor >= 3 -> + #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, + dh_y = Y}, + signed_params = Sig, + hashsign = {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}}; +dec_hs(_Version, ?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P:PLen/binary, ?UINT16(GLen), G:GLen/binary, ?UINT16(YLen), Y:YLen/binary, ?UINT16(Len), Sig:Len/binary>>) -> #server_key_exchange{params = #server_dh_params{dh_p = P,dh_g = G, dh_y = Y}, - signed_params = Sig}; -dec_hs(?CERTIFICATE_REQUEST, + signed_params = Sig, hashsign = undefined}; +dec_hs({Major, Minor}, ?CERTIFICATE_REQUEST, + <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary, + ?UINT16(HashSignsLen), HashSigns:HashSignsLen/binary, + ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) + when Major == 3, Minor >= 3 -> + HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} || + <<?BYTE(Hash), ?BYTE(Sign)>> <= HashSigns], + #certificate_request{certificate_types = CertTypes, + hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos}, + certificate_authorities = CertAuths}; +dec_hs(_Version, ?CERTIFICATE_REQUEST, <<?BYTE(CertTypesLen), CertTypes:CertTypesLen/binary, ?UINT16(CertAuthsLen), CertAuths:CertAuthsLen/binary>>) -> #certificate_request{certificate_types = CertTypes, certificate_authorities = CertAuths}; -dec_hs(?SERVER_HELLO_DONE, <<>>) -> +dec_hs(_Version, ?SERVER_HELLO_DONE, <<>>) -> #server_hello_done{}; -dec_hs(?CERTIFICATE_VERIFY,<<?UINT16(_), Signature/binary>>)-> +dec_hs({Major, Minor}, ?CERTIFICATE_VERIFY,<<HashSign:2/binary, ?UINT16(SignLen), Signature:SignLen/binary>>) + when Major == 3, Minor >= 3 -> + #certificate_verify{hashsign_algorithm = hashsign_dec(HashSign), signature = Signature}; +dec_hs(_Version, ?CERTIFICATE_VERIFY,<<?UINT16(SignLen), Signature:SignLen/binary>>)-> #certificate_verify{signature = Signature}; -dec_hs(?CLIENT_KEY_EXCHANGE, PKEPMS) -> +dec_hs(_Version, ?CLIENT_KEY_EXCHANGE, PKEPMS) -> #client_key_exchange{exchange_keys = PKEPMS}; -dec_hs(?FINISHED, VerifyData) -> +dec_hs(_Version, ?FINISHED, VerifyData) -> #finished{verify_data = VerifyData}; -dec_hs(_, _) -> +dec_hs(_, _, _) -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE)). dec_client_key(PKEPMS, ?KEY_EXCHANGE_RSA, {3, 0}) -> @@ -918,6 +966,15 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar dec_hello_extensions(Rest, [{renegotiation_info, #renegotiation_info{renegotiated_connection = RenegotiateInfo}} | Acc]); +dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Acc) -> + SignAlgoListLen = Len - 2, + <<?UINT16(SignAlgoListLen), SignAlgoList/binary>> = ExtData, + HashSignAlgos = [{ssl_cipher:hash_algorithm(Hash), ssl_cipher:sign_algorithm(Sign)} || + <<?BYTE(Hash), ?BYTE(Sign)>> <= SignAlgoList], + dec_hello_extensions(Rest, [{hash_signs, + #hash_sign_algos{hash_sign_algos = HashSignAlgos}} | Acc]); + %% Ignore data following the ClientHello (i.e., %% extensions) if not understood. dec_hello_extensions(<<?UINT16(_), ?UINT16(Len), _Unknown:Len/binary, Rest/binary>>, Acc) -> @@ -959,14 +1016,19 @@ enc_hs(#client_hello{client_version = {Major, Minor}, session_id = SessionID, cipher_suites = CipherSuites, compression_methods = CompMethods, - renegotiation_info = RenegotiationInfo}, _Version) -> + renegotiation_info = RenegotiationInfo, + hash_signs = HashSigns}, _Version) -> SIDLength = byte_size(SessionID), BinCompMethods = list_to_binary(CompMethods), CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), - Extensions = hello_extensions(RenegotiationInfo), - ExtensionsBin = enc_hello_extensions(Extensions), + Extensions0 = hello_extensions(RenegotiationInfo), + Extensions1 = if + Major == 3, Minor >=3 -> Extensions0 ++ hello_extensions(HashSigns); + true -> Extensions0 + end, + ExtensionsBin = enc_hello_extensions(Extensions1), {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SIDLength), SessionID/binary, ?UINT16(CsLength), BinCipherSuites/binary, @@ -990,15 +1052,30 @@ enc_hs(#certificate{asn1_certificates = ASN1CertList}, _Version) -> {?CERTIFICATE, <<?UINT24(ACLen), ASN1Certs:ACLen/binary>>}; enc_hs(#server_key_exchange{params = #server_dh_params{ dh_p = P, dh_g = G, dh_y = Y}, - signed_params = SignedParams}, _Version) -> + signed_params = SignedParams, hashsign = HashSign}, Version) -> PLen = byte_size(P), GLen = byte_size(G), YLen = byte_size(Y), - SignedLen = byte_size(SignedParams), + Signature = enc_sign(HashSign, SignedParams, Version), {?SERVER_KEY_EXCHANGE, <<?UINT16(PLen), P/binary, ?UINT16(GLen), G/binary, ?UINT16(YLen), Y/binary, - ?UINT16(SignedLen), SignedParams/binary>> + Signature/binary>> + }; +enc_hs(#certificate_request{certificate_types = CertTypes, + hashsign_algorithms = #hash_sign_algos{hash_sign_algos = HashSignAlgos}, + certificate_authorities = CertAuths}, + {Major, Minor}) + when Major == 3, Minor >= 3 -> + HashSigns= << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || + {Hash, Sign} <- HashSignAlgos >>, + CertTypesLen = byte_size(CertTypes), + HashSignsLen = byte_size(HashSigns), + CertAuthsLen = byte_size(CertAuths), + {?CERTIFICATE_REQUEST, + <<?BYTE(CertTypesLen), CertTypes/binary, + ?UINT16(HashSignsLen), HashSigns/binary, + ?UINT16(CertAuthsLen), CertAuths/binary>> }; enc_hs(#certificate_request{certificate_types = CertTypes, certificate_authorities = CertAuths}, @@ -1013,8 +1090,8 @@ enc_hs(#server_hello_done{}, _Version) -> {?SERVER_HELLO_DONE, <<>>}; enc_hs(#client_key_exchange{exchange_keys = ExchangeKeys}, Version) -> {?CLIENT_KEY_EXCHANGE, enc_cke(ExchangeKeys, Version)}; -enc_hs(#certificate_verify{signature = BinSig}, _) -> - EncSig = enc_bin_sig(BinSig), +enc_hs(#certificate_verify{signature = BinSig, hashsign_algorithm = HashSign}, Version) -> + EncSig = enc_sign(HashSign, BinSig, Version), {?CERTIFICATE_VERIFY, EncSig}; enc_hs(#finished{verify_data = VerifyData}, _Version) -> {?FINISHED, VerifyData}. @@ -1028,14 +1105,23 @@ enc_cke(#client_diffie_hellman_public{dh_public = DHPublic}, _) -> Len = byte_size(DHPublic), <<?UINT16(Len), DHPublic/binary>>. -enc_bin_sig(BinSig) -> - Size = byte_size(BinSig), - <<?UINT16(Size), BinSig/binary>>. +enc_sign({HashAlg, SignAlg}, Signature, _Version = {Major, Minor}) + when Major == 3, Minor >= 3-> + SignLen = byte_size(Signature), + HashSign = hashsign_enc(HashAlg, SignAlg), + <<HashSign/binary, ?UINT16(SignLen), Signature/binary>>; +enc_sign(_HashSign, Sign, _Version) -> + SignLen = byte_size(Sign), + <<?UINT16(SignLen), Sign/binary>>. -%% Renegotiation info, only current extension +hello_extensions(undefined) -> + []; +%% Renegotiation info hello_extensions(#renegotiation_info{renegotiated_connection = undefined}) -> []; hello_extensions(#renegotiation_info{} = Info) -> + [Info]; +hello_extensions(#hash_sign_algos{} = Info) -> [Info]. enc_hello_extensions(Extensions) -> @@ -1053,7 +1139,14 @@ enc_hello_extensions([#renegotiation_info{renegotiated_connection = ?byte(0) = I enc_hello_extensions([#renegotiation_info{renegotiated_connection = Info} | Rest], Acc) -> InfoLen = byte_size(Info), Len = InfoLen +1, - enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>). + enc_hello_extensions(Rest, <<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), ?BYTE(InfoLen), Info/binary, Acc/binary>>); + +enc_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> + SignAlgoList = << <<(ssl_cipher:hash_algorithm(Hash)):8, (ssl_cipher:sign_algorithm(Sign)):8>> || + {Hash, Sign} <- HashSignAlgos >>, + ListLen = byte_size(SignAlgoList), + Len = ListLen + 2, + enc_hello_extensions(Rest, <<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), ?UINT16(ListLen), SignAlgoList/binary, Acc/binary>>). from_3bytes(Bin3) -> @@ -1081,6 +1174,14 @@ certificate_types({KeyExchange, _, _, _}) certificate_types(_) -> <<?BYTE(?RSA_SIGN)>>. +hashsign_dec(<<?BYTE(HashAlgo), ?BYTE(SignAlgo)>>) -> + {ssl_cipher:hash_algorithm(HashAlgo), ssl_cipher:sign_algorithm(SignAlgo)}. + +hashsign_enc(HashAlgo, SignAlgo) -> + Hash = ssl_cipher:hash_algorithm(HashAlgo), + Sign = ssl_cipher:sign_algorithm(SignAlgo), + <<?BYTE(Hash), ?BYTE(Sign)>>. + certificate_authorities(CertDbHandle, CertDbRef) -> Authorities = certificate_authorities_from_db(CertDbHandle, CertDbRef), Enc = fun(#'OTPCertificate'{tbsCertificate=TBSCert}) -> @@ -1099,43 +1200,43 @@ certificate_authorities_from_db(CertDbHandle, CertDbRef) -> [Cert | Acc]; (_, Acc) -> Acc - end, + end, ssl_certificate_db:foldl(ConnectionCerts, [], CertDbHandle). -digitally_signed(Hash, #'RSAPrivateKey'{} = Key) -> + +digitally_signed({3, Minor}, Hash, HashAlgo, Key) when Minor >= 3 -> + public_key:sign({digest, Hash}, HashAlgo, Key); +digitally_signed(_Version, Hash, _HashAlgo, #'DSAPrivateKey'{} = Key) -> + public_key:sign({digest, Hash}, sha, Key); +digitally_signed(_Version, Hash, _HashAlgo, #'RSAPrivateKey'{} = Key) -> public_key:encrypt_private(Hash, Key, - [{rsa_pad, rsa_pkcs1_padding}]); -digitally_signed(Hash, #'DSAPrivateKey'{} = Key) -> - public_key:sign(Hash, none, Key). - -calc_master_secret({3,0}, PremasterSecret, ClientRandom, ServerRandom) -> + [{rsa_pad, rsa_pkcs1_padding}]). + +calc_master_secret({3,0}, _PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) -> ssl_ssl3:master_secret(PremasterSecret, ClientRandom, ServerRandom); -calc_master_secret({3,N},PremasterSecret, ClientRandom, ServerRandom) - when N == 1; N == 2 -> - ssl_tls1:master_secret(PremasterSecret, ClientRandom, ServerRandom). +calc_master_secret({3,_}, PrfAlgo, PremasterSecret, ClientRandom, ServerRandom) -> + ssl_tls1:master_secret(PrfAlgo, PremasterSecret, ClientRandom, ServerRandom). -setup_keys({3,0}, MasterSecret, +setup_keys({3,0}, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS) -> - ssl_ssl3:setup_keys(MasterSecret, ServerRandom, + ssl_ssl3:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KML, EKML, IVS); -setup_keys({3,1}, MasterSecret, +setup_keys({3,N}, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, _EKML, IVS) -> - ssl_tls1:setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, + ssl_tls1:setup_keys(N, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, KML, IVS). -calc_finished({3, 0}, Role, MasterSecret, Hashes) -> - ssl_ssl3:finished(Role, MasterSecret, Hashes); -calc_finished({3, N}, Role, MasterSecret, Hashes) - when N == 1; N == 2 -> - ssl_tls1:finished(Role, MasterSecret, Hashes). +calc_finished({3, 0}, Role, _PrfAlgo, MasterSecret, Handshake) -> + ssl_ssl3:finished(Role, MasterSecret, lists:reverse(Handshake)); +calc_finished({3, N}, Role, PrfAlgo, MasterSecret, Handshake) -> + ssl_tls1:finished(Role, N, PrfAlgo, MasterSecret, lists:reverse(Handshake)). -calc_certificate_verify({3, 0}, MasterSecret, Algorithm, Hashes) -> - ssl_ssl3:certificate_verify(Algorithm, MasterSecret, Hashes); -calc_certificate_verify({3, N}, _, Algorithm, Hashes) - when N == 1; N == 2 -> - ssl_tls1:certificate_verify(Algorithm, Hashes). +calc_certificate_verify({3, 0}, HashAlgo, MasterSecret, Handshake) -> + ssl_ssl3:certificate_verify(HashAlgo, MasterSecret, lists:reverse(Handshake)); +calc_certificate_verify({3, N}, HashAlgo, _MasterSecret, Handshake) -> + ssl_tls1:certificate_verify(HashAlgo, N, lists:reverse(Handshake)). key_exchange_alg(rsa) -> ?KEY_EXCHANGE_RSA; @@ -1155,7 +1256,29 @@ apply_user_fun(Fun, OtpCert, ExtensionOrError, UserState0, SslState) -> {unknown, {SslState, UserState}} end. -alg_oid(#'RSAPrivateKey'{}) -> - ?'rsaEncryption'; -alg_oid(#'DSAPrivateKey'{}) -> - ?'id-dsa'. +certificate_verify_rsa(Hashes, sha, Signature, PublicKey, {Major, Minor}) + when Major == 3, Minor >= 3 -> + public_key:verify({digest, Hashes}, sha, Signature, PublicKey); +certificate_verify_rsa(Hashes, HashAlgo, Signature, PublicKey, {Major, Minor}) + when Major == 3, Minor >= 3 -> + public_key:verify({digest, Hashes}, HashAlgo, Signature, PublicKey); +certificate_verify_rsa(Hashes, _HashAlgo, Signature, PublicKey, _Version) -> + case public_key:decrypt_public(Signature, PublicKey, + [{rsa_pad, rsa_pkcs1_padding}]) of + Hashes -> true; + _ -> false + end. + +-define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}). +-define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}). + +-define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_RSA(MD)). + +default_hash_signs() -> + #hash_sign_algos{hash_sign_algos = + [?TLSEXT_SIGALG(sha512), + ?TLSEXT_SIGALG(sha384), + ?TLSEXT_SIGALG(sha256), + ?TLSEXT_SIGALG(sha), + ?TLSEXT_SIGALG_DSA(sha), + ?TLSEXT_SIGALG_RSA(md5)]}. diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index fb0ebac7d1..cc17dc2975 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -31,6 +31,13 @@ -type algo_oid() :: ?'rsaEncryption' | ?'id-dsa'. -type public_key_params() :: #'Dss-Parms'{} | term(). -type public_key_info() :: {algo_oid(), #'RSAPublicKey'{} | integer() , public_key_params()}. +-type tls_handshake_history() :: {[binary()], [binary()]}. + +%% Signature algorithms +-define(ANON, 0). +-define(RSA, 1). +-define(DSA, 2). +-define(ECDSA, 3). -record(session, { session_id, @@ -89,7 +96,8 @@ session_id, % opaque SessionID<0..32> cipher_suites, % cipher_suites<2..2^16-1> compression_methods, % compression_methods<1..2^8-1>, - renegotiation_info + renegotiation_info, + hash_signs % supported combinations of hashes/signature algos }). -record(server_hello, { @@ -98,7 +106,8 @@ session_id, % opaque SessionID<0..32> cipher_suite, % cipher_suites compression_method, % compression_method - renegotiation_info + renegotiation_info, + hash_signs % supported combinations of hashes/signature algos }). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -129,7 +138,8 @@ -record(server_key_exchange, { params, %% #server_rsa_params{} | #server_dh_params{} - signed_params %% #signature{} + signed_params, %% #signature{} + hashsign %% term(atom(), atom()) }). %% enum { anonymous, rsa, dsa } SignatureAlgorithm; @@ -159,6 +169,7 @@ -record(certificate_request, { certificate_types, %ClientCertificateType <1..2^8-1> + hashsign_algorithms, %%SignatureAndHashAlgorithm <2^16-1>; certificate_authorities %DistinguishedName <0..2^16-1> }). @@ -193,6 +204,7 @@ %%% Certificate verify - RFC 4346 section 7.4.8 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -record(certificate_verify, { + hashsign_algorithm, signature % binary() }). @@ -213,6 +225,15 @@ renegotiated_connection }). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Signature Algorithms RFC 5746 section 7.4.1.4.1. +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +-define(SIGNATURE_ALGORITHMS_EXT, 13). + +-record(hash_sign_algos, { + hash_sign_algos + }). + -endif. % -ifdef(ssl_handshake). diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 18cfcdcd68..b8f2ae3b51 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -34,7 +34,7 @@ -type host() :: inet:ip_address() | inet:hostname(). -type session_id() :: 0 | binary(). -type tls_version() :: {integer(), integer()}. --type tls_atom_version() :: sslv3 | tlsv1. +-type tls_atom_version() :: sslv3 | tlsv1 | 'tlsv1.1' | 'tlsv1.2'. -type certdb_ref() :: reference(). -type db_handle() :: term(). -type key_algo() :: null | rsa | dhe_rsa | dhe_dss | dh_anon. @@ -69,11 +69,11 @@ -define(TRUE, 0). -define(FALSE, 1). --define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). % TODO: This is temporary -%-define(DEFAULT_SUPPORTED_VERSIONS, ['tlsv1.1', tlsv1, sslv3]). +-define(DEFAULT_SUPPORTED_VERSIONS, [tlsv1, sslv3]). %% Add 'tlsv1.1' in R16 +-define(ALL_SUPPORTED_VERSIONS, ['tlsv1.2', 'tlsv1.1', tlsv1, sslv3]). -record(ssl_options, { - versions, % 'tlsv1.1' | tlsv1 | sslv3 + versions, % 'tlsv1.2' | 'tlsv1.1' | tlsv1 | sslv3 verify, % verify_none | verify_peer verify_fun, % fun(CertVerifyErrors) -> boolean() fail_if_no_peer_cert, % boolean() diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 6389ff03f5..af2bfa394d 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -export([start_link/1, start_link_dist/1, connection_init/2, cache_pem_file/2, lookup_trusted_cert/4, - client_session_id/4, server_session_id/4, + new_session_id/1, clean_cert_db/2, register_session/2, register_session/3, invalidate_session/2, - invalidate_session/3]). + invalidate_session/3, clear_pem_cache/0]). % Spawn export -export([init_session_validator/1]). @@ -56,9 +56,12 @@ -define('24H_in_msec', 8640000). -define('24H_in_sec', 8640). +-define(GEN_UNIQUE_ID_MAX_TRIES, 10). -define(SESSION_VALIDATION_INTERVAL, 60000). --define(CERTIFICATE_CACHE_CLEANUP, 30000). +-define(CLEAR_PEM_CACHE, 120000). -define(CLEAN_SESSION_DB, 60000). +-define(CLEAN_CERT_DB, 500). +-define(NOT_TO_BIG, 10). %%==================================================================== %% API @@ -82,26 +85,46 @@ start_link_dist(Opts) -> gen_server:start_link({local, ssl_manager_dist}, ?MODULE, [ssl_manager_dist, Opts], []). %%-------------------------------------------------------------------- --spec connection_init(string()| {der, list()}, client | server) -> - {ok, certdb_ref(), db_handle(), db_handle()}. +-spec connection_init(binary()| {der, list()}, client | server) -> + {ok, certdb_ref(), db_handle(), db_handle(), db_handle(), db_handle()}. %% %% Description: Do necessary initializations for a new connection. %%-------------------------------------------------------------------- +connection_init({der, _} = Trustedcerts, Role) -> + call({connection_init, Trustedcerts, Role}); + +connection_init(<<>> = Trustedcerts, Role) -> + call({connection_init, Trustedcerts, Role}); + connection_init(Trustedcerts, Role) -> call({connection_init, Trustedcerts, Role}). + %%-------------------------------------------------------------------- --spec cache_pem_file(string(), term()) -> {ok, term()} | {error, reason()}. +-spec cache_pem_file(binary(), term()) -> {ok, term()} | {error, reason()}. %% %% Description: Cach a pem file and return its content. %%-------------------------------------------------------------------- cache_pem_file(File, DbHandle) -> - try file:read_file_info(File) of - {ok, #file_info{mtime = LastWrite}} -> - cache_pem_file(File, LastWrite, DbHandle) - catch - _:Reason -> - {error, Reason} + MD5 = crypto:md5(File), + case ssl_certificate_db:lookup_cached_pem(DbHandle, MD5) of + [{Content,_}] -> + {ok, Content}; + [Content] -> + {ok, Content}; + undefined -> + call({cache_pem, {MD5, File}}) end. + +%%-------------------------------------------------------------------- +-spec clear_pem_cache() -> ok. +%% +%% Description: Clear the PEM cache +%%-------------------------------------------------------------------- +clear_pem_cache() -> + %% Not supported for distribution at the moement, should it be? + put(ssl_manager, ssl_manager), + call(unconditionally_clear_pem_cache). + %%-------------------------------------------------------------------- -spec lookup_trusted_cert(term(), reference(), serialnumber(), issuer()) -> undefined | @@ -114,22 +137,15 @@ lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer) -> ssl_certificate_db:lookup_trusted_cert(DbHandle, Ref, SerialNumber, Issuer). %%-------------------------------------------------------------------- --spec client_session_id(host(), inet:port_number(), #ssl_options{}, - der_cert() | undefined) -> session_id(). +-spec new_session_id(integer()) -> session_id(). %% -%% Description: Select a session id for the client. +%% Description: Creates a session id for the server. %%-------------------------------------------------------------------- -client_session_id(Host, Port, SslOpts, OwnCert) -> - call({client_session_id, Host, Port, SslOpts, OwnCert}). +new_session_id(Port) -> + call({new_session_id, Port}). -%%-------------------------------------------------------------------- --spec server_session_id(host(), inet:port_number(), #ssl_options{}, - der_cert()) -> session_id(). -%% -%% Description: Select a session id for the server. -%%-------------------------------------------------------------------- -server_session_id(Port, SuggestedSessionId, SslOpts, OwnCert) -> - call({server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}). +clean_cert_db(Ref, File) -> + erlang:send_after(?CLEAN_CERT_DB, self(), {clean_cert_db, Ref, File}). %%-------------------------------------------------------------------- -spec register_session(inet:port_number(), #session{}) -> ok. @@ -177,6 +193,7 @@ init([Name, Opts]) -> SessionCache = CacheCb:init(proplists:get_value(session_cb_init_args, Opts, [])), Timer = erlang:send_after(SessionLifeTime * 1000, self(), validate_sessions), + erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache), {ok, #state{certificate_db = CertDb, session_cache = SessionCache, session_cache_cb = CacheCb, @@ -194,55 +211,44 @@ init([Name, Opts]) -> %% %% Description: Handling call messages %%-------------------------------------------------------------------- -handle_call({{connection_init, "", _Role}, Pid}, _From, - #state{certificate_db = [CertDb |_], +handle_call({{connection_init, <<>>, _Role}, _Pid}, _From, + #state{certificate_db = [CertDb, FileRefDb, PemChace], session_cache = Cache} = State) -> - erlang:monitor(process, Pid), - Result = {ok, make_ref(),CertDb, Cache}, + Result = {ok, make_ref(),CertDb, FileRefDb, PemChace, Cache}, {reply, Result, State}; handle_call({{connection_init, Trustedcerts, _Role}, Pid}, _From, - #state{certificate_db = [CertDb|_] =Db, + #state{certificate_db = [CertDb, FileRefDb, PemChace] = Db, session_cache = Cache} = State) -> - erlang:monitor(process, Pid), Result = try {ok, Ref} = ssl_certificate_db:add_trusted_certs(Pid, Trustedcerts, Db), - {ok, Ref, CertDb, Cache} + {ok, Ref, CertDb, FileRefDb, PemChace, Cache} catch _:Reason -> {error, Reason} end, {reply, Result, State}; -handle_call({{client_session_id, Host, Port, SslOpts, OwnCert}, _}, _, - #state{session_cache = Cache, - session_cache_cb = CacheCb} = State) -> - Id = ssl_session:id({Host, Port, SslOpts}, Cache, CacheCb, OwnCert), - {reply, Id, State}; - -handle_call({{server_session_id, Port, SuggestedSessionId, SslOpts, OwnCert}, _}, +handle_call({{new_session_id,Port}, _}, _, #state{session_cache_cb = CacheCb, - session_cache = Cache, - session_lifetime = LifeTime} = State) -> - Id = ssl_session:id(Port, SuggestedSessionId, SslOpts, - Cache, CacheCb, LifeTime, OwnCert), + session_cache = Cache} = State) -> + Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb), {reply, Id, State}; -handle_call({{cache_pem, File, LastWrite}, Pid}, _, + +handle_call({{cache_pem, File}, _Pid}, _, #state{certificate_db = Db} = State) -> - try ssl_certificate_db:cache_pem_file(Pid, File, LastWrite, Db) of + try ssl_certificate_db:cache_pem_file(File, Db) of Result -> {reply, Result, State} catch _:Reason -> {reply, {error, Reason}, State} end; -handle_call({{recache_pem, File, LastWrite}, Pid}, From, - #state{certificate_db = Db} = State) -> - ssl_certificate_db:uncache_pem_file(File, Db), - cast({recache_pem, File, LastWrite, Pid, From}), - {noreply, State}. +handle_call({unconditionally_clear_pem_cache, _},_, #state{certificate_db = [_,_,PemChace]} = State) -> + ssl_certificate_db:clear(PemChace), + {reply, ok, State}. %%-------------------------------------------------------------------- -spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. @@ -278,22 +284,7 @@ handle_cast({invalidate_session, Host, Port, handle_cast({invalidate_session, Port, #session{session_id = ID} = Session}, #state{session_cache = Cache, session_cache_cb = CacheCb} = State) -> - invalidate_session(Cache, CacheCb, {Port, ID}, Session, State); - -handle_cast({recache_pem, File, LastWrite, Pid, From}, - #state{certificate_db = [_, FileToRefDb, _]} = State0) -> - case ssl_certificate_db:lookup(File, FileToRefDb) of - undefined -> - {reply, Msg, State} = - handle_call({{cache_pem, File, LastWrite}, Pid}, From, State0), - gen_server:reply(From, Msg), - {noreply, State}; - _ -> %% Send message to self letting cleanup messages be handled - %% first so that no reference to the old version of file - %% exists when we cache the new one. - cast({recache_pem, File, LastWrite, Pid, From}), - {noreply, State0} - end. + invalidate_session(Cache, CacheCb, {Port, ID}, Session, State). %%-------------------------------------------------------------------- -spec handle_info(msg(), #state{}) -> {noreply, #state{}}. @@ -318,23 +309,38 @@ handle_info({delayed_clean_session, Key}, #state{session_cache = Cache, CacheCb:delete(Cache, Key), {noreply, State}; -handle_info({'EXIT', _, _}, State) -> - %% Session validator died!! Do we need to take any action? - %% maybe error log +handle_info(clear_pem_cache, #state{certificate_db = [_,_,PemChace]} = State) -> + case ssl_certificate_db:db_size(PemChace) of + N when N < ?NOT_TO_BIG -> + ok; + _ -> + ssl_certificate_db:clear(PemChace) + end, + erlang:send_after(?CLEAR_PEM_CACHE, self(), clear_pem_cache), {noreply, State}; -handle_info({'DOWN', _Ref, _Type, _Pid, ecacertfile}, State) -> - {noreply, State}; -handle_info({'DOWN', _Ref, _Type, Pid, shutdown}, State) -> - handle_info({remove_trusted_certs, Pid}, State); -handle_info({'DOWN', _Ref, _Type, Pid, _Reason}, State) -> - erlang:send_after(?CERTIFICATE_CACHE_CLEANUP, self(), - {remove_trusted_certs, Pid}), +handle_info({clean_cert_db, Ref, File}, + #state{certificate_db = [CertDb,RefDb, PemCache]} = State) -> + case ssl_certificate_db:ref_count(Ref, RefDb, 0) of + 0 -> + MD5 = crypto:md5(File), + case ssl_certificate_db:lookup_cached_pem(PemCache, MD5) of + [{Content, Ref}] -> + ssl_certificate_db:insert(MD5, Content, PemCache); + undefined -> + ok + end, + ssl_certificate_db:remove(Ref, RefDb), + ssl_certificate_db:remove_trusted_certs(Ref, CertDb); + _ -> + ok + end, {noreply, State}; -handle_info({remove_trusted_certs, Pid}, - #state{certificate_db = Db} = State) -> - ssl_certificate_db:remove_trusted_certs(Pid, Db), + +handle_info({'EXIT', _, _}, State) -> + %% Session validator died!! Do we need to take any action? + %% maybe error log {noreply, State}; handle_info(_Info, State) -> @@ -406,19 +412,6 @@ session_validation({{Port, _}, Session}, LifeTime) -> validate_session(Port, Session, LifeTime), LifeTime. -cache_pem_file(File, LastWrite, DbHandle) -> - case ssl_certificate_db:lookup_cached_certs(DbHandle,File) of - [{_, {Mtime, Content}}] -> - case LastWrite of - Mtime -> - {ok, Content}; - _ -> - call({recache_pem, File, LastWrite}) - end; - [] -> - call({cache_pem, File, LastWrite}) - end. - delay_time() -> case application:get_env(ssl, session_delay_cleanup_time) of {ok, Time} when is_integer(Time) -> @@ -448,3 +441,28 @@ last_delay_timer({{_,_},_}, TRef, {LastServer, _}) -> {LastServer, TRef}; last_delay_timer({_,_}, TRef, {_, LastClient}) -> {TRef, LastClient}. + +%% If we can not generate a not allready in use session ID in +%% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The +%% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which +%% states : "If we can not find a session id in +%% ?GEN_UNIQUE_ID_MAX_TRIES either the RAND code is broken or someone +%% is trying to open roughly very close to 2^128 (or 2^256) SSL +%% sessions to our server" +new_id(_, 0, _, _) -> + <<>>; +new_id(Port, Tries, Cache, CacheCb) -> + Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES), + case CacheCb:lookup(Cache, {Port, Id}) of + undefined -> + Now = calendar:datetime_to_gregorian_seconds({date(), time()}), + %% New sessions can not be set to resumable + %% until handshake is compleate and the + %% other session values are set. + CacheCb:update(Cache, {Port, Id}, #session{session_id = Id, + is_resumable = false, + time_stamp = Now}), + Id; + _ -> + new_id(Port, Tries - 1, Cache, CacheCb) + end. diff --git a/lib/ssl/src/ssl_record.erl b/lib/ssl/src/ssl_record.erl index 830026c825..8e93ce4634 100644 --- a/lib/ssl/src/ssl_record.erl +++ b/lib/ssl/src/ssl_record.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -383,6 +383,8 @@ get_tls_records_aux(Data, Acc) -> %% Description: Creates a protocol version record from a version atom %% or vice versa. %%-------------------------------------------------------------------- +protocol_version('tlsv1.2') -> + {3, 3}; protocol_version('tlsv1.1') -> {3, 2}; protocol_version(tlsv1) -> @@ -391,6 +393,8 @@ protocol_version(sslv3) -> {3, 0}; protocol_version(sslv2) -> %% Backwards compatibility {2, 0}; +protocol_version({3, 3}) -> + 'tlsv1.2'; protocol_version({3, 2}) -> 'tlsv1.1'; protocol_version({3, 1}) -> @@ -445,9 +449,9 @@ supported_protocol_versions() -> end, case application:get_env(ssl, protocol_version) of undefined -> - lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS); + lists:map(Fun, supported_protocol_versions([])); {ok, []} -> - lists:map(Fun, ?DEFAULT_SUPPORTED_VERSIONS); + lists:map(Fun, supported_protocol_versions([])); {ok, Vsns} when is_list(Vsns) -> Versions = lists:filter(fun is_acceptable_version/1, lists:map(Fun, Vsns)), supported_protocol_versions(Versions); @@ -457,7 +461,16 @@ supported_protocol_versions() -> end. supported_protocol_versions([]) -> - ?DEFAULT_SUPPORTED_VERSIONS; + Vsns = case sufficient_tlsv1_2_crypto_support() of + true -> + %%?ALL_SUPPORTED_VERSIONS; %% Add TlS-1.2 as default in R16 + ?DEFAULT_SUPPORTED_VERSIONS; + false -> + ?DEFAULT_SUPPORTED_VERSIONS + end, + application:set_env(ssl, protocol_version, Vsns), + Vsns; + supported_protocol_versions([_|_] = Vsns) -> Vsns. @@ -561,14 +574,14 @@ highest_protocol_version() -> initial_connection_state(ConnectionEnd) -> #connection_state{security_parameters = - initial_security_params(ConnectionEnd), + initial_security_params(ConnectionEnd), sequence_number = 0 }. initial_security_params(ConnectionEnd) -> SecParams = #security_parameters{connection_end = ConnectionEnd, compression_algorithm = ?NULL}, - ssl_cipher:security_parameters(?TLS_NULL_WITH_NULL_NULL, + ssl_cipher:security_parameters(highest_protocol_version(), ?TLS_NULL_WITH_NULL_NULL, SecParams). empty_connection_state(ConnectionEnd) -> @@ -633,7 +646,7 @@ cipher(Type, Version, Fragment, CS0) -> BCA} }} = hash_and_bump_seqno(CS0, Type, Version, Length, Fragment), - {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment), + {Ciphered, CipherS1} = ssl_cipher:cipher(BCA, CipherS0, MacHash, Fragment, Version), CS2 = CS1#connection_state{cipher_state=CipherS1}, {Ciphered, CS2}. @@ -687,6 +700,17 @@ mac_hash({_,_}, ?NULL, _MacSecret, _SeqNo, _Type, mac_hash({3, 0}, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) -> ssl_ssl3:mac_hash(MacAlg, MacSecret, SeqNo, Type, Length, Fragment); mac_hash({3, N} = Version, MacAlg, MacSecret, SeqNo, Type, Length, Fragment) - when N =:= 1; N =:= 2 -> + when N =:= 1; N =:= 2; N =:= 3 -> ssl_tls1:mac_hash(MacAlg, MacSecret, SeqNo, Type, Version, Length, Fragment). + +sufficient_tlsv1_2_crypto_support() -> + Data = "Sampl", + Data2 = "e #1", + Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39, + 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>, + try + crypto:sha256_mac(Key, lists:flatten([Data, Data2])), + true + catch _:_ -> false + end. diff --git a/lib/ssl/src/ssl_record.hrl b/lib/ssl/src/ssl_record.hrl index 282d642138..f73da92a52 100644 --- a/lib/ssl/src/ssl_record.hrl +++ b/lib/ssl/src/ssl_record.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -47,6 +47,7 @@ key_material_length, % unit 8 expanded_key_material_length, % unit 8 mac_algorithm, % unit 8 + prf_algorithm, % unit 8 hash_size, % unit 8 compression_algorithm, % unit 8 master_secret, % opaque 48 @@ -97,10 +98,15 @@ %-define(TRUE, 0). %% Already defined by ssl_internal.hrl %-define(FALSE, 1). %% Already defined by ssl_internal.hrl -%% MACAlgorithm +%% MAC and PRF Algorithms %-define(NULL, 0). %% Already defined by ssl_internal.hrl -define(MD5, 1). -define(SHA, 2). +-define(MD5SHA, 4711). %% Not defined in protocol used to represent old prf +-define(SHA224, 3). +-define(SHA256, 4). +-define(SHA384, 5). +-define(SHA512, 6). %% CompressionMethod % -define(NULL, 0). %% Already defined by ssl_internal.hrl @@ -176,7 +182,8 @@ content, % opaque content[TLSCompressed.length]; mac, % opaque MAC[CipherSpec.hash_size]; padding, % unit 8 padding[GenericBlockCipher.padding_length]; - padding_length % uint8 padding_length; + padding_length, % uint8 padding_length; + next_iv % opaque IV[SecurityParameters.record_iv_length]; }). -endif. % -ifdef(ssl_record). diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index df5d7e0146..2ad422fc03 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -28,9 +28,9 @@ -include("ssl_internal.hrl"). %% Internal application API --export([is_new/2, id/4, id/7, valid_session/2]). +-export([is_new/2, client_id/4, server_id/6, valid_session/2]). --define(GEN_UNIQUE_ID_MAX_TRIES, 10). +-define('24H_in_sec', 8640). -type seconds() :: integer(). @@ -48,13 +48,13 @@ is_new(_ClientSuggestion, _ServerDecision) -> true. %%-------------------------------------------------------------------- --spec id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(), +-spec client_id({host(), inet:port_number(), #ssl_options{}}, db_handle(), atom(), undefined | binary()) -> binary(). %% -%% Description: Should be called by the client side to get an id +%% Description: Should be called by the client side to get an id %% for the client hello message. %%-------------------------------------------------------------------- -id(ClientInfo, Cache, CacheCb, OwnCert) -> +client_id(ClientInfo, Cache, CacheCb, OwnCert) -> case select_session(ClientInfo, Cache, CacheCb, OwnCert) of no_session -> <<>>; @@ -62,27 +62,6 @@ id(ClientInfo, Cache, CacheCb, OwnCert) -> SessionId end. -%%-------------------------------------------------------------------- --spec id(inet:port_number(), binary(), #ssl_options{}, db_handle(), - atom(), seconds(), binary()) -> binary(). -%% -%% Description: Should be called by the server side to get an id -%% for the server hello message. -%%-------------------------------------------------------------------- -id(Port, <<>>, _, Cache, CacheCb, _, _) -> - new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb); - -id(Port, SuggestedSessionId, #ssl_options{reuse_sessions = ReuseEnabled, - reuse_session = ReuseFun}, - Cache, CacheCb, SecondLifeTime, OwnCert) -> - case is_resumable(SuggestedSessionId, Port, ReuseEnabled, - ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) of - true -> - SuggestedSessionId; - false -> - new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb) - end. -%%-------------------------------------------------------------------- -spec valid_session(#session{}, seconds()) -> boolean(). %% %% Description: Check that the session has not expired @@ -91,57 +70,51 @@ valid_session(#session{time_stamp = TimeStamp}, LifeTime) -> Now = calendar:datetime_to_gregorian_seconds({date(), time()}), Now - TimeStamp < LifeTime. +server_id(Port, <<>>, _SslOpts, _Cert, _, _) -> + {ssl_manager:new_session_id(Port), undefined}; +server_id(Port, SuggestedId, + #ssl_options{reuse_sessions = ReuseEnabled, + reuse_session = ReuseFun}, + Cert, Cache, CacheCb) -> + LifeTime = case application:get_env(ssl, session_lifetime) of + {ok, Time} when is_integer(Time) -> Time; + _ -> ?'24H_in_sec' + end, + case is_resumable(SuggestedId, Port, ReuseEnabled,ReuseFun, + Cache, CacheCb, LifeTime, Cert) + of + {true, Resumed} -> + {SuggestedId, Resumed}; + {false, undefined} -> + {ssl_manager:new_session_id(Port), undefined} + end. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- +select_session({_, _, #ssl_options{reuse_sessions=false}}, _Cache, _CacheCb, _OwnCert) -> + no_session; select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> Sessions = CacheCb:select_session(Cache, {HostIP, Port}), select_session(Sessions, SslOpts, OwnCert). select_session([], _, _) -> no_session; - -select_session(Sessions, #ssl_options{ciphers = Ciphers, - reuse_sessions = ReuseSession}, OwnCert) -> - IsResumable = - fun(Session) -> - ReuseSession andalso resumable(Session#session.is_resumable) andalso - lists:member(Session#session.cipher_suite, Ciphers) - andalso (OwnCert == Session#session.own_certificate) +select_session(Sessions, #ssl_options{ciphers = Ciphers}, OwnCert) -> + IsNotResumable = + fun([_Id, Session]) -> + not (resumable(Session#session.is_resumable) andalso + lists:member(Session#session.cipher_suite, Ciphers) + andalso (OwnCert == Session#session.own_certificate)) end, - case [Id || [Id, Session] <- Sessions, IsResumable(Session)] of - [] -> - no_session; - List -> - hd(List) - end. - -%% If we can not generate a not allready in use session ID in -%% ?GEN_UNIQUE_ID_MAX_TRIES we make the new session uncacheable The -%% value of ?GEN_UNIQUE_ID_MAX_TRIES is stolen from open SSL which -%% states : "If we can not find a session id in -%% ?GEN_UNIQUE_ID_MAX_TRIES either the RAND code is broken or someone -%% is trying to open roughly very close to 2^128 (or 2^256) SSL -%% sessions to our server" -new_id(_, 0, _, _) -> - <<>>; -new_id(Port, Tries, Cache, CacheCb) -> - Id = crypto:rand_bytes(?NUM_OF_SESSION_ID_BYTES), - case CacheCb:lookup(Cache, {Port, Id}) of - undefined -> - Now = calendar:datetime_to_gregorian_seconds({date(), time()}), - %% New sessions can not be set to resumable - %% until handshake is compleate and the - %% other session values are set. - CacheCb:update(Cache, {Port, Id}, #session{session_id = Id, - is_resumable = false, - time_stamp = Now}), - Id; - _ -> - new_id(Port, Tries - 1, Cache, CacheCb) + case lists:dropwhile(IsNotResumable, Sessions) of + [] -> no_session; + [[Id, _]|_] -> Id end. -is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache, +is_resumable(_, _, false, _, _, _, _, _) -> + {false, undefined}; +is_resumable(SuggestedSessionId, Port, true, ReuseFun, Cache, CacheCb, SecondLifeTime, OwnCert) -> case CacheCb:lookup(Cache, {Port, SuggestedSessionId}) of #session{cipher_suite = CipherSuite, @@ -149,14 +122,17 @@ is_resumable(SuggestedSessionId, Port, ReuseEnabled, ReuseFun, Cache, compression_method = Compression, is_resumable = IsResumable, peer_certificate = PeerCert} = Session -> - ReuseEnabled - andalso resumable(IsResumable) + case resumable(IsResumable) andalso (OwnCert == SessionOwnCert) - andalso valid_session(Session, SecondLifeTime) - andalso ReuseFun(SuggestedSessionId, PeerCert, - Compression, CipherSuite); + andalso valid_session(Session, SecondLifeTime) + andalso ReuseFun(SuggestedSessionId, PeerCert, + Compression, CipherSuite) + of + true -> {true, Session}; + false -> {false, undefined} + end; undefined -> - false + {false, undefined} end. resumable(new) -> diff --git a/lib/ssl/src/ssl_session_cache.erl b/lib/ssl/src/ssl_session_cache.erl index f9bbf905e1..5c6ee3c54c 100644 --- a/lib/ssl/src/ssl_session_cache.erl +++ b/lib/ssl/src/ssl_session_cache.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -32,7 +32,7 @@ %% Description: Return table reference. Called by ssl_manager process. %%-------------------------------------------------------------------- init(_) -> - ets:new(cache_name(), [set, protected]). + ets:new(cache_name(), [ordered_set, protected]). %%-------------------------------------------------------------------- %% Description: Handles cache table at termination of ssl manager. diff --git a/lib/ssl/src/ssl_ssl3.erl b/lib/ssl/src/ssl_ssl3.erl index f2926b2d2f..a11c5b8c0c 100644 --- a/lib/ssl/src/ssl_ssl3.erl +++ b/lib/ssl/src/ssl_ssl3.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -54,9 +54,9 @@ master_secret(PremasterSecret, ClientRandom, ServerRandom) -> Block = generate_keyblock(PremasterSecret, ClientRandom, ServerRandom, 48), Block. --spec finished(client | server, binary(), {binary(), binary()}) -> binary(). +-spec finished(client | server, binary(), [binary()]) -> binary(). -finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> +finished(Role, MasterSecret, Handshake) -> %% draft-ietf-tls-ssl-version3-00 - 5.6.9 Finished %% struct { %% opaque md5_hash[16]; @@ -70,13 +70,13 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% SHA(handshake_messages + Sender + %% master_secret + pad1)); Sender = get_sender(Role), - MD5 = handshake_hash(?MD5, MasterSecret, Sender, MD5Hash), - SHA = handshake_hash(?SHA, MasterSecret, Sender, SHAHash), + MD5 = handshake_hash(?MD5, MasterSecret, Sender, Handshake), + SHA = handshake_hash(?SHA, MasterSecret, Sender, Handshake), <<MD5/binary, SHA/binary>>. --spec certificate_verify(OID::tuple(), binary(), {binary(), binary()}) -> binary(). +-spec certificate_verify(md5sha | sha, binary(), [binary()]) -> binary(). -certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) -> +certificate_verify(md5sha, MasterSecret, Handshake) -> %% md5_hash %% MD5(master_secret + pad_2 + %% MD5(handshake_messages + master_secret + pad_1)); @@ -84,15 +84,16 @@ certificate_verify(?'rsaEncryption', MasterSecret, {MD5Hash, SHAHash}) -> %% SHA(master_secret + pad_2 + %% SHA(handshake_messages + master_secret + pad_1)); - MD5 = handshake_hash(?MD5, MasterSecret, undefined, MD5Hash), - SHA = handshake_hash(?SHA, MasterSecret, undefined, SHAHash), + MD5 = handshake_hash(?MD5, MasterSecret, undefined, Handshake), + SHA = handshake_hash(?SHA, MasterSecret, undefined, Handshake), <<MD5/binary, SHA/binary>>; -certificate_verify(?'id-dsa', MasterSecret, {_, SHAHash}) -> +certificate_verify(sha, MasterSecret, Handshake) -> %% sha_hash %% SHA(master_secret + pad_2 + %% SHA(handshake_messages + master_secret + pad_1)); - handshake_hash(?SHA, MasterSecret, undefined, SHAHash). + + handshake_hash(?SHA, MasterSecret, undefined, Handshake). -spec mac_hash(integer(), binary(), integer(), integer(), integer(), binary()) -> binary(). @@ -152,28 +153,17 @@ suites() -> %%% Internal functions %%-------------------------------------------------------------------- -hash(?MD5, Data) -> +hash(?MD5, Data) -> crypto:md5(Data); -hash(?SHA, Data) -> +hash(?SHA, Data) -> crypto:sha(Data). -hash_update(?MD5, Context, Data) -> - crypto:md5_update(Context, Data); -hash_update(?SHA, Context, Data) -> - crypto:sha_update(Context, Data). - -hash_final(?MD5, Context) -> - crypto:md5_final(Context); -hash_final(?SHA, Context) -> - crypto:sha_final(Context). - %%pad_1(?NULL) -> %% ""; pad_1(?MD5) -> <<"666666666666666666666666666666666666666666666666">>; pad_1(?SHA) -> <<"6666666666666666666666666666666666666666">>. - %%pad_2(?NULL) -> %% ""; pad_2(?MD5) -> @@ -189,19 +179,11 @@ mac_hash(Method, Secret, Data) -> InnerHash = hash(Method, [Secret, pad_1(Method), Data]), hash(Method, [Secret, pad_2(Method), InnerHash]). -handshake_hash(Method, HandshakeHash, Extra) -> - HSH = hash_update(Method, HandshakeHash, Extra), - hash_final(Method, HSH). - -handshake_hash(Method, MasterSecret, undefined, HandshakeHash) -> - InnerHash = - handshake_hash(Method, HandshakeHash, - [MasterSecret, pad_1(Method)]), +handshake_hash(Method, MasterSecret, undefined, Handshake) -> + InnerHash = hash(Method, [Handshake, MasterSecret, pad_1(Method)]), hash(Method, [MasterSecret, pad_2(Method), InnerHash]); -handshake_hash(Method, MasterSecret, Sender, HandshakeHash) -> - InnerHash = - handshake_hash(Method, HandshakeHash, - [Sender, MasterSecret, pad_1(Method)]), +handshake_hash(Method, MasterSecret, Sender, Handshake) -> + InnerHash = hash(Method, [Handshake, Sender, MasterSecret, pad_1(Method)]), hash(Method, [MasterSecret, pad_2(Method), InnerHash]). get_sender(client) -> "CLNT"; diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl index 5f9850c386..1daf9640ab 100644 --- a/lib/ssl/src/ssl_tls1.erl +++ b/lib/ssl/src/ssl_tls1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2010. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -26,27 +26,29 @@ -include("ssl_cipher.hrl"). -include("ssl_internal.hrl"). --include("ssl_record.hrl"). +-include("ssl_record.hrl"). --export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7, - setup_keys/6, suites/0]). +-export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7, + setup_keys/8, suites/1, prf/5]). %%==================================================================== %% Internal application API %%==================================================================== --spec master_secret(binary(), binary(), binary()) -> binary(). +-spec master_secret(integer(), binary(), binary(), binary()) -> binary(). -master_secret(PreMasterSecret, ClientRandom, ServerRandom) -> - %% RFC 2246 & 4346 - 8.1 %% master_secret = PRF(pre_master_secret, - %% "master secret", ClientHello.random + - %% ServerHello.random)[0..47]; - prf(PreMasterSecret, <<"master secret">>, +master_secret(PrfAlgo, PreMasterSecret, ClientRandom, ServerRandom) -> + %% RFC 2246 & 4346 && RFC 5246 - 8.1 %% master_secret = PRF(pre_master_secret, + %% "master secret", ClientHello.random + + %% ServerHello.random)[0..47]; + + prf(PrfAlgo, PreMasterSecret, <<"master secret">>, [ClientRandom, ServerRandom], 48). --spec finished(client | server, binary(), {binary(), binary()}) -> binary(). +-spec finished(client | server, integer(), integer(), binary(), [binary()]) -> binary(). -finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> +finished(Role, Version, PrfAlgo, MasterSecret, Handshake) + when Version == 1; Version == 2; PrfAlgo == ?MD5SHA -> %% RFC 2246 & 4346 - 7.4.9. Finished %% struct { %% opaque verify_data[12]; @@ -55,26 +57,39 @@ finished(Role, MasterSecret, {MD5Hash, SHAHash}) -> %% verify_data %% PRF(master_secret, finished_label, MD5(handshake_messages) + %% SHA-1(handshake_messages)) [0..11]; - MD5 = hash_final(?MD5, MD5Hash), - SHA = hash_final(?SHA, SHAHash), - prf(MasterSecret, finished_label(Role), [MD5, SHA], 12). + MD5 = crypto:md5(Handshake), + SHA = crypto:sha(Handshake), + prf(?MD5SHA, MasterSecret, finished_label(Role), [MD5, SHA], 12); + +finished(Role, Version, PrfAlgo, MasterSecret, Handshake) + when Version == 3 -> + %% RFC 5246 - 7.4.9. Finished + %% struct { + %% opaque verify_data[12]; + %% } Finished; + %% + %% verify_data + %% PRF(master_secret, finished_label, Hash(handshake_messages)) [0..11]; + Hash = crypto:hash(mac_algo(PrfAlgo), Handshake), + prf(PrfAlgo, MasterSecret, finished_label(Role), Hash, 12). --spec certificate_verify(OID::tuple(), {binary(), binary()}) -> binary(). +-spec certificate_verify(md5sha | sha, integer(), [binary()]) -> binary(). -certificate_verify(?'rsaEncryption', {MD5Hash, SHAHash}) -> - MD5 = hash_final(?MD5, MD5Hash), - SHA = hash_final(?SHA, SHAHash), +certificate_verify(md5sha, _Version, Handshake) -> + MD5 = crypto:md5(Handshake), + SHA = crypto:sha(Handshake), <<MD5/binary, SHA/binary>>; -certificate_verify(?'id-dsa', {_, SHAHash}) -> - hash_final(?SHA, SHAHash). +certificate_verify(HashAlgo, _Version, Handshake) -> + Hash = crypto:hash(HashAlgo, Handshake). --spec setup_keys(binary(), binary(), binary(), integer(), - integer(), integer()) -> {binary(), binary(), binary(), +-spec setup_keys(integer(), integer(), binary(), binary(), binary(), integer(), + integer(), integer()) -> {binary(), binary(), binary(), binary(), binary(), binary()}. -setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, - KeyMatLen, IVSize) -> +setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, + KeyMatLen, IVSize) + when Version == 1 -> %% RFC 2246 - 6.3. Key calculation %% key_block = PRF(SecurityParameters.master_secret, %% "key expansion", @@ -88,36 +103,67 @@ setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, %% client_write_IV[SecurityParameters.IV_size] %% server_write_IV[SecurityParameters.IV_size] WantedLength = 2 * (HashSize + KeyMatLen + IVSize), - KeyBlock = prf(MasterSecret, "key expansion", + KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion", [ServerRandom, ClientRandom], WantedLength), <<ClientWriteMacSecret:HashSize/binary, ServerWriteMacSecret:HashSize/binary, ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary, ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock, {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, - ServerWriteKey, ClientIV, ServerIV}. + ServerWriteKey, ClientIV, ServerIV}; + +%% TLS v1.1 +setup_keys(Version, _PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, + KeyMatLen, IVSize) + when Version == 2 -> + %% RFC 4346 - 6.3. Key calculation + %% key_block = PRF(SecurityParameters.master_secret, + %% "key expansion", + %% SecurityParameters.server_random + + %% SecurityParameters.client_random); + %% Then the key_block is partitioned as follows: + %% client_write_MAC_secret[SecurityParameters.hash_size] + %% server_write_MAC_secret[SecurityParameters.hash_size] + %% client_write_key[SecurityParameters.key_material_length] + %% server_write_key[SecurityParameters.key_material_length] + %% + %% RFC 4346 is incomplete, the client and server IVs have to + %% be generated just like for TLS 1.0 + WantedLength = 2 * (HashSize + KeyMatLen + IVSize), + KeyBlock = prf(?MD5SHA, MasterSecret, "key expansion", + [ServerRandom, ClientRandom], WantedLength), + <<ClientWriteMacSecret:HashSize/binary, + ServerWriteMacSecret:HashSize/binary, + ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary, + ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock, + {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, + ServerWriteKey, ClientIV, ServerIV}; -%% TLS v1.1 uncomment when supported. -%% setup_keys(MasterSecret, ServerRandom, ClientRandom, HashSize, KeyMatLen) -> -%% %% RFC 4346 - 6.3. Key calculation -%% %% key_block = PRF(SecurityParameters.master_secret, -%% %% "key expansion", -%% %% SecurityParameters.server_random + -%% %% SecurityParameters.client_random); -%% %% Then the key_block is partitioned as follows: -%% %% client_write_MAC_secret[SecurityParameters.hash_size] -%% %% server_write_MAC_secret[SecurityParameters.hash_size] -%% %% client_write_key[SecurityParameters.key_material_length] -%% %% server_write_key[SecurityParameters.key_material_length] -%% WantedLength = 2 * (HashSize + KeyMatLen), -%% KeyBlock = prf(MasterSecret, "key expansion", -%% [ServerRandom, ClientRandom], WantedLength), -%% <<ClientWriteMacSecret:HashSize/binary, -%% ServerWriteMacSecret:HashSize/binary, -%% ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary>> -%% = KeyBlock, -%% {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, -%% ServerWriteKey, undefined, undefined}. +%% TLS v1.2 +setup_keys(Version, PrfAlgo, MasterSecret, ServerRandom, ClientRandom, HashSize, + KeyMatLen, IVSize) + when Version == 3 -> + %% RFC 5246 - 6.3. Key calculation + %% key_block = PRF(SecurityParameters.master_secret, + %% "key expansion", + %% SecurityParameters.server_random + + %% SecurityParameters.client_random); + %% Then the key_block is partitioned as follows: + %% client_write_MAC_secret[SecurityParameters.hash_size] + %% server_write_MAC_secret[SecurityParameters.hash_size] + %% client_write_key[SecurityParameters.key_material_length] + %% server_write_key[SecurityParameters.key_material_length] + %% client_write_IV[SecurityParameters.fixed_iv_length] + %% server_write_IV[SecurityParameters.fixed_iv_length] + WantedLength = 2 * (HashSize + KeyMatLen + IVSize), + KeyBlock = prf(PrfAlgo, MasterSecret, "key expansion", + [ServerRandom, ClientRandom], WantedLength), + <<ClientWriteMacSecret:HashSize/binary, + ServerWriteMacSecret:HashSize/binary, + ClientWriteKey:KeyMatLen/binary, ServerWriteKey:KeyMatLen/binary, + ClientIV:IVSize/binary, ServerIV:IVSize/binary>> = KeyBlock, + {ClientWriteMacSecret, ServerWriteMacSecret, ClientWriteKey, + ServerWriteKey, ClientIV, ServerIV}. -spec mac_hash(integer(), binary(), integer(), integer(), tls_version(), integer(), binary()) -> binary(). @@ -134,9 +180,9 @@ mac_hash(Method, Mac_write_secret, Seq_num, Type, {Major, Minor}, Fragment]), Mac. --spec suites() -> [cipher_suite()]. +-spec suites(1|2|3) -> [cipher_suite()]. -suites() -> +suites(Minor) when Minor == 1; Minor == 2-> [ ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA, ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA, @@ -152,7 +198,19 @@ suites() -> ?TLS_RSA_WITH_RC4_128_MD5, ?TLS_DHE_RSA_WITH_DES_CBC_SHA, ?TLS_RSA_WITH_DES_CBC_SHA - ]. + ]; + +suites(Minor) when Minor == 3 -> + [ + ?TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_256_CBC_SHA256, + ?TLS_RSA_WITH_AES_256_CBC_SHA256, + ?TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + ?TLS_DHE_DSS_WITH_AES_128_CBC_SHA256, + ?TLS_RSA_WITH_AES_128_CBC_SHA256 + %% ?TLS_DH_anon_WITH_AES_128_CBC_SHA256, + %% ?TLS_DH_anon_WITH_AES_256_CBC_SHA256 + ] ++ suites(2). %%-------------------------------------------------------------------- %%% Internal functions @@ -163,7 +221,19 @@ hmac_hash(?NULL, _, _) -> hmac_hash(?MD5, Key, Value) -> crypto:md5_mac(Key, Value); hmac_hash(?SHA, Key, Value) -> - crypto:sha_mac(Key, Value). + crypto:sha_mac(Key, Value); +hmac_hash(?SHA256, Key, Value) -> + crypto:sha256_mac(Key, Value); +hmac_hash(?SHA384, Key, Value) -> + crypto:sha384_mac(Key, Value); +hmac_hash(?SHA512, Key, Value) -> + crypto:sha512_mac(Key, Value). + +mac_algo(?MD5) -> md5; +mac_algo(?SHA) -> sha; +mac_algo(?SHA256) -> sha256; +mac_algo(?SHA384) -> sha384; +mac_algo(?SHA512) -> sha512. % First, we define a data expansion function, P_hash(secret, data) that % uses a single hash function to expand a secret and seed into an @@ -182,7 +252,7 @@ p_hash(_Secret, _Seed, WantedLength, _Method, _N, [Last | Acc]) when WantedLength =< 0 -> Keep = byte_size(Last) + WantedLength, <<B:Keep/binary, _/binary>> = Last, - lists:reverse(Acc, [B]); + list_to_binary(lists:reverse(Acc, [B])); p_hash(Secret, Seed, WantedLength, Method, N, Acc) -> N1 = N+1, Bin = hmac_hash(Method, Secret, [a(N1, Secret, Seed, Method), Seed]), @@ -214,13 +284,18 @@ split_secret(BinSecret) -> <<_:Div/binary, Secret2:EvenLength/binary>> = BinSecret, {Secret1, Secret2}. -prf(Secret, Label, Seed, WantedLength) -> +prf(?MD5SHA, Secret, Label, Seed, WantedLength) -> %% PRF(secret, label, seed) = P_MD5(S1, label + seed) XOR %% P_SHA-1(S2, label + seed); {S1, S2} = split_secret(Secret), LS = list_to_binary([Label, Seed]), crypto:exor(p_hash(S1, LS, WantedLength, ?MD5), - p_hash(S2, LS, WantedLength, ?SHA)). + p_hash(S2, LS, WantedLength, ?SHA)); + +prf(MAC, Secret, Label, Seed, WantedLength) -> + %% PRF(secret, label, seed) = P_SHA256(secret, label + seed); + LS = list_to_binary([Label, Seed]), + p_hash(Secret, LS, WantedLength, MAC). %%%% Misc help functions %%%% @@ -228,8 +303,3 @@ finished_label(client) -> <<"client finished">>; finished_label(server) -> <<"server finished">>. - -hash_final(?MD5, Conntext) -> - crypto:md5_final(Conntext); -hash_final(?SHA, Conntext) -> - crypto:sha_final(Conntext). diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index 1c61eb7ccc..a8476b104f 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2011-2011. All Rights Reserved. +%% Copyright Ericsson AB 2011-2012. 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 diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index 6b1da63d08..a0f54c0359 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -120,11 +120,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(HRL_FILES_NEEDED_IN_TEST) $(COVER_FILE) $(RELSYSDIR) - $(INSTALL_DATA) ssl.spec ssl.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(HRL_FILES_NEEDED_IN_TEST) $(COVER_FILE) "$(RELSYSDIR)" + $(INSTALL_DATA) ssl.spec ssl.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 590ecf33ca..93f7209aea 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -27,9 +27,11 @@ -include_lib("common_test/include/ct.hrl"). -include_lib("public_key/include/public_key.hrl"). +-include("ssl_internal.hrl"). -include("ssl_alert.hrl"). -include("ssl_internal.hrl"). -include("ssl_record.hrl"). +-include("ssl_handshake.hrl"). -define('24H_in_sec', 86400). -define(TIMEOUT, 60000). @@ -50,10 +52,10 @@ %%-------------------------------------------------------------------- init_per_suite(Config0) -> Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), - ssl:start(), %% make rsa certs using oppenssl Result = @@ -90,46 +92,28 @@ end_per_suite(_Config) -> %% variable, but should NOT alter/remove any existing entries. %% Description: Initialization before each test case %%-------------------------------------------------------------------- -init_per_testcase(session_cache_process_list, Config) -> - init_customized_session_cache(list, Config); - -init_per_testcase(session_cache_process_mnesia, Config) -> - mnesia:start(), - init_customized_session_cache(mnesia, Config); - -init_per_testcase(reuse_session_expired, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_lifetime, ?EXPIRE), - ssl:start(), - [{watchdog, Dog} | Config]; - init_per_testcase(no_authority_key_identifier, Config) -> %% Clear cach so that root cert will not %% be found. - ssl:stop(), - ssl:start(), + ssl:clear_pem_cache(), Config; -init_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3; - TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3; - TestCase == ciphers_dsa_signed_certs_ssl3; - TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3 -> +init_per_testcase(protocol_versions, Config) -> ssl:stop(), application:load(ssl), - application:set_env(ssl, protocol_version, sslv3), + %% For backwards compatibility sslv2 should be filtered out. + application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), ssl:start(), Config; -init_per_testcase(protocol_versions, Config) -> +init_per_testcase(reuse_session_expired, Config0) -> + Config = lists:keydelete(watchdog, 1, Config0), + Dog = ssl_test_lib:timetrap(?EXPIRE * 1000 * 5), ssl:stop(), application:load(ssl), - %% For backwards compatibility sslv2 should be filtered out. - application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), + application:set_env(ssl, session_lifetime, ?EXPIRE), ssl:start(), - Config; + [{watchdog, Dog} | Config]; init_per_testcase(empty_protocol_versions, Config) -> ssl:stop(), @@ -138,24 +122,15 @@ init_per_testcase(empty_protocol_versions, Config) -> ssl:start(), Config; -init_per_testcase(different_ca_peer_sign, Config0) -> - ssl_test_lib:make_mix_cert(Config0); +%% init_per_testcase(different_ca_peer_sign, Config0) -> +%% ssl_test_lib:make_mix_cert(Config0); init_per_testcase(_TestCase, Config0) -> + test_server:format("TLS/SSL version ~p~n ", [ssl_record:supported_protocol_versions()]), Config = lists:keydelete(watchdog, 1, Config0), Dog = test_server:timetrap(?TIMEOUT), [{watchdog, Dog} | Config]. -init_customized_session_cache(Type, Config0) -> - Config = lists:keydelete(watchdog, 1, Config0), - Dog = test_server:timetrap(?TIMEOUT), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_cb, ?MODULE), - application:set_env(ssl, session_cb_init_args, [Type]), - ssl:start(), - [{watchdog, Dog} | Config]. - %%-------------------------------------------------------------------- %% Function: end_per_testcase(TestCase, Config) -> _ %% Case - atom() @@ -164,27 +139,10 @@ init_customized_session_cache(Type, Config0) -> %% A list of key/value pairs, holding the test case configuration. %% Description: Cleanup after each test case %%-------------------------------------------------------------------- -end_per_testcase(session_cache_process_list, Config) -> - application:unset_env(ssl, session_cb), - end_per_testcase(default_action, Config); -end_per_testcase(session_cache_process_mnesia, Config) -> - application:unset_env(ssl, session_cb), - application:unset_env(ssl, session_cb_init_args), - mnesia:stop(), - ssl:stop(), - ssl:start(), - end_per_testcase(default_action, Config); end_per_testcase(reuse_session_expired, Config) -> application:unset_env(ssl, session_lifetime), end_per_testcase(default_action, Config); -end_per_testcase(TestCase, Config) when TestCase == ciphers_rsa_signed_certs_ssl3; - TestCase == ciphers_rsa_signed_certs_openssl_names_ssl3; - TestCase == ciphers_dsa_signed_certs_ssl3; - TestCase == ciphers_dsa_signed_certs_openssl_names_ssl3; - TestCase == protocol_versions; - TestCase == empty_protocol_versions-> - application:unset_env(ssl, protocol_version), - end_per_testcase(default_action, Config); + end_per_testcase(_TestCase, Config) -> Dog = ?config(watchdog, Config), case Dog of @@ -205,73 +163,170 @@ end_per_testcase(_TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [app, alerts, connection_info, protocol_versions, - empty_protocol_versions, controlling_process, - controller_dies, client_closes_socket, - connect_dist, peername, peercert, sockname, socket_options, - invalid_inet_get_option, invalid_inet_get_option_not_list, + [ + {group, basic}, + {group, options}, + {group, session}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'} + ]. + +groups() -> + [{basic, [], basic_tests()}, + {options, [], options_tests()}, + {'tlsv1.2', [], all_versions_groups()}, + {'tlsv1.1', [], all_versions_groups()}, + {'tlsv1', [], all_versions_groups() ++ rizzo_tests()}, + {'sslv3', [], all_versions_groups() ++ rizzo_tests()}, + {api,[], api_tests()}, + {certificate_verify, [], certificate_verify_tests()}, + {session, [], session_tests()}, + {renegotiate, [], renegotiate_tests()}, + {ciphers, [], cipher_tests()}, + {error_handling_tests, [], error_handling_tests()} + ]. + +all_versions_groups ()-> + [{group, api}, + {group, certificate_verify}, + {group, renegotiate}, + {group, ciphers}, + {group, error_handling_tests}]. + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. + + +end_per_group(_GroupName, Config) -> + Config. + +basic_tests() -> + [app, + alerts, + send_close, + connect_twice, + connect_dist + ]. + +options_tests() -> + [der_input, + misc_ssl_options, + 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, + 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, - shutdown_write, shutdown_both, shutdown_error, - ciphers_rsa_signed_certs, ciphers_rsa_signed_certs_ssl3, - ciphers_rsa_signed_certs_openssl_names, - ciphers_rsa_signed_certs_openssl_names_ssl3, - ciphers_dsa_signed_certs, ciphers_dsa_signed_certs_ssl3, - ciphers_dsa_signed_certs_openssl_names, - ciphers_dsa_signed_certs_openssl_names_ssl3, - anonymous_cipher_suites, - default_reject_anonymous, - send_close, - close_transport_accept, dh_params, - server_verify_peer_passive, server_verify_peer_active, + dh_params, + ecertfile, + ecacertfile, + ekeyfile, + eoptions, + protocol_versions, + empty_protocol_versions, + ipv6, + reuseaddr]. + +api_tests() -> + [connection_info, + peername, + peercert, + sockname, + versions, + controlling_process, + upgrade, + upgrade_with_timeout, + shutdown, + shutdown_write, + shutdown_both, + shutdown_error, + hibernate + ]. + +certificate_verify_tests() -> + [server_verify_peer_passive, + server_verify_peer_active, server_verify_peer_active_once, - server_verify_none_passive, server_verify_none_active, + server_verify_none_passive, + server_verify_none_active, server_verify_none_active_once, - server_verify_no_cacerts, server_require_peer_cert_ok, + server_verify_no_cacerts, + server_require_peer_cert_ok, server_require_peer_cert_fail, server_verify_client_once_passive, server_verify_client_once_active, server_verify_client_once_active_once, - client_verify_none_passive, client_verify_none_active, + client_verify_none_passive, + client_verify_none_active, client_verify_none_active_once, - reuse_session, - reuse_session_expired, - server_does_not_want_to_reuse_session, - client_renegotiate, server_renegotiate, - client_renegotiate_reused_session, - server_renegotiate_reused_session, - client_no_wrap_sequence_number, - server_no_wrap_sequence_number, extended_key_usage_verify_peer, + extended_key_usage_verify_peer, extended_key_usage_verify_none, - no_authority_key_identifier, invalid_signature_client, - invalid_signature_server, cert_expired, + invalid_signature_client, + invalid_signature_server, + cert_expired, client_with_cert_cipher_suites_handshake, verify_fun_always_run_client, verify_fun_always_run_server, - unknown_server_ca_fail, der_input, + unknown_server_ca_fail, unknown_server_ca_accept_verify_none, unknown_server_ca_accept_verify_peer, unknown_server_ca_accept_backwardscompatibility, - %%different_ca_peer_sign, - no_reuses_session_server_restart_new_cert, - no_reuses_session_server_restart_new_cert_file, reuseaddr, - hibernate, connect_twice, renegotiate_dos_mitigate_active, - renegotiate_dos_mitigate_passive, - tcp_error_propagation_in_active_mode, rizzo, no_rizzo_rc4 + no_authority_key_identifier ]. -groups() -> - []. +session_tests() -> + [reuse_session, + reuse_session_expired, + server_does_not_want_to_reuse_session, + no_reuses_session_server_restart_new_cert, + no_reuses_session_server_restart_new_cert_file]. -init_per_group(_GroupName, Config) -> - Config. +renegotiate_tests() -> + [client_renegotiate, + server_renegotiate, + client_renegotiate_reused_session, + server_renegotiate_reused_session, + client_no_wrap_sequence_number, + server_no_wrap_sequence_number, + renegotiate_dos_mitigate_active, + renegotiate_dos_mitigate_passive]. -end_per_group(_GroupName, Config) -> - Config. +cipher_tests() -> + [cipher_suites, + ciphers_rsa_signed_certs, + ciphers_rsa_signed_certs_openssl_names, + ciphers_dsa_signed_certs, + ciphers_dsa_signed_certs_openssl_names, + anonymous_cipher_suites, + default_reject_anonymous]. + +error_handling_tests()-> + [controller_dies, + client_closes_socket, + tcp_error_propagation_in_active_mode, + tcp_connect, + tcp_connect_big, + close_transport_accept + ]. + +rizzo_tests() -> + [rizzo, + no_rizzo_rc4]. %% Test cases starts here. %%-------------------------------------------------------------------- @@ -1724,21 +1779,7 @@ ciphers_rsa_signed_certs(Config) when is_list(Config) -> ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:rsa_suites(), - test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]), - run_suites(Ciphers, Version, Config, rsa). - -ciphers_rsa_signed_certs_ssl3(doc) -> - ["Test all rsa ssl cipher suites in ssl3"]; - -ciphers_rsa_signed_certs_ssl3(suite) -> - []; - -ciphers_rsa_signed_certs_ssl3(Config) when is_list(Config) -> - Version = - ssl_record:protocol_version({3,0}), - - Ciphers = ssl_test_lib:rsa_suites(), - test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]), + test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, rsa). ciphers_rsa_signed_certs_openssl_names(doc) -> @@ -1755,18 +1796,6 @@ ciphers_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> run_suites(Ciphers, Version, Config, rsa). -ciphers_rsa_signed_certs_openssl_names_ssl3(doc) -> - ["Test all dsa ssl cipher suites in ssl3"]; - -ciphers_rsa_signed_certs_openssl_names_ssl3(suite) -> - []; - -ciphers_rsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) -> - Version = ssl_record:protocol_version({3,0}), - Ciphers = ssl_test_lib:openssl_rsa_suites(), - run_suites(Ciphers, Version, Config, rsa). - - ciphers_dsa_signed_certs(doc) -> ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; @@ -1778,23 +1807,8 @@ ciphers_dsa_signed_certs(Config) when is_list(Config) -> ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Ciphers = ssl_test_lib:dsa_suites(), - test_server:format("tls1 erlang cipher suites ~p~n", [Ciphers]), - run_suites(Ciphers, Version, Config, dsa). - -ciphers_dsa_signed_certs_ssl3(doc) -> - ["Test all dsa ssl cipher suites in ssl3"]; - -ciphers_dsa_signed_certs_ssl3(suite) -> - []; - -ciphers_dsa_signed_certs_ssl3(Config) when is_list(Config) -> - Version = - ssl_record:protocol_version({3,0}), - - Ciphers = ssl_test_lib:dsa_suites(), - test_server:format("ssl3 erlang cipher suites ~p~n", [Ciphers]), + test_server:format("~p erlang cipher suites ~p~n", [Version, Ciphers]), run_suites(Ciphers, Version, Config, dsa). - ciphers_dsa_signed_certs_openssl_names(doc) -> ["Test all dsa ssl cipher suites in highest support ssl/tls version"]; @@ -1810,18 +1824,6 @@ ciphers_dsa_signed_certs_openssl_names(Config) when is_list(Config) -> test_server:format("tls1 openssl cipher suites ~p~n", [Ciphers]), run_suites(Ciphers, Version, Config, dsa). - -ciphers_dsa_signed_certs_openssl_names_ssl3(doc) -> - ["Test all dsa ssl cipher suites in ssl3"]; - -ciphers_dsa_signed_certs_openssl_names_ssl3(suite) -> - []; - -ciphers_dsa_signed_certs_openssl_names_ssl3(Config) when is_list(Config) -> - Version = ssl_record:protocol_version({3,0}), - Ciphers = ssl_test_lib:openssl_dsa_suites(), - run_suites(Ciphers, Version, Config, dsa). - anonymous_cipher_suites(doc)-> ["Test the anonymous ciphersuites"]; anonymous_cipher_suites(suite) -> @@ -1858,7 +1860,7 @@ run_suites(Ciphers, Version, Config, Type) -> end. erlang_cipher_suite(Suite) when is_list(Suite)-> - ssl_cipher:suite_definition(ssl_cipher:openssl_suite(Suite)); + ssl:suite_definition(ssl_cipher:openssl_suite(Suite)); erlang_cipher_suite(Suite) -> Suite. @@ -2085,7 +2087,9 @@ reuse_session_expired(Config) when is_list(Config) -> Server ! listen, %% Make sure session is unregistered due to expiration - test_server:sleep((?EXPIRE+1) * 1000), + test_server:sleep((?EXPIRE+1)), + [{session_id, Id} |_] = SessionInfo, + make_sure_expired(Hostname, Port, Id), Client2 = ssl_test_lib:start_client([{node, ClientNode}, @@ -2104,6 +2108,22 @@ reuse_session_expired(Config) when is_list(Config) -> ssl_test_lib:close(Client1), ssl_test_lib:close(Client2). +make_sure_expired(Host, Port, Id) -> + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + Cache = element(2, State), + case ssl_session_cache:lookup(Cache, {{Host, Port}, Id}) of + undefined -> + ok; + #session{is_resumable = false} -> + ok; + _ -> + test_server:sleep(?SLEEP), + make_sure_expired(Host, Port, Id) + end. + + %%-------------------------------------------------------------------- server_does_not_want_to_reuse_session(doc) -> ["Test reuse of sessions (short handshake)"]; @@ -3649,6 +3669,8 @@ no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client0), + ssl:clear_pem_cache(), + NewServerOpts = new_config(PrivDir, DsaServerOpts), Server1 = @@ -3872,16 +3894,16 @@ tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ServerOpts}]), + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), {Client, #sslsocket{pid=Pid} = SslSocket} = ssl_test_lib:start_client([return_socket, - {node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, receive_msg, []}}, - {options, ClientOpts}]), + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, receive_msg, []}}, + {options, ClientOpts}]), {status, _, _, StatusInfo} = sys:get_status(Pid), [_, _,_, _, Prop] = StatusInfo, @@ -3892,6 +3914,32 @@ tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> Pid ! {tcp_error, Socket, etimedout}, ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}). + + +%%-------------------------------------------------------------------- + +recv_error_handling(doc) -> + ["Special case of call error handling"]; +recv_error_handling(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, recv_close, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + {_Client, #sslsocket{} = SslSocket} = ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + ssl:close(SslSocket), + ssl_test_lib:check_result(Server, ok). + + %%-------------------------------------------------------------------- rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is @@ -3899,21 +3947,22 @@ rizzo(doc) -> ["Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as i rizzo(Config) when is_list(Config) -> Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(), Y =/= rc4_128], - run_send_recv_rizzo(Ciphers, Config, sslv3, - {?MODULE, send_recv_result_active_rizzo, []}), - run_send_recv_rizzo(Ciphers, Config, tlsv1, + Prop = ?config(tc_group_properties, Config), + Version = proplists:get_value(name, Prop), + run_send_recv_rizzo(Ciphers, Config, Version, {?MODULE, send_recv_result_active_rizzo, []}). - +%%-------------------------------------------------------------------- no_rizzo_rc4(doc) -> ["Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"]; no_rizzo_rc4(Config) when is_list(Config) -> Ciphers = [X || X ={_,Y,_} <- ssl:cipher_suites(),Y == rc4_128], - run_send_recv_rizzo(Ciphers, Config, sslv3, - {?MODULE, send_recv_result_active_no_rizzo, []}), - run_send_recv_rizzo(Ciphers, Config, tlsv1, + Prop = ?config(tc_group_properties, Config), + Version = proplists:get_value(name, Prop), + run_send_recv_rizzo(Ciphers, Config, Version, {?MODULE, send_recv_result_active_no_rizzo, []}). +%%-------------------------------------------------------------------- run_send_recv_rizzo(Ciphers, Config, Version, Mfa) -> Result = lists:map(fun(Cipher) -> rizzo_test(Cipher, Config, Version, Mfa) end, @@ -3967,6 +4016,15 @@ send_recv_result(Socket) -> {ok,"Hello world"} = ssl:recv(Socket, 11), ok. +recv_close(Socket) -> + {error, closed} = ssl:recv(Socket, 11), + receive + {_,{error,closed}} -> + error_extra_close_sent_to_user_process + after 500 -> + ok + end. + send_recv_result_active(Socket) -> ssl:send(Socket, "Hello world"), receive diff --git a/lib/ssl/test/ssl_cipher_SUITE.erl b/lib/ssl/test/ssl_cipher_SUITE.erl index 99bc21e820..83beeb0131 100644 --- a/lib/ssl/test/ssl_cipher_SUITE.erl +++ b/lib/ssl/test/ssl_cipher_SUITE.erl @@ -27,6 +27,7 @@ -include("ssl_internal.hrl"). -include("ssl_record.hrl"). -include("ssl_cipher.hrl"). +-include("ssl_alert.hrl"). -define(TIMEOUT, 600000). @@ -103,7 +104,7 @@ end_per_testcase(_TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [aes_decipher_good, aes_decipher_fail]. + [aes_decipher_good, aes_decipher_good_tls11, aes_decipher_fail, aes_decipher_fail_tls11]. groups() -> []. @@ -131,10 +132,39 @@ aes_decipher_good(Config) when is_list(Config) -> 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, - Version = {3,3}, - Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56,72,69,76,76,79,10>>, + Content = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56, "HELLO\n">>, Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>, + Version = {3,0}, {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), + Version1 = {3,1}, + {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), + ok. + +%%-------------------------------------------------------------------- + +aes_decipher_good_tls11(doc) -> + ["Decipher a known TLS 1.1 cryptotext."]; + +aes_decipher_good_tls11(suite) -> + []; + +%% the fragment is actuall a TLS 1.1 record, with +%% Version = TLS 1.1, we get the correct NextIV in #cipher_state +aes_decipher_good_tls11(Config) when is_list(Config) -> + HashSz = 32, + CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, + key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,148>>}, + Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8, + 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, + 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, + 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, + Content = <<"HELLO\n">>, + NextIV = <<183,139,16,132,10,209,67,86,168,100,61,217,145,57,36,56>>, + Mac = <<71,136,212,107,223,200,70,232,127,116,148,205,232,35,158,113,237,174,15,217,192,168,35,8,6,107,107,233,25,174,90,111>>, + Version = {3,2}, + {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), + Version1 = {3,2}, + {Content, Mac, #cipher_state{iv = NextIV}} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), ok. %%-------------------------------------------------------------------- @@ -154,10 +184,38 @@ aes_decipher_fail(Config) when is_list(Config) -> 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, - Version = {3,3}, + Version = {3,0}, {Content, Mac, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), 32 = byte_size(Content), 32 = byte_size(Mac), + Version1 = {3,1}, + {Content1, Mac1, _} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), + 32 = byte_size(Content1), + 32 = byte_size(Mac1), + ok. + +%%-------------------------------------------------------------------- + +aes_decipher_fail_tls11(doc) -> + ["Decipher a known TLS 1.1 cryptotext."]; + +aes_decipher_fail_tls11(suite) -> + []; + +%% same as above, last byte of key replaced +%% stricter padding checks in TLS 1.1 mean we get an alert instead +aes_decipher_fail_tls11(Config) when is_list(Config) -> + HashSz = 32, + CipherState = #cipher_state{iv = <<59,201,85,117,188,206,224,136,5,109,46,70,104,79,4,9>>, + key = <<72,196,247,97,62,213,222,109,210,204,217,186,172,184,197,254>>}, + Fragment = <<220,193,179,139,171,33,143,245,202,47,123,251,13,232,114,8, + 190,162,74,31,186,227,119,155,94,74,119,79,169,193,240,160, + 198,181,81,19,98,162,213,228,74,224,253,168,156,59,195,122, + 108,101,107,242,20,15,169,150,163,107,101,94,93,104,241,165>>, + Version = {3,2}, + #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version), + Version1 = {3,3}, + #alert{level = ?FATAL, description = ?BAD_RECORD_MAC} = ssl_cipher:decipher(?AES, HashSz, CipherState, Fragment, Version1), ok. %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl index 06182970e3..818f7f1897 100644 --- a/lib/ssl/test/ssl_dist_SUITE.erl +++ b/lib/ssl/test/ssl_dist_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. 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 @@ -369,7 +369,7 @@ mk_node_cmdline(ListenPort, Name, Args) -> _ -> "-name " end, {ok, Pwd} = file:get_cwd(), - Prog ++ " " + "\"" ++ Prog ++ "\" " ++ Static ++ " " ++ NameSw ++ " " ++ Name ++ " " ++ "-pa " ++ Pa ++ " " @@ -729,7 +729,7 @@ add_ssl_opts_config(Config) -> [{ssl_opts, "-boot " ++ Script} | Config] catch _:_ -> - [{ssl_opts, "-pa " ++ filename:dirname(code:which(ssl))} + [{ssl_opts, "-pa \"" ++ filename:dirname(code:which(ssl))++"\""} | add_comment_config( "Bootscript wasn't used since the test wasn't run on an " "installed OTP system.", diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index 08c23b2d47..946865a3d8 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -48,7 +48,8 @@ decode_hello_handshake(_Config) -> 16#00, 16#00, 16#33, 16#74, 16#00, 16#07, 16#06, 16#73, 16#70, 16#64, 16#79, 16#2f, 16#32>>, - {Records, _Buffer} = ssl_handshake:get_tls_handshake(HelloPacket, <<>>), + Version = {3, 0}, + {Records, _Buffer} = ssl_handshake:get_tls_handshake(Version, HelloPacket, <<>>), {Hello, _Data} = hd(Records), #renegotiation_info{renegotiated_connection = <<0>>} = Hello#server_hello.renegotiation_info. diff --git a/lib/ssl/test/ssl_packet_SUITE.erl b/lib/ssl/test/ssl_packet_SUITE.erl index 4b74f57a60..8ce80cb725 100644 --- a/lib/ssl/test/ssl_packet_SUITE.erl +++ b/lib/ssl/test/ssl_packet_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -53,6 +53,7 @@ %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), @@ -121,15 +122,56 @@ end_per_testcase(_TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> + [ + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'} + ]. + +groups() -> + [{'tlsv1.2', [], packet_tests()}, + {'tlsv1.1', [], packet_tests()}, + {'tlsv1', [], packet_tests()}, + {'sslv3', [], packet_tests()}]. + +packet_tests() -> + active_packet_tests() ++ active_once_packet_tests() ++ passive_packet_tests() ++ + [packet_send_to_large, + packet_cdr_decode, packet_cdr_decode_list, + packet_http_decode, packet_http_decode_list, + packet_http_bin_decode_multi, + packet_line_decode, packet_line_decode_list, + packet_asn1_decode, packet_asn1_decode_list, + packet_tpkt_decode, packet_tpkt_decode_list, + packet_sunrm_decode, packet_sunrm_decode_list]. + +passive_packet_tests() -> [packet_raw_passive_many_small, packet_0_passive_many_small, packet_1_passive_many_small, packet_2_passive_many_small, packet_4_passive_many_small, - packet_raw_passive_some_big, packet_0_passive_some_big, - packet_1_passive_some_big, packet_2_passive_some_big, + packet_raw_passive_some_big, + packet_0_passive_some_big, + packet_1_passive_some_big, + packet_2_passive_some_big, packet_4_passive_some_big, - packet_raw_active_once_many_small, + packet_httph_passive, + packet_httph_bin_passive, + packet_http_error_passive, + packet_wait_passive, + packet_size_passive, + packet_baddata_passive, + %% inet header option should be deprecated! + header_decode_one_byte_passive, + header_decode_two_bytes_passive, + header_decode_two_bytes_two_sent_passive, + header_decode_two_bytes_one_sent_passive + ]. + +active_once_packet_tests() -> + [packet_raw_active_once_many_small, packet_0_active_once_many_small, packet_1_active_once_many_small, packet_2_active_once_many_small, @@ -139,44 +181,49 @@ all() -> packet_1_active_once_some_big, packet_2_active_once_some_big, packet_4_active_once_some_big, - packet_raw_active_many_small, - packet_0_active_many_small, packet_1_active_many_small, - packet_2_active_many_small, packet_4_active_many_small, - packet_raw_active_some_big, packet_0_active_some_big, - packet_1_active_some_big, packet_2_active_some_big, - packet_4_active_some_big, packet_send_to_large, - packet_wait_passive, packet_wait_active, - packet_baddata_passive, packet_baddata_active, - packet_size_passive, packet_size_active, - packet_cdr_decode, packet_cdr_decode_list, - packet_http_decode, packet_http_decode_list, - packet_http_bin_decode_multi, packet_http_error_passive, - packet_httph_active, packet_httph_bin_active, - packet_httph_active_once, packet_httph_bin_active_once, - packet_httph_passive, packet_httph_bin_passive, - packet_line_decode, packet_line_decode_list, - packet_asn1_decode, packet_asn1_decode_list, - packet_tpkt_decode, packet_tpkt_decode_list, - packet_sunrm_decode, packet_sunrm_decode_list, - {group, header} + packet_httph_active_once, + packet_httph_bin_active_once ]. -groups() -> - [{header, [], [ header_decode_one_byte, - header_decode_two_bytes, - header_decode_two_bytes_one_sent, - header_decode_two_bytes_two_sent]}]. +active_packet_tests() -> + [packet_raw_active_many_small, + packet_0_active_many_small, + packet_1_active_many_small, + packet_2_active_many_small, + packet_4_active_many_small, + packet_raw_active_some_big, + packet_0_active_some_big, + packet_1_active_some_big, + packet_2_active_some_big, + packet_4_active_some_big, + packet_httph_active, + packet_httph_bin_active, + packet_wait_active, + packet_baddata_active, + packet_size_active, + %% inet header option should be deprecated! + header_decode_one_byte_active, + header_decode_two_bytes_active, + header_decode_two_bytes_two_sent_active, + header_decode_two_bytes_one_sent_active + ]. -init_per_group(header, Config) -> - case ssl_record:highest_protocol_version(ssl_record:supported_protocol_versions()) of - {3, N} when N < 2 -> - {skip, ""}; + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, "Missing crypto support"} + end; _ -> + ssl:start(), Config - end; + end. -init_per_group(_, Config) -> - Config. end_per_group(_GroupName, Config) -> Config. @@ -2435,11 +2482,11 @@ packet_sunrm_decode_list(Config) when is_list(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- -header_decode_one_byte(doc) -> +header_decode_one_byte_active(doc) -> ["Test setting the packet option {header, 1}"]; -header_decode_one_byte(suite) -> +header_decode_one_byte_active(suite) -> []; -header_decode_one_byte(Config) when is_list(Config) -> +header_decode_one_byte_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2448,7 +2495,7 @@ header_decode_one_byte(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_header_decode, + {mfa, {?MODULE, server_header_decode_active, [Data, [11 | <<"Hello world">>]]}}, {options, [{active, true}, binary, {header,1}|ServerOpts]}]), @@ -2457,7 +2504,7 @@ header_decode_one_byte(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_header_decode, + {mfa, {?MODULE, client_header_decode_active, [Data, [11 | <<"Hello world">> ]]}}, {options, [{active, true}, {header, 1}, binary | ClientOpts]}]), @@ -2469,11 +2516,11 @@ header_decode_one_byte(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes(doc) -> +header_decode_two_bytes_active(doc) -> ["Test setting the packet option {header, 2}"]; -header_decode_two_bytes(suite) -> +header_decode_two_bytes_active(suite) -> []; -header_decode_two_bytes(Config) when is_list(Config) -> +header_decode_two_bytes_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2482,7 +2529,7 @@ header_decode_two_bytes(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_header_decode, + {mfa, {?MODULE, server_header_decode_active, [Data, [11, $H | <<"ello world">> ]]}}, {options, [{active, true}, binary, {header,2}|ServerOpts]}]), @@ -2491,7 +2538,7 @@ header_decode_two_bytes(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_header_decode, + {mfa, {?MODULE, client_header_decode_active, [Data, [11, $H | <<"ello world">> ]]}}, {options, [{active, true}, {header, 2}, binary | ClientOpts]}]), @@ -2504,11 +2551,11 @@ header_decode_two_bytes(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_two_sent(doc) -> - ["Test setting the packet option {header, 2} and sending on byte"]; -header_decode_two_bytes_two_sent(suite) -> +header_decode_two_bytes_two_sent_active(doc) -> + ["Test setting the packet option {header, 2} and sending two byte"]; +header_decode_two_bytes_two_sent_active(suite) -> []; -header_decode_two_bytes_two_sent(Config) when is_list(Config) -> +header_decode_two_bytes_two_sent_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2517,8 +2564,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_header_decode, - [Data, [$H, $e | <<>> ]]}}, + {mfa, {?MODULE, server_header_decode_active, + [Data, [$H, $e]]}}, {options, [{active, true}, binary, {header,2}|ServerOpts]}]), @@ -2526,8 +2573,8 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_header_decode, - [Data, [$H, $e | <<>> ]]}}, + {mfa, {?MODULE, client_header_decode_active, + [Data, [$H, $e]]}}, {options, [{active, true}, {header, 2}, binary | ClientOpts]}]), @@ -2539,11 +2586,11 @@ header_decode_two_bytes_two_sent(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -header_decode_two_bytes_one_sent(doc) -> - ["Test setting the packet option {header, 2} and sending on byte"]; -header_decode_two_bytes_one_sent(suite) -> +header_decode_two_bytes_one_sent_active(doc) -> + ["Test setting the packet option {header, 2} and sending one byte"]; +header_decode_two_bytes_one_sent_active(suite) -> []; -header_decode_two_bytes_one_sent(Config) when is_list(Config) -> +header_decode_two_bytes_one_sent_active(Config) when is_list(Config) -> ClientOpts = ?config(client_opts, Config), ServerOpts = ?config(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -2552,7 +2599,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) -> Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, server_header_decode, + {mfa, {?MODULE, server_header_decode_active, [Data, "H"]}}, {options, [{active, true}, binary, {header,2}|ServerOpts]}]), @@ -2561,7 +2608,7 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) -> Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, {host, Hostname}, {from, self()}, - {mfa, {?MODULE, client_header_decode, + {mfa, {?MODULE, client_header_decode_active, [Data, "H"]}}, {options, [{active, true}, {header, 2}, binary | ClientOpts]}]), @@ -2571,6 +2618,143 @@ header_decode_two_bytes_one_sent(Config) when is_list(Config) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- + +header_decode_one_byte_passive(doc) -> + ["Test setting the packet option {header, 1}"]; +header_decode_one_byte_passive(suite) -> + []; +header_decode_one_byte_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:8, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode_passive, + [Data, [11 | <<"Hello world">>]]}}, + {options, [{active, false}, binary, + {header,1}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode_passive, + [Data, [11 | <<"Hello world">> ]]}}, + {options, [{active, false}, {header, 1}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_passive(doc) -> + ["Test setting the packet option {header, 2}"]; +header_decode_two_bytes_passive(suite) -> + []; +header_decode_two_bytes_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<11:8, "Hello world">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode_passive, + [Data, [11, $H | <<"ello world">> ]]}}, + {options, [{active, false}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode_passive, + [Data, [11, $H | <<"ello world">> ]]}}, + {options, [{active, false}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_two_sent_passive(doc) -> + ["Test setting the packet option {header, 2} and sending two byte"]; +header_decode_two_bytes_two_sent_passive(suite) -> + []; +header_decode_two_bytes_two_sent_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<"He">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode_passive, + [Data, [$H, $e]]}}, + {options, [{active, false}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode_passive, + [Data, [$H, $e]]}}, + {options, [{active, false}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- + +header_decode_two_bytes_one_sent_passive(doc) -> + ["Test setting the packet option {header, 2} and sending one byte"]; +header_decode_two_bytes_one_sent_passive(suite) -> + []; +header_decode_two_bytes_one_sent_passive(Config) when is_list(Config) -> + ClientOpts = ?config(client_opts, Config), + ServerOpts = ?config(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = <<"H">>, + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, server_header_decode_passive, + [Data, "H"]}}, + {options, [{active, false}, binary, + {header,2}|ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, client_header_decode_passive, + [Data, "H"]}}, + {options, [{active, false}, {header, 2}, + binary | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). %%-------------------------------------------------------------------- %% Internal functions @@ -2757,29 +2941,52 @@ client_packet_decode(Socket, P1, P2, Packet) -> Other2 -> exit({?LINE, Other2}) end. -server_header_decode(Socket, Packet, Result) -> +server_header_decode_active(Socket, Packet, Result) -> receive - {ssl, Socket, Result} -> ok; - Other1 -> exit({?LINE, Other1}) - end, - ok = ssl:send(Socket, Packet), - receive - {ssl, Socket, Result} -> ok; - Other2 -> exit({?LINE, Other2}) + {ssl, Socket, Result} -> + ok; + {ssl, Socket, Other1} -> + check_header_result(Result, Other1) end, ok = ssl:send(Socket, Packet). -client_header_decode(Socket, Packet, Result) -> +client_header_decode_active(Socket, Packet, Result) -> ok = ssl:send(Socket, Packet), receive - {ssl, Socket, Result} -> ok; - Other1 -> exit({?LINE, Other1}) + {ssl, Socket, Result} -> + ok; + {ssl, Socket, Other1} -> + check_header_result(Result, Other1) + end. + +server_header_decode_passive(Socket, Packet, Result) -> + case ssl:recv(Socket, 0) of + {ok, Result} -> + ok; + {ok, Other} -> + check_header_result(Result, Other) end, + ok = ssl:send(Socket, Packet). + +client_header_decode_passive(Socket, Packet, Result) -> ok = ssl:send(Socket, Packet), - receive - {ssl, Socket, Result} -> ok; - Other2 -> exit({?LINE, Other2}) + + case ssl:recv(Socket, 0) of + {ok, Result} -> + ok; + {ok, Other} -> + check_header_result(Result, Other) end. + +%% The inet header option is a broken option as it does not buffer until it gets enough data. +%% This check only checks that it has the same behavior as inet, but it is a quite useless +%% option and the bitsynax makes it obsolete! +check_header_result([Byte1 | _], [Byte1]) -> + ok; +check_header_result([Byte1, Byte2 | _], [Byte1, Byte2]) -> + ok; +check_header_result(_,Got) -> + exit({?LINE, Got}). server_line_packet_decode(Socket, Packet) when is_binary(Packet) -> [L1, L2] = string:tokens(binary_to_list(Packet), "\n"), diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index 24e86b3913..c97f97e70b 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -37,6 +37,7 @@ %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), @@ -102,23 +103,56 @@ end_per_testcase(_TestCase, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> + [ + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'} + ]. + +groups() -> + [ + {'tlsv1.2', [], payload_tests()}, + {'tlsv1.1', [], payload_tests()}, + {'tlsv1', [], payload_tests()}, + {'sslv3', [], payload_tests()} + ]. + +payload_tests() -> [server_echos_passive_small, server_echos_active_once_small, - server_echos_active_small, client_echos_passive_small, + server_echos_active_small, + client_echos_passive_small, client_echos_active_once_small, - client_echos_active_small, server_echos_passive_big, - server_echos_active_once_big, server_echos_active_big, - client_echos_passive_big, client_echos_active_once_big, - client_echos_active_big, server_echos_passive_huge, - server_echos_active_once_huge, server_echos_active_huge, + client_echos_active_small, + server_echos_passive_big, + server_echos_active_once_big, + server_echos_active_big, + client_echos_passive_big, + client_echos_active_once_big, + client_echos_active_big, + server_echos_passive_huge, + server_echos_active_once_huge, + server_echos_active_huge, client_echos_passive_huge, - client_echos_active_once_huge, client_echos_active_huge]. + client_echos_active_once_huge, + client_echos_active_huge]. -groups() -> - []. -init_per_group(_GroupName, Config) -> - Config. +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. end_per_group(_GroupName, Config) -> Config. diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index 491aa893c2..6d758ecb01 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -49,6 +49,7 @@ %%-------------------------------------------------------------------- init_per_suite(Config0) -> Dog = ssl_test_lib:timetrap(?LONG_TIMEOUT *2), + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index fa8a1826f2..b39c995552 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -708,3 +708,33 @@ state([{data,[{"StateData", State}]} | _]) -> State; state([_ | Rest]) -> state(Rest). + +is_tls_version('tlsv1.2') -> + true; +is_tls_version('tlsv1.1') -> + true; +is_tls_version('tlsv1') -> + true; +is_tls_version('sslv3') -> + true; +is_tls_version(_) -> + false. + +init_tls_version(Version) -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, protocol_version, Version), + ssl:start(). + +sufficient_crypto_support('tlsv1.2') -> + Data = "Sampl", + Data2 = "e #1", + Key = <<0,1,2,3,16,17,18,19,32,33,34,35,48,49,50,51,4,5,6,7,20,21,22,23,36,37,38,39, + 52,53,54,55,8,9,10,11,24,25,26,27,40,41,42,43,56,57,58,59>>, + try + crypto:sha256_mac(Key, lists:flatten([Data, Data2])), + true + catch _:_ -> false + end; +sufficient_crypto_support(_) -> + true. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 01fca1f166..d446014f7b 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -50,6 +50,7 @@ init_per_suite(Config0) -> false -> {skip, "Openssl not found"}; _ -> + catch crypto:stop(), try crypto:start() of ok -> application:start(public_key), @@ -106,7 +107,8 @@ init_per_testcase(TestCase, Config0) -> special_init(TestCase, Config) when TestCase == erlang_client_openssl_server_renegotiate; TestCase == erlang_client_openssl_server_no_wrap_sequence_number; - TestCase == erlang_server_openssl_client_no_wrap_sequence_number -> + TestCase == erlang_server_openssl_client_no_wrap_sequence_number + -> check_sane_openssl_renegotaite(Config); special_init(ssl2_erlang_server_openssl_client, Config) -> @@ -149,37 +151,59 @@ end_per_testcase(_, Config) -> suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> - [erlang_client_openssl_server, + [ + {group, basic}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'} + ]. + +groups() -> + [{basic, [], basic_tests()}, + {'tlsv1.2', [], all_versions_tests()}, + {'tlsv1.1', [], all_versions_tests()}, + {'tlsv1', [], all_versions_tests()}, + {'sslv3', [], all_versions_tests()}]. + +basic_tests() -> + [basic_erlang_client_openssl_server, + basic_erlang_server_openssl_client, + expired_session]. + +all_versions_tests() -> + [ + erlang_client_openssl_server, erlang_server_openssl_client, - tls1_erlang_client_openssl_server_dsa_cert, - tls1_erlang_server_openssl_client_dsa_cert, - ssl3_erlang_client_openssl_server_dsa_cert, - ssl3_erlang_server_openssl_client_dsa_cert, + erlang_client_openssl_server_dsa_cert, + erlang_server_openssl_client_dsa_cert, erlang_server_openssl_client_reuse_session, erlang_client_openssl_server_renegotiate, erlang_client_openssl_server_no_wrap_sequence_number, erlang_server_openssl_client_no_wrap_sequence_number, erlang_client_openssl_server_no_server_ca_cert, - ssl3_erlang_client_openssl_server, - ssl3_erlang_server_openssl_client, - ssl3_erlang_client_openssl_server_client_cert, - ssl3_erlang_server_openssl_client_client_cert, - ssl3_erlang_server_erlang_client_client_cert, - tls1_erlang_client_openssl_server, - tls1_erlang_server_openssl_client, - tls1_erlang_client_openssl_server_client_cert, - tls1_erlang_server_openssl_client_client_cert, - tls1_erlang_server_erlang_client_client_cert, - ciphers_rsa_signed_certs, ciphers_dsa_signed_certs, + erlang_client_openssl_server_client_cert, + erlang_server_openssl_client_client_cert, + ciphers_rsa_signed_certs, + ciphers_dsa_signed_certs, erlang_client_bad_openssl_server, - expired_session, - ssl2_erlang_server_openssl_client]. + ssl2_erlang_server_openssl_client + ]. -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case check_sane_openssl_version(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName), + Config; + false -> + {skip, openssl_does_not_support_version} + end; + _ -> + ssl:start(), + Config + end. end_per_group(_GroupName, Config) -> Config. @@ -187,12 +211,11 @@ end_per_group(_GroupName, Config) -> %% Test cases starts here. %%-------------------------------------------------------------------- - -erlang_client_openssl_server(doc) -> +basic_erlang_client_openssl_server(doc) -> ["Test erlang client with openssl server"]; -erlang_client_openssl_server(suite) -> +basic_erlang_client_openssl_server(suite) -> []; -erlang_client_openssl_server(Config) when is_list(Config) -> +basic_erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), ClientOpts = ?config(client_opts, Config), @@ -204,8 +227,8 @@ erlang_client_openssl_server(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile, test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -230,13 +253,12 @@ erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, false), ok. - %%-------------------------------------------------------------------- -erlang_server_openssl_client(doc) -> +basic_erlang_server_openssl_client(doc) -> ["Test erlang server with openssl client"]; -erlang_server_openssl_client(suite) -> +basic_erlang_server_openssl_client(suite) -> []; -erlang_server_openssl_client(Config) when is_list(Config) -> +basic_erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_opts, Config), @@ -249,8 +271,8 @@ erlang_server_openssl_client(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ + + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ " -host localhost", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -265,30 +287,26 @@ erlang_server_openssl_client(Config) when is_list(Config) -> close_port(OpenSslPort), process_flag(trap_exit, false), ok. - -%%-------------------------------------------------------------------- - -tls1_erlang_client_openssl_server_dsa_cert(doc) -> - ["Test erlang server with openssl client"]; -tls1_erlang_client_openssl_server_dsa_cert(suite) -> +%%-------------------------------------------------------------------- +erlang_client_openssl_server(doc) -> + ["Test erlang client with openssl server"]; +erlang_client_openssl_server(suite) -> []; -tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> +erlang_client_openssl_server(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = ?config(client_dsa_opts, Config), - ServerOpts = ?config(server_dsa_opts, Config), + ServerOpts = ?config(server_opts, Config), + ClientOpts = ?config(client_opts, Config), {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - + Data = "From openssl to erlang", Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1 -msg", + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -cert " ++ CertFile ++ " -key " ++ KeyFile, test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -302,44 +320,39 @@ tls1_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ClientOpts}]), - port_command(OpensslPort, Data), - ssl_test_lib:check_result(Client, ok), - + ssl_test_lib:check_result(Client, ok), + %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), ssl_test_lib:close(Client), process_flag(trap_exit, false), ok. -%%-------------------------------------------------------------------- -tls1_erlang_server_openssl_client_dsa_cert(doc) -> +%%-------------------------------------------------------------------- +erlang_server_openssl_client(doc) -> ["Test erlang server with openssl client"]; -tls1_erlang_server_openssl_client_dsa_cert(suite) -> +erlang_server_openssl_client(suite) -> []; -tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> +erlang_server_openssl_client(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = ?config(client_dsa_opts, Config), - ServerOpts = ?config(server_dsa_verify_opts, Config), + ServerOpts = ?config(server_opts, Config), {_, ServerNode, _} = ssl_test_lib:run_where(Config), Data = "From openssl to erlang", - CaCertFile = proplists:get_value(cacertfile, ClientOpts), - CertFile = proplists:get_value(certfile, ClientOpts), - KeyFile = proplists:get_value(keyfile, ClientOpts), - + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ - " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -tls1 -msg", + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -host localhost", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -347,7 +360,7 @@ tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> port_command(OpenSslPort, Data), ssl_test_lib:check_result(Server, ok), - + %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), @@ -356,11 +369,11 @@ tls1_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ssl3_erlang_client_openssl_server_dsa_cert(doc) -> +erlang_client_openssl_server_dsa_cert(doc) -> ["Test erlang server with openssl client"]; -ssl3_erlang_client_openssl_server_dsa_cert(suite) -> +erlang_client_openssl_server_dsa_cert(suite) -> []; -ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> +erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), ServerOpts = ?config(server_dsa_opts, Config), @@ -373,10 +386,11 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> CaCertFile = proplists:get_value(cacertfile, ServerOpts), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3 -msg", + ++ " -key " ++ KeyFile ++ " -Verify 2 -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -400,49 +414,46 @@ ssl3_erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> ssl_test_lib:close(Client), process_flag(trap_exit, false), ok. - -%%-------------------------------------------------------------------- - -ssl3_erlang_server_openssl_client_dsa_cert(doc) -> +%%-------------------------------------------------------------------- +erlang_server_openssl_client_dsa_cert(doc) -> ["Test erlang server with openssl client"]; -ssl3_erlang_server_openssl_client_dsa_cert(suite) -> +erlang_server_openssl_client_dsa_cert(suite) -> []; -ssl3_erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> +erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ClientOpts = ?config(client_dsa_opts, Config), - ServerOpts = ?config(server_dsa_verify_opts, Config), + ServerOpts = ?config(server_dsa_verify_opts, Config), {_, ServerNode, _} = ssl_test_lib:run_where(Config), - + Data = "From openssl to erlang", CaCertFile = proplists:get_value(cacertfile, ClientOpts), CertFile = proplists:get_value(certfile, ClientOpts), KeyFile = proplists:get_value(keyfile, ClientOpts), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, ServerOpts}]), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ - " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -ssl3 -msg", + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -host localhost " ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile + ++ " -key " ++ KeyFile ++ " -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), + + OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), port_command(OpenSslPort, Data), - + ssl_test_lib:check_result(Server, ok), - + %% Clean close down! Server needs to be closed first !! ssl_test_lib:close(Server), close_port(OpenSslPort), process_flag(trap_exit, false), ok. - %%-------------------------------------------------------------------- erlang_server_openssl_client_reuse_session(doc) -> @@ -464,8 +475,8 @@ erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> {reconnect_times, 5}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -reconnect", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -501,8 +512,9 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -552,8 +564,8 @@ erlang_client_openssl_server_no_wrap_sequence_number(Config) when is_list(Config Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -600,10 +612,10 @@ erlang_server_openssl_client_no_wrap_sequence_number(Config) when is_list(Config {from, self()}, {mfa, {ssl_test_lib, trigger_renegotiate, [[Data, N+2]]}}, - {options, [{renegotiate_at, N} | ServerOpts]}]), + {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -host localhost -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -639,8 +651,8 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -msg", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -667,85 +679,11 @@ erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> ok. %%-------------------------------------------------------------------- -ssl3_erlang_client_openssl_server(doc) -> - ["Test erlang client with openssl server"]; -ssl3_erlang_client_openssl_server(suite) -> - []; -ssl3_erlang_client_openssl_server(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -ssl3", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - wait_for_openssl_server(), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - connection_info, [sslv3]}}, - {options, - [{versions, [sslv3]} | ClientOpts]}]), - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- - -ssl3_erlang_server_openssl_client(doc) -> - ["Test erlang server with openssl client"]; -ssl3_erlang_server_openssl_client(suite) -> - []; -ssl3_erlang_server_openssl_client(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_opts, Config), - - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, - {?MODULE, connection_info, [sslv3]}}, - {options, - [{versions, [sslv3]} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ - " -host localhost -ssl3", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - ssl_test_lib:check_result(Server, ok), - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- -ssl3_erlang_client_openssl_server_client_cert(doc) -> +erlang_client_openssl_server_client_cert(doc) -> ["Test erlang client with openssl server when client sends cert"]; -ssl3_erlang_client_openssl_server_client_cert(suite) -> +erlang_client_openssl_server_client_cert(suite) -> []; -ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> +erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), ClientOpts = ?config(client_verification_opts, Config), @@ -758,10 +696,10 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> CertFile = proplists:get_value(certfile, ServerOpts), CaCertFile = proplists:get_value(cacertfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -Verify 2 -ssl3", + ++ " -key " ++ KeyFile ++ " -Verify 2", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -787,11 +725,11 @@ ssl3_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ssl3_erlang_server_openssl_client_client_cert(doc) -> +erlang_server_openssl_client_client_cert(doc) -> ["Test erlang server with openssl client when client sends cert"]; -ssl3_erlang_server_openssl_client_client_cert(suite) -> +erlang_server_openssl_client_client_cert(suite) -> []; -ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> +erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), ClientOpts = ?config(client_verification_opts, Config), @@ -812,10 +750,10 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> CaCertFile = proplists:get_value(cacertfile, ClientOpts), CertFile = proplists:get_value(certfile, ClientOpts), KeyFile = proplists:get_value(keyfile, ClientOpts), - + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ - " -host localhost -ssl3", + ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ version_flag(Version) ++ + " -host localhost", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -833,15 +771,15 @@ ssl3_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -ssl3_erlang_server_erlang_client_client_cert(doc) -> +erlang_server_erlang_client_client_cert(doc) -> ["Test erlang server with erlang client when client sends cert"]; -ssl3_erlang_server_erlang_client_client_cert(suite) -> +erlang_server_erlang_client_client_cert(suite) -> []; -ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> +erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> process_flag(trap_exit, true), ServerOpts = ?config(server_verification_opts, Config), ClientOpts = ?config(client_verification_opts, Config), - + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Data = "From erlang to erlang", @@ -863,7 +801,7 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast {mfa, {ssl, send, [Data]}}, {options, - [{versions, [sslv3]} | ClientOpts]}]), + [{versions, [Version]} | ClientOpts]}]), ssl_test_lib:check_result(Server, ok, Client, ok), @@ -871,215 +809,8 @@ ssl3_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> ssl_test_lib:close(Client), process_flag(trap_exit, false), ok. - - -%%-------------------------------------------------------------------- - -tls1_erlang_client_openssl_server(doc) -> - ["Test erlang client with openssl server"]; -tls1_erlang_client_openssl_server(suite) -> - []; -tls1_erlang_client_openssl_server(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_opts, Config), - ClientOpts = ?config(client_opts, Config), - - - test_server:format("Server Opts", [ServerOpts]), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ " -tls1", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - wait_for_openssl_server(), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - connection_info, [tlsv1]}}, - {options, - [{versions, [tlsv1]} | ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- - -tls1_erlang_server_openssl_client(doc) -> - ["Test erlang server with openssl client"]; -tls1_erlang_server_openssl_client(suite) -> - []; -tls1_erlang_server_openssl_client(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_opts, Config), - - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, - {?MODULE, connection_info, [tlsv1]}}, - {options, - [{versions, [tlsv1]} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Cmd = "openssl s_client -port " ++ integer_to_list(Port) ++ - " -host localhost -tls1", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- - -tls1_erlang_client_openssl_server_client_cert(doc) -> - ["Test erlang client with openssl server when client sends cert"]; -tls1_erlang_client_openssl_server_client_cert(suite) -> - []; -tls1_erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_verification_opts, Config), - ClientOpts = ?config(client_verification_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ - " -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -Verify 2 -tls1", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpensslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - - wait_for_openssl_server(), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, ClientOpts}]), - port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. - %%-------------------------------------------------------------------- -tls1_erlang_server_openssl_client_client_cert(doc) -> - ["Test erlang server with openssl client when client sends cert"]; -tls1_erlang_server_openssl_client_client_cert(suite) -> - []; -tls1_erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_verification_opts, Config), - ClientOpts = ?config(client_verification_opts, Config), - - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, - [{verify , verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - CaCertFile = proplists:get_value(cacertfile, ClientOpts), - CertFile = proplists:get_value(certfile, ClientOpts), - KeyFile = proplists:get_value(keyfile, ClientOpts), - - Cmd = "openssl s_client -cert " ++ CertFile ++ " -CAfile " ++ CaCertFile - ++ " -key " ++ KeyFile ++ " -port " ++ integer_to_list(Port) ++ - " -host localhost -tls1", - - test_server:format("openssl cmd: ~p~n", [Cmd]), - - OpenSslPort = open_port({spawn, Cmd}, [stderr_to_stdout]), - port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- -tls1_erlang_server_erlang_client_client_cert(doc) -> - ["Test erlang server with erlang client when client sends cert"]; -tls1_erlang_server_erlang_client_client_cert(suite) -> - []; -tls1_erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ?config(server_verification_opts, Config), - ClientOpts = ?config(client_verification_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, - [{verify , verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl, send, [Data]}}, - {options, - [{versions, [tlsv1]} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - process_flag(trap_exit, false), - ok. -%%-------------------------------------------------------------------- - ciphers_rsa_signed_certs(doc) -> ["Test cipher suites that uses rsa certs"]; @@ -1186,12 +917,6 @@ cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> process_flag(trap_exit, false), Return. - -version_flag(tlsv1) -> - " -tls1 "; -version_flag(sslv3) -> - " -ssl3 ". - %%-------------------------------------------------------------------- erlang_client_bad_openssl_server(doc) -> [""]; @@ -1207,8 +932,8 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + Version = ssl_record:protocol_version(ssl_record:highest_protocol_version([])), + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ version_flag(Version) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -1222,7 +947,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> {from, self()}, {mfa, {?MODULE, server_sent_garbage, []}}, {options, - [{versions, [tlsv1]} | ClientOpts]}]), + [{versions, [Version]} | ClientOpts]}]), %% Send garbage port_command(OpensslPort, ?OPENSSL_GARBAGE), @@ -1241,7 +966,7 @@ erlang_client_bad_openssl_server(Config) when is_list(Config) -> {from, self()}, {mfa, {ssl_test_lib, no_result_msg, []}}, {options, - [{versions, [tlsv1]} | ClientOpts]}]), + [{versions, [Version]} | ClientOpts]}]), %% Clean close down! Server needs to be closed first !! close_port(OpensslPort), @@ -1267,8 +992,8 @@ expired_session(Config) when is_list(Config) -> Port = ssl_test_lib:inet_port(node()), CertFile = proplists:get_value(certfile, ServerOpts), KeyFile = proplists:get_value(keyfile, ServerOpts), - - Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ + + Cmd = "openssl s_server -accept " ++ integer_to_list(Port) ++ " -cert " ++ CertFile ++ " -key " ++ KeyFile ++ "", test_server:format("openssl cmd: ~p~n", [Cmd]), @@ -1433,21 +1158,46 @@ wait_for_openssl_server() -> %% more so than sleep!) test_server:sleep(?SLEEP) end. - + +version_flag(tlsv1) -> + " -tls1 "; +version_flag('tlsv1.1') -> + " -tls1_1 "; +version_flag('tlsv1.2') -> + " -tls1_2 "; +version_flag(sslv3) -> + " -ssl3 ". + check_sane_openssl_renegotaite(Config) -> case os:cmd("openssl version") of "OpenSSL 0.9.8" ++ _ -> - {skip, "Known renegotiation bug in OppenSSL"}; + {skip, "Known renegotiation bug in OpenSSL"}; "OpenSSL 0.9.7" ++ _ -> - {skip, "Known renegotiation bug in OppenSSL"}; + {skip, "Known renegotiation bug in OpenSSL"}; _ -> Config end. check_sane_openssl_sslv2(Config) -> case os:cmd("openssl version") of - "OpenSSL 1.0.0" ++ _ -> + "OpenSSL 1." ++ _ -> {skip, "sslv2 by default turned of in 1.*"}; _ -> Config end. + +check_sane_openssl_version(Version) -> + case {Version, os:cmd("openssl version")} of + {_, "OpenSSL 1.0.1" ++ _} -> + true; + {'tlsv1.2', "OpenSSL 1.0" ++ _} -> + false; + {'tlsv1.1', "OpenSSL 1.0" ++ _} -> + false; + {'tlsv1.2', "OpenSSL 0" ++ _} -> + false; + {'tlsv1.1', "OpenSSL 0" ++ _} -> + false; + {_, _} -> + true + end. diff --git a/lib/ssl/vsn.mk b/lib/ssl/vsn.mk index 2255798f1d..e381b73c27 100644 --- a/lib/ssl/vsn.mk +++ b/lib/ssl/vsn.mk @@ -1 +1 @@ -SSL_VSN = 5.0 +SSL_VSN = 5.1 diff --git a/lib/stdlib/doc/src/Makefile b/lib/stdlib/doc/src/Makefile index 6c92756ae7..c3138c4d9f 100644 --- a/lib/stdlib/doc/src/Makefile +++ b/lib/stdlib/doc/src/Makefile @@ -173,15 +173,15 @@ $(SPECDIR)/specs_erl_id_trans.xml: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man6 - $(INSTALL_DATA) $(MAN6_FILES) $(RELEASE_PATH)/man/man6 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man6" + $(INSTALL_DATA) $(MAN6_FILES) "$(RELEASE_PATH)/man/man6" release_spec: diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml index 7ce2defb72..7b8e279788 100644 --- a/lib/stdlib/doc/src/binary.xml +++ b/lib/stdlib/doc/src/binary.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>2009</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/stdlib/doc/src/ets.xml b/lib/stdlib/doc/src/ets.xml index 0486090e92..487b06473f 100644 --- a/lib/stdlib/doc/src/ets.xml +++ b/lib/stdlib/doc/src/ets.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml index ef81b06500..1601ea9892 100644 --- a/lib/stdlib/doc/src/gen_event.xml +++ b/lib/stdlib/doc/src/gen_event.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml index 9edff1b9cf..abb4c73c7b 100644 --- a/lib/stdlib/doc/src/gen_server.xml +++ b/lib/stdlib/doc/src/gen_server.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index 96a0942710..8b31f3ac3d 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 42a26ee44a..4834426d5c 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -30,6 +30,171 @@ </header> <p>This document describes the changes made to the STDLIB application.</p> +<section><title>STDLIB 1.18.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + References to <c>is_constant/1</c> (which was removed in + the R12 release) has been removed from documentation and + code.</p> + <p> + Own Id: OTP-6454 Aux Id: seq10407 </p> + </item> + <item> + <p> + Leave control back to gen_server during supervisor's + restart loop</p> + <p> + When an attempt to restart a child failed, supervisor + would earlier keep the execution flow and try to restart + the child over and over again until it either succeeded + or the restart frequency limit was reached. If none of + these happened, supervisor would hang forever in this + loop.</p> + <p> + This commit adds a timer of 0 ms where the control is + left back to the gen_server which implements the + supervisor. This way any incoming request to the + supervisor will be handled - which could help breaking + the infinite loop - e.g. shutdown request for the + supervisor or for the problematic child.</p> + <p> + This introduces some incompatibilities in stdlib due to + new return values from supervisor: <list> + <item>restart_child/2 can now return + {error,restarting}</item> <item>delete_child/2 can now + return {error,restarting}</item> <item>which_children/1 + returns a list of {Id,Child,Type,Mods}, where Child, in + addition to the old pid() or 'undefined', now also can be + 'restarting'.</item> </list></p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9549</p> + </item> + <item> + <p> + If a temporary child's start function returned 'ignore', + then the supervisor would keep it's child specification. + This has been corrected. Child specifications for + non-existing temporary children shall never be kept.</p> + <p> + Own Id: OTP-9782 Aux Id: seq11964 </p> + </item> + <item> + <p> Use universal time as base in error logger + <p> + Previous conversion used the deprecated + calendar:local_time_to_universal_time/1 </p></p> + <p> + Own Id: OTP-9854</p> + </item> + <item> + <p>Calling a guard test (such as is_list/1) from the + top-level in a guard, would cause a compiler crash if + there was a local definition with the same name. + Corrected to reject the program with an error + message.</p> + <p> + Own Id: OTP-9866</p> + </item> + <item> + <p> + Fix the type spec from the doc of binary:part/3 (Thanks + to Ricardo Catalinas Jim�nez)</p> + <p> + Own Id: OTP-9920</p> + </item> + <item> + <p> + Correct spelling of registered (Thanks to Richard + Carlsson)</p> + <p> + Own Id: OTP-9925</p> + </item> + <item> + <p> + Put gb_trees documentation into alphabetical order + (Thanks to Aidan Hobson Sayers)</p> + <p> + Own Id: OTP-9929</p> + </item> + <item> + <p> + Fix bug in ETS with <c>compressed</c> option and + insertion of term containing large integers (>2G) on + 64-bit machines. Seen to cause emulator crash. (Thanks to + Diego Llarrull for excellent bug report)</p> + <p> + Own Id: OTP-9932</p> + </item> + <item> + <p> + Add plugin support for alternative name lookup This patch + introduces a new way of locating a behaviour instance: + {via, Module, Name}. (Thanks to Ulf Wiger)</p> + <p> + Own Id: OTP-9945</p> + </item> + <item> + <p> The function <c>digraph_utils:condensation/1</c> used + to create a digraph containing loops contradicting the + documentation which states that the created digraph is + free of cycles. This bug has been fixed. (Thanks to + Kostis Sagonas for finding the bug.) </p> + <p> + Own Id: OTP-9953</p> + </item> + <item> + <p> When an escript ends now all printout to standard + output and standard error gets out on the terminal. This + bug has been corrected by changing the behaviour of + erlang:halt/0,1, which should fix the same problem for + other escript-like applications, i.e that data stored in + the output port driver buffers got lost when printing on + a TTY and exiting through erlang:halt/0,1. </p> + <p> The BIF:s erlang:halt/0,1 has gotten improved + semantics and there is a new BIF erlang:halt/2 to + accomplish something like the old semantics. See the + documentation. </p> + <p> Now erlang:halt/0 and erlang:halt/1 with an integer + argument will close all ports and allow all pending async + threads operations to finish before exiting the emulator. + Previously erlang:halt/0 and erlang:halt(0) would just + wait for pending async threads operations but not close + ports. And erlang:halt/1 with a non-zero integer argument + would not even wait for pending async threads operations. + </p> + <p> To roughly the old behaviour, to not wait for ports + and async threads operations when you exit the emulator, + you use erlang:halt/2 with an integer first argument and + an option list containing {flush,false} as the second + argument. Note that now is flushing not dependant of the + exit code, and you can not only flush async threads + operations which we deemed as a strange behaviour anyway. + </p> + <p>Also, erlang:halt/1,2 has gotten a new feature: If the + first argument is the atom 'abort' the emulator is + aborted producing a core dump, if the operating system so + allows. </p> + <p> + Own Id: OTP-9985</p> + </item> + <item> + <p> + Add escript win32 alternative invocation. escript can now + be started as both "escript.exe" and "escript" (Thanks to + Pierre Rouleau)</p> + <p> + Own Id: OTP-9997</p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 1.18</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml index 9b41672bf1..f9a5e245b4 100644 --- a/lib/stdlib/doc/src/supervisor.xml +++ b/lib/stdlib/doc/src/supervisor.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2011</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/stdlib/doc/src/unicode_usage.xml b/lib/stdlib/doc/src/unicode_usage.xml index a7e010a05f..b7b5d497d0 100644 --- a/lib/stdlib/doc/src/unicode_usage.xml +++ b/lib/stdlib/doc/src/unicode_usage.xml @@ -53,7 +53,7 @@ <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 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> +<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, but 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> <title>Standard Unicode representation in Erlang</title> @@ -210,6 +210,12 @@ Eshell V5.7 (abort with ^G) </section> </section> <section> +<title>Unicode in environment variables and parameters</title> +<p>Environment variables and their interpretation is handled much in the same way as file names. If Unicode file names are enabled, environment variables as well as parameters to the Erlang VM are expected to be in Unicode.</p> +<p>If Unicode file names are enabled, the calls to <seealso marker="kernel:os#os_getenv/0"><c>os:getenv/0</c></seealso>, <seealso marker="kernel:os#os_getenv/1"><c>os:getenv/1</c></seealso> and <seealso marker="kernel:os#os_putenv/2"><c>os:putenv/2</c></seealso> will handle Unicode strings. On Unix-like platforms, the built-in functions will translate environment variables in UTF-8 to/from Unicode strings, possibly with codepoints > 255. On Windows the Unicode versions of the environment system API will be used, also allowing for codepoints > 255.</p> +<p>On Unix-like operating systems, parameters are expected to be UTF-8 without translation if Unicode file names are enabled.</p> +</section> +<section> <title>Unicode-aware modules</title> <p>Most of the modules in Erlang/OTP are of course Unicode-unaware in the sense that they have no notion of Unicode and really shouldn't have. Typically they handle non-textual or byte-oriented data (like <c>gen_tcp</c> etc).</p> <p>Modules that actually handle textual data (like <c>io_lib</c>, <c>string</c> etc) are sometimes subject to conversion or extension to be able to handle Unicode characters.</p> diff --git a/lib/stdlib/examples/Makefile b/lib/stdlib/examples/Makefile index 25c88d1de3..198aceb3a9 100644 --- a/lib/stdlib/examples/Makefile +++ b/lib/stdlib/examples/Makefile @@ -47,6 +47,6 @@ RELSYSDIR = $(RELEASE_PATH)/lib/stdlib-$(STDLIB_VSN)/examples EXAMPLES = erl_id_trans.erl release_spec: - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EXAMPLES) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EXAMPLES) "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/stdlib/src/Makefile b/lib/stdlib/src/Makefile index 90e239b00f..54186a3ba7 100644 --- a/lib/stdlib/src/Makefile +++ b/lib/stdlib/src/Makefile @@ -167,6 +167,7 @@ docs: # This is a trick so that the preloaded files will get the correct type # specifications. primary_bootstrap_compiler: \ + $(BOOTSTRAP_COMPILER)/ebin/epp.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_scan.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_parse.beam \ $(BOOTSTRAP_COMPILER)/ebin/erl_lint.beam \ @@ -198,13 +199,13 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) erl_parse.yrl $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) erl_parse.yrl "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/stdlib/src/erl_compile.erl b/lib/stdlib/src/erl_compile.erl index ff032b129c..81bec21a3f 100644 --- a/lib/stdlib/src/erl_compile.erl +++ b/lib/stdlib/src/erl_compile.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -57,17 +57,7 @@ compile_cmdline(List) -> end. my_halt(Reason) -> - case process_info(group_leader(), status) of - {_,waiting} -> - %% Now all output data is down in the driver. - %% Give the driver some extra time before halting. - receive after 1 -> ok end, - halt(Reason); - _ -> - %% Probably still processing I/O requests. - erlang:yield(), - my_halt(Reason) - end. + erlang:halt(Reason). %% Run the the compiler in a separate process, trapping EXITs. diff --git a/lib/stdlib/src/erl_expand_records.erl b/lib/stdlib/src/erl_expand_records.erl index 1c69a131f9..9759a8f001 100644 --- a/lib/stdlib/src/erl_expand_records.erl +++ b/lib/stdlib/src/erl_expand_records.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/stdlib/src/erl_internal.erl b/lib/stdlib/src/erl_internal.erl index cd3b531d10..3063881890 100644 --- a/lib/stdlib/src/erl_internal.erl +++ b/lib/stdlib/src/erl_internal.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 @@ -287,6 +287,7 @@ bif(group_leader, 0) -> true; bif(group_leader, 2) -> true; bif(halt, 0) -> true; bif(halt, 1) -> true; +bif(halt, 2) -> true; bif(hd, 1) -> true; bif(integer_to_list, 1) -> true; bif(integer_to_list, 2) -> true; diff --git a/lib/stdlib/src/erl_lint.erl b/lib/stdlib/src/erl_lint.erl index a1af0057ca..648ff349a4 100644 --- a/lib/stdlib/src/erl_lint.erl +++ b/lib/stdlib/src/erl_lint.erl @@ -2,7 +2,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -248,6 +248,8 @@ format_error({illegal_guard_local_call, {F,A}}) -> io_lib:format("call to local/imported function ~w/~w is illegal in guard", [F,A]); format_error(illegal_guard_expr) -> "illegal guard expression"; +format_error(deprecated_tuple_fun) -> + "tuple funs are deprecated and will be removed in R16"; %% --- exports --- format_error({explicit_export,F,A}) -> io_lib:format("in this release, the call to ~w/~w must be written " @@ -1914,7 +1916,8 @@ gexpr({call,Line,{remote,_Lr,{atom,_Lm,erlang},{atom,_Lf,F}},As}, Vt, St0) -> true -> {Asvt,St1}; false -> {Asvt,add_error(Line, illegal_guard_expr, St1)} end; -gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, Vt, St) -> +gexpr({call,L,{tuple,Lt,[{atom,Lm,erlang},{atom,Lf,F}]},As}, Vt, St0) -> + St = add_warning(L, deprecated_tuple_fun, St0), gexpr({call,L,{remote,Lt,{atom,Lm,erlang},{atom,Lf,F}},As}, Vt, St); gexpr({op,Line,Op,A}, Vt, St0) -> {Avt,St1} = gexpr(A, Vt, St0), diff --git a/lib/stdlib/src/error_logger_file_h.erl b/lib/stdlib/src/error_logger_file_h.erl index 08f1873803..e9364ed787 100644 --- a/lib/stdlib/src/error_logger_file_h.erl +++ b/lib/stdlib/src/error_logger_file_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/stdlib/src/error_logger_tty_h.erl b/lib/stdlib/src/error_logger_tty_h.erl index 48e069a407..ad5891f191 100644 --- a/lib/stdlib/src/error_logger_tty_h.erl +++ b/lib/stdlib/src/error_logger_tty_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/stdlib/src/escript.erl b/lib/stdlib/src/escript.erl index ad49d89908..498d850df3 100644 --- a/lib/stdlib/src/escript.erl +++ b/lib/stdlib/src/escript.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -22,7 +22,7 @@ -export([script_name/0, create/2, extract/2]). %% Internal API. --export([start/0, start/1]). +-export([start/0, start/1, parse_file/1]). %%----------------------------------------------------------------------- @@ -346,7 +346,8 @@ parse_and_run(File, Args, Options) -> case Source of archive -> {ok, FileInfo} = file:read_file_info(File), - case code:set_primary_archive(File, FormsOrBin, FileInfo) of + case code:set_primary_archive(File, FormsOrBin, FileInfo, + fun escript:parse_file/1) of ok when CheckOnly -> case code:load_file(Module) of {module, _} -> @@ -396,6 +397,19 @@ parse_and_run(File, Args, Options) -> %% Parse script %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Only used as callback by erl_prim_loader +parse_file(File) -> + try parse_file(File, false) of + {_Source, _Module, FormsOrBin, _HasRecs, _Mode} + when is_binary(FormsOrBin) -> + {ok, FormsOrBin}; + _ -> + {error, no_archive_bin} + catch + throw:Reason -> + {error, Reason} + end. + parse_file(File, CheckOnly) -> {HeaderSz, NextLineNo, Fd, Sections} = parse_header(File, false), @@ -848,17 +862,7 @@ fatal(Str) -> throw(Str). my_halt(Reason) -> - case process_info(group_leader(), status) of - {_,waiting} -> - %% Now all output data is down in the driver. - %% Give the driver some extra time before halting. - receive after 1 -> ok end, - halt(Reason); - _ -> - %% Probably still processing I/O requests. - erlang:yield(), - my_halt(Reason) - end. + erlang:halt(Reason). hidden_apply(App, M, F, Args) -> try diff --git a/lib/stdlib/src/filelib.erl b/lib/stdlib/src/filelib.erl index d532cea187..b098d4cb91 100644 --- a/lib/stdlib/src/filelib.erl +++ b/lib/stdlib/src/filelib.erl @@ -264,6 +264,9 @@ ensure_dir(F) -> case do_is_dir(Dir, file) of true -> ok; + false when Dir =:= F -> + %% Protect against infinite loop + {error,einval}; false -> ensure_dir(Dir), case file:make_dir(Dir) of diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index dbfcbea4f7..870af4e95f 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -726,6 +726,8 @@ nativename(Name0) -> _ -> Name end. +win32_nativename(Name) when is_binary(Name) -> + binary:replace(Name, <<"/">>, <<"\\">>, [global]); win32_nativename([$/|Rest]) -> [$\\|win32_nativename(Rest)]; win32_nativename([C|Rest]) -> diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl index ca05df20c0..2b8ba86909 100644 --- a/lib/stdlib/src/gen_event.erl +++ b/lib/stdlib/src/gen_event.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl index 51dc0bcee2..e480e2ac11 100644 --- a/lib/stdlib/src/gen_fsm.erl +++ b/lib/stdlib/src/gen_fsm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl index b384e8baf7..04308a51b7 100644 --- a/lib/stdlib/src/gen_server.erl +++ b/lib/stdlib/src/gen_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -270,7 +270,7 @@ enter_loop(Mod, Options, State) -> enter_loop(Mod, Options, State, self(), infinity). enter_loop(Mod, Options, State, ServerName = {Scope, _}) - when Scope == local; Scope == local -> + when Scope == local; Scope == global -> enter_loop(Mod, Options, State, ServerName, infinity); enter_loop(Mod, Options, State, ServerName = {via, _, _}) -> diff --git a/lib/stdlib/src/lib.erl b/lib/stdlib/src/lib.erl index 314fd60903..cf4b87d7eb 100644 --- a/lib/stdlib/src/lib.erl +++ b/lib/stdlib/src/lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -209,7 +209,7 @@ explain_reason(badarg, error, [], _PF, _S) -> explain_reason({badarg,V}, error=Cl, [], PF, S) -> % orelse, andalso format_value(V, <<"bad argument: ">>, Cl, PF, S); explain_reason(badarith, error, [], _PF, _S) -> - <<"bad argument in an arithmetic expression">>; + <<"an error occurred when evaluating an arithmetic expression">>; explain_reason({badarity,{Fun,As}}, error, [], _PF, _S) when is_function(Fun) -> %% Only the arity is displayed, not the arguments As. diff --git a/lib/stdlib/src/supervisor.erl b/lib/stdlib/src/supervisor.erl index c10da1989c..7d3c5a0e21 100644 --- a/lib/stdlib/src/supervisor.erl +++ b/lib/stdlib/src/supervisor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile index 4de6ea3ee7..29b8e28d3a 100644 --- a/lib/stdlib/test/Makefile +++ b/lib/stdlib/test/Makefile @@ -135,10 +135,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) stdlib.spec $(EMAKEFILE) \ - $(ERL_FILES) $(COVERFILE) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/stdlib/test/edlin_expand_SUITE.erl b/lib/stdlib/test/edlin_expand_SUITE.erl index a0e198ce09..0cd2688e2e 100644 --- a/lib/stdlib/test/edlin_expand_SUITE.erl +++ b/lib/stdlib/test/edlin_expand_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2010-2011. All Rights Reserved. +%% Copyright Ericsson AB 2010-2012. 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 @@ -46,10 +46,10 @@ groups() -> []. init_per_suite(Config) -> - true = code:delete(expand_test), - true = code:delete(expand_test1), - true = code:delete('ExpandTestCaps'), - true = code:delete('ExpandTestCaps1'), + (catch code:delete(expand_test)), + (catch code:delete(expand_test1)), + (catch code:delete('ExpandTestCaps')), + (catch code:delete('ExpandTestCaps1')), Config. end_per_suite(_Config) -> diff --git a/lib/stdlib/test/erl_eval_SUITE.erl b/lib/stdlib/test/erl_eval_SUITE.erl index ca2f18a05a..b0c7d562d5 100644 --- a/lib/stdlib/test/erl_eval_SUITE.erl +++ b/lib/stdlib/test/erl_eval_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 diff --git a/lib/stdlib/test/erl_expand_records_SUITE.erl b/lib/stdlib/test/erl_expand_records_SUITE.erl index 8b162cfda0..01cdb92d7b 100644 --- a/lib/stdlib/test/erl_expand_records_SUITE.erl +++ b/lib/stdlib/test/erl_expand_records_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/stdlib/test/erl_lint_SUITE.erl b/lib/stdlib/test/erl_lint_SUITE.erl index 4e93f056ad..9f9d97b619 100644 --- a/lib/stdlib/test/erl_lint_SUITE.erl +++ b/lib/stdlib/test/erl_lint_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2011. All Rights Reserved. +%% Copyright Ericsson AB 1999-2012. 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 @@ -1331,17 +1331,20 @@ guard(Config) when is_list(Config) -> foo. ">>, [warn_unused_vars, nowarn_obsolete_guard], - {errors,[{2,erl_lint,illegal_guard_expr}, - {4,erl_lint,illegal_guard_expr}, - {6,erl_lint,illegal_guard_expr}, - {8,erl_lint,illegal_guard_expr}, - {10,erl_lint,illegal_guard_expr}, - {12,erl_lint,illegal_guard_expr}, - {14,erl_lint,illegal_guard_expr}, - {16,erl_lint,illegal_guard_expr}, - {18,erl_lint,illegal_guard_expr}, - {20,erl_lint,illegal_guard_expr}], - []}}, + {error,[{2,erl_lint,illegal_guard_expr}, + {4,erl_lint,illegal_guard_expr}, + {6,erl_lint,illegal_guard_expr}, + {8,erl_lint,illegal_guard_expr}, + {10,erl_lint,illegal_guard_expr}, + {12,erl_lint,illegal_guard_expr}, + {14,erl_lint,illegal_guard_expr}, + {16,erl_lint,illegal_guard_expr}, + {18,erl_lint,illegal_guard_expr}, + {20,erl_lint,illegal_guard_expr}], + [{8,erl_lint,deprecated_tuple_fun}, + {14,erl_lint,deprecated_tuple_fun}, + {20,erl_lint,deprecated_tuple_fun}, + {28,erl_lint,deprecated_tuple_fun}]}}, {guard6, <<"-record(apa,{a=a,b=foo:bar()}). apa() -> diff --git a/lib/stdlib/test/escript_SUITE.erl b/lib/stdlib/test/escript_SUITE.erl index 9f95df062b..38c085616d 100644 --- a/lib/stdlib/test/escript_SUITE.erl +++ b/lib/stdlib/test/escript_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2011. All Rights Reserved. +%% Copyright Ericsson AB 2007-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -29,6 +29,7 @@ module_script/1, beam_script/1, archive_script/1, + archive_script_file_access/1, epp/1, create_and_extract/1, foldl/1, @@ -44,7 +45,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. all() -> [basic, errors, strange_name, emulator_flags, module_script, beam_script, archive_script, epp, - create_and_extract, foldl, overflow]. + create_and_extract, foldl, overflow, + archive_script_file_access]. groups() -> []. @@ -62,7 +64,7 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(_Case, Config) -> - ?line Dog = ?t:timetrap(?t:minutes(1)), + ?line Dog = ?t:timetrap(?t:minutes(2)), [{watchdog,Dog}|Config]. end_per_testcase(_Case, Config) -> @@ -356,7 +358,7 @@ beam_script(Config) when is_list(Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Create an archive file containing two entire applications plus two %% alternate main modules. Generate a new escript containing the archive -%% (with .app and .beam files and ) and the escript header. +%% (with .app and .beam files and) and the escript header. archive_script(Config) when is_list(Config) -> %% Copy the orig files to priv_dir @@ -464,6 +466,147 @@ archive_script(Config) when is_list(Config) -> ok. +%% Test the correction of OTP-10071 +%% The errors identified are +%% +%% a) If primary archive was named "xxx", then a file in the same +%% directory named "xxxyyy" would be interpreted as a file named yyy +%% inside the archive. +%% +%% b) erl_prim_loader did not correctly create and normalize absolute +%% paths for primary archive and files inside it, so unless given +%% with exact same path files inside the archive would not be +%% found. E.g. if escript was started as ./xxx then "xxx/file" +%% would not be found since erl_prim_loader would try to match +%% /full/path/to/xxx with /full/path/to/./xxx. Same problem with +%% ../. Also, the use of symlinks in the path to the archive would +%% cause problems. +%% +%% c) Depending on how the primary archive was built, +%% erl_prim_loader:list_dir/1 would sometimes return an empty string +%% inside the file list. This was a virtual element representing the +%% top directory of the archive. This shall not occur. +%% +archive_script_file_access(Config) when is_list(Config) -> + %% Copy the orig files to priv_dir + DataDir = ?config(data_dir, Config), + PrivDir = ?config(priv_dir, Config), + + MainMod = "archive_script_file_access", + MainSrc = MainMod ++ ".erl", + MainBeam = MainMod ++ ".beam", + + Archive = filename:join([PrivDir, "archive_script_file_access.zip"]), + ?line {ok, _} = zip:create(Archive, ["archive_script_file_access"], + [{compress, []}, {cwd, DataDir}]), + ?line {ok, _} = zip:extract(Archive, [{cwd, PrivDir}]), + TopDir = filename:join([PrivDir, "archive_script_file_access"]), + + %% Compile the code + ?line ok = compile_files([MainSrc], TopDir, TopDir), + + %% First, create a file structure which will be included in the archive: + %% + %% dir1/ + %% dir1/subdir1/ + %% dir1/subdir1/file1 + %% + {ok, OldDir} = file:get_cwd(), + ok = file:set_cwd(TopDir), + DummyDir = "dir1", + DummySubDir = filename:join(DummyDir, "subdir1"), + RelDummyFile = filename:join(DummySubDir, "file1"), + DummyFile = filename:join(TopDir,RelDummyFile), + ok = filelib:ensure_dir(DummyFile), + ok = file:write_file(DummyFile, ["foo\nbar\nbaz"]), + + %% 1. Create zip archive by adding the dummy file and the beam + %% file as binaries to zip. + %% + %% This used to provoke the following issues when the script was run as + %% "./<script_name>": + %% a. erl_prim_loader:read_file_info/1 returning 'error' + %% b. erl_prim_loader:list_dir/1 returning {ok, ["dir1", [], "file1"]} + %% leading to an infinite loop in reltool_target:spec_dir/1 + Files1 = + lists:map(fun(Filename) -> + {ok, Bin} = file:read_file(Filename), + {Filename,Bin} + end, + [RelDummyFile,MainBeam]), + {ok, {"mem", Bin1}} = zip:create("mem", Files1, [memory]), + + %% Create the escript + ScriptName1 = "archive_script_file_access1", + Script1 = filename:join([PrivDir, ScriptName1]), + Flags = "-escript main " ++ MainMod, + ok = escript:create(Script1,[shebang,{emu_args,Flags},{archive,Bin1}]), + ok = file:change_mode(Script1,8#00744), + + %% If supported, create a symlink to the script. This is used to + %% test error b) described above this test case. + SymlinkName1 = "symlink_to_"++ScriptName1, + Symlink1 = filename:join([PrivDir, SymlinkName1]), + file:make_symlink(ScriptName1,Symlink1), % will fail if not supported + + %% Also add a dummy file in the same directory with the same name + %% as the script except is also has an extension. This used to + %% test error a) described above this test case. + ok = file:write_file(Script1 ++ ".extension", + <<"same name as script, but with extension">>), + + %% Change to script's directory and run it as "./<script_name>" + ok = file:set_cwd(PrivDir), + run(PrivDir, "./" ++ ScriptName1 ++ " " ++ ScriptName1, + [<<"ExitCode:0">>]), + ok = file:set_cwd(TopDir), + + + %% 2. Create zip archive by letting zip read the files from the file system + %% + %% The difference compared to the archive_script_file_access1 is + %% that this will have a file element for each directory in the + %% archive - while archive_script_file_access1 will only have a + %% file element per regular file. + Files2 = [DummyDir,MainBeam], + {ok, {"mem", Bin2}} = zip:create("mem", Files2, [memory]), + + %% Create the escript + ScriptName2 = "archive_script_file_access2", + Script2 = filename:join([PrivDir, ScriptName2]), + ok = escript:create(Script2,[shebang,{emu_args,Flags},{archive,Bin2}]), + ok = file:change_mode(Script2,8#00744), + + %% Also add a dummy file in the same directory with the same name + %% as the script except is also has an extension. This used to + %% test error a) described above this test case. + ok = file:write_file(Script2 ++ ".extension", + <<"same name as script, but with extension">>), + + %% If supported, create a symlink to the script. This is used to + %% test error b) described above this test case. + SymlinkName2 = "symlink_to_"++ScriptName2, + Symlink2 = filename:join([PrivDir, SymlinkName2]), + file:make_symlink(ScriptName2,Symlink2), % will fail if not supported + + %% Change to script's directory and run it as "./<script_name>" + ok = file:set_cwd(PrivDir), + run(PrivDir, "./" ++ ScriptName2 ++ " " ++ ScriptName2, + [<<"ExitCode:0">>]), + + %% 3. If symlinks are supported, run one of the scripts via a symlink. + %% + %% This is in order to test error b) described above this test case. + case file:read_link(Symlink2) of + {ok,_} -> + run(PrivDir, "./" ++ SymlinkName2 ++ " " ++ ScriptName2, + [<<"ExitCode:0">>]); + _ -> % not supported + ok + end, + ok = file:set_cwd(OldDir). + + compile_app(TopDir, AppName) -> AppDir = filename:join([TopDir, AppName]), SrcDir = filename:join([AppDir, "src"]), diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl index de56579998..431a51b0e5 100644 --- a/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl +++ b/lib/stdlib/test/escript_SUITE_data/archive_script/archive_script_main2.erl @@ -21,6 +21,8 @@ -export([main/1]). +-include_lib("kernel/include/file.hrl"). + -define(DUMMY, archive_script_dummy). -define(DICT, archive_script_dict). @@ -32,7 +34,7 @@ main(MainArgs) -> io:format("dummy:~p\n",[[E || E <- ErlArgs, element(1, E) =:= ?DUMMY]]), %% Start the applications - {error, {not_started, ?DICT}} = application:start(archive_script_dummy), + {error, {not_started, ?DICT}} = application:start(?DUMMY), ok = application:start(?DICT), ok = application:start(?DUMMY), @@ -57,4 +59,17 @@ main(MainArgs) -> ok = ?DICT:erase(Tab, Key), error = ?DICT:find(Tab, Key), ok = ?DICT:erase(Tab), + + %% Check mtime related caching bug with escript/primary archive files + Escript = escript:script_name(), + {ok, FileInfo} = file:read_file_info(Escript), + %% Modify mtime of archive file and try to reload module + FileInfo2 = FileInfo#file_info{mtime=calendar:now_to_local_time(now())}, + ok = file:write_file_info(Escript, FileInfo2), + Module = ?DICT, + {file, _} = code:is_loaded(Module), + true = code:delete(Module), + false = code:is_loaded(Module), + {module, Module} = code:ensure_loaded(Module), + ok. diff --git a/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl new file mode 100644 index 0000000000..b03c8ba70d --- /dev/null +++ b/lib/stdlib/test/escript_SUITE_data/archive_script_file_access/archive_script_file_access.erl @@ -0,0 +1,105 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2012. All Rights Reserved. +%% +%% The contents of this file are subject to the Erlang Public License, +%% Version 1.1, (the "License"); you may not use this file except in +%% compliance with the License. You should have received a copy of the +%% Erlang Public License along with this software. If not, it can be +%% retrieved online at http://www.erlang.org/. +%% +%% Software distributed under the License is distributed on an "AS IS" +%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See +%% the License for the specific language governing rights and limitations +%% under the License. +%% +%% %CopyrightEnd% +%% +-module(archive_script_file_access). +-behaviour(escript). + +-export([main/1]). + +-include_lib("kernel/include/file.hrl"). + +main([RelArchiveFile]) -> + + AbsArchiveFile = filename:absname(RelArchiveFile), + DotSlashArchiveFile = "./" ++ RelArchiveFile, + + Beam = atom_to_list(?MODULE) ++ ".beam", + AbsBeam = filename:join(AbsArchiveFile,Beam), + RelBeam = filename:join(RelArchiveFile,Beam), + DotSlashBeam = filename:join(DotSlashArchiveFile,Beam), + Dir = "dir1", + AbsDir = filename:join(AbsArchiveFile,Dir), + RelDir = filename:join(RelArchiveFile,Dir), + DotSlashDir = filename:join(DotSlashArchiveFile,Dir), + SubDir = "subdir1", + AbsSubDir = filename:join(AbsDir,SubDir), + RelSubDir = filename:join(RelDir,SubDir), + DotSlashSubDir = filename:join(DotSlashDir,SubDir), + + {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(RelArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(DotSlashArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/"), + {ok,List1} = erl_prim_loader:list_dir(AbsArchiveFile ++ "/."), + {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsDir,".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([RelDir,".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashDir,".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([AbsSubDir,"..",".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([RelSubDir,"..",".."])), + {ok,List1} = erl_prim_loader:list_dir(filename:join([DotSlashSubDir,"..",".."])), + false = lists:member([],List1), + + %% If symlinks are supported on this platform... + RelSymlinkArchiveFile = "symlink_to_" ++ RelArchiveFile, + case file:read_link(RelSymlinkArchiveFile) of + {ok,_} -> + DotSlashSymlinkArchiveFile = "./" ++ RelSymlinkArchiveFile, + AbsSymlinkArchiveFile=filename:join(filename:dirname(AbsArchiveFile), + RelSymlinkArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(AbsSymlinkArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(RelSymlinkArchiveFile), + {ok,List1} = erl_prim_loader:list_dir(DotSlashSymlinkArchiveFile); + _ -> % not supported + ok + end, + + + {ok,List2} = erl_prim_loader:list_dir(AbsDir), + {ok,List2} = erl_prim_loader:list_dir(RelDir), + {ok,List2} = erl_prim_loader:list_dir(DotSlashDir), + false = lists:member([],List2), + + error = erl_prim_loader:list_dir(AbsBeam), + error = erl_prim_loader:list_dir(RelBeam), + error = erl_prim_loader:list_dir(DotSlashBeam), + + error = erl_prim_loader:get_file(AbsArchiveFile), + error = erl_prim_loader:get_file(RelArchiveFile), + error = erl_prim_loader:get_file(DotSlashArchiveFile), + error = erl_prim_loader:get_file(AbsArchiveFile ++ "/"), + error = erl_prim_loader:get_file(AbsArchiveFile ++ "/."), + {ok,Bin,AbsBeam} = erl_prim_loader:get_file(AbsBeam), + {ok,Bin,RelBeam} = erl_prim_loader:get_file(RelBeam), + {ok,Bin,DotSlashBeam} = erl_prim_loader:get_file(DotSlashBeam), + + {ok,#file_info{type=directory}=DFI} = + erl_prim_loader:read_file_info(AbsArchiveFile), + {ok,DFI} = erl_prim_loader:read_file_info(RelArchiveFile), + {ok,DFI} = erl_prim_loader:read_file_info(DotSlashArchiveFile), + {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/"), + {ok,DFI} = erl_prim_loader:read_file_info(AbsArchiveFile ++ "/."), + {ok,#file_info{type=regular}=RFI} = erl_prim_loader:read_file_info(AbsBeam), + {ok,RFI} = erl_prim_loader:read_file_info(RelBeam), + {ok,RFI} = erl_prim_loader:read_file_info(DotSlashBeam), + + F = AbsArchiveFile ++ ".extension", + error = erl_prim_loader:list_dir(F), + {ok,_,_} = erl_prim_loader:get_file(F), + {ok,#file_info{type=regular}} = erl_prim_loader:read_file_info(F), + + ok. diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 59532b65a0..95f10b1df3 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 @@ -74,6 +74,7 @@ -export([bad_table/1, types/1]). -export([otp_9932/1]). -export([otp_9423/1]). +-export([otp_10182/1]). -export([init_per_testcase/2, end_per_testcase/2]). %% Convenience for manual testing @@ -146,6 +147,7 @@ all() -> exit_many_large_table_owner, exit_many_tables_owner, exit_many_many_tables_owner, write_concurrency, heir, give_away, setopts, bad_table, types, + otp_10182, otp_9932, otp_9423]. @@ -715,30 +717,17 @@ adjust_xmem([T1,T2,T3,T4], {A0,B0,C0,D0} = _Mem0) -> TabDiff = ?TAB_STRUCT_SZ, Mem1 = {A0+TabDiff, B0+TabDiff, C0+TabDiff, D0+TabDiff}, - Mem2 = case {erlang:system_info({wordsize,internal}),erlang:system_info({wordsize,external})} of - %% Halfword, corrections for regular pointers occupying two internal words. - {4,8} -> - {A1,B1,C1,D1} = Mem1, - {A1+4*ets:info(T1, size)+?DB_TREE_STACK_NEED, - B1+3*ets:info(T2, size)+?DB_HASH_SIZEOF_EXTSEG, - C1+3*ets:info(T3, size)+?DB_HASH_SIZEOF_EXTSEG, - D1+3*ets:info(T4, size)+?DB_HASH_SIZEOF_EXTSEG}; - _ -> - Mem1 - end, - - %% Adjust for hybrid and shared heaps: - %% Each record is one word smaller. - %%Mem2 = case erlang:system_info(heap_type) of - %% private -> - %% Mem1; - %% _ -> - %% {A1,B1,C1,D1} = Mem1, - %% {A1-ets:info(T1, size),B1-ets:info(T2, size), - %% C1-ets:info(T3, size),D1-ets:info(T4, size)} - %% end, - %%{Mem2,{ets:info(T1,stats),ets:info(T2,stats),ets:info(T3,stats),ets:info(T4,stats)}}. - Mem2. + case {erlang:system_info({wordsize,internal}),erlang:system_info({wordsize,external})} of + %% Halfword, corrections for regular pointers occupying two internal words. + {4,8} -> + {A1,B1,C1,D1} = Mem1, + {A1+4*ets:info(T1, size)+?DB_TREE_STACK_NEED, + B1+3*ets:info(T2, size)+?DB_HASH_SIZEOF_EXTSEG, + C1+3*ets:info(T3, size)+?DB_HASH_SIZEOF_EXTSEG, + D1+3*ets:info(T4, size)+?DB_HASH_SIZEOF_EXTSEG}; + _ -> + Mem1 + end. t_whitebox(doc) -> ["Diverse whitebox testes"]; @@ -1037,6 +1026,8 @@ t_test_ms(Config) when is_list(Config) -> [{{'$1','$2'},[{'<','$1','$2'}],['$$']}]), ?line {ok,false} = ets:test_ms({a,b}, [{{'$1','$2'},[{'>','$1','$2'}],['$$']}]), + Tpl = {a,gb_sets:new()}, + ?line {ok,Tpl} = ets:test_ms(Tpl, [{{'_','_'}, [], ['$_']}]), % OTP-10190 ?line {error,[{error,String}]} = ets:test_ms({a,b}, [{{'$1','$2'}, [{'flurp','$1','$2'}], @@ -5483,6 +5474,20 @@ otp_9423(Config) when is_list(Config) -> Skipped -> Skipped end. + +%% Corrupted binary in compressed table +otp_10182(Config) when is_list(Config) -> + Bin = <<"aHR0cDovL2hvb3RzdWl0ZS5jb20vYy9wcm8tYWRyb2xsLWFi">>, + Key = {test, Bin}, + Value = base64:decode(Bin), + In = {Key,Value}, + Db = ets:new(undefined, [set, protected, {read_concurrency, true}, compressed]), + ets:insert(Db, In), + [Out] = ets:lookup(Db, Key), + io:format("In : ~p\nOut: ~p\n", [In,Out]), + ets:delete(Db), + In = Out. + diff --git a/lib/stdlib/test/filename_SUITE.erl b/lib/stdlib/test/filename_SUITE.erl index 4cfa589660..99516c0c04 100644 --- a/lib/stdlib/test/filename_SUITE.erl +++ b/lib/stdlib/test/filename_SUITE.erl @@ -25,7 +25,7 @@ -export([pathtype/1,rootname/1,split/1,find_src/1]). -export([absname_bin/1, absname_bin_2/1, basename_bin_1/1, basename_bin_2/1, - dirname_bin/1, extension_bin/1, join_bin/1]). + dirname_bin/1, extension_bin/1, join_bin/1, t_nativename_bin/1]). -export([pathtype_bin/1,rootname_bin/1,split_bin/1]). -include_lib("test_server/include/test_server.hrl"). @@ -38,7 +38,7 @@ all() -> join, pathtype, rootname, split, t_nativename, find_src, absname_bin, absname_bin_2, basename_bin_1, basename_bin_2, dirname_bin, extension_bin, - join_bin, pathtype_bin, rootname_bin, split_bin]. + join_bin, pathtype_bin, rootname_bin, split_bin, t_nativename_bin]. groups() -> []. @@ -804,3 +804,14 @@ split_bin(Config) when is_list(Config) -> ok end. +t_nativename_bin(Config) when is_list(Config) -> + ?line <<"abcedf">> = filename:nativename(<<"abcedf">>), + case os:type() of + {win32, _} -> + ?line <<"a:\\temp\\arne.exe">> = + filename:nativename(<<"A:/temp//arne.exe/">>); + _ -> + ?line <<"/usr/tmp/arne">> = + filename:nativename(<<"/usr/tmp//arne/">>) + end. + diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl index cdf15ba017..48ef7e55ed 100644 --- a/lib/stdlib/test/gen_server_SUITE.erl +++ b/lib/stdlib/test/gen_server_SUITE.erl @@ -37,7 +37,8 @@ % spawn export -export([spec_init_local/2, spec_init_global/2, spec_init_via/2, - spec_init_default_timeout/2, spec_init_anonymous/1, + spec_init_default_timeout/2, spec_init_global_default_timeout/2, + spec_init_anonymous/1, spec_init_anonymous_default_timeout/1, spec_init_not_proc_lib/1, cast_fast_messup/0]). @@ -749,7 +750,7 @@ spec_init(suite) -> spec_init(Config) when is_list(Config) -> OldFlag = process_flag(trap_exit, true), - + ?line {ok, Pid0} = start_link(spec_init_local, [{ok, my_server}, []]), ?line ok = gen_server:call(Pid0, started_p), ?line ok = gen_server:call(Pid0, stop), @@ -819,6 +820,14 @@ spec_init(Config) when is_list(Config) -> test_server:fail(gen_server_did_not_die) end, + %% Before the OTP-10130 fix this failed because a timeout message + %% was generated as the spawned process crashed because a {global, Name} + %% was matched as a timeout value instead of matching on scope. + {ok, _PidHurra} = + start_link(spec_init_global_default_timeout, [{ok, hurra}, []]), + timer:sleep(1000), + ok = gen_server:call(_PidHurra, started_p), + ?line Pid5 = erlang:spawn_link(?MODULE, spec_init_not_proc_lib, [[]]), receive @@ -1125,6 +1134,15 @@ spec_init_default_timeout({ok, Name}, Options) -> %% Supervised init can occur here ... gen_server:enter_loop(?MODULE, Options, {}, {local, Name}). +%% OTP-10130, A bug was introduced where global scope was not matched when +%% enter_loop/4 was called (no timeout). +spec_init_global_default_timeout({ok, Name}, Options) -> + process_flag(trap_exit, true), + global:register_name(Name, self()), + proc_lib:init_ack({ok, self()}), + %% Supervised init can occur here ... + gen_server:enter_loop(?MODULE, Options, {}, {global, Name}). + spec_init_anonymous(Options) -> process_flag(trap_exit, true), proc_lib:init_ack({ok, self()}), diff --git a/lib/stdlib/test/io_proto_SUITE.erl b/lib/stdlib/test/io_proto_SUITE.erl index b69cd74edb..661d57c85b 100644 --- a/lib/stdlib/test/io_proto_SUITE.erl +++ b/lib/stdlib/test/io_proto_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2011. All Rights Reserved. +%% Copyright Ericsson AB 2009-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -50,7 +50,7 @@ -define(privdir(Conf), ?config(priv_dir, Conf)). -endif. --define(debug, true). +%%-define(debug, true). -ifdef(debug). -define(format(S, A), io:format(S, A)). @@ -137,7 +137,7 @@ unicode_prompt(Config) when is_list(Config) -> {putline, "io:get_line('')."}, {putline, "hej"}, {getline, "<<\"hej\\n\">>"} - ],[],[],"-pa "++ PA), + ],[],[],"-pa \""++ PA++"\""), %% And one with oldshell ?line rtnode([{putline,""}, {putline, "2."}, @@ -153,7 +153,7 @@ unicode_prompt(Config) when is_list(Config) -> {putline, "io:get_line('')."}, {putline, "hej"}, {getline_re, ".*<<\"hej\\\\n\">>"} - ],[],[],"-oldshell -pa "++PA), + ],[],[],"-oldshell -pa \""++PA++"\""), ok. @@ -732,7 +732,7 @@ bc_with_r12_1(Config) -> PA = filename:dirname(code:which(?MODULE)), Name1 = io_proto_r12_1, ?line N1 = list_to_atom(atom_to_list(Name1) ++ "@" ++ hostname()), - ?line ?t:start_node(Name1, peer, [{args, "-pz "++PA},{erl,[{release,"r12b"}]}]), + ?line ?t:start_node(Name1, peer, [{args, "-pz \""++PA++"\""},{erl,[{release,"r12b"}]}]), DataDir = ?config(data_dir,Config), %PrivDir = ?config(priv_dir,Config), FileName1 = filename:join([DataDir,"testdata_latin1.dat"]), @@ -908,7 +908,7 @@ bc_with_r12_gl_1(_Config,Machine) -> PA = filename:dirname(code:which(?MODULE)), Name1 = io_proto_r12_gl_1, ?line N1 = list_to_atom(atom_to_list(Name1) ++ "@" ++ hostname()), - ?line ?t:start_node(Name1, peer, [{args, "-pz "++PA},{erl,[{release,"r12b"}]}]), + ?line ?t:start_node(Name1, peer, [{args, "-pz \""++PA++"\""},{erl,[{release,"r12b"}]}]), TestDataLine1 = [229,228,246], TestDataLine1BinUtf = unicode:characters_to_binary(TestDataLine1), TestDataLine1BinLatin = list_to_binary(TestDataLine1), @@ -1290,7 +1290,7 @@ eof_on_pipe(Config) when is_list(Config) -> end end, CommandLine1 = EchoLine ++ - Erl++" -noshell -eval " + "\""++Erl++"\" -noshell -eval " "'io:format(\"~p\",[io:get_line(\"\")])," "io:format(\"~p\",[io:get_line(\"\")])," "io:format(\"~p\",[io:get_line(\"\")]).' -run init stop", @@ -1301,7 +1301,7 @@ eof_on_pipe(Config) when is_list(Config) -> exit({unexpected1,Other1}) end, CommandLine2 = EchoLine ++ - Erl++" -noshell -eval " + "\""++Erl++"\" -noshell -eval " "'io:setopts([binary]),io:format(\"~p\",[io:get_line(\"\")])," "io:format(\"~p\",[io:get_line(\"\")])," "io:format(\"~p\",[io:get_line(\"\")]).' -run init stop", @@ -1340,7 +1340,8 @@ rtnode(Commands,Nodename,ErlPrefix,Extra) -> ?line {skip, Reason2}; Tempdir -> ?line SPid = - start_runerl_node(RunErl,ErlPrefix++Erl, + start_runerl_node(RunErl,ErlPrefix++ + "\\\""++Erl++"\\\"", Tempdir,Nodename, Extra), ?line CPid = start_toerl_server(ToErl,Tempdir), ?line erase(getline_skipped), @@ -1607,10 +1608,10 @@ start_runerl_node(RunErl,Erl,Tempdir,Nodename,Extra) -> " "++Extra end, spawn(fun() -> - ?dbg(RunErl++" "++Tempdir++"/ "++Tempdir++" \""++ - Erl++XArg++XXArg++"\""), - os:cmd(RunErl++" "++Tempdir++"/ "++Tempdir++" \""++ - Erl++XArg++XXArg++"\"") + ?dbg("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++ + " \""++Erl++XArg++XXArg++"\""), + os:cmd("\""++RunErl++"\" "++Tempdir++"/ "++Tempdir++ + " \""++Erl++XArg++XXArg++"\"") end). start_toerl_server(ToErl,Tempdir) -> @@ -1640,7 +1641,7 @@ try_to_erl(Command, N) -> end. toerl_server(Parent,ToErl,Tempdir) -> - Port = try_to_erl(ToErl++" "++Tempdir++"/ 2>/dev/null",8), + Port = try_to_erl("\""++ToErl++"\" "++Tempdir++"/ 2>/dev/null",8), case Port of P when is_port(P) -> Parent ! {self(),started}; diff --git a/lib/stdlib/test/qlc_SUITE.erl b/lib/stdlib/test/qlc_SUITE.erl index 50a76cdfb5..192268f90e 100644 --- a/lib/stdlib/test/qlc_SUITE.erl +++ b/lib/stdlib/test/qlc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. 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 @@ -2969,12 +2969,14 @@ lookup1(Config) when is_list(Config) -> [3] = lookup_keys(Q) end, [{1,a},{3,3}])">>, + {cres, <<"A = 3, etsc(fun(E) -> Q = qlc:q([X || X <- ets:table(E), A =:= {erlang,element}(1, X)]), [{3,3}] = qlc:e(Q), [3] = lookup_keys(Q) end, [{1,a},{3,3}])">>, + {warnings,[{3,erl_lint,deprecated_tuple_fun}]}}, <<"etsc(fun(E) -> A = 3, @@ -3439,12 +3441,14 @@ lookup2(Config) when is_list(Config) -> [r] = qlc:e(Q), [r] = lookup_keys(Q) end, [{keypos,1}], [#r{}])">>, + {cres, <<"etsc(fun(E) -> Q = qlc:q([element(1, X) || X <- ets:table(E), {erlang,is_record}(X, r, 2)]), [r] = qlc:e(Q), [r] = lookup_keys(Q) end, [{keypos,1}], [#r{}])">>, + {warnings,[{4,erl_lint,deprecated_tuple_fun}]}}, {cres, <<"etsc(fun(E) -> Q = qlc:q([element(1, X) || X <- ets:table(E), @@ -3465,12 +3469,14 @@ lookup2(Config) when is_list(Config) -> [r] = qlc:e(Q), [r] = lookup_keys(Q) end, [{keypos,1}], [#r{}])">>, + {cres, <<"etsc(fun(E) -> Q = qlc:q([element(1, X) || X <- ets:table(E), {erlang,is_record}(X, r)]), [r] = qlc:e(Q), [r] = lookup_keys(Q) - end, [{keypos,1}], [#r{}])">> + end, [{keypos,1}], [#r{}])">>, + {warnings,[{4,erl_lint,deprecated_tuple_fun}]}} ], ?line run(Config, <<"-record(r, {a}).\n">>, TsR), @@ -7927,15 +7933,23 @@ run_test(Config, Extra, {cres, Body, Opts, ExpectedCompileReturn}) -> ok end, + wait_for_expected(R, Before, SourceFile, true), + code:purge(Mod); +run_test(Config, Extra, Body) -> + run_test(Config, Extra, {cres,Body,[]}). + +wait_for_expected(R, Before, SourceFile, Wait) -> Ms = erlang:process_info(self(),messages), After = {get(), pps(), ets:all(), Ms}, - code:purge(Mod), case {R, After} of - {ok, Before} -> ok; - _ -> expected({ok,Before}, {R,After}, SourceFile) - end; -run_test(Config, Extra, Body) -> - run_test(Config, Extra, {cres,Body,[]}). + {ok, Before} -> + ok; + _ when Wait -> + timer:sleep(1000), + wait_for_expected(R, Before, SourceFile, false); + _ -> + expected({ok,Before}, {R,After}, SourceFile) + end. unload_pt() -> erlang:garbage_collect(), % get rid of references to qlc_pt... diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl index d6d946a28f..a542745e67 100644 --- a/lib/stdlib/test/re_SUITE.erl +++ b/lib/stdlib/test/re_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 diff --git a/lib/stdlib/test/shell_SUITE.erl b/lib/stdlib/test/shell_SUITE.erl index a881742f13..4b83e42ee0 100644 --- a/lib/stdlib/test/shell_SUITE.erl +++ b/lib/stdlib/test/shell_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -153,7 +153,7 @@ start_restricted_from_shell(Config) when is_list(Config) -> comm_err(<<"begin init:stop() end.">>), ?line "exception exit: restricted shell does not allow init:stop()" = comm_err(<<"begin F = fun() -> init:stop() end, F() end.">>), - ?line "exception error: bad argument in an arithmetic expression" = + ?line "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"begin +a end.">>), ?line "exception exit: restricted shell does not allow a + b" = comm_err(<<"begin a+b end.">>), @@ -2359,7 +2359,7 @@ otp_6554(Config) when is_list(Config) -> comm_err(<<"fun(X) -> not X end(a).">>), ?line "exception error: bad argument: a" = comm_err(<<"fun(A, B) -> A orelse B end(a, b).">>), - ?line "exception error: bad argument in an arithmetic expression" = + ?line "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"math:sqrt(2)/round(math:sqrt(0)).">>), ?line "exception error: interpreted function with arity 1 called with no arguments" = comm_err(<<"fun(V) -> V end().">>), @@ -2478,9 +2478,9 @@ otp_6554(Config) when is_list(Config) -> " receive {'EXIT', Pid, {{nocatch,foo},_}} -> ok end " "end.">>), - ?line "exception error: bad argument in an arithmetic expression" = + ?line "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"begin catch_exception(true), 1/0 end.">>), - ?line "exception error: bad argument in an arithmetic expression" = + ?line "exception error: an error occurred when evaluating an arithmetic expression" = comm_err(<<"begin catch_exception(false), 1/0 end.">>), ?line "exception error: no function clause matching call to catch_exception/1" = comm_err(<<"catch_exception(1).">>), @@ -2637,7 +2637,7 @@ otp_8393(Config) when is_list(Config) -> prompt_err(<<"shell:prompt_func('> ').">>), ?line _ = shell:prompt_func(default), - ?line "exception error: bad argument in an arithmetic expression"++_ = + ?line "exception error: an error occurred when evaluating an arithmetic expression"++_ = prompt_err(<<"shell:prompt_func({shell_SUITE,prompt4}).">>), ?line _ = shell:prompt_func(default), @@ -2710,7 +2710,7 @@ prompt3(L) -> integer_to_list(N). prompt4(_L) -> - erlang:apply({erlang,'/'}, [1,0]). + erlang:apply(fun erlang:'/'/2, [1,0]). prompt5(_L) -> [1050,1072,1082,1074,1086,32,1077,32,85,110,105,99,111,100,101,32,63]. diff --git a/lib/stdlib/vsn.mk b/lib/stdlib/vsn.mk index 694d39ce9c..3df7495ec4 100644 --- a/lib/stdlib/vsn.mk +++ b/lib/stdlib/vsn.mk @@ -1 +1 @@ -STDLIB_VSN = 1.18.1 +STDLIB_VSN = 1.18.2 diff --git a/lib/syntax_tools/doc/overview.edoc b/lib/syntax_tools/doc/overview.edoc index 23eadce8fe..df02ad0b3a 100644 --- a/lib/syntax_tools/doc/overview.edoc +++ b/lib/syntax_tools/doc/overview.edoc @@ -1,5 +1,9 @@ + -*- html -*- -@author Richard Carlsson <[email protected]> + Syntax Tools overview page + + +@author Richard Carlsson <[email protected]> @copyright 1997-2004 Richard Carlsson @version {@version} @title Erlang Syntax Tools diff --git a/lib/syntax_tools/doc/src/Makefile b/lib/syntax_tools/doc/src/Makefile index 291b3e3047..d1c6989bd9 100644 --- a/lib/syntax_tools/doc/src/Makefile +++ b/lib/syntax_tools/doc/src/Makefile @@ -125,13 +125,13 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/syntax_tools/doc/src/notes.xml b/lib/syntax_tools/doc/src/notes.xml index b9ac587043..434a3e721e 100644 --- a/lib/syntax_tools/doc/src/notes.xml +++ b/lib/syntax_tools/doc/src/notes.xml @@ -31,6 +31,21 @@ <p>This document describes the changes made to the Syntax_Tools application.</p> +<section><title>Syntax_Tools 1.6.8</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>Minor suppressions and fixes of compilation + warnings</p> + <p> + Own Id: OTP-10016</p> + </item> + </list> + </section> + +</section> + <section><title>Syntax_Tools 1.6.7.2</title> <section><title>Improvements and New Features</title> diff --git a/lib/syntax_tools/examples/Makefile b/lib/syntax_tools/examples/Makefile index 7cfe9185c2..2724b0899b 100644 --- a/lib/syntax_tools/examples/Makefile +++ b/lib/syntax_tools/examples/Makefile @@ -49,8 +49,8 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLE_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/syntax_tools/src/Makefile b/lib/syntax_tools/src/Makefile index bac138e95a..dca5e78be9 100644 --- a/lib/syntax_tools/src/Makefile +++ b/lib/syntax_tools/src/Makefile @@ -80,10 +80,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(OBJECTS) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(SOURCES) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(OBJECTS) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(SOURCES) "$(RELSYSDIR)/src" release_docs_spec: diff --git a/lib/syntax_tools/src/epp_dodger.erl b/lib/syntax_tools/src/epp_dodger.erl index 9f6f7d815e..b3ced34c14 100644 --- a/lib/syntax_tools/src/epp_dodger.erl +++ b/lib/syntax_tools/src/epp_dodger.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 2001-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_comment_scan.erl b/lib/syntax_tools/src/erl_comment_scan.erl index 108ab3bffd..b833e1c069 100644 --- a/lib/syntax_tools/src/erl_comment_scan.erl +++ b/lib/syntax_tools/src/erl_comment_scan.erl @@ -16,7 +16,7 @@ %% %% ===================================================================== %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_prettypr.erl b/lib/syntax_tools/src/erl_prettypr.erl index 7caf0b3db6..f4bbf975c3 100644 --- a/lib/syntax_tools/src/erl_prettypr.erl +++ b/lib/syntax_tools/src/erl_prettypr.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_recomment.erl b/lib/syntax_tools/src/erl_recomment.erl index fc7c515700..7b2f9f7adb 100644 --- a/lib/syntax_tools/src/erl_recomment.erl +++ b/lib/syntax_tools/src/erl_recomment.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_syntax.erl b/lib/syntax_tools/src/erl_syntax.erl index 32fd3722d6..76a6a6dc36 100644 --- a/lib/syntax_tools/src/erl_syntax.erl +++ b/lib/syntax_tools/src/erl_syntax.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== @@ -26,23 +24,20 @@ %% This module defines an abstract data type for representing Erlang %% source code as syntax trees, in a way that is backwards compatible %% with the data structures created by the Erlang standard library -%% parser module <code>erl_parse</code> (often referred to as "parse +%% parser module `erl_parse' (often referred to as "parse %% trees", which is a bit of a misnomer). This means that all -%% <code>erl_parse</code> trees are valid abstract syntax trees, but the +%% `erl_parse' trees are valid abstract syntax trees, but the %% reverse is not true: abstract syntax trees can in general not be used -%% as input to functions expecting an <code>erl_parse</code> tree. +%% as input to functions expecting an `erl_parse' tree. %% However, as long as an abstract syntax tree represents a correct -%% Erlang program, the function <a -%% href="#revert-1"><code>revert/1</code></a> should be able to -%% transform it to the corresponding <code>erl_parse</code> +%% Erlang program, the function {@link revert/1} should be able to +%% transform it to the corresponding `erl_parse' %% representation. %% -%% A recommended starting point for the first-time user is the -%% documentation of the <a -%% href="#type-syntaxTree"><code>syntaxTree()</code></a> data type, and -%% the function <a href="#type-1"><code>type/1</code></a>. +%% A recommended starting point for the first-time user is the documentation +%% of the {@link syntaxTree()} data type, and the function {@link type/1}. %% -%% <h3><b>NOTES:</b></h3> +%% == NOTES: == %% %% This module deals with the composition and decomposition of %% <em>syntactic</em> entities (as opposed to semantic ones); its @@ -52,36 +47,31 @@ %% in general, the user is assumed to pass type-correct arguments - if %% this is not done, the effects are not defined. %% -%% With the exception of the <code>erl_parse</code> data structures, +%% With the exception of the {@link erl_parse()} data structures, %% the internal representations of abstract syntax trees are subject to %% change without notice, and should not be documented outside this %% module. Furthermore, we do not give any guarantees on how an abstract %% syntax tree may or may not be represented, <em>with the following %% exceptions</em>: no syntax tree is represented by a single atom, such -%% as <code>none</code>, by a list constructor <code>[X | Y]</code>, or -%% by the empty list <code>[]</code>. This can be relied on when writing +%% as `none', by a list constructor `[X | Y]', or +%% by the empty list `[]'. This can be relied on when writing %% functions that operate on syntax trees. -%% @type syntaxTree(). An abstract syntax tree. The -%% <code>erl_parse</code> "parse tree" representation is a subset of the -%% <code>syntaxTree()</code> representation. +%% @type syntaxTree(). An abstract syntax tree. The {@link erl_parse()} +%% "parse tree" representation is a proper subset of the `syntaxTree()' +%% representation. %% %% Every abstract syntax tree node has a <em>type</em>, given by the -%% function <a href="#type-1"><code>type/1</code></a>. Each node also -%% has associated <em>attributes</em>; see <a -%% href="#get_attrs-1"><code>get_attrs/1</code></a> for details. The -%% functions <a href="#make_tree-2"><code>make_tree/2</code></a> and <a -%% href="#subtrees-1"><code>subtrees/1</code></a> are generic +%% function {@link type/1}. Each node also has associated +%% <em>attributes</em>; see {@link get_attrs/1} for details. The functions +%% {@link make_tree/2} and {@link subtrees/1} are generic %% constructor/decomposition functions for abstract syntax trees. The -%% functions <a href="#abstract-1"><code>abstract/1</code></a> and <a -%% href="#concrete-1"><code>concrete/1</code></a> convert between +%% functions {@link abstract/1} and {@link concrete/1} convert between %% constant Erlang terms and their syntactic representations. The set of -%% syntax tree nodes is extensible through the <a -%% href="#tree-2"><code>tree/2</code></a> function. +%% syntax tree nodes is extensible through the {@link tree/2} function. %% -%% A syntax tree can be transformed to the <code>erl_parse</code> -%% representation with the <a href="#revert-1"><code>revert/1</code></a> -%% function. +%% A syntax tree can be transformed to the {@link erl_parse()} +%% representation with the {@link revert/1} function. -module(erl_syntax). @@ -309,7 +299,7 @@ data/1, is_tree/1]). --export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0]). +-export_type([forms/0, syntaxTree/0, syntaxTreeAttributes/0, padding/0]). %% ===================================================================== %% IMPLEMENTATION NOTES: @@ -390,11 +380,15 @@ -record(wrapper, {type :: atom(), attr = #attr{} :: #attr{}, - tree :: term()}). + tree :: erl_parse()}). %% ===================================================================== --type syntaxTree() :: #tree{} | #wrapper{} | tuple(). % XXX: refine +-type syntaxTree() :: #tree{} | #wrapper{} | erl_parse(). + +-type erl_parse() :: erl_parse:abstract_form() | erl_parse:abstract_expr(). +%% The representation built by the Erlang standard library parser +%% `erl_parse'. This is a subset of the {@link syntaxTree()} type. %% ===================================================================== %% @@ -404,12 +398,11 @@ %% ===================================================================== -%% @spec type(Node::syntaxTree()) -> atom() -%% -%% @doc Returns the type tag of <code>Node</code>. If <code>Node</code> +%% @doc Returns the type tag of `Node'. If `Node' %% does not represent a syntax tree, evaluation fails with reason -%% <code>badarg</code>. Node types currently defined by this module are: -%% <p><center><table border="1"> +%% `badarg'. Node types currently defined by this module are: +%% +%% <center><table border="1"> %% <tr> %% <td>application</td> %% <td>arity_qualifier</td> @@ -476,12 +469,13 @@ %% <td>variable</td> %% <td>warning_marker</td> %% </tr> -%% </table></center></p> -%% <p>The user may (for special purposes) create additional nodes -%% with other type tags, using the <code>tree/2</code> function.</p> +%% </table></center> +%% +%% The user may (for special purposes) create additional nodes +%% with other type tags, using the {@link tree/2} function. %% -%% <p>Note: The primary constructor functions for a node type should -%% always have the same name as the node type itself.</p> +%% Note: The primary constructor functions for a node type should +%% always have the same name as the node type itself. %% %% @see tree/2 %% @see application/3 @@ -606,39 +600,38 @@ type(Node) -> %% ===================================================================== -%% @spec is_leaf(Node::syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> is a leaf node, -%% otherwise <code>false</code>. The currently recognised leaf node +%% @doc Returns `true' if `Node' is a leaf node, +%% otherwise `false'. The currently recognised leaf node %% types are: -%% <p><center><table border="1"> +%% +%% <center><table border="1"> %% <tr> -%% <td><code>atom</code></td> -%% <td><code>char</code></td> -%% <td><code>comment</code></td> -%% <td><code>eof_marker</code></td> -%% <td><code>error_marker</code></td> +%% <td>`atom'</td> +%% <td>`char'</td> +%% <td>`comment'</td> +%% <td>`eof_marker'</td> +%% <td>`error_marker'</td> %% </tr><tr> -%% <td><code>float</code></td> -%% <td><code>integer</code></td> -%% <td><code>nil</code></td> -%% <td><code>operator</code></td> -%% <td><code>string</code></td> +%% <td>`float'</td> +%% <td>`integer'</td> +%% <td>`nil'</td> +%% <td>`operator'</td> +%% <td>`string'</td> %% </tr><tr> -%% <td><code>text</code></td> -%% <td><code>underscore</code></td> -%% <td><code>variable</code></td> -%% <td><code>warning_marker</code></td> +%% <td>`text'</td> +%% <td>`underscore'</td> +%% <td>`variable'</td> +%% <td>`warning_marker'</td> %% </tr> -%% </table></center></p> -%% <p>A node of type <code>tuple</code> is a leaf node if and only if -%% its arity is zero.</p> +%% </table></center> %% -%% <p>Note: not all literals are leaf nodes, and vice versa. E.g., +%% A node of type `tuple' is a leaf node if and only if its arity is zero. +%% +%% Note: not all literals are leaf nodes, and vice versa. E.g., %% tuples with nonzero arity and nonempty lists may be literals, but are %% not leaf nodes. Variables, on the other hand, are leaf nodes but not -%% literals.</p> -%% +%% literals. +%% %% @see type/1 %% @see is_literal/1 @@ -666,29 +659,29 @@ is_leaf(Node) -> %% ===================================================================== -%% @spec is_form(Node::syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> is a syntax tree +%% @doc Returns `true' if `Node' is a syntax tree %% representing a so-called "source code form", otherwise -%% <code>false</code>. Forms are the Erlang source code units which, +%% `false'. Forms are the Erlang source code units which, %% placed in sequence, constitute an Erlang program. Current form types %% are: -%% <p><center><table border="1"> +%% +%% <center><table border="1"> %% <tr> -%% <td><code>attribute</code></td> -%% <td><code>comment</code></td> -%% <td><code>error_marker</code></td> -%% <td><code>eof_marker</code></td> -%% <td><code>form_list</code></td> +%% <td>`attribute'</td> +%% <td>`comment'</td> +%% <td>`error_marker'</td> +%% <td>`eof_marker'</td> +%% <td>`form_list'</td> %% </tr><tr> -%% <td><code>function</code></td> -%% <td><code>rule</code></td> -%% <td><code>warning_marker</code></td> -%% <td><code>text</code></td> +%% <td>`function'</td> +%% <td>`rule'</td> +%% <td>`warning_marker'</td> +%% <td>`text'</td> %% </tr> -%% </table></center></p> +%% </table></center> +%% %% @see type/1 -%% @see attribute/2 +%% @see attribute/2 %% @see comment/2 %% @see eof_marker/0 %% @see error_marker/1 @@ -715,10 +708,8 @@ is_form(Node) -> %% ===================================================================== -%% @spec get_pos(Node::syntaxTree()) -> term() -%% %% @doc Returns the position information associated with -%% <code>Node</code>. This is usually a nonnegative integer (indicating +%% `Node'. This is usually a nonnegative integer (indicating %% the source code line number), but may be any term. By default, all %% new tree nodes have their associated position information set to the %% integer zero. @@ -750,10 +741,7 @@ get_pos(Node) -> %% ===================================================================== -%% @spec set_pos(Node::syntaxTree(), Pos::term()) -> syntaxTree() -%% -%% @doc Sets the position information of <code>Node</code> to -%% <code>Pos</code>. +%% @doc Sets the position information of `Node' to `Pos'. %% %% @see get_pos/1 %% @see copy_pos/2 @@ -774,14 +762,10 @@ set_pos(Node, Pos) -> %% ===================================================================== -%% @spec copy_pos(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() -%% -%% @doc Copies the position information from <code>Source</code> to -%% <code>Target</code>. +%% @doc Copies the position information from `Source' to `Target'. %% -%% <p>This is equivalent to <code>set_pos(Target, -%% get_pos(Source))</code>, but potentially more efficient.</p> +%% This is equivalent to `set_pos(Target, +%% get_pos(Source))', but potentially more efficient. %% %% @see get_pos/1 %% @see set_pos/2 @@ -811,24 +795,20 @@ set_com(Node, Com) -> %% ===================================================================== -%% @spec get_precomments(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the associated pre-comments of a node. This is a %% possibly empty list of abstract comments, in top-down textual order. %% When the code is formatted, pre-comments are typically displayed %% directly above the node. For example: -%% <pre> -%% % Pre-comment of function -%% foo(X) -> {bar, X}.</pre> +%% ```% Pre-comment of function +%% foo(X) -> {bar, X}.''' %% -%% <p>If possible, the comment should be moved before any preceding +%% If possible, the comment should be moved before any preceding %% separator characters on the same line. E.g.: -%% <pre> -%% foo([X | Xs]) -> -%% % Pre-comment of 'bar(X)' node -%% [bar(X) | foo(Xs)]; -%% ...</pre> -%% (where the comment is moved before the "<code>[</code>").</p> +%% ```foo([X | Xs]) -> +%% % Pre-comment of 'bar(X)' node +%% [bar(X) | foo(Xs)]; +%% ...''' +%% (where the comment is moved before the "`['"). %% %% @see comment/2 %% @see set_precomments/2 @@ -846,11 +826,8 @@ get_precomments_1(#attr{com = #com{pre = Cs}}) -> Cs. %% ===================================================================== -%% @spec set_precomments(Node::syntaxTree(), -%% Comments::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Sets the pre-comments of <code>Node</code> to -%% <code>Comments</code>. <code>Comments</code> should be a possibly +%% @doc Sets the pre-comments of `Node' to +%% `Comments'. `Comments' should be a possibly %% empty list of abstract comments, in top-down textual order. %% %% @see comment/2 @@ -880,15 +857,11 @@ set_precomments_1(#attr{com = Com} = Attr, Cs) -> %% ===================================================================== -%% @spec add_precomments(Comments::[syntaxTree()], -%% Node::syntaxTree()) -> syntaxTree() +%% @doc Appends `Comments' to the pre-comments of `Node'. %% -%% @doc Appends <code>Comments</code> to the pre-comments of -%% <code>Node</code>. -%% -%% <p>Note: This is equivalent to <code>set_precomments(Node, -%% get_precomments(Node) ++ Comments)</code>, but potentially more -%% efficient.</p> +%% Note: This is equivalent to `set_precomments(Node, +%% get_precomments(Node) ++ Comments)', but potentially more +%% efficient. %% %% @see comment/2 %% @see get_precomments/1 @@ -915,24 +888,20 @@ add_precomments_1(Cs, #attr{com = Com} = Attr) -> %% ===================================================================== -%% @spec get_postcomments(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the associated post-comments of a node. This is a %% possibly empty list of abstract comments, in top-down textual order. %% When the code is formatted, post-comments are typically displayed to %% the right of and/or below the node. For example: -%% <pre> -%% {foo, X, Y} % Post-comment of tuple</pre> +%% ```{foo, X, Y} % Post-comment of tuple''' %% -%% <p>If possible, the comment should be moved past any following +%% If possible, the comment should be moved past any following %% separator characters on the same line, rather than placing the %% separators on the following line. E.g.: -%% <pre> -%% foo([X | Xs], Y) -> -%% foo(Xs, bar(X)); % Post-comment of 'bar(X)' node -%% ...</pre> -%% (where the comment is moved past the rightmost "<code>)</code>" and -%% the "<code>;</code>").</p> +%% ```foo([X | Xs], Y) -> +%% foo(Xs, bar(X)); % Post-comment of 'bar(X)' node +%% ...''' +%% (where the comment is moved past the rightmost "`)'" and +%% the "`;'"). %% %% @see comment/2 %% @see set_postcomments/2 @@ -950,11 +919,8 @@ get_postcomments_1(#attr{com = #com{post = Cs}}) -> Cs. %% ===================================================================== -%% @spec set_postcomments(Node::syntaxTree(), -%% Comments::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Sets the post-comments of <code>Node</code> to -%% <code>Comments</code>. <code>Comments</code> should be a possibly +%% @doc Sets the post-comments of `Node' to +%% `Comments'. `Comments' should be a possibly %% empty list of abstract comments, in top-down textual order %% %% @see comment/2 @@ -984,15 +950,11 @@ set_postcomments_1(#attr{com = Com} = Attr, Cs) -> %% ===================================================================== -%% @spec add_postcomments(Comments::[syntaxTree()], -%% Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Appends <code>Comments</code> to the post-comments of -%% <code>Node</code>. +%% @doc Appends `Comments' to the post-comments of `Node'. %% -%% <p>Note: This is equivalent to <code>set_postcomments(Node, -%% get_postcomments(Node) ++ Comments)</code>, but potentially more -%% efficient.</p> +%% Note: This is equivalent to `set_postcomments(Node, +%% get_postcomments(Node) ++ Comments)', but potentially more +%% efficient. %% %% @see comment/2 %% @see get_postcomments/1 @@ -1019,14 +981,12 @@ add_postcomments_1(Cs, #attr{com = Com} = Attr) -> %% ===================================================================== -%% @spec has_comments(Node::syntaxTree()) -> boolean() +%% @doc Yields `false' if the node has no associated +%% comments, and `true' otherwise. %% -%% @doc Yields <code>false</code> if the node has no associated -%% comments, and <code>true</code> otherwise. -%% -%% <p>Note: This is equivalent to <code>(get_precomments(Node) == []) -%% and (get_postcomments(Node) == [])</code>, but potentially more -%% efficient.</p> +%% Note: This is equivalent to `(get_precomments(Node) == []) +%% and (get_postcomments(Node) == [])', but potentially more +%% efficient. %% %% @see get_precomments/1 %% @see get_postcomments/1 @@ -1050,13 +1010,11 @@ has_comments(_) -> false. %% ===================================================================== -%% @spec remove_comments(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Clears the associated comments of <code>Node</code>. +%% @doc Clears the associated comments of `Node'. %% -%% <p>Note: This is equivalent to -%% <code>set_precomments(set_postcomments(Node, []), [])</code>, but -%% potentially more efficient.</p> +%% Note: This is equivalent to +%% `set_precomments(set_postcomments(Node, []), [])', but +%% potentially more efficient. %% %% @see set_precomments/2 %% @see set_postcomments/2 @@ -1075,16 +1033,12 @@ remove_comments(Node) -> %% ===================================================================== -%% @spec copy_comments(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() -%% -%% @doc Copies the pre- and postcomments from <code>Source</code> to -%% <code>Target</code>. +%% @doc Copies the pre- and postcomments from `Source' to `Target'. %% -%% <p>Note: This is equivalent to -%% <code>set_postcomments(set_precomments(Target, -%% get_precomments(Source)), get_postcomments(Source))</code>, but -%% potentially more efficient.</p> +%% Note: This is equivalent to +%% `set_postcomments(set_precomments(Target, +%% get_precomments(Source)), get_postcomments(Source))', but +%% potentially more efficient. %% %% @see comment/2 %% @see get_precomments/1 @@ -1099,16 +1053,13 @@ copy_comments(Source, Target) -> %% ===================================================================== -%% @spec join_comments(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() +%% @doc Appends the comments of `Source' to the current +%% comments of `Target'. %% -%% @doc Appends the comments of <code>Source</code> to the current -%% comments of <code>Target</code>. -%% -%% <p>Note: This is equivalent to -%% <code>add_postcomments(get_postcomments(Source), -%% add_precomments(get_precomments(Source), Target))</code>, but -%% potentially more efficient.</p> +%% Note: This is equivalent to +%% `add_postcomments(get_postcomments(Source), +%% add_precomments(get_precomments(Source), Target))', but +%% potentially more efficient. %% %% @see comment/2 %% @see get_precomments/1 @@ -1125,8 +1076,6 @@ join_comments(Source, Target) -> %% ===================================================================== -%% @spec get_ann(syntaxTree()) -> [term()] -%% %% @doc Returns the list of user annotations associated with a syntax %% tree node. For a newly created node, this is the empty list. The %% annotations may be any terms. @@ -1142,11 +1091,7 @@ get_ann(_) -> []. %% ===================================================================== -%% @spec set_ann(Node::syntaxTree(), Annotations::[term()]) -> -%% syntaxTree() -%% -%% @doc Sets the list of user annotations of <code>Node</code> to -%% <code>Annotations</code>. +%% @doc Sets the list of user annotations of `Node' to `Annotations'. %% %% @see get_ann/1 %% @see add_ann/2 @@ -1168,13 +1113,11 @@ set_ann(Node, As) -> %% ===================================================================== -%% @spec add_ann(Annotation::term(), Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Appends the term <code>Annotation</code> to the list of user -%% annotations of <code>Node</code>. +%% @doc Appends the term `Annotation' to the list of user +%% annotations of `Node'. %% -%% <p>Note: this is equivalent to <code>set_ann(Node, [Annotation | -%% get_ann(Node)])</code>, but potentially more efficient.</p> +%% Note: this is equivalent to `set_ann(Node, [Annotation | +%% get_ann(Node)])', but potentially more efficient. %% %% @see get_ann/1 %% @see set_ann/2 @@ -1195,14 +1138,10 @@ add_ann(A, Node) -> %% ===================================================================== -%% @spec copy_ann(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() +%% @doc Copies the list of user annotations from `Source' to `Target'. %% -%% @doc Copies the list of user annotations from <code>Source</code> to -%% <code>Target</code>. -%% -%% <p>Note: this is equivalent to <code>set_ann(Target, -%% get_ann(Source))</code>, but potentially more efficient.</p> +%% Note: this is equivalent to `set_ann(Target, +%% get_ann(Source))', but potentially more efficient. %% %% @see get_ann/1 %% @see set_ann/2 @@ -1214,23 +1153,20 @@ copy_ann(Source, Target) -> %% ===================================================================== -%% @spec get_attrs(syntaxTree()) -> syntaxTreeAttributes() -%% %% @doc Returns a representation of the attributes associated with a %% syntax tree node. The attributes are all the extra information that %% can be attached to a node. Currently, this includes position %% information, source code comments, and user annotations. The result %% of this function cannot be inspected directly; only attached to -%% another node (cf. <code>set_attrs/2</code>). +%% another node (see {@link set_attrs/2}). %% -%% <p>For accessing individual attributes, see <code>get_pos/1</code>, -%% <code>get_ann/1</code>, <code>get_precomments/1</code> and -%% <code>get_postcomments/1</code>.</p> +%% For accessing individual attributes, see {@link get_pos/1}, +%% {@link get_ann/1}, {@link get_precomments/1} and +%% {@link get_postcomments/1}. %% %% @type syntaxTreeAttributes(). This is an abstract representation of -%% syntax tree node attributes; see the function <a -%% href="#get_attrs-1"><code>get_attrs/1</code></a>. -%% +%% syntax tree node attributes; see the function {@link get_attrs/1}. +%% %% @see set_attrs/2 %% @see get_pos/1 %% @see get_ann/1 @@ -1247,11 +1183,7 @@ get_attrs(Node) -> #attr{pos = get_pos(Node), %% ===================================================================== -%% @spec set_attrs(Node::syntaxTree(), -%% Attributes::syntaxTreeAttributes()) -> syntaxTree() -%% -%% @doc Sets the attributes of <code>Node</code> to -%% <code>Attributes</code>. +%% @doc Sets the attributes of `Node' to `Attributes'. %% %% @see get_attrs/1 %% @see copy_attrs/2 @@ -1270,14 +1202,10 @@ set_attrs(Node, Attr) -> %% ===================================================================== -%% @spec copy_attrs(Source::syntaxTree(), Target::syntaxTree()) -> -%% syntaxTree() +%% @doc Copies the attributes from `Source' to `Target'. %% -%% @doc Copies the attributes from <code>Source</code> to -%% <code>Target</code>. -%% -%% <p>Note: this is equivalent to <code>set_attrs(Target, -%% get_attrs(Source))</code>, but potentially more efficient.</p> +%% Note: this is equivalent to `set_attrs(Target, +%% get_attrs(Source))', but potentially more efficient. %% %% @see get_attrs/1 %% @see set_attrs/2 @@ -1289,7 +1217,6 @@ copy_attrs(S, T) -> %% ===================================================================== -%% @spec comment(Strings) -> syntaxTree() %% @equiv comment(none, Strings) -spec comment([string()]) -> syntaxTree(). @@ -1299,22 +1226,19 @@ comment(Strings) -> %% ===================================================================== -%% @spec comment(Padding, Strings::[string()]) -> syntaxTree() -%% Padding = none | integer() -%% %% @doc Creates an abstract comment with the given padding and text. If -%% <code>Strings</code> is a (possibly empty) list +%% `Strings' is a (possibly empty) list %% <code>["<em>Txt1</em>", ..., "<em>TxtN</em>"]</code>, the result %% represents the source code text %% <pre> -%% %<em>Txt1</em> -%% ... -%% %<em>TxtN</em></pre> -%% <code>Padding</code> states the number of empty character positions +%% %<em>Txt1</em> +%% ... +%% %<em>TxtN</em></pre> +%% `Padding' states the number of empty character positions %% to the left of the comment separating it horizontally from -%% source code on the same line (if any). If <code>Padding</code> is -%% <code>none</code>, a default positive number is used. If -%% <code>Padding</code> is an integer less than 1, there should be no +%% source code on the same line (if any). If `Padding' is +%% `none', a default positive number is used. If +%% `Padding' is an integer less than 1, there should be no %% separating space. Comments are in themselves regarded as source %% program forms. %% @@ -1338,8 +1262,6 @@ comment(Pad, Strings) -> %% ===================================================================== -%% @spec comment_text(syntaxTree()) -> [string()] -%% %% @doc Returns the lines of text of the abstract comment. %% %% @see comment/2 @@ -1351,11 +1273,8 @@ comment_text(Node) -> %% ===================================================================== -%% @spec comment_padding(syntaxTree()) -> none | integer() -%% %% @doc Returns the amount of padding before the comment, or -%% <code>none</code>. The latter means that a default padding may be -%% used. +%% `none'. The latter means that a default padding may be used. %% %% @see comment/2 @@ -1366,23 +1285,21 @@ comment_padding(Node) -> %% ===================================================================== -%% @spec form_list(Forms::[syntaxTree()]) -> syntaxTree() -%% %% @doc Creates an abstract sequence of "source code forms". If -%% <code>Forms</code> is <code>[F1, ..., Fn]</code>, where each -%% <code>Fi</code> is a form (cf. <code>is_form/1</code>, the result +%% `Forms' is `[F1, ..., Fn]', where each +%% `Fi' is a form (see {@link is_form/1}, the result %% represents %% <pre> -%% <em>F1</em> -%% ... -%% <em>Fn</em></pre> -%% where the <code>Fi</code> are separated by one or more line breaks. A -%% node of type <code>form_list</code> is itself regarded as a source -%% code form; cf. <code>flatten_form_list/1</code>. -%% -%% <p>Note: this is simply a way of grouping source code forms as a +%% <em>F1</em> +%% ... +%% <em>Fn</em></pre> +%% where the `Fi' are separated by one or more line breaks. A +%% node of type `form_list' is itself regarded as a source +%% code form; see {@link flatten_form_list/1}. +%% +%% Note: this is simply a way of grouping source code forms as a %% single syntax tree, usually in order to form an Erlang module -%% definition.</p> +%% definition. %% %% @see form_list_elements/1 %% @see is_form/1 @@ -1401,9 +1318,7 @@ form_list(Forms) -> %% ===================================================================== -%% @spec form_list_elements(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of subnodes of a <code>form_list</code> node. +%% @doc Returns the list of subnodes of a `form_list' node. %% %% @see form_list/1 @@ -1414,10 +1329,8 @@ form_list_elements(Node) -> %% ===================================================================== -%% @spec flatten_form_list(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Flattens sublists of a <code>form_list</code> node. Returns -%% <code>Node</code> with all subtrees of type <code>form_list</code> +%% @doc Flattens sublists of a `form_list' node. Returns +%% `Node' with all subtrees of type `form_list' %% recursively expanded, yielding a single "flat" abstract form %% sequence. %% @@ -1443,10 +1356,8 @@ flatten_form_list_1([], As) -> %% ===================================================================== -%% @spec text(String::string()) -> syntaxTree() -%% %% @doc Creates an abstract piece of source code text. The result -%% represents exactly the sequence of characters in <code>String</code>. +%% represents exactly the sequence of characters in `String'. %% This is useful in cases when one wants full control of the resulting %% output, e.g., for the appearance of floating-point numbers or macro %% definitions. @@ -1463,10 +1374,7 @@ text(String) -> %% ===================================================================== -%% @spec text_string(syntaxTree()) -> string() -%% -%% @doc Returns the character sequence represented by a -%% <code>text</code> node. +%% @doc Returns the character sequence represented by a `text' node. %% %% @see text/1 @@ -1477,18 +1385,15 @@ text_string(Node) -> %% ===================================================================== -%% @spec variable(Name) -> syntaxTree() -%% Name = atom() | string() -%% %% @doc Creates an abstract variable with the given name. -%% <code>Name</code> may be any atom or string that represents a +%% `Name' may be any atom or string that represents a %% lexically valid variable name, but <em>not</em> a single underscore -%% character; cf. <code>underscore/0</code>. +%% character; see {@link underscore/0}. %% -%% <p>Note: no checking is done whether the character sequence +%% Note: no checking is done whether the character sequence %% represents a proper variable name, i.e., whether or not its first %% character is an uppercase Erlang character, or whether it does not -%% contain control characters, whitespace, etc.</p> +%% contain control characters, whitespace, etc. %% %% @see variable_name/1 %% @see variable_literal/1 @@ -1517,9 +1422,7 @@ revert_variable(Node) -> %% ===================================================================== -%% @spec variable_name(syntaxTree()) -> atom() -%% -%% @doc Returns the name of a <code>variable</code> node as an atom. +%% @doc Returns the name of a `variable' node as an atom. %% %% @see variable/1 @@ -1535,9 +1438,7 @@ variable_name(Node) -> %% ===================================================================== -%% @spec variable_literal(syntaxTree()) -> string() -%% -%% @doc Returns the name of a <code>variable</code> node as a string. +%% @doc Returns the name of a `variable' node as a string. %% %% @see variable/1 @@ -1553,9 +1454,7 @@ variable_literal(Node) -> %% ===================================================================== -%% @spec underscore() -> syntaxTree() -%% -%% @doc Creates an abstract universal pattern ("<code>_</code>"). The +%% @doc Creates an abstract universal pattern ("`_'"). The %% lexical representation is a single underscore character. Note that %% this is <em>not</em> a variable, lexically speaking. %% @@ -1579,10 +1478,8 @@ revert_underscore(Node) -> %% ===================================================================== -%% @spec integer(Value::integer()) -> syntaxTree() -%% %% @doc Creates an abstract integer literal. The lexical representation -%% is the canonical decimal numeral of <code>Value</code>. +%% is the canonical decimal numeral of `Value'. %% %% @see integer_value/1 %% @see integer_literal/1 @@ -1608,11 +1505,8 @@ revert_integer(Node) -> %% ===================================================================== -%% @spec is_integer(Node::syntaxTree(), Value::integer()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>integer</code> and represents <code>Value</code>, otherwise -%% <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `integer' and represents `Value', otherwise `false'. %% %% @see integer/1 @@ -1630,9 +1524,7 @@ is_integer(Node, Value) -> %% ===================================================================== -%% @spec integer_value(syntaxTree()) -> integer() -%% -%% @doc Returns the value represented by an <code>integer</code> node. +%% @doc Returns the value represented by an `integer' node. %% %% @see integer/1 @@ -1648,10 +1540,7 @@ integer_value(Node) -> %% ===================================================================== -%% @spec integer_literal(syntaxTree()) -> string() -%% -%% @doc Returns the numeral string represented by an -%% <code>integer</code> node. +%% @doc Returns the numeral string represented by an `integer' node. %% %% @see integer/1 @@ -1662,11 +1551,8 @@ integer_literal(Node) -> %% ===================================================================== -%% @spec float(Value::float()) -> syntaxTree() -%% %% @doc Creates an abstract floating-point literal. The lexical -%% representation is the decimal floating-point numeral of -%% <code>Value</code>. +%% representation is the decimal floating-point numeral of `Value'. %% %% @see float_value/1 %% @see float_literal/1 @@ -1701,9 +1587,7 @@ revert_float(Node) -> %% ===================================================================== -%% @spec float_value(syntaxTree()) -> float() -%% -%% @doc Returns the value represented by a <code>float</code> node. Note +%% @doc Returns the value represented by a `float' node. Note %% that floating-point values should usually not be compared for %% equality. %% @@ -1721,10 +1605,7 @@ float_value(Node) -> %% ===================================================================== -%% @spec float_literal(syntaxTree()) -> string() -%% -%% @doc Returns the numeral string represented by a <code>float</code> -%% node. +%% @doc Returns the numeral string represented by a `float' node. %% %% @see float/1 @@ -1735,17 +1616,15 @@ float_literal(Node) -> %% ===================================================================== -%% @spec char(Value::char()) -> syntaxTree() -%% %% @doc Creates an abstract character literal. The result represents -%% "<code>$<em>Name</em></code>", where <code>Name</code> corresponds to -%% <code>Value</code>. +%% "<code>$<em>Name</em></code>", where `Name' corresponds to +%% `Value'. %% -%% <p>Note: the literal corresponding to a particular character value is -%% not uniquely defined. E.g., the character "<code>a</code>" can be -%% written both as "<code>$a</code>" and "<code>$\141</code>", and a Tab -%% character can be written as "<code>$\11</code>", "<code>$\011</code>" -%% or "<code>$\t</code>".</p> +%% Note: the literal corresponding to a particular character value is +%% not uniquely defined. E.g., the character "`a'" can be +%% written both as "`$a'" and "`$\141'", and a Tab +%% character can be written as "`$\11'", "`$\011'" +%% or "`$\t'". %% %% @see char_value/1 %% @see char_literal/1 @@ -1771,11 +1650,8 @@ revert_char(Node) -> %% ===================================================================== -%% @spec is_char(Node::syntaxTree(), Value::char()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>char</code> and represents <code>Value</code>, otherwise -%% <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `char' and represents `Value', otherwise `false'. %% %% @see char/1 @@ -1793,9 +1669,7 @@ is_char(Node, Value) -> %% ===================================================================== -%% @spec char_value(syntaxTree()) -> char() -%% -%% @doc Returns the value represented by a <code>char</code> node. +%% @doc Returns the value represented by a `char' node. %% %% @see char/1 @@ -1811,10 +1685,8 @@ char_value(Node) -> %% ===================================================================== -%% @spec char_literal(syntaxTree()) -> string() -%% -%% @doc Returns the literal string represented by a <code>char</code> -%% node. This includes the leading "<code>$</code>" character. +%% @doc Returns the literal string represented by a `char' +%% node. This includes the leading "`$'" character. %% %% @see char/1 @@ -1825,16 +1697,14 @@ char_literal(Node) -> %% ===================================================================== -%% @spec string(Value::string()) -> syntaxTree() -%% %% @doc Creates an abstract string literal. The result represents %% <code>"<em>Text</em>"</code> (including the surrounding -%% double-quotes), where <code>Text</code> corresponds to the sequence -%% of characters in <code>Value</code>, but not representing a -%% <em>specific</em> string literal. E.g., the result of -%% <code>string("x\ny")</code> represents any and all of -%% <code>"x\ny"</code>, <code>"x\12y"</code>, <code>"x\012y"</code> and -%% <code>"x\^Jy"</code>; cf. <code>char/1</code>. +%% double-quotes), where `Text' corresponds to the sequence +%% of characters in `Value', but not representing a +%% <em>specific</em> string literal. +%% +%% For example, the result of `string("x\ny")' represents any and all of +%% `"x\ny"', `"x\12y"', `"x\012y"' and `"x\^Jy"'; see {@link char/1}. %% %% @see string_value/1 %% @see string_literal/1 @@ -1861,11 +1731,8 @@ revert_string(Node) -> %% ===================================================================== -%% @spec is_string(Node::syntaxTree(), Value::string()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>string</code> and represents <code>Value</code>, otherwise -%% <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `string' and represents `Value', otherwise `false'. %% %% @see string/1 @@ -1883,9 +1750,7 @@ is_string(Node, Value) -> %% ===================================================================== -%% @spec string_value(syntaxTree()) -> string() -%% -%% @doc Returns the value represented by a <code>string</code> node. +%% @doc Returns the value represented by a `string' node. %% %% @see string/1 @@ -1901,9 +1766,7 @@ string_value(Node) -> %% ===================================================================== -%% @spec string_literal(syntaxTree()) -> string() -%% -%% @doc Returns the literal string represented by a <code>string</code> +%% @doc Returns the literal string represented by a `string' %% node. This includes surrounding double-quote characters. %% %% @see string/1 @@ -1915,11 +1778,8 @@ string_literal(Node) -> %% ===================================================================== -%% @spec atom(Name) -> syntaxTree() -%% Name = atom() | string() -%% %% @doc Creates an abstract atom literal. The print name of the atom is -%% the character sequence represented by <code>Name</code>. +%% the character sequence represented by `Name'. %% %% @see atom_value/1 %% @see atom_name/1 @@ -1948,11 +1808,8 @@ revert_atom(Node) -> %% ===================================================================== -%% @spec is_atom(Node::syntaxTree(), Value::atom()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>atom</code> and represents <code>Value</code>, otherwise -%% <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `atom' and represents `Value', otherwise `false'. %% %% @see atom/1 @@ -1970,9 +1827,7 @@ is_atom(Node, Value) -> %% ===================================================================== -%% @spec atom_value(syntaxTree()) -> atom() -%% -%% @doc Returns the value represented by an <code>atom</code> node. +%% @doc Returns the value represented by an `atom' node. %% %% @see atom/1 @@ -1988,9 +1843,7 @@ atom_value(Node) -> %% ===================================================================== -%% @spec atom_name(syntaxTree()) -> string() -%% -%% @doc Returns the printname of an <code>atom</code> node. +%% @doc Returns the printname of an `atom' node. %% %% @see atom/1 @@ -2001,15 +1854,12 @@ atom_name(Node) -> %% ===================================================================== -%% @spec atom_literal(syntaxTree()) -> string() -%% -%% @doc Returns the literal string represented by an <code>atom</code> +%% @doc Returns the literal string represented by an `atom' %% node. This includes surrounding single-quote characters if necessary. %% -%% <p>Note that e.g. the result of <code>atom("x\ny")</code> represents -%% any and all of <code>'x\ny'</code>, <code>'x\12y'</code>, -%% <code>'x\012y'</code> and <code>'x\^Jy\'</code>; cf. -%% <code>string/1</code>.</p> +%% Note that e.g. the result of `atom("x\ny")' represents +%% any and all of `'x\ny'', `'x\12y'', +%% `'x\012y'' and `'x\^Jy\''; see {@link string/1}. %% %% @see atom/1 %% @see string/1 @@ -2021,14 +1871,12 @@ atom_literal(Node) -> %% ===================================================================== -%% @spec tuple(Elements::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract tuple. If <code>Elements</code> is -%% <code>[X1, ..., Xn]</code>, the result represents +%% @doc Creates an abstract tuple. If `Elements' is +%% `[X1, ..., Xn]', the result represents %% "<code>{<em>X1</em>, ..., <em>Xn</em>}</code>". %% -%% <p>Note: The Erlang language has distinct 1-tuples, i.e., -%% <code>{X}</code> is always distinct from <code>X</code> itself.</p> +%% Note: The Erlang language has distinct 1-tuples, i.e., +%% `{X}' is always distinct from `X' itself. %% %% @see tuple_elements/1 %% @see tuple_size/1 @@ -2055,10 +1903,7 @@ revert_tuple(Node) -> %% ===================================================================== -%% @spec tuple_elements(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of element subtrees of a <code>tuple</code> -%% node. +%% @doc Returns the list of element subtrees of a `tuple' node. %% %% @see tuple/1 @@ -2074,13 +1919,11 @@ tuple_elements(Node) -> %% ===================================================================== -%% @spec tuple_size(syntaxTree()) -> integer() -%% -%% @doc Returns the number of elements of a <code>tuple</code> node. +%% @doc Returns the number of elements of a `tuple' node. %% -%% <p>Note: this is equivalent to -%% <code>length(tuple_elements(Node))</code>, but potentially more -%% efficient.</p> +%% Note: this is equivalent to +%% `length(tuple_elements(Node))', but potentially more +%% efficient. %% %% @see tuple/1 %% @see tuple_elements/1 @@ -2092,7 +1935,6 @@ tuple_size(Node) -> %% ===================================================================== -%% @spec list(List) -> syntaxTree() %% @equiv list(List, none) -spec list([syntaxTree()]) -> syntaxTree(). @@ -2102,35 +1944,31 @@ list(List) -> %% ===================================================================== -%% @spec list(List, Tail) -> syntaxTree() -%% List = [syntaxTree()] -%% Tail = none | syntaxTree() -%% %% @doc Constructs an abstract list skeleton. The result has type -%% <code>list</code> or <code>nil</code>. If <code>List</code> is a -%% nonempty list <code>[E1, ..., En]</code>, the result has type -%% <code>list</code> and represents either "<code>[<em>E1</em>, ..., -%% <em>En</em>]</code>", if <code>Tail</code> is <code>none</code>, or +%% `list' or `nil'. If `List' is a +%% nonempty list `[E1, ..., En]', the result has type +%% `list' and represents either "<code>[<em>E1</em>, ..., +%% <em>En</em>]</code>", if `Tail' is `none', or %% otherwise "<code>[<em>E1</em>, ..., <em>En</em> | -%% <em>Tail</em>]</code>". If <code>List</code> is the empty list, -%% <code>Tail</code> <em>must</em> be <code>none</code>, and in that -%% case the result has type <code>nil</code> and represents -%% "<code>[]</code>" (cf. <code>nil/0</code>). +%% <em>Tail</em>]</code>". If `List' is the empty list, +%% `Tail' <em>must</em> be `none', and in that +%% case the result has type `nil' and represents +%% "`[]'" (see {@link nil/0}). %% -%% <p>The difference between lists as semantic objects (built up of +%% The difference between lists as semantic objects (built up of %% individual "cons" and "nil" terms) and the various syntactic forms %% for denoting lists may be bewildering at first. This module provides %% functions both for exact control of the syntactic representation as %% well as for the simple composition and deconstruction in terms of -%% cons and head/tail operations.</p> +%% cons and head/tail operations. %% -%% <p>Note: in <code>list(Elements, none)</code>, the "nil" list -%% terminator is implicit and has no associated information (cf. -%% <code>get_attrs/1</code>), while in the seemingly equivalent -%% <code>list(Elements, Tail)</code> when <code>Tail</code> has type -%% <code>nil</code>, the list terminator subtree <code>Tail</code> may +%% Note: in `list(Elements, none)', the "nil" list +%% terminator is implicit and has no associated information (see +%% {@link get_attrs/1}), while in the seemingly equivalent +%% `list(Elements, Tail)' when `Tail' has type +%% `nil', the list terminator subtree `Tail' may %% have attached attributes such as position, comments, and annotations, -%% which will be preserved in the result.</p> +%% which will be preserved in the result. %% %% @see nil/0 %% @see list/1 @@ -2187,10 +2025,8 @@ revert_list(Node) -> S, P). %% ===================================================================== -%% @spec nil() -> syntaxTree() -%% %% @doc Creates an abstract empty list. The result represents -%% "<code>[]</code>". The empty list is traditionally called "nil". +%% "`[]'". The empty list is traditionally called "nil". %% %% @see list/2 %% @see is_list_skeleton/1 @@ -2213,13 +2049,11 @@ revert_nil(Node) -> %% ===================================================================== -%% @spec list_prefix(Node::syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the prefix element subtrees of a <code>list</code> node. -%% If <code>Node</code> represents "<code>[<em>E1</em>, ..., +%% @doc Returns the prefix element subtrees of a `list' node. +%% If `Node' represents "<code>[<em>E1</em>, ..., %% <em>En</em>]</code>" or "<code>[<em>E1</em>, ..., <em>En</em> | -%% <em>Tail</em>]</code>", the returned value is <code>[E1, ..., -%% En]</code>. +%% <em>Tail</em>]</code>", the returned value is `[E1, ..., +%% En]'. %% %% @see list/2 @@ -2227,28 +2061,31 @@ revert_nil(Node) -> list_prefix(Node) -> case unwrap(Node) of - {cons, _, Head, _} -> - [Head]; + {cons, _, Head, Tail} -> + [Head | cons_prefix(Tail)]; Node1 -> (data(Node1))#list.prefix end. +%% collects sequences of conses; cf. cons_suffix/1 below +cons_prefix({cons, _, Head, Tail}) -> + [Head | cons_prefix(Tail)]; +cons_prefix(_) -> + []. + %% ===================================================================== -%% @spec list_suffix(Node::syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the suffix subtree of a <code>list</code> node, if one -%% exists. If <code>Node</code> represents "<code>[<em>E1</em>, ..., +%% @doc Returns the suffix subtree of a `list' node, if one +%% exists. If `Node' represents "<code>[<em>E1</em>, ..., %% <em>En</em> | <em>Tail</em>]</code>", the returned value is -%% <code>Tail</code>, otherwise, i.e., if <code>Node</code> represents -%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", <code>none</code> is +%% `Tail', otherwise, i.e., if `Node' represents +%% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", `none' is %% returned. %% -%% <p>Note that even if this function returns some <code>Tail</code> -%% that is not <code>none</code>, the type of <code>Tail</code> can be -%% <code>nil</code>, if the tail has been given explicitly, and the list -%% skeleton has not been compacted (cf. -%% <code>compact_list/1</code>).</p> +%% Note that even if this function returns some `Tail' +%% that is not `none', the type of `Tail' can be +%% `nil', if the tail has been given explicitly, and the list +%% skeleton has not been compacted (see {@link compact_list/1}). %% %% @see list/2 %% @see nil/0 @@ -2259,34 +2096,36 @@ list_prefix(Node) -> list_suffix(Node) -> case unwrap(Node) of {cons, _, _, Tail} -> - %% If there could be comments/annotations on the tail node, - %% we should not return `none' even if it has type `nil'. - case Tail of + case cons_suffix(Tail) of {nil, _} -> - none; % no interesting information is lost - _ -> - Tail + none; + Tail1 -> + Tail1 end; Node1 -> (data(Node1))#list.suffix end. +%% skips sequences of conses; cf. cons_prefix/1 above +cons_suffix({cons, _, _, Tail}) -> + cons_suffix(Tail); +cons_suffix(Tail) -> + Tail. + %% ===================================================================== -%% @spec cons(Head::syntaxTree(), Tail::syntaxTree()) -> syntaxTree() -%% %% @doc "Optimising" list skeleton cons operation. Creates an abstract -%% list skeleton whose first element is <code>Head</code> and whose tail -%% corresponds to <code>Tail</code>. This is similar to -%% <code>list([Head], Tail)</code>, except that <code>Tail</code> may -%% not be <code>none</code>, and that the result does not necessarily +%% list skeleton whose first element is `Head' and whose tail +%% corresponds to `Tail'. This is similar to +%% `list([Head], Tail)', except that `Tail' may +%% not be `none', and that the result does not necessarily %% represent exactly "<code>[<em>Head</em> | <em>Tail</em>]</code>", but -%% may depend on the <code>Tail</code> subtree. E.g., if -%% <code>Tail</code> represents <code>[X, Y]</code>, the result may +%% may depend on the `Tail' subtree. E.g., if +%% `Tail' represents `[X, Y]', the result may %% represent "<code>[<em>Head</em>, X, Y]</code>", rather than %% "<code>[<em>Head</em> | [X, Y]]</code>". Annotations on -%% <code>Tail</code> itself may be lost if <code>Tail</code> represents -%% a list skeleton, but comments on <code>Tail</code> are propagated to +%% `Tail' itself may be lost if `Tail' represents +%% a list skeleton, but comments on `Tail' are propagated to %% the result. %% %% @see list/2 @@ -2308,10 +2147,8 @@ cons(Head, Tail) -> %% ===================================================================== -%% @spec list_head(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the head element subtree of a <code>list</code> node. If -%% <code>Node</code> represents "<code>[<em>Head</em> ...]</code>", the +%% @doc Returns the head element subtree of a `list' node. If +%% `Node' represents "<code>[<em>Head</em> ...]</code>", the %% result will represent "<code><em>Head</em></code>". %% %% @see list/2 @@ -2325,15 +2162,13 @@ list_head(Node) -> %% ===================================================================== -%% @spec list_tail(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the tail of a <code>list</code> node. If -%% <code>Node</code> represents a single-element list +%% @doc Returns the tail of a `list' node. If +%% `Node' represents a single-element list %% "<code>[<em>E</em>]</code>", then the result has type -%% <code>nil</code>, representing "<code>[]</code>". If -%% <code>Node</code> represents "<code>[<em>E1</em>, <em>E2</em> +%% `nil', representing "`[]'". If +%% `Node' represents "<code>[<em>E1</em>, <em>E2</em> %% ...]</code>", the result will represent "<code>[<em>E2</em> -%% ...]</code>", and if <code>Node</code> represents +%% ...]</code>", and if `Node' represents %% "<code>[<em>Head</em> | <em>Tail</em>]</code>", the result will %% represent "<code><em>Tail</em></code>". %% @@ -2358,10 +2193,8 @@ list_tail(Node) -> %% ===================================================================== -%% @spec is_list_skeleton(syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> has type -%% <code>list</code> or <code>nil</code>, otherwise <code>false</code>. +%% @doc Returns `true' if `Node' has type +%% `list' or `nil', otherwise `false'. %% %% @see list/2 %% @see nil/0 @@ -2377,24 +2210,22 @@ is_list_skeleton(Node) -> %% ===================================================================== -%% @spec is_proper_list(Node::syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> represents a -%% proper list, and <code>false</code> otherwise. A proper list is a -%% list skeleton either on the form "<code>[]</code>" or +%% @doc Returns `true' if `Node' represents a +%% proper list, and `false' otherwise. A proper list is a +%% list skeleton either on the form "`[]'" or %% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", or "<code>[... | -%% <em>Tail</em>]</code>" where recursively <code>Tail</code> also +%% <em>Tail</em>]</code>" where recursively `Tail' also %% represents a proper list. %% -%% <p>Note: Since <code>Node</code> is a syntax tree, the actual +%% Note: Since `Node' is a syntax tree, the actual %% run-time values corresponding to its subtrees may often be partially -%% or completely unknown. Thus, if <code>Node</code> represents e.g. -%% "<code>[... | Ns]</code>" (where <code>Ns</code> is a variable), then -%% the function will return <code>false</code>, because it is not known -%% whether <code>Ns</code> will be bound to a list at run-time. If -%% <code>Node</code> instead represents e.g. "<code>[1, 2, 3]</code>" or -%% "<code>[A | []]</code>", then the function will return -%% <code>true</code>.</p> +%% or completely unknown. Thus, if `Node' represents e.g. +%% "`[... | Ns]'" (where `Ns' is a variable), then +%% the function will return `false', because it is not known +%% whether `Ns' will be bound to a list at run-time. If +%% `Node' instead represents e.g. "`[1, 2, 3]'" or +%% "`[A | []]'", then the function will return +%% `true'. %% %% @see list/2 @@ -2417,14 +2248,11 @@ is_proper_list(Node) -> %% ===================================================================== -%% @spec list_elements(Node::syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of element subtrees of a list skeleton. -%% <code>Node</code> must represent a proper list. E.g., if -%% <code>Node</code> represents "<code>[<em>X1</em>, <em>X2</em> | +%% `Node' must represent a proper list. E.g., if +%% `Node' represents "<code>[<em>X1</em>, <em>X2</em> | %% [<em>X3</em>, <em>X4</em> | []]</code>", then -%% <code>list_elements(Node)</code> yields the list <code>[X1, X2, X3, -%% X4]</code>. +%% `list_elements(Node)' yields the list `[X1, X2, X3, X4]'. %% %% @see list/2 %% @see is_proper_list/1 @@ -2450,17 +2278,15 @@ list_elements(Node, As) -> %% ===================================================================== -%% @spec list_length(Node::syntaxTree()) -> integer() -%% %% @doc Returns the number of element subtrees of a list skeleton. -%% <code>Node</code> must represent a proper list. E.g., if -%% <code>Node</code> represents "<code>[X1 | [X2, X3 | [X4, X5, -%% X6]]]</code>", then <code>list_length(Node)</code> returns the +%% `Node' must represent a proper list. E.g., if +%% `Node' represents "`[X1 | [X2, X3 | [X4, X5, +%% X6]]]'", then `list_length(Node)' returns the %% integer 6. %% -%% <p>Note: this is equivalent to -%% <code>length(list_elements(Node))</code>, but potentially more -%% efficient.</p> +%% Note: this is equivalent to +%% `length(list_elements(Node))', but potentially more +%% efficient. %% %% @see list/2 %% @see is_proper_list/1 @@ -2487,18 +2313,16 @@ list_length(Node, A) -> %% ===================================================================== -%% @spec normalize_list(Node::syntaxTree()) -> syntaxTree() -%% %% @doc Expands an abstract list skeleton to its most explicit form. If -%% <code>Node</code> represents "<code>[<em>E1</em>, ..., <em>En</em> | +%% `Node' represents "<code>[<em>E1</em>, ..., <em>En</em> | %% <em>Tail</em>]</code>", the result represents "<code>[<em>E1</em> | %% ... [<em>En</em> | <em>Tail1</em>] ... ]</code>", where -%% <code>Tail1</code> is the result of -%% <code>normalize_list(Tail)</code>. If <code>Node</code> represents +%% `Tail1' is the result of +%% `normalize_list(Tail)'. If `Node' represents %% "<code>[<em>E1</em>, ..., <em>En</em>]</code>", the result simply %% represents "<code>[<em>E1</em> | ... [<em>En</em> | []] ... -%% ]</code>". If <code>Node</code> does not represent a list skeleton, -%% <code>Node</code> itself is returned. +%% ]</code>". If `Node' does not represent a list skeleton, +%% `Node' itself is returned. %% %% @see list/2 %% @see compact_list/1 @@ -2528,16 +2352,14 @@ normalize_list_1(Es, Tail) -> %% ===================================================================== -%% @spec compact_list(Node::syntaxTree()) -> syntaxTree() -%% %% @doc Yields the most compact form for an abstract list skeleton. The %% result either represents "<code>[<em>E1</em>, ..., <em>En</em> | -%% <em>Tail</em>]</code>", where <code>Tail</code> is not a list +%% <em>Tail</em>]</code>", where `Tail' is not a list %% skeleton, or otherwise simply "<code>[<em>E1</em>, ..., -%% <em>En</em>]</code>". Annotations on subtrees of <code>Node</code> +%% <em>En</em>]</code>". Annotations on subtrees of `Node' %% that represent list skeletons may be lost, but comments will be -%% propagated to the result. Returns <code>Node</code> itself if -%% <code>Node</code> does not represent a list skeleton. +%% propagated to the result. Returns `Node' itself if +%% `Node' does not represent a list skeleton. %% %% @see list/2 %% @see normalize_list/1 @@ -2575,10 +2397,8 @@ compact_list(Node) -> %% ===================================================================== -%% @spec binary(Fields::[syntaxTree()]) -> syntaxTree() -%% %% @doc Creates an abstract binary-object template. If -%% <code>Fields</code> is <code>[F1, ..., Fn]</code>, the result +%% `Fields' is `[F1, ..., Fn]', the result %% represents "<code><<<em>F1</em>, ..., %% <em>Fn</em>>></code>". %% @@ -2611,10 +2431,7 @@ revert_binary(Node) -> %% ===================================================================== -%% @spec binary_fields(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of field subtrees of a <code>binary</code> -%% node. +%% @doc Returns the list of field subtrees of a `binary' node. %% %% @see binary/1 %% @see binary_field/2 @@ -2631,7 +2448,6 @@ binary_fields(Node) -> %% ===================================================================== -%% @spec binary_field(Body) -> syntaxTree() %% @equiv binary_field(Body, []) -spec binary_field(syntaxTree()) -> syntaxTree(). @@ -2641,15 +2457,11 @@ binary_field(Body) -> %% ===================================================================== -%% @spec binary_field(Body::syntaxTree(), Size, -%% Types::[syntaxTree()]) -> syntaxTree() -%% Size = none | syntaxTree() -%% %% @doc Creates an abstract binary template field. -%% If <code>Size</code> is <code>none</code>, this is equivalent to -%% "<code>binary_field(Body, Types)</code>", otherwise it is -%% equivalent to "<code>binary_field(size_qualifier(Body, Size), -%% Types)</code>". +%% If `Size' is `none', this is equivalent to +%% "`binary_field(Body, Types)'", otherwise it is +%% equivalent to "`binary_field(size_qualifier(Body, Size), +%% Types)'". %% %% (This is a utility function.) %% @@ -2667,13 +2479,10 @@ binary_field(Body, Size, Types) -> %% ===================================================================== -%% @spec binary_field(Body::syntaxTree(), Types::[syntaxTree()]) -> -%% syntaxTree() -%% %% @doc Creates an abstract binary template field. If -%% <code>Types</code> is the empty list, the result simply represents -%% "<code><em>Body</em></code>", otherwise, if <code>Types</code> is -%% <code>[T1, ..., Tn]</code>, the result represents +%% `Types' is the empty list, the result simply represents +%% "<code><em>Body</em></code>", otherwise, if `Types' is +%% `[T1, ..., Tn]', the result represents %% "<code><em>Body</em>/<em>T1</em>-...-<em>Tn</em></code>". %% %% @see binary/1 @@ -2727,9 +2536,7 @@ revert_binary_field(Node) -> %% ===================================================================== -%% @spec binary_field_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>binary_field</code>. +%% @doc Returns the body subtree of a `binary_field'. %% %% @see binary_field/2 @@ -2749,12 +2556,10 @@ binary_field_body(Node) -> %% ===================================================================== -%% @spec binary_field_types(Node::syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of type-specifier subtrees of a -%% <code>binary_field</code> node. If <code>Node</code> represents +%% `binary_field' node. If `Node' represents %% "<code>.../<em>T1</em>, ..., <em>Tn</em></code>", the result is -%% <code>[T1, ..., Tn]</code>, otherwise the result is the empty list. +%% `[T1, ..., Tn]', otherwise the result is the empty list. %% %% @see binary_field/2 @@ -2774,14 +2579,12 @@ binary_field_types(Node) -> %% ===================================================================== -%% @spec binary_field_size(Node::syntaxTree()) -> none | syntaxTree() -%% %% @doc Returns the size specifier subtree of a -%% <code>binary_field</code> node, if any. If <code>Node</code> +%% `binary_field' node, if any. If `Node' %% represents "<code><em>Body</em>:<em>Size</em></code>" or %% "<code><em>Body</em>:<em>Size</em>/<em>T1</em>, ..., -%% <em>Tn</em></code>", the result is <code>Size</code>, otherwise -%% <code>none</code> is returned. +%% <em>Tn</em></code>", the result is `Size', otherwise +%% `none' is returned. %% %% (This is a utility function.) %% @@ -2810,9 +2613,6 @@ binary_field_size(Node) -> %% ===================================================================== -%% @spec size_qualifier(Body::syntaxTree(), Size::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract size qualifier. The result represents %% "<code><em>Body</em>:<em>Size</em></code>". %% @@ -2834,10 +2634,7 @@ size_qualifier(Body, Size) -> %% ===================================================================== -%% @spec size_qualifier_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>size_qualifier</code> -%% node. +%% @doc Returns the body subtree of a `size_qualifier' node. %% %% @see size_qualifier/2 @@ -2848,10 +2645,8 @@ size_qualifier_body(Node) -> %% ===================================================================== -%% @spec size_qualifier_argument(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the argument subtree (the size) of a -%% <code>size_qualifier</code> node. +%% `size_qualifier' node. %% %% @see size_qualifier/2 @@ -2862,16 +2657,14 @@ size_qualifier_argument(Node) -> %% ===================================================================== -%% @spec error_marker(Error::term()) -> syntaxTree() -%% %% @doc Creates an abstract error marker. The result represents an %% occurrence of an error in the source code, with an associated Erlang -%% I/O ErrorInfo structure given by <code>Error</code> (see module +%% I/O ErrorInfo structure given by `Error' (see module %% {@link //stdlib/io} for details). Error markers are regarded as source %% code forms, but have no defined lexical form. %% -%% <p>Note: this is supported only for backwards compatibility with -%% existing parsers and tools.</p> +%% Note: this is supported only for backwards compatibility with +%% existing parsers and tools. %% %% @see error_marker_info/1 %% @see warning_marker/1 @@ -2902,10 +2695,7 @@ revert_error_marker(Node) -> %% ===================================================================== -%% @spec error_marker_info(syntaxTree()) -> term() -%% -%% @doc Returns the ErrorInfo structure of an <code>error_marker</code> -%% node. +%% @doc Returns the ErrorInfo structure of an `error_marker' node. %% %% @see error_marker/1 @@ -2921,16 +2711,14 @@ error_marker_info(Node) -> %% ===================================================================== -%% @spec warning_marker(Error::term()) -> syntaxTree() -%% %% @doc Creates an abstract warning marker. The result represents an %% occurrence of a possible problem in the source code, with an -%% associated Erlang I/O ErrorInfo structure given by <code>Error</code> +%% associated Erlang I/O ErrorInfo structure given by `Error' %% (see module {@link //stdlib/io} for details). Warning markers are %% regarded as source code forms, but have no defined lexical form. %% -%% <p>Note: this is supported only for backwards compatibility with -%% existing parsers and tools.</p> +%% Note: this is supported only for backwards compatibility with +%% existing parsers and tools. %% %% @see warning_marker_info/1 %% @see error_marker/1 @@ -2961,10 +2749,7 @@ revert_warning_marker(Node) -> %% ===================================================================== -%% @spec warning_marker_info(syntaxTree()) -> term() -%% -%% @doc Returns the ErrorInfo structure of a <code>warning_marker</code> -%% node. +%% @doc Returns the ErrorInfo structure of a `warning_marker' node. %% %% @see warning_marker/1 @@ -2980,16 +2765,14 @@ warning_marker_info(Node) -> %% ===================================================================== -%% @spec eof_marker() -> syntaxTree() -%% %% @doc Creates an abstract end-of-file marker. This represents the %% end of input when reading a sequence of source code forms. An %% end-of-file marker is itself regarded as a source code form %% (namely, the last in any sequence in which it occurs). It has no %% defined lexical form. %% -%% <p>Note: this is retained only for backwards compatibility with -%% existing parsers and tools.</p> +%% Note: this is retained only for backwards compatibility with +%% existing parsers and tools. %% %% @see error_marker/1 %% @see warning_marker/1 @@ -3013,7 +2796,6 @@ revert_eof_marker(Node) -> %% ===================================================================== -%% @spec attribute(Name) -> syntaxTree() %% @equiv attribute(Name, none) -spec attribute(syntaxTree()) -> syntaxTree(). @@ -3023,23 +2805,20 @@ attribute(Name) -> %% ===================================================================== -%% @spec attribute(Name::syntaxTree(), Arguments) -> syntaxTree() -%% Arguments = none | [syntaxTree()] -%% %% @doc Creates an abstract program attribute. If -%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result +%% `Arguments' is `[A1, ..., An]', the result %% represents "<code>-<em>Name</em>(<em>A1</em>, ..., -%% <em>An</em>).</code>". Otherwise, if <code>Arguments</code> is -%% <code>none</code>, the result represents +%% <em>An</em>).</code>". Otherwise, if `Arguments' is +%% `none', the result represents %% "<code>-<em>Name</em>.</code>". The latter form makes it possible %% to represent preprocessor directives such as -%% "<code>-endif.</code>". Attributes are source code forms. +%% "`-endif.'". Attributes are source code forms. %% -%% <p>Note: The preprocessor macro definition directive +%% Note: The preprocessor macro definition directive %% "<code>-define(<em>Name</em>, <em>Body</em>).</code>" has relatively -%% few requirements on the syntactical form of <code>Body</code> (viewed -%% as a sequence of tokens). The <code>text</code> node type can be used -%% for a <code>Body</code> that is not a normal Erlang construct.</p> +%% few requirements on the syntactical form of `Body' (viewed +%% as a sequence of tokens). The `text' node type can be used +%% for a `Body' that is not a normal Erlang construct. %% %% @see attribute/1 %% @see attribute_name/1 @@ -3233,9 +3012,7 @@ revert_module_name(A) -> %% ===================================================================== -%% @spec attribute_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of an <code>attribute</code> node. +%% @doc Returns the name subtree of an `attribute' node. %% %% @see attribute/1 @@ -3251,15 +3028,12 @@ attribute_name(Node) -> %% ===================================================================== -%% @spec attribute_arguments(Node::syntaxTree()) -> -%% none | [syntaxTree()] -%% %% @doc Returns the list of argument subtrees of an -%% <code>attribute</code> node, if any. If <code>Node</code> +%% `attribute' node, if any. If `Node' %% represents "<code>-<em>Name</em>.</code>", the result is -%% <code>none</code>. Otherwise, if <code>Node</code> represents +%% `none'. Otherwise, if `Node' represents %% "<code>-<em>Name</em>(<em>E1</em>, ..., <em>En</em>).</code>", -%% <code>[E1, ..., E1]</code> is returned. +%% `[E1, ..., E1]' is returned. %% %% @see attribute/1 @@ -3326,9 +3100,6 @@ attribute_arguments(Node) -> %% ===================================================================== -%% @spec arity_qualifier(Body::syntaxTree(), Arity::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract arity qualifier. The result represents %% "<code><em>Body</em>/<em>Arity</em></code>". %% @@ -3350,10 +3121,7 @@ arity_qualifier(Body, Arity) -> %% ===================================================================== -%% @spec arity_qualifier_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of an <code>arity_qualifier</code> -%% node. +%% @doc Returns the body subtree of an `arity_qualifier' node. %% %% @see arity_qualifier/2 @@ -3364,10 +3132,8 @@ arity_qualifier_body(Node) -> %% ===================================================================== -%% @spec arity_qualifier_argument(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the argument (the arity) subtree of an -%% <code>arity_qualifier</code> node. +%% `arity_qualifier' node. %% %% @see arity_qualifier/2 @@ -3378,9 +3144,6 @@ arity_qualifier_argument(Node) -> %% ===================================================================== -%% @spec module_qualifier(Module::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract module qualifier. The result represents %% "<code><em>Module</em>:<em>Body</em></code>". %% @@ -3414,10 +3177,8 @@ revert_module_qualifier(Node) -> %% ===================================================================== -%% @spec module_qualifier_argument(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the argument (the module) subtree of a -%% <code>module_qualifier</code> node. +%% `module_qualifier' node. %% %% @see module_qualifier/2 @@ -3433,10 +3194,7 @@ module_qualifier_argument(Node) -> %% ===================================================================== -%% @spec module_qualifier_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>module_qualifier</code> -%% node. +%% @doc Returns the body subtree of a `module_qualifier' node. %% %% @see module_qualifier/2 @@ -3452,11 +3210,9 @@ module_qualifier_body(Node) -> %% ===================================================================== -%% @spec qualified_name(Segments::[syntaxTree()]) -> syntaxTree() -%% %% @doc Creates an abstract qualified name. The result represents %% "<code><em>S1</em>.<em>S2</em>. ... .<em>Sn</em></code>", if -%% <code>Segments</code> is <code>[S1, S2, ..., Sn]</code>. +%% `Segments' is `[S1, S2, ..., Sn]'. %% %% @see qualified_name_segments/1 @@ -3484,10 +3240,8 @@ revert_qualified_name(Node) -> %% ===================================================================== -%% @spec qualified_name_segments(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of name segments of a -%% <code>qualified_name</code> node. +%% `qualified_name' node. %% %% @see qualified_name/1 @@ -3503,13 +3257,10 @@ qualified_name_segments(Node) -> %% ===================================================================== -%% @spec function(Name::syntaxTree(), Clauses::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract function definition. If <code>Clauses</code> -%% is <code>[C1, ..., Cn]</code>, the result represents +%% @doc Creates an abstract function definition. If `Clauses' +%% is `[C1, ..., Cn]', the result represents %% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em> -%% <em>Cn</em>.</code>". More exactly, if each <code>Ci</code> +%% <em>Cn</em>.</code>". More exactly, if each `Ci' %% represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) <em>Gi</em> -> %% <em>Bi</em></code>", then the result represents %% "<code><em>Name</em>(<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> -> @@ -3523,13 +3274,12 @@ qualified_name_segments(Node) -> %% @see is_form/1 %% @see rule/2 --record(function, {name, clauses}). -%% XXX: This one is problematic because there is a tuple with the same -%% tag and size that comes from 'erl_parse' -%% -record(function, {name :: syntaxTree(), clauses :: [syntaxTree()]}). +%% Don't use the name 'function' for this record, to avoid confusion with +%% the tuples on the form {function,Name,Arity} used by erl_parse. +-record(func, {name :: syntaxTree(), clauses :: [syntaxTree()]}). %% type(Node) = function -%% data(Node) = #function{name :: Name, clauses :: Clauses} +%% data(Node) = #func{name :: Name, clauses :: Clauses} %% %% Name = syntaxTree() %% Clauses = [syntaxTree()] @@ -3556,7 +3306,7 @@ qualified_name_segments(Node) -> -spec function(syntaxTree(), [syntaxTree()]) -> syntaxTree(). function(Name, Clauses) -> - tree(function, #function{name = Name, clauses = Clauses}). + tree(function, #func{name = Name, clauses = Clauses}). revert_function(Node) -> Name = function_name(Node), @@ -3572,9 +3322,7 @@ revert_function(Node) -> %% ===================================================================== -%% @spec function_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of a <code>function</code> node. +%% @doc Returns the name subtree of a `function' node. %% %% @see function/2 @@ -3585,15 +3333,12 @@ function_name(Node) -> {function, Pos, Name, _, _} -> set_pos(atom(Name), Pos); Node1 -> - (data(Node1))#function.name + (data(Node1))#func.name end. %% ===================================================================== -%% @spec function_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>function</code> -%% node. +%% @doc Returns the list of clause subtrees of a `function' node. %% %% @see function/2 @@ -3604,21 +3349,19 @@ function_clauses(Node) -> {function, _, _, _, Clauses} -> Clauses; Node1 -> - (data(Node1))#function.clauses + (data(Node1))#func.clauses end. %% ===================================================================== -%% @spec function_arity(Node::syntaxTree()) -> integer() -%% -%% @doc Returns the arity of a <code>function</code> node. The result +%% @doc Returns the arity of a `function' node. The result %% is the number of parameter patterns in the first clause of the %% function; subsequent clauses are ignored. %% -%% <p>An exception is thrown if <code>function_clauses(Node)</code> +%% An exception is thrown if `function_clauses(Node)' %% returns an empty list, or if the first element of that list is not -%% a syntax tree <code>C</code> of type <code>clause</code> such that -%% <code>clause_patterns(C)</code> is a nonempty list.</p> +%% a syntax tree `C' of type `clause' such that +%% `clause_patterns(C)' is a nonempty list. %% %% @see function/2 %% @see function_clauses/1 @@ -3634,7 +3377,6 @@ function_arity(Node) -> %% ===================================================================== -%% @spec clause(Guard, Body) -> syntaxTree() %% @equiv clause([], Guard, Body) -type guard() :: 'none' | syntaxTree() | [syntaxTree()] | [[syntaxTree()]]. @@ -3646,34 +3388,28 @@ clause(Guard, Body) -> %% ===================================================================== -%% @spec clause(Patterns::[syntaxTree()], Guard, -%% Body::[syntaxTree()]) -> syntaxTree() -%% Guard = none | syntaxTree() -%% | [syntaxTree()] | [[syntaxTree()]] -%% -%% @doc Creates an abstract clause. If <code>Patterns</code> is -%% <code>[P1, ..., Pn]</code> and <code>Body</code> is <code>[B1, ..., -%% Bm]</code>, then if <code>Guard</code> is <code>none</code>, the +%% @doc Creates an abstract clause. If `Patterns' is +%% `[P1, ..., Pn]' and `Body' is `[B1, ..., +%% Bm]', then if `Guard' is `none', the %% result represents "<code>(<em>P1</em>, ..., <em>Pn</em>) -> %% <em>B1</em>, ..., <em>Bm</em></code>", otherwise, unless -%% <code>Guard</code> is a list, the result represents +%% `Guard' is a list, the result represents %% "<code>(<em>P1</em>, ..., <em>Pn</em>) when <em>Guard</em> -> %% <em>B1</em>, ..., <em>Bm</em></code>". %% -%% <p>For simplicity, the <code>Guard</code> argument may also be any +%% For simplicity, the `Guard' argument may also be any %% of the following: %% <ul> -%% <li>An empty list <code>[]</code>. This is equivalent to passing -%% <code>none</code>.</li> -%% <li>A nonempty list <code>[E1, ..., Ej]</code> of syntax trees. -%% This is equivalent to passing <code>conjunction([E1, ..., -%% Ej])</code>.</li> -%% <li>A nonempty list of lists of syntax trees <code>[[E1_1, ..., -%% E1_k1], ..., [Ej_1, ..., Ej_kj]]</code>, which is equivalent -%% to passing <code>disjunction([conjunction([E1_1, ..., -%% E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])])</code>.</li> +%% <li>An empty list `[]'. This is equivalent to passing +%% `none'.</li> +%% <li>A nonempty list `[E1, ..., Ej]' of syntax trees. +%% This is equivalent to passing `conjunction([E1, ..., +%% Ej])'.</li> +%% <li>A nonempty list of lists of syntax trees `[[E1_1, ..., +%% E1_k1], ..., [Ej_1, ..., Ej_kj]]', which is equivalent +%% to passing `disjunction([conjunction([E1_1, ..., +%% E1_k1]), ..., conjunction([Ej_1, ..., Ej_kj])])'.</li> %% </ul> -%% </p> %% %% @see clause/2 %% @see clause_patterns/1 @@ -3789,10 +3525,7 @@ unfold_try_clause({clause, Pos, [{tuple, _, [C, V, _]}], %% ===================================================================== -%% @spec clause_patterns(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of pattern subtrees of a <code>clause</code> -%% node. +%% @doc Returns the list of pattern subtrees of a `clause' node. %% %% @see clause/3 @@ -3808,13 +3541,11 @@ clause_patterns(Node) -> %% ===================================================================== -%% @spec clause_guard(Node::syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the guard subtree of a <code>clause</code> node, if -%% any. If <code>Node</code> represents "<code>(<em>P1</em>, ..., +%% @doc Returns the guard subtree of a `clause' node, if +%% any. If `Node' represents "<code>(<em>P1</em>, ..., %% <em>Pn</em>) when <em>Guard</em> -> <em>B1</em>, ..., -%% <em>Bm</em></code>", <code>Guard</code> is returned. Otherwise, the -%% result is <code>none</code>. +%% <em>Bm</em></code>", `Guard' is returned. Otherwise, the +%% result is `none'. %% %% @see clause/3 @@ -3836,10 +3567,7 @@ clause_guard(Node) -> %% ===================================================================== -%% @spec clause_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Return the list of body subtrees of a <code>clause</code> -%% node. +%% @doc Return the list of body subtrees of a `clause' node. %% %% @see clause/3 @@ -3855,10 +3583,8 @@ clause_body(Node) -> %% ===================================================================== -%% @spec disjunction(List::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract disjunction. If <code>List</code> is -%% <code>[E1, ..., En]</code>, the result represents +%% @doc Creates an abstract disjunction. If `List' is +%% `[E1, ..., En]', the result represents %% "<code><em>E1</em>; ...; <em>En</em></code>". %% %% @see disjunction_body/1 @@ -3874,10 +3600,8 @@ disjunction(Tests) -> %% ===================================================================== -%% @spec disjunction_body(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of body subtrees of a -%% <code>disjunction</code> node. +%% `disjunction' node. %% %% @see disjunction/1 @@ -3888,10 +3612,8 @@ disjunction_body(Node) -> %% ===================================================================== -%% @spec conjunction(List::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract conjunction. If <code>List</code> is -%% <code>[E1, ..., En]</code>, the result represents +%% @doc Creates an abstract conjunction. If `List' is +%% `[E1, ..., En]', the result represents %% "<code><em>E1</em>, ..., <em>En</em></code>". %% %% @see conjunction_body/1 @@ -3907,10 +3629,8 @@ conjunction(Tests) -> %% ===================================================================== -%% @spec conjunction_body(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of body subtrees of a -%% <code>conjunction</code> node. +%% `conjunction' node. %% %% @see conjunction/1 @@ -3921,8 +3641,6 @@ conjunction_body(Node) -> %% ===================================================================== -%% @spec catch_expr(Expr::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract catch-expression. The result represents %% "<code>catch <em>Expr</em></code>". %% @@ -3949,9 +3667,7 @@ revert_catch_expr(Node) -> %% ===================================================================== -%% @spec catch_expr_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>catch_expr</code> node. +%% @doc Returns the body subtree of a `catch_expr' node. %% %% @see catch_expr/1 @@ -3967,9 +3683,6 @@ catch_expr_body(Node) -> %% ===================================================================== -%% @spec match_expr(Pattern::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract match-expression. The result represents %% "<code><em>Pattern</em> = <em>Body</em></code>". %% @@ -4002,9 +3715,7 @@ revert_match_expr(Node) -> %% ===================================================================== -%% @spec match_expr_pattern(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the pattern subtree of a <code>match_expr</code> node. +%% @doc Returns the pattern subtree of a `match_expr' node. %% %% @see match_expr/2 @@ -4020,9 +3731,7 @@ match_expr_pattern(Node) -> %% ===================================================================== -%% @spec match_expr_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>match_expr</code> node. +%% @doc Returns the body subtree of a `match_expr' node. %% %% @see match_expr/2 @@ -4038,15 +3747,12 @@ match_expr_body(Node) -> %% ===================================================================== -%% @spec operator(Name) -> syntaxTree() -%% Name = atom() | string() -%% %% @doc Creates an abstract operator. The name of the operator is the -%% character sequence represented by <code>Name</code>. This is +%% character sequence represented by `Name'. This is %% analogous to the print name of an atom, but an operator is never %% written within single-quotes; e.g., the result of -%% <code>operator('++')</code> represents "<code>++</code>" rather -%% than "<code>'++'</code>". +%% `operator('++')' represents "`++'" rather +%% than "`'++''". %% %% @see operator_name/1 %% @see operator_literal/1 @@ -4064,9 +3770,7 @@ operator(Name) -> %% ===================================================================== -%% @spec operator_name(syntaxTree()) -> atom() -%% -%% @doc Returns the name of an <code>operator</code> node. Note that +%% @doc Returns the name of an `operator' node. Note that %% the name is returned as an atom. %% %% @see operator/1 @@ -4078,11 +3782,8 @@ operator_name(Node) -> %% ===================================================================== -%% @spec operator_literal(syntaxTree()) -> string() -%% %% @doc Returns the literal string represented by an -%% <code>operator</code> node. This is simply the operator name as a -%% string. +%% `operator' node. This is simply the operator name as a string. %% %% @see operator/1 @@ -4093,9 +3794,6 @@ operator_literal(Node) -> %% ===================================================================== -%% @spec infix_expr(Left::syntaxTree(), Operator::syntaxTree(), -%% Right::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract infix operator expression. The result %% represents "<code><em>Left</em> <em>Operator</em> %% <em>Right</em></code>". @@ -4144,10 +3842,8 @@ revert_infix_expr(Node) -> %% ===================================================================== -%% @spec infix_expr_left(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the left argument subtree of an -%% <code>infix_expr</code> node. +%% `infix_expr' node. %% %% @see infix_expr/3 @@ -4163,10 +3859,7 @@ infix_expr_left(Node) -> %% ===================================================================== -%% @spec infix_expr_operator(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the operator subtree of an <code>infix_expr</code> -%% node. +%% @doc Returns the operator subtree of an `infix_expr' node. %% %% @see infix_expr/3 @@ -4182,10 +3875,8 @@ infix_expr_operator(Node) -> %% ===================================================================== -%% @spec infix_expr_right(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the right argument subtree of an -%% <code>infix_expr</code> node. +%% `infix_expr' node. %% %% @see infix_expr/3 @@ -4201,9 +3892,6 @@ infix_expr_right(Node) -> %% ===================================================================== -%% @spec prefix_expr(Operator::syntaxTree(), Argument::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract prefix operator expression. The result %% represents "<code><em>Operator</em> <em>Argument</em></code>". %% @@ -4247,10 +3935,7 @@ revert_prefix_expr(Node) -> %% ===================================================================== -%% @spec prefix_expr_operator(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the operator subtree of a <code>prefix_expr</code> -%% node. +%% @doc Returns the operator subtree of a `prefix_expr' node. %% %% @see prefix_expr/2 @@ -4266,10 +3951,7 @@ prefix_expr_operator(Node) -> %% ===================================================================== -%% @spec prefix_expr_argument(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the argument subtree of a <code>prefix_expr</code> -%% node. +%% @doc Returns the argument subtree of a `prefix_expr' node. %% %% @see prefix_expr/2 @@ -4285,7 +3967,6 @@ prefix_expr_argument(Node) -> %% ===================================================================== -%% @spec record_field(Name) -> syntaxTree() %% @equiv record_field(Name, none) -spec record_field(syntaxTree()) -> syntaxTree(). @@ -4295,11 +3976,8 @@ record_field(Name) -> %% ===================================================================== -%% @spec record_field(Name::syntaxTree(), Value) -> syntaxTree() -%% Value = none | syntaxTree() -%% %% @doc Creates an abstract record field specification. If -%% <code>Value</code> is <code>none</code>, the result represents +%% `Value' is `none', the result represents %% simply "<code><em>Name</em></code>", otherwise it represents %% "<code><em>Name</em> = <em>Value</em></code>". %% @@ -4321,9 +3999,7 @@ record_field(Name, Value) -> %% ===================================================================== -%% @spec record_field_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of a <code>record_field</code> node. +%% @doc Returns the name subtree of a `record_field' node. %% %% @see record_field/2 @@ -4334,13 +4010,11 @@ record_field_name(Node) -> %% ===================================================================== -%% @spec record_field_value(syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the value subtree of a <code>record_field</code> node, -%% if any. If <code>Node</code> represents -%% "<code><em>Name</em></code>", <code>none</code> is -%% returned. Otherwise, if <code>Node</code> represents -%% "<code><em>Name</em> = <em>Value</em></code>", <code>Value</code> +%% @doc Returns the value subtree of a `record_field' node, +%% if any. If `Node' represents +%% "<code><em>Name</em></code>", `none' is +%% returned. Otherwise, if `Node' represents +%% "<code><em>Name</em> = <em>Value</em></code>", `Value' %% is returned. %% %% @see record_field/2 @@ -4352,15 +4026,12 @@ record_field_value(Node) -> %% ===================================================================== -%% @spec record_index_expr(Type::syntaxTree(), Field::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract record field index expression. The result %% represents "<code>#<em>Type</em>.<em>Field</em></code>". %% -%% <p>(Note: the function name <code>record_index/2</code> is reserved +%% (Note: the function name `record_index/2' is reserved %% by the Erlang compiler, which is why that name could not be used -%% for this constructor.)</p> +%% for this constructor.) %% %% @see record_index_expr_type/1 %% @see record_index_expr_field/1 @@ -4399,10 +4070,7 @@ revert_record_index_expr(Node) -> %% ===================================================================== -%% @spec record_index_expr_type(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the type subtree of a <code>record_index_expr</code> -%% node. +%% @doc Returns the type subtree of a `record_index_expr' node. %% %% @see record_index_expr/2 @@ -4418,10 +4086,7 @@ record_index_expr_type(Node) -> %% ===================================================================== -%% @spec record_index_expr_field(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the field subtree of a <code>record_index_expr</code> -%% node. +%% @doc Returns the field subtree of a `record_index_expr' node. %% %% @see record_index_expr/2 @@ -4437,7 +4102,6 @@ record_index_expr_field(Node) -> %% ===================================================================== -%% @spec record_access(Argument, Field) -> syntaxTree() %% @equiv record_access(Argument, none, Field) -spec record_access(syntaxTree(), syntaxTree()) -> syntaxTree(). @@ -4447,17 +4111,13 @@ record_access(Argument, Field) -> %% ===================================================================== -%% @spec record_access(Argument::syntaxTree(), Type, -%% Field::syntaxTree()) -> syntaxTree() -%% Type = none | syntaxTree() -%% %% @doc Creates an abstract record field access expression. If -%% <code>Type</code> is not <code>none</code>, the result represents +%% `Type' is not `none', the result represents %% "<code><em>Argument</em>#<em>Type</em>.<em>Field</em></code>". %% -%% <p>If <code>Type</code> is <code>none</code>, the result represents +%% If `Type' is `none', the result represents %% "<code><em>Argument</em>.<em>Field</em></code>". This is a special -%% form only allowed within Mnemosyne queries.</p> +%% form only allowed within Mnemosyne queries. %% %% @see record_access/2 %% @see record_access_argument/1 @@ -4512,10 +4172,7 @@ revert_record_access(Node) -> %% ===================================================================== -%% @spec record_access_argument(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the argument subtree of a <code>record_access</code> -%% node. +%% @doc Returns the argument subtree of a `record_access' node. %% %% @see record_access/3 @@ -4533,14 +4190,12 @@ record_access_argument(Node) -> %% ===================================================================== -%% @spec record_access_type(syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the type subtree of a <code>record_access</code> node, -%% if any. If <code>Node</code> represents -%% "<code><em>Argument</em>.<em>Field</em></code>", <code>none</code> -%% is returned, otherwise if <code>Node</code> represents +%% @doc Returns the type subtree of a `record_access' node, +%% if any. If `Node' represents +%% "<code><em>Argument</em>.<em>Field</em></code>", `none' +%% is returned, otherwise if `Node' represents %% "<code><em>Argument</em>#<em>Type</em>.<em>Field</em></code>", -%% <code>Type</code> is returned. +%% `Type' is returned. %% %% @see record_access/3 @@ -4558,10 +4213,7 @@ record_access_type(Node) -> %% ===================================================================== -%% @spec record_access_field(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the field subtree of a <code>record_access</code> -%% node. +%% @doc Returns the field subtree of a `record_access' node. %% %% @see record_access/3 @@ -4579,7 +4231,6 @@ record_access_field(Node) -> %% ===================================================================== -%% @spec record_expr(Type, Fields) -> syntaxTree() %% @equiv record_expr(none, Type, Fields) -spec record_expr(syntaxTree(), [syntaxTree()]) -> syntaxTree(). @@ -4589,13 +4240,9 @@ record_expr(Type, Fields) -> %% ===================================================================== -%% @spec record_expr(Argument, Type::syntaxTree(), -%% Fields::[syntaxTree()]) -> syntaxTree() -%% Argument = none | syntaxTree() -%% -%% @doc Creates an abstract record expression. If <code>Fields</code> is -%% <code>[F1, ..., Fn]</code>, then if <code>Argument</code> is -%% <code>none</code>, the result represents +%% @doc Creates an abstract record expression. If `Fields' is +%% `[F1, ..., Fn]', then if `Argument' is +%% `none', the result represents %% "<code>#<em>Type</em>{<em>F1</em>, ..., <em>Fn</em>}</code>", %% otherwise it represents %% "<code><em>Argument</em>#<em>Type</em>{<em>F1</em>, ..., @@ -4661,14 +4308,12 @@ revert_record_expr(Node) -> %% ===================================================================== -%% @spec record_expr_argument(syntaxTree()) -> none | syntaxTree() -%% -%% @doc Returns the argument subtree of a <code>record_expr</code> node, -%% if any. If <code>Node</code> represents -%% "<code>#<em>Type</em>{...}</code>", <code>none</code> is returned. -%% Otherwise, if <code>Node</code> represents +%% @doc Returns the argument subtree of a `record_expr' node, +%% if any. If `Node' represents +%% "<code>#<em>Type</em>{...}</code>", `none' is returned. +%% Otherwise, if `Node' represents %% "<code><em>Argument</em>#<em>Type</em>{...}</code>", -%% <code>Argument</code> is returned. +%% `Argument' is returned. %% %% @see record_expr/3 @@ -4686,9 +4331,7 @@ record_expr_argument(Node) -> %% ===================================================================== -%% @spec record_expr_type(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the type subtree of a <code>record_expr</code> node. +%% @doc Returns the type subtree of a `record_expr' node. %% %% @see record_expr/3 @@ -4706,10 +4349,8 @@ record_expr_type(Node) -> %% ===================================================================== -%% @spec record_expr_fields(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of field subtrees of a -%% <code>record_expr</code> node. +%% `record_expr' node. %% %% @see record_expr/3 @@ -4727,15 +4368,11 @@ record_expr_fields(Node) -> %% ===================================================================== -%% @spec application(Module, Function::syntaxTree(), -%% Arguments::[syntaxTree()]) -> syntaxTree() -%% Module = none | syntaxTree() -%% %% @doc Creates an abstract function application expression. If -%% <code>Module</code> is <code>none</code>, this is call is equivalent -%% to <code>application(Function, Arguments)</code>, otherwise it is -%% equivalent to <code>application(module_qualifier(Module, Function), -%% Arguments)</code>. +%% `Module' is `none', this is call is equivalent +%% to `application(Function, Arguments)', otherwise it is +%% equivalent to `application(module_qualifier(Module, Function), +%% Arguments)'. %% %% (This is a utility function.) %% @@ -4752,11 +4389,8 @@ application(Module, Name, Arguments) -> %% ===================================================================== -%% @spec application(Operator::syntaxTree(), -%% Arguments::[syntaxTree()]) -> syntaxTree() -%% %% @doc Creates an abstract function application expression. If -%% <code>Arguments</code> is <code>[A1, ..., An]</code>, the result +%% `Arguments' is `[A1, ..., An]', the result %% represents "<code><em>Operator</em>(<em>A1</em>, ..., %% <em>An</em>)</code>". %% @@ -4794,14 +4428,11 @@ revert_application(Node) -> %% ===================================================================== -%% @spec application_operator(syntaxTree()) -> syntaxTree() +%% @doc Returns the operator subtree of an `application' node. %% -%% @doc Returns the operator subtree of an <code>application</code> -%% node. -%% -%% <p>Note: if <code>Node</code> represents +%% Note: if `Node' represents %% "<code><em>M</em>:<em>F</em>(...)</code>", then the result is the -%% subtree representing "<code><em>M</em>:<em>F</em></code>".</p> +%% subtree representing "<code><em>M</em>:<em>F</em></code>". %% %% @see application/2 %% @see module_qualifier/2 @@ -4818,10 +4449,8 @@ application_operator(Node) -> %% ===================================================================== -%% @spec application_arguments(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of argument subtrees of an -%% <code>application</code> node. +%% `application' node. %% %% @see application/2 @@ -4837,11 +4466,8 @@ application_arguments(Node) -> %% ===================================================================== -%% @spec list_comp(Template::syntaxTree(), Body::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract list comprehension. If <code>Body</code> is -%% <code>[E1, ..., En]</code>, the result represents +%% @doc Creates an abstract list comprehension. If `Body' is +%% `[E1, ..., En]', the result represents %% "<code>[<em>Template</em> || <em>E1</em>, ..., <em>En</em>]</code>". %% %% @see list_comp_template/1 @@ -4876,9 +4502,7 @@ revert_list_comp(Node) -> %% ===================================================================== -%% @spec list_comp_template(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the template subtree of a <code>list_comp</code> node. +%% @doc Returns the template subtree of a `list_comp' node. %% %% @see list_comp/2 @@ -4894,10 +4518,7 @@ list_comp_template(Node) -> %% ===================================================================== -%% @spec list_comp_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of body subtrees of a <code>list_comp</code> -%% node. +%% @doc Returns the list of body subtrees of a `list_comp' node. %% %% @see list_comp/2 @@ -4912,11 +4533,8 @@ list_comp_body(Node) -> end. %% ===================================================================== -%% @spec binary_comp(Template::syntaxTree(), Body::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract binary comprehension. If <code>Body</code> is -%% <code>[E1, ..., En]</code>, the result represents +%% @doc Creates an abstract binary comprehension. If `Body' is +%% `[E1, ..., En]', the result represents %% "<code><<<em>Template</em> || <em>E1</em>, ..., <em>En</em>>></code>". %% %% @see binary_comp_template/1 @@ -4951,9 +4569,7 @@ revert_binary_comp(Node) -> %% ===================================================================== -%% @spec binary_comp_template(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the template subtree of a <code>binary_comp</code> node. +%% @doc Returns the template subtree of a `binary_comp' node. %% %% @see binary_comp/2 @@ -4969,10 +4585,7 @@ binary_comp_template(Node) -> %% ===================================================================== -%% @spec binary_comp_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of body subtrees of a <code>binary_comp</code> -%% node. +%% @doc Returns the list of body subtrees of a `binary_comp' node. %% %% @see binary_comp/2 @@ -4988,8 +4601,6 @@ binary_comp_body(Node) -> %% ===================================================================== -%% @spec query_expr(Body::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract Mnemosyne query expression. The result %% represents "<code>query <em>Body</em> end</code>". %% @@ -5018,9 +4629,7 @@ revert_query_expr(Node) -> %% ===================================================================== -%% @spec query_expr_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>query_expr</code> node. +%% @doc Returns the body subtree of a `query_expr' node. %% %% @see query_expr/1 @@ -5036,13 +4645,10 @@ query_expr_body(Node) -> %% ===================================================================== -%% @spec rule(Name::syntaxTree(), Clauses::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract Mnemosyne rule. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the results represents +%% @doc Creates an abstract Mnemosyne rule. If `Clauses' is +%% `[C1, ..., Cn]', the results represents %% "<code><em>Name</em> <em>C1</em>; ...; <em>Name</em> -%% <em>Cn</em>.</code>". More exactly, if each <code>Ci</code> +%% <em>Cn</em>.</code>". More exactly, if each `Ci' %% represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) <em>Gi</em> -> %% <em>Bi</em></code>", then the result represents %% "<code><em>Name</em>(<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> :- @@ -5097,9 +4703,7 @@ revert_rule(Node) -> %% ===================================================================== -%% @spec rule_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of a <code>rule</code> node. +%% @doc Returns the name subtree of a `rule' node. %% %% @see rule/2 @@ -5114,9 +4718,7 @@ rule_name(Node) -> end. %% ===================================================================== -%% @spec rule_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>rule</code> node. +%% @doc Returns the list of clause subtrees of a `rule' node. %% %% @see rule/2 @@ -5131,16 +4733,14 @@ rule_clauses(Node) -> end. %% ===================================================================== -%% @spec rule_arity(Node::syntaxTree()) -> integer() -%% -%% @doc Returns the arity of a <code>rule</code> node. The result is the +%% @doc Returns the arity of a `rule' node. The result is the %% number of parameter patterns in the first clause of the rule; %% subsequent clauses are ignored. %% -%% <p>An exception is thrown if <code>rule_clauses(Node)</code> returns +%% An exception is thrown if `rule_clauses(Node)' returns %% an empty list, or if the first element of that list is not a syntax -%% tree <code>C</code> of type <code>clause</code> such that -%% <code>clause_patterns(C)</code> is a nonempty list.</p> +%% tree `C' of type `clause' such that +%% `clause_patterns(C)' is a nonempty list. %% %% @see rule/2 %% @see rule_clauses/1 @@ -5156,9 +4756,6 @@ rule_arity(Node) -> %% ===================================================================== -%% @spec generator(Pattern::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract generator. The result represents %% "<code><em>Pattern</em> <- <em>Body</em></code>". %% @@ -5193,9 +4790,7 @@ revert_generator(Node) -> %% ===================================================================== -%% @spec generator_pattern(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the pattern subtree of a <code>generator</code> node. +%% @doc Returns the pattern subtree of a `generator' node. %% %% @see generator/2 @@ -5211,9 +4806,7 @@ generator_pattern(Node) -> %% ===================================================================== -%% @spec generator_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>generator</code> node. +%% @doc Returns the body subtree of a `generator' node. %% %% @see generator/2 @@ -5229,9 +4822,6 @@ generator_body(Node) -> %% ===================================================================== -%% @spec binary_generator(Pattern::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract binary_generator. The result represents %% "<code><em>Pattern</em> <- <em>Body</em></code>". %% @@ -5266,9 +4856,7 @@ revert_binary_generator(Node) -> %% ===================================================================== -%% @spec binary_generator_pattern(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the pattern subtree of a <code>generator</code> node. +%% @doc Returns the pattern subtree of a `generator' node. %% %% @see binary_generator/2 @@ -5284,9 +4872,7 @@ binary_generator_pattern(Node) -> %% ===================================================================== -%% @spec binary_generator_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>generator</code> node. +%% @doc Returns the body subtree of a `generator' node. %% %% @see binary_generator/2 @@ -5302,10 +4888,8 @@ binary_generator_body(Node) -> %% ===================================================================== -%% @spec block_expr(Body::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract block expression. If <code>Body</code> is -%% <code>[B1, ..., Bn]</code>, the result represents "<code>begin +%% @doc Creates an abstract block expression. If `Body' is +%% `[B1, ..., Bn]', the result represents "<code>begin %% <em>B1</em>, ..., <em>Bn</em> end</code>". %% %% @see block_expr_body/1 @@ -5321,7 +4905,7 @@ binary_generator_body(Node) -> %% %% Body = [erl_parse()] \ [] --spec block_expr(Body::[syntaxTree()]) -> syntaxTree(). +-spec block_expr([syntaxTree()]) -> syntaxTree(). block_expr(Body) -> tree(block_expr, Body). @@ -5333,10 +4917,7 @@ revert_block_expr(Node) -> %% ===================================================================== -%% @spec block_expr_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of body subtrees of a <code>block_expr</code> -%% node. +%% @doc Returns the list of body subtrees of a `block_expr' node. %% %% @see block_expr/1 @@ -5352,12 +4933,10 @@ block_expr_body(Node) -> %% ===================================================================== -%% @spec if_expr(Clauses::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract if-expression. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the result represents "<code>if +%% @doc Creates an abstract if-expression. If `Clauses' is +%% `[C1, ..., Cn]', the result represents "<code>if %% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each -%% <code>Ci</code> represents "<code>() <em>Gi</em> -> +%% `Ci' represents "<code>() <em>Gi</em> -> %% <em>Bi</em></code>", then the result represents "<code>if %% <em>G1</em> -> <em>B1</em>; ...; <em>Gn</em> -> <em>Bn</em> %% end</code>". @@ -5392,10 +4971,7 @@ revert_if_expr(Node) -> %% ===================================================================== -%% @spec if_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of an <code>if_expr</code> -%% node. +%% @doc Returns the list of clause subtrees of an `if_expr' node. %% %% @see if_expr/1 @@ -5411,13 +4987,10 @@ if_expr_clauses(Node) -> %% ===================================================================== -%% @spec case_expr(Argument::syntaxTree(), Clauses::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract case-expression. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the result represents "<code>case +%% @doc Creates an abstract case-expression. If `Clauses' is +%% `[C1, ..., Cn]', the result represents "<code>case %% <em>Argument</em> of <em>C1</em>; ...; <em>Cn</em> end</code>". More -%% exactly, if each <code>Ci</code> represents "<code>(<em>Pi</em>) +%% exactly, if each `Ci' represents "<code>(<em>Pi</em>) %% <em>Gi</em> -> <em>Bi</em></code>", then the result represents %% "<code>case <em>Argument</em> of <em>P1</em> <em>G1</em> -> %% <em>B1</em>; ...; <em>Pn</em> <em>Gn</em> -> <em>Bn</em> end</code>". @@ -5461,9 +5034,7 @@ revert_case_expr(Node) -> %% ===================================================================== -%% @spec case_expr_argument(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the argument subtree of a <code>case_expr</code> node. +%% @doc Returns the argument subtree of a `case_expr' node. %% %% @see case_expr/2 @@ -5479,10 +5050,7 @@ case_expr_argument(Node) -> %% ===================================================================== -%% @spec case_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>case_expr</code> -%% node. +%% @doc Returns the list of clause subtrees of a `case_expr' node. %% %% @see case_expr/2 @@ -5498,12 +5066,10 @@ case_expr_clauses(Node) -> %% ===================================================================== -%% @spec cond_expr(Clauses::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract cond-expression. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the result represents "<code>cond +%% @doc Creates an abstract cond-expression. If `Clauses' is +%% `[C1, ..., Cn]', the result represents "<code>cond %% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each -%% <code>Ci</code> represents "<code>() <em>Ei</em> -> +%% `Ci' represents "<code>() <em>Ei</em> -> %% <em>Bi</em></code>", then the result represents "<code>cond %% <em>E1</em> -> <em>B1</em>; ...; <em>En</em> -> <em>Bn</em> %% end</code>". @@ -5538,10 +5104,7 @@ revert_cond_expr(Node) -> %% ===================================================================== -%% @spec cond_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>cond_expr</code> -%% node. +%% @doc Returns the list of clause subtrees of a `cond_expr' node. %% %% @see cond_expr/1 @@ -5557,7 +5120,6 @@ cond_expr_clauses(Node) -> %% ===================================================================== -%% @spec receive_expr(Clauses) -> syntaxTree() %% @equiv receive_expr(Clauses, none, []) -spec receive_expr([syntaxTree()]) -> syntaxTree(). @@ -5567,25 +5129,21 @@ receive_expr(Clauses) -> %% ===================================================================== -%% @spec receive_expr(Clauses::[syntaxTree()], Timeout, -%% Action::[syntaxTree()]) -> syntaxTree() -%% Timeout = none | syntaxTree() -%% -%% @doc Creates an abstract receive-expression. If <code>Timeout</code> -%% is <code>none</code>, the result represents "<code>receive -%% <em>C1</em>; ...; <em>Cn</em> end</code>" (the <code>Action</code> -%% argument is ignored). Otherwise, if <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code> and <code>Action</code> is <code>[A1, ..., -%% Am]</code>, the result represents "<code>receive <em>C1</em>; ...; +%% @doc Creates an abstract receive-expression. If `Timeout' +%% is `none', the result represents "<code>receive +%% <em>C1</em>; ...; <em>Cn</em> end</code>" (the `Action' +%% argument is ignored). Otherwise, if `Clauses' is +%% `[C1, ..., Cn]' and `Action' is `[A1, ..., +%% Am]', the result represents "<code>receive <em>C1</em>; ...; %% <em>Cn</em> after <em>Timeout</em> -> <em>A1</em>, ..., <em>Am</em> -%% end</code>". More exactly, if each <code>Ci</code> represents +%% end</code>". More exactly, if each `Ci' represents %% "<code>(<em>Pi</em>) <em>Gi</em> -> <em>Bi</em></code>", then the %% result represents "<code>receive <em>P1</em> <em>G1</em> -> %% <em>B1</em>; ...; <em>Pn</em> <em>Gn</em> -> <em>Bn</em> ... %% end</code>". %% -%% <p>Note that in Erlang, a receive-expression must have at least one -%% clause if no timeout part is specified.</p> +%% Note that in Erlang, a receive-expression must have at least one +%% clause if no timeout part is specified. %% %% @see receive_expr_clauses/1 %% @see receive_expr_timeout/1 @@ -5649,11 +5207,8 @@ revert_receive_expr(Node) -> %% ===================================================================== -%% @spec receive_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% type(Node) = receive_expr -%% %% @doc Returns the list of clause subtrees of a -%% <code>receive_expr</code> node. +%% `receive_expr' node. %% %% @see receive_expr/3 @@ -5671,15 +5226,12 @@ receive_expr_clauses(Node) -> %% ===================================================================== -%% @spec receive_expr_timeout(Node::syntaxTree()) -> Timeout -%% Timeout = none | syntaxTree() -%% -%% @doc Returns the timeout subtree of a <code>receive_expr</code> node, -%% if any. If <code>Node</code> represents "<code>receive <em>C1</em>; -%% ...; <em>Cn</em> end</code>", <code>none</code> is returned. -%% Otherwise, if <code>Node</code> represents "<code>receive +%% @doc Returns the timeout subtree of a `receive_expr' node, +%% if any. If `Node' represents "<code>receive <em>C1</em>; +%% ...; <em>Cn</em> end</code>", `none' is returned. +%% Otherwise, if `Node' represents "<code>receive %% <em>C1</em>; ...; <em>Cn</em> after <em>Timeout</em> -> ... end</code>", -%% <code>Timeout</code> is returned. +%% `Timeout' is returned. %% %% @see receive_expr/3 @@ -5697,10 +5249,8 @@ receive_expr_timeout(Node) -> %% ===================================================================== -%% @spec receive_expr_action(Node::syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of action body subtrees of a -%% <code>receive_expr</code> node. If <code>Node</code> represents +%% `receive_expr' node. If `Node' represents %% "<code>receive <em>C1</em>; ...; <em>Cn</em> end</code>", this is the %% empty list. %% @@ -5720,8 +5270,6 @@ receive_expr_action(Node) -> %% ===================================================================== -%% @spec try_expr(Body::syntaxTree(), Handlers::[syntaxTree()]) -> -%% syntaxTree() %% @equiv try_expr(Body, [], Handlers) -spec try_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree(). @@ -5731,8 +5279,6 @@ try_expr(Body, Handlers) -> %% ===================================================================== -%% @spec try_expr(Body::syntaxTree(), Clauses::[syntaxTree()], -%% Handlers::[syntaxTree()]) -> syntaxTree() %% @equiv try_expr(Body, Clauses, Handlers, []) -spec try_expr([syntaxTree()], [syntaxTree()], [syntaxTree()]) -> syntaxTree(). @@ -5742,8 +5288,6 @@ try_expr(Body, Clauses, Handlers) -> %% ===================================================================== -%% @spec try_after_expr(Body::syntaxTree(), After::[syntaxTree()]) -> -%% syntaxTree() %% @equiv try_expr(Body, [], [], After) -spec try_after_expr([syntaxTree()], [syntaxTree()]) -> syntaxTree(). @@ -5753,30 +5297,26 @@ try_after_expr(Body, After) -> %% ===================================================================== -%% @spec try_expr(Body::[syntaxTree()], Clauses::[syntaxTree()], -%% Handlers::[syntaxTree()], After::[syntaxTree()]) -> -%% syntaxTree() -%% -%% @doc Creates an abstract try-expression. If <code>Body</code> is -%% <code>[B1, ..., Bn]</code>, <code>Clauses</code> is <code>[C1, ..., -%% Cj]</code>, <code>Handlers</code> is <code>[H1, ..., Hk]</code>, and -%% <code>After</code> is <code>[A1, ..., Am]</code>, the result +%% @doc Creates an abstract try-expression. If `Body' is +%% `[B1, ..., Bn]', `Clauses' is `[C1, ..., +%% Cj]', `Handlers' is `[H1, ..., Hk]', and +%% `After' is `[A1, ..., Am]', the result %% represents "<code>try <em>B1</em>, ..., <em>Bn</em> of <em>C1</em>; %% ...; <em>Cj</em> catch <em>H1</em>; ...; <em>Hk</em> after %% <em>A1</em>, ..., <em>Am</em> end</code>". More exactly, if each -%% <code>Ci</code> represents "<code>(<em>CPi</em>) <em>CGi</em> -> -%% <em>CBi</em></code>", and each <code>Hi</code> represents +%% `Ci' represents "<code>(<em>CPi</em>) <em>CGi</em> -> +%% <em>CBi</em></code>", and each `Hi' represents %% "<code>(<em>HPi</em>) <em>HGi</em> -> <em>HBi</em></code>", then the %% result represents "<code>try <em>B1</em>, ..., <em>Bn</em> of %% <em>CP1</em> <em>CG1</em> -> <em>CB1</em>; ...; <em>CPj</em> %% <em>CGj</em> -> <em>CBj</em> catch <em>HP1</em> <em>HG1</em> -> %% <em>HB1</em>; ...; <em>HPk</em> <em>HGk</em> -> <em>HBk</em> after -%% <em>A1</em>, ..., <em>Am</em> end</code>"; cf. -%% <code>case_expr/2</code>. If <code>Clauses</code> is the empty list, -%% the <code>of ...</code> section is left out. If <code>After</code> is -%% the empty list, the <code>after ...</code> section is left out. If -%% <code>Handlers</code> is the empty list, and <code>After</code> is -%% nonempty, the <code>catch ...</code> section is left out. +%% <em>A1</em>, ..., <em>Am</em> end</code>"; see +%% {@link case_expr/2}. If `Clauses' is the empty list, +%% the `of ...' section is left out. If `After' is +%% the empty list, the `after ...' section is left out. If +%% `Handlers' is the empty list, and `After' is +%% nonempty, the `catch ...' section is left out. %% %% @see try_expr_body/1 %% @see try_expr_clauses/1 @@ -5834,10 +5374,7 @@ revert_try_expr(Node) -> %% ===================================================================== -%% @spec try_expr_body(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of body subtrees of a <code>try_expr</code> -%% node. +%% @doc Returns the list of body subtrees of a `try_expr' node. %% %% @see try_expr/4 @@ -5853,10 +5390,8 @@ try_expr_body(Node) -> %% ===================================================================== -%% @spec try_expr_clauses(Node::syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of case-clause subtrees of a -%% <code>try_expr</code> node. If <code>Node</code> represents +%% `try_expr' node. If `Node' represents %% "<code>try <em>Body</em> catch <em>H1</em>; ...; <em>Hn</em> %% end</code>", the result is the empty list. %% @@ -5874,10 +5409,8 @@ try_expr_clauses(Node) -> %% ===================================================================== -%% @spec try_expr_handlers(syntaxTree()) -> [syntaxTree()] -%% %% @doc Returns the list of handler-clause subtrees of a -%% <code>try_expr</code> node. +%% `try_expr' node. %% %% @see try_expr/4 @@ -5893,10 +5426,7 @@ try_expr_handlers(Node) -> %% ===================================================================== -%% @spec try_expr_after(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of "after" subtrees of a <code>try_expr</code> -%% node. +%% @doc Returns the list of "after" subtrees of a `try_expr' node. %% %% @see try_expr/4 @@ -5912,9 +5442,6 @@ try_expr_after(Node) -> %% ===================================================================== -%% @spec class_qualifier(Class::syntaxTree(), Body::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract class qualifier. The result represents %% "<code><em>Class</em>:<em>Body</em></code>". %% @@ -5937,10 +5464,8 @@ class_qualifier(Class, Body) -> %% ===================================================================== -%% @spec class_qualifier_argument(syntaxTree()) -> syntaxTree() -%% %% @doc Returns the argument (the class) subtree of a -%% <code>class_qualifier</code> node. +%% `class_qualifier' node. %% %% @see class_qualifier/2 @@ -5951,9 +5476,7 @@ class_qualifier_argument(Node) -> %% ===================================================================== -%% @spec class_qualifier_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>class_qualifier</code> node. +%% @doc Returns the body subtree of a `class_qualifier' node. %% %% @see class_qualifier/2 @@ -5964,13 +5487,10 @@ class_qualifier_body(Node) -> %% ===================================================================== -%% @spec implicit_fun(Name::syntaxTree(), Arity::syntaxTree()) -> -%% syntaxTree() -%% %% @doc Creates an abstract "implicit fun" expression. If -%% <code>Arity</code> is <code>none</code>, this is equivalent to -%% <code>implicit_fun(Name)</code>, otherwise it is equivalent to -%% <code>implicit_fun(arity_qualifier(Name, Arity))</code>. +%% `Arity' is `none', this is equivalent to +%% `implicit_fun(Name)', otherwise it is equivalent to +%% `implicit_fun(arity_qualifier(Name, Arity))'. %% %% (This is a utility function.) %% @@ -5986,14 +5506,11 @@ implicit_fun(Name, Arity) -> %% ===================================================================== -%% @spec implicit_fun(Module::syntaxTree(), Name::syntaxTree(), -%% Arity::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract module-qualified "implicit fun" expression. -%% If <code>Module</code> is <code>none</code>, this is equivalent to -%% <code>implicit_fun(Name, Arity)</code>, otherwise it is equivalent to -%% <code>implicit_fun(module_qualifier(Module, arity_qualifier(Name, -%% Arity))</code>. +%% If `Module' is `none', this is equivalent to +%% `implicit_fun(Name, Arity)', otherwise it is equivalent to +%% `implicit_fun(module_qualifier(Module, arity_qualifier(Name, +%% Arity))'. %% %% (This is a utility function.) %% @@ -6010,10 +5527,8 @@ implicit_fun(Module, Name, Arity) -> %% ===================================================================== -%% @spec implicit_fun(Name::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract "implicit fun" expression. The result -%% represents "<code>fun <em>Name</em></code>". <code>Name</code> should +%% represents "<code>fun <em>Name</em></code>". `Name' should %% represent either <code><em>F</em>/<em>A</em></code> or %% <code><em>M</em>:<em>F</em>/<em>A</em></code> %% @@ -6072,15 +5587,13 @@ revert_implicit_fun(Node) -> %% ===================================================================== -%% @spec implicit_fun_name(Node::syntaxTree()) -> syntaxTree() +%% @doc Returns the name subtree of an `implicit_fun' node. %% -%% @doc Returns the name subtree of an <code>implicit_fun</code> node. -%% -%% <p>Note: if <code>Node</code> represents "<code>fun +%% Note: if `Node' represents "<code>fun %% <em>N</em>/<em>A</em></code>" or "<code>fun %% <em>M</em>:<em>N</em>/<em>A</em></code>", then the result is the %% subtree representing "<code><em>N</em>/<em>A</em></code>" or -%% "<code><em>M</em>:<em>N</em>/<em>A</em></code>", respectively.</p> +%% "<code><em>M</em>:<em>N</em>/<em>A</em></code>", respectively. %% %% @see implicit_fun/1 %% @see arity_qualifier/2 @@ -6110,12 +5623,10 @@ implicit_fun_name(Node) -> %% ===================================================================== -%% @spec fun_expr(Clauses::[syntaxTree()]) -> syntaxTree() -%% -%% @doc Creates an abstract fun-expression. If <code>Clauses</code> is -%% <code>[C1, ..., Cn]</code>, the result represents "<code>fun +%% @doc Creates an abstract fun-expression. If `Clauses' is +%% `[C1, ..., Cn]', the result represents "<code>fun %% <em>C1</em>; ...; <em>Cn</em> end</code>". More exactly, if each -%% <code>Ci</code> represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) +%% `Ci' represents "<code>(<em>Pi1</em>, ..., <em>Pim</em>) %% <em>Gi</em> -> <em>Bi</em></code>", then the result represents %% "<code>fun (<em>P11</em>, ..., <em>P1m</em>) <em>G1</em> -> %% <em>B1</em>; ...; (<em>Pn1</em>, ..., <em>Pnm</em>) <em>Gn</em> -> @@ -6152,10 +5663,7 @@ revert_fun_expr(Node) -> %% ===================================================================== -%% @spec fun_expr_clauses(syntaxTree()) -> [syntaxTree()] -%% -%% @doc Returns the list of clause subtrees of a <code>fun_expr</code> -%% node. +%% @doc Returns the list of clause subtrees of a `fun_expr' node. %% %% @see fun_expr/1 @@ -6171,16 +5679,14 @@ fun_expr_clauses(Node) -> %% ===================================================================== -%% @spec fun_expr_arity(syntaxTree()) -> integer() -%% -%% @doc Returns the arity of a <code>fun_expr</code> node. The result is +%% @doc Returns the arity of a `fun_expr' node. The result is %% the number of parameter patterns in the first clause of the %% fun-expression; subsequent clauses are ignored. %% -%% <p>An exception is thrown if <code>fun_expr_clauses(Node)</code> +%% An exception is thrown if `fun_expr_clauses(Node)' %% returns an empty list, or if the first element of that list is not a -%% syntax tree <code>C</code> of type <code>clause</code> such that -%% <code>clause_patterns(C)</code> is a nonempty list.</p> +%% syntax tree `C' of type `clause' such that +%% `clause_patterns(C)' is a nonempty list. %% %% @see fun_expr/1 %% @see fun_expr_clauses/1 @@ -6194,8 +5700,6 @@ fun_expr_arity(Node) -> %% ===================================================================== -%% @spec parentheses(Body::syntaxTree()) -> syntaxTree() -%% %% @doc Creates an abstract parenthesised expression. The result %% represents "<code>(<em>Body</em>)</code>", independently of the %% context. @@ -6215,9 +5719,7 @@ revert_parentheses(Node) -> %% ===================================================================== -%% @spec parentheses_body(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the body subtree of a <code>parentheses</code> node. +%% @doc Returns the body subtree of a `parentheses' node. %% %% @see parentheses/1 @@ -6228,7 +5730,6 @@ parentheses_body(Node) -> %% ===================================================================== -%% @spec macro(Name) -> syntaxTree() %% @equiv macro(Name, none) -spec macro(syntaxTree()) -> syntaxTree(). @@ -6238,25 +5739,22 @@ macro(Name) -> %% ===================================================================== -%% @spec macro(Name::syntaxTree(), Arguments) -> syntaxTree() -%% Arguments = none | [syntaxTree()] -%% -%% @doc Creates an abstract macro application. If <code>Arguments</code> -%% is <code>none</code>, the result represents -%% "<code>?<em>Name</em></code>", otherwise, if <code>Arguments</code> -%% is <code>[A1, ..., An]</code>, the result represents +%% @doc Creates an abstract macro application. If `Arguments' +%% is `none', the result represents +%% "<code>?<em>Name</em></code>", otherwise, if `Arguments' +%% is `[A1, ..., An]', the result represents %% "<code>?<em>Name</em>(<em>A1</em>, ..., <em>An</em>)</code>". %% -%% <p>Notes: if <code>Arguments</code> is the empty list, the result +%% Notes: if `Arguments' is the empty list, the result %% will thus represent "<code>?<em>Name</em>()</code>", including a pair -%% of matching parentheses.</p> +%% of matching parentheses. %% -%% <p>The only syntactical limitation imposed by the preprocessor on the +%% The only syntactical limitation imposed by the preprocessor on the %% arguments to a macro application (viewed as sequences of tokens) is %% that they must be balanced with respect to parentheses, brackets, -%% <code>begin ... end</code>, <code>case ... end</code>, etc. The -%% <code>text</code> node type can be used to represent arguments which -%% are not regular Erlang constructs.</p> +%% `begin ... end', `case ... end', etc. The +%% `text' node type can be used to represent arguments which +%% are not regular Erlang constructs. %% %% @see macro_name/1 %% @see macro_arguments/1 @@ -6278,9 +5776,7 @@ macro(Name, Arguments) -> %% ===================================================================== -%% @spec macro_name(syntaxTree()) -> syntaxTree() -%% -%% @doc Returns the name subtree of a <code>macro</code> node. +%% @doc Returns the name subtree of a `macro' node. %% %% @see macro/2 @@ -6291,14 +5787,12 @@ macro_name(Node) -> %% ===================================================================== -%% @spec macro_arguments(Node::syntaxTree()) -> none | [syntaxTree()] -%% -%% @doc Returns the list of argument subtrees of a <code>macro</code> -%% node, if any. If <code>Node</code> represents -%% "<code>?<em>Name</em></code>", <code>none</code> is returned. -%% Otherwise, if <code>Node</code> represents +%% @doc Returns the list of argument subtrees of a `macro' +%% node, if any. If `Node' represents +%% "<code>?<em>Name</em></code>", `none' is returned. +%% Otherwise, if `Node' represents %% "<code>?<em>Name</em>(<em>A1</em>, ..., <em>An</em>)</code>", -%% <code>[A1, ..., An]</code> is returned. +%% `[A1, ..., An]' is returned. %% %% @see macro/2 @@ -6309,15 +5803,13 @@ macro_arguments(Node) -> %% ===================================================================== -%% @spec abstract(Term::term()) -> syntaxTree() -%% %% @doc Returns the syntax tree corresponding to an Erlang term. -%% <code>Term</code> must be a literal term, i.e., one that can be +%% `Term' must be a literal term, i.e., one that can be %% represented as a source code literal. Thus, it may not contain a %% process identifier, port, reference, binary or function value as a %% subterm. The function recognises printable strings, in order to get a %% compact and readable representation. Evaluation fails with reason -%% <code>badarg</code> if <code>Term</code> is not a literal term. +%% `badarg' if `Term' is not a literal term. %% %% @see concrete/1 %% @see is_literal/1 @@ -6367,19 +5859,17 @@ abstract_tail(H, T) -> %% ===================================================================== -%% @spec concrete(Node::syntaxTree()) -> term() -%% %% @doc Returns the Erlang term represented by a syntax tree. Evaluation -%% fails with reason <code>badarg</code> if <code>Node</code> does not +%% fails with reason `badarg' if `Node' does not %% represent a literal term. %% -%% <p>Note: Currently, the set of syntax trees which have a concrete +%% Note: Currently, the set of syntax trees which have a concrete %% representation is larger than the set of trees which can be built -%% using the function <code>abstract/1</code>. An abstract character -%% will be concretised as an integer, while <code>abstract/1</code> does +%% using the function {@link abstract/1}. An abstract character +%% will be concretised as an integer, while {@link abstract/1} does %% not at present yield an abstract character for any input. (Use the -%% <code>char/1</code> function to explicitly create an abstract -%% character.)</p> +%% {@link char/1} function to explicitly create an abstract +%% character.) %% %% @see abstract/1 %% @see is_literal/1 @@ -6422,7 +5912,7 @@ concrete(Node) -> {value, concrete(F), []} end, [], true), B; - _ -> + _ -> erlang:error({badarg, Node}) end. @@ -6433,12 +5923,10 @@ concrete_list([]) -> %% ===================================================================== -%% @spec is_literal(Node::syntaxTree()) -> boolean() -%% -%% @doc Returns <code>true</code> if <code>Node</code> represents a -%% literal term, otherwise <code>false</code>. This function returns -%% <code>true</code> if and only if the value of -%% <code>concrete(Node)</code> is defined. +%% @doc Returns `true' if `Node' represents a +%% literal term, otherwise `false'. This function returns +%% `true' if and only if the value of +%% `concrete(Node)' is defined. %% %% @see abstract/1 %% @see concrete/1 @@ -6469,21 +5957,19 @@ is_literal(T) -> %% ===================================================================== -%% @spec revert(Tree::syntaxTree()) -> syntaxTree() -%% -%% @doc Returns an <code>erl_parse</code>-compatible representation of a -%% syntax tree, if possible. If <code>Tree</code> represents a +%% @doc Returns an `erl_parse'-compatible representation of a +%% syntax tree, if possible. If `Tree' represents a %% well-formed Erlang program or expression, the conversion should work -%% without problems. Typically, <code>is_tree/1</code> yields -%% <code>true</code> if conversion failed (i.e., the result is still an -%% abstract syntax tree), and <code>false</code> otherwise. +%% without problems. Typically, {@link is_tree/1} yields +%% `true' if conversion failed (i.e., the result is still an +%% abstract syntax tree), and `false' otherwise. %% -%% <p>The <code>is_tree/1</code> test is not completely foolproof. For a -%% few special node types (e.g. <code>arity_qualifier</code>), if such a +%% The {@link is_tree/1} test is not completely foolproof. For a +%% few special node types (e.g. `arity_qualifier'), if such a %% node occurs in a context where it is not expected, it will be left %% unchanged as a non-reverted subtree of the result. This can only -%% happen if <code>Tree</code> does not actually represent legal Erlang -%% code.</p> +%% happen if `Tree' does not actually represent legal Erlang +%% code. %% %% @see revert_forms/1 %% @see //stdlib/erl_parse @@ -6493,9 +5979,13 @@ is_literal(T) -> revert(Node) -> case is_tree(Node) of false -> - %% Just remove any wrapper. `erl_parse' nodes never contain - %% abstract syntax tree nodes as subtrees. - unwrap(Node); + %% Just remove any wrapper and copy the position. `erl_parse' + %% nodes never contain abstract syntax tree nodes as subtrees. + case unwrap(Node) of + {error, Info} -> {error, setelement(1,Info,get_pos(Node))}; + {warning, Info} -> {warning, setelement(1,Info,get_pos(Node))}; + Node1 -> setelement(2,Node1,get_pos(Node)) + end; true -> case is_leaf(Node) of true -> @@ -6615,16 +6105,12 @@ revert_root(Node) -> %% ===================================================================== -%% @spec revert_forms(Forms) -> [erl_parse()] -%% -%% Forms = syntaxTree() | [syntaxTree()] -%% %% @doc Reverts a sequence of Erlang source code forms. The sequence can -%% be given either as a <code>form_list</code> syntax tree (possibly +%% be given either as a `form_list' syntax tree (possibly %% nested), or as a list of "program form" syntax trees. If successful, -%% the corresponding flat list of <code>erl_parse</code>-compatible -%% syntax trees is returned (cf. <code>revert/1</code>). If some program -%% form could not be reverted, <code>{error, Form}</code> is thrown. +%% the corresponding flat list of `erl_parse'-compatible +%% syntax trees is returned (see {@link revert/1}). If some program +%% form could not be reverted, `{error, Form}' is thrown. %% Standalone comments in the form sequence are discarded. %% %% @see revert/1 @@ -6633,10 +6119,10 @@ revert_root(Node) -> -type forms() :: syntaxTree() | [syntaxTree()]. -%% -spec revert_forms(forms()) -> [erl_parse()]. +-spec revert_forms(forms()) -> [erl_parse()]. -revert_forms(L) when is_list(L) -> - revert_forms(form_list(L)); +revert_forms(Forms) when is_list(Forms) -> + revert_forms(form_list(Forms)); revert_forms(T) -> case type(T) of form_list -> @@ -6673,60 +6159,54 @@ revert_forms_1([]) -> %% ===================================================================== -%% @spec subtrees(Node::syntaxTree()) -> [[syntaxTree()]] -%% %% @doc Returns the grouped list of all subtrees of a syntax tree. If -%% <code>Node</code> is a leaf node (cf. <code>is_leaf/1</code>), this +%% `Node' is a leaf node (see {@link is_leaf/1}), this %% is the empty list, otherwise the result is always a nonempty list, -%% containing the lists of subtrees of <code>Node</code>, in +%% containing the lists of subtrees of `Node', in %% left-to-right order as they occur in the printed program text, and %% grouped by category. Often, each group contains only a single %% subtree. %% -%% <p>Depending on the type of <code>Node</code>, the size of some +%% Depending on the type of `Node', the size of some %% groups may be variable (e.g., the group consisting of all the %% elements of a tuple), while others always contain the same number of %% elements - usually exactly one (e.g., the group containing the %% argument expression of a case-expression). Note, however, that the %% exact structure of the returned list (for a given node type) should %% in general not be depended upon, since it might be subject to change -%% without notice.</p> +%% without notice. %% -%% <p>The function <code>subtrees/1</code> and the constructor functions -%% <code>make_tree/2</code> and <code>update_tree/2</code> can be a +%% The function {@link subtrees/1} and the constructor functions +%% {@link make_tree/2} and {@link update_tree/2} can be a %% great help if one wants to traverse a syntax tree, visiting all its %% subtrees, but treat nodes of the tree in a uniform way in most or all %% cases. Using these functions makes this simple, and also assures that %% your code is not overly sensitive to extensions of the syntax tree %% data type, because any node types not explicitly handled by your code -%% can be left to a default case.</p> +%% can be left to a default case. %% -%% <p>For example: -%% <pre> -%% postorder(F, Tree) -> +%% For example: +%% ```postorder(F, Tree) -> %% F(case subtrees(Tree) of %% [] -> Tree; %% List -> update_tree(Tree, %% [[postorder(F, Subtree) %% || Subtree <- Group] %% || Group <- List]) -%% end). -%% </pre> -%% maps the function <code>F</code> on <code>Tree</code> and all its +%% end).''' +%% maps the function `F' on `Tree' and all its %% subtrees, doing a post-order traversal of the syntax tree. (Note the -%% use of <code>update_tree/2</code> to preserve node attributes.) For a +%% use of {@link update_tree/2} to preserve node attributes.) For a %% simple function like: -%% <pre> -%% f(Node) -> +%% ```f(Node) -> %% case type(Node) of %% atom -> atom("a_" ++ atom_name(Node)); %% _ -> Node -%% end. -%% </pre> -%% the call <code>postorder(fun f/1, Tree)</code> will yield a new -%% representation of <code>Tree</code> in which all atom names have been +%% end.''' +%% the call `postorder(fun f/1, Tree)' will yield a new +%% representation of `Tree' in which all atom names have been %% extended with the prefix "a_", but nothing else (including comments, -%% annotations and line numbers) has been changed.</p> +%% annotations and line numbers) has been changed. %% %% @see make_tree/2 %% @see type/1 @@ -6896,12 +6376,9 @@ subtrees(T) -> %% ===================================================================== -%% @spec update_tree(Node::syntaxTree(), Groups::[[syntaxTree()]]) -> -%% syntaxTree() -%% %% @doc Creates a syntax tree with the same type and attributes as the -%% given tree. This is equivalent to <code>copy_attrs(Node, -%% make_tree(type(Node), Groups))</code>. +%% given tree. This is equivalent to `copy_attrs(Node, +%% make_tree(type(Node), Groups))'. %% %% @see make_tree/2 %% @see copy_attrs/2 @@ -6914,23 +6391,20 @@ update_tree(Node, Groups) -> %% ===================================================================== -%% @spec make_tree(Type::atom(), Groups::[[syntaxTree()]]) -> -%% syntaxTree() -%% %% @doc Creates a syntax tree with the given type and subtrees. -%% <code>Type</code> must be a node type name (cf. <code>type/1</code>) -%% that does not denote a leaf node type (cf. <code>is_leaf/1</code>). -%% <code>Groups</code> must be a <em>nonempty</em> list of groups of +%% `Type' must be a node type name (see {@link type/1}) +%% that does not denote a leaf node type (see {@link is_leaf/1}). +%% `Groups' must be a <em>nonempty</em> list of groups of %% syntax trees, representing the subtrees of a node of the given type, %% in left-to-right order as they would occur in the printed program -%% text, grouped by category as done by <code>subtrees/1</code>. +%% text, grouped by category as done by {@link subtrees/1}. %% -%% <p>The result of <code>copy_attrs(Node, make_tree(type(Node), -%% subtrees(Node)))</code> (cf. <code>update_tree/2</code>) represents -%% the same source code text as the original <code>Node</code>, assuming -%% that <code>subtrees(Node)</code> yields a nonempty list. However, it +%% The result of `copy_attrs(Node, make_tree(type(Node), +%% subtrees(Node)))' (see {@link update_tree/2}) represents +%% the same source code text as the original `Node', assuming +%% that `subtrees(Node)' yields a nonempty list. However, it %% does not necessarily have the same data representation as -%% <code>Node</code>.</p> +%% `Node'. %% %% @see update_tree/2 %% @see subtrees/1 @@ -6995,42 +6469,40 @@ make_tree(tuple, [E]) -> tuple(E). %% ===================================================================== -%% @spec meta(Tree::syntaxTree()) -> syntaxTree() -%% %% @doc Creates a meta-representation of a syntax tree. The result %% represents an Erlang expression "<code><em>MetaTree</em></code>" %% which, if evaluated, will yield a new syntax tree representing the -%% same source code text as <code>Tree</code> (although the actual data +%% same source code text as `Tree' (although the actual data %% representation may be different). The expression represented by -%% <code>MetaTree</code> is <em>implementation independent</em> with +%% `MetaTree' is <em>implementation independent</em> with %% regard to the data structures used by the abstract syntax tree -%% implementation. Comments attached to nodes of <code>Tree</code> will +%% implementation. Comments attached to nodes of `Tree' will %% be preserved, but other attributes are lost. %% -%% <p>Any node in <code>Tree</code> whose node type is -%% <code>variable</code> (cf. <code>type/1</code>), and whose list of -%% annotations (cf. <code>get_ann/1</code>) contains the atom -%% <code>meta_var</code>, will remain unchanged in the resulting tree, -%% except that exactly one occurrence of <code>meta_var</code> is -%% removed from its annotation list.</p> +%% Any node in `Tree' whose node type is +%% `variable' (see {@link type/1}), and whose list of +%% annotations (see {@link get_ann/1}) contains the atom +%% `meta_var', will remain unchanged in the resulting tree, +%% except that exactly one occurrence of `meta_var' is +%% removed from its annotation list. %% -%% <p>The main use of the function <code>meta/1</code> is to transform a -%% data structure <code>Tree</code>, which represents a piece of program +%% The main use of the function `meta/1' is to transform a +%% data structure `Tree', which represents a piece of program %% code, into a form that is <em>representation independent when -%% printed</em>. E.g., suppose <code>Tree</code> represents a variable -%% named "V". Then (assuming a function <code>print/1</code> for -%% printing syntax trees), evaluating <code>print(abstract(Tree))</code> -%% - simply using <code>abstract/1</code> to map the actual data +%% printed</em>. E.g., suppose `Tree' represents a variable +%% named "V". Then (assuming a function `print/1' for +%% printing syntax trees), evaluating `print(abstract(Tree))' +%% - simply using {@link abstract/1} to map the actual data %% structure onto a syntax tree representation - would output a string -%% that might look something like "<code>{tree, variable, ..., "V", -%% ...}</code>", which is obviously dependent on the implementation of +%% that might look something like "`{tree, variable, ..., "V", +%% ...}'", which is obviously dependent on the implementation of %% the abstract syntax trees. This could e.g. be useful for caching a %% syntax tree in a file. However, in some situations like in a program %% generator generator (with two "generator"), it may be unacceptable. -%% Using <code>print(meta(Tree))</code> instead would output a +%% Using `print(meta(Tree))' instead would output a %% <em>representation independent</em> syntax tree generating %% expression; in the above case, something like -%% "<code>erl_syntax:variable("V")</code>".</p> +%% "`erl_syntax:variable("V")'". %% %% @see abstract/1 %% @see type/1 @@ -7161,60 +6633,56 @@ meta_call(F, As) -> %% ===================================================================== -%% @spec tree(Type) -> syntaxTree() %% @equiv tree(Type, []) --spec tree(atom()) -> syntaxTree(). +-spec tree(atom()) -> #tree{}. tree(Type) -> tree(Type, []). %% ===================================================================== -%% @spec tree(Type::atom(), Data::term()) -> syntaxTree() -%% %% @doc <em>For special purposes only</em>. Creates an abstract syntax -%% tree node with type tag <code>Type</code> and associated data -%% <code>Data</code>. +%% tree node with type tag `Type' and associated data +%% `Data'. %% -%% <p>This function and the related <code>is_tree/1</code> and -%% <code>data/1</code> provide a uniform way to extend the set of -%% <code>erl_parse</code> node types. The associated data is any term, -%% whose format may depend on the type tag.</p> +%% This function and the related {@link is_tree/1} and +%% {@link data/1} provide a uniform way to extend the set of +%% `erl_parse' node types. The associated data is any term, +%% whose format may depend on the type tag. %% -%% <h4>Notes:</h4> +%% === Notes: === %% <ul> %% <li>Any nodes created outside of this module must have type tags %% distinct from those currently defined by this module; see -%% <code>type/1</code> for a complete list.</li> +%% {@link type/1} for a complete list.</li> %% <li>The type tag of a syntax tree node may also be used -%% as a primary tag by the <code>erl_parse</code> representation; +%% as a primary tag by the `erl_parse' representation; %% in that case, the selector functions for that node type %% <em>must</em> handle both the abstract syntax tree and the -%% <code>erl_parse</code> form. The function <code>type(T)</code> +%% `erl_parse' form. The function `type(T)' %% should return the correct type tag regardless of the -%% representation of <code>T</code>, so that the user sees no -%% difference between <code>erl_syntax</code> and -%% <code>erl_parse</code> nodes.</li> +%% representation of `T', so that the user sees no +%% difference between `erl_syntax' and +%% `erl_parse' nodes.</li> %% </ul> +%% %% @see is_tree/1 %% @see data/1 %% @see type/1 --spec tree(atom(), term()) -> syntaxTree(). +-spec tree(atom(), term()) -> #tree{}. tree(Type, Data) -> #tree{type = Type, data = Data}. %% ===================================================================== -%% @spec is_tree(Tree::syntaxTree()) -> boolean() -%% -%% @doc <em>For special purposes only</em>. Returns <code>true</code> if -%% <code>Tree</code> is an abstract syntax tree and <code>false</code> +%% @doc <em>For special purposes only</em>. Returns `true' if +%% `Tree' is an abstract syntax tree and `false' %% otherwise. %% -%% <p><em>Note</em>: this function yields <code>false</code> for all -%% "old-style" <code>erl_parse</code>-compatible "parse trees".</p> +%% <em>Note</em>: this function yields `false' for all +%% "old-style" `erl_parse'-compatible "parse trees". %% %% @see tree/2 @@ -7227,12 +6695,10 @@ is_tree(_) -> %% ===================================================================== -%% @spec data(Tree::syntaxTree()) -> term() -%% %% @doc <em>For special purposes only</em>. Returns the associated data %% of a syntax tree node. Evaluation fails with reason -%% <code>badarg</code> if <code>is_tree(Node)</code> does not yield -%% <code>true</code>. +%% `badarg' if `is_tree(Node)' does not yield +%% `true'. %% %% @see tree/2 @@ -7248,26 +6714,19 @@ data(T) -> erlang:error({badarg, T}). %% ===================================================================== -%% @spec wrap(Node::erl_parse()) -> syntaxTree() -%% -%% @type erl_parse() = erl_parse:parse_tree(). The "parse tree" -%% representation built by the Erlang standard library parser -%% <code>erl_parse</code>. This is a subset of the -%% <a href="#type-syntaxTree"><code>syntaxTree</code></a> type. -%% -%% @doc Creates a wrapper structure around an <code>erl_parse</code> +%% @doc Creates a wrapper structure around an `erl_parse' %% "parse tree". %% -%% <p>This function and the related <code>unwrap/1</code> and -%% <code>is_wrapper/1</code> provide a uniform way to attach arbitrary -%% information to an <code>erl_parse</code> tree. Some information about +%% This function and the related {@link unwrap/1} and +%% {@link is_wrapper/1} provide a uniform way to attach arbitrary +%% information to an `erl_parse' tree. Some information about %% the encapsuled tree may be cached in the wrapper, such as the node %% type. All functions on syntax trees must behave so that the user sees -%% no difference between wrapped and non-wrapped <code>erl_parse</code> +%% no difference between wrapped and non-wrapped `erl_parse' %% trees. <em>Attaching a wrapper onto another wrapper structure is an -%% error</em>.</p> +%% error</em>. -%%-spec wrap(erl_parse:parse_tree()) -> syntaxTree(). +-spec wrap(erl_parse()) -> #wrapper{}. wrap(Node) -> %% We assume that Node is an old-school `erl_parse' tree. @@ -7276,24 +6735,20 @@ wrap(Node) -> %% ===================================================================== -%% @spec unwrap(Node::syntaxTree()) -> syntaxTree() -%% -%% @doc Removes any wrapper structure, if present. If <code>Node</code> +%% @doc Removes any wrapper structure, if present. If `Node' %% is a wrapper structure, this function returns the wrapped -%% <code>erl_parse</code> tree; otherwise it returns <code>Node</code> +%% `erl_parse' tree; otherwise it returns `Node' %% itself. --spec unwrap(syntaxTree()) -> syntaxTree(). +-spec unwrap(syntaxTree()) -> #tree{} | erl_parse(). unwrap(#wrapper{tree = Node}) -> Node; unwrap(Node) -> Node. % This could also be a new-form node. %% ===================================================================== -%% @spec is_wrapper(Term::term()) -> boolean() -%% -%% @doc Returns <code>true</code> if the argument is a wrapper -%% structure, otherwise <code>false</code>. +%% @doc Returns `true' if the argument is a wrapper +%% structure, otherwise `false'. -ifndef(NO_UNUSED). -spec is_wrapper(term()) -> boolean(). diff --git a/lib/syntax_tools/src/erl_syntax_lib.erl b/lib/syntax_tools/src/erl_syntax_lib.erl index 97dfbfd7cd..36cd35f15d 100644 --- a/lib/syntax_tools/src/erl_syntax_lib.erl +++ b/lib/syntax_tools/src/erl_syntax_lib.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1997-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/erl_tidy.erl b/lib/syntax_tools/src/erl_tidy.erl index 09efc9c392..59cf6c0a92 100644 --- a/lib/syntax_tools/src/erl_tidy.erl +++ b/lib/syntax_tools/src/erl_tidy.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1999-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/igor.erl b/lib/syntax_tools/src/igor.erl index aa933eb54b..37e561cbbe 100644 --- a/lib/syntax_tools/src/igor.erl +++ b/lib/syntax_tools/src/igor.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 1998-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/src/prettypr.erl b/lib/syntax_tools/src/prettypr.erl index c13fa30998..1b5ba6b05a 100644 --- a/lib/syntax_tools/src/prettypr.erl +++ b/lib/syntax_tools/src/prettypr.erl @@ -14,10 +14,8 @@ %% Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 %% USA %% -%% $Id$ -%% %% @copyright 2000-2006 Richard Carlsson -%% @author Richard Carlsson <[email protected]> +%% @author Richard Carlsson <[email protected]> %% @end %% ===================================================================== diff --git a/lib/syntax_tools/test/Makefile b/lib/syntax_tools/test/Makefile index 3e31bdbd50..d4733b9a42 100644 --- a/lib/syntax_tools/test/Makefile +++ b/lib/syntax_tools/test/Makefile @@ -57,9 +57,9 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) syntax_tools.spec syntax_tools.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) syntax_tools.spec syntax_tools.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/syntax_tools/vsn.mk b/lib/syntax_tools/vsn.mk index 962492befd..8f774c5d75 100644 --- a/lib/syntax_tools/vsn.mk +++ b/lib/syntax_tools/vsn.mk @@ -1 +1 @@ -SYNTAX_TOOLS_VSN = 1.6.7.2 +SYNTAX_TOOLS_VSN = 1.6.9 diff --git a/lib/test_server/doc/src/Makefile b/lib/test_server/doc/src/Makefile index b32f3d3c59..3ce549f0e1 100644 --- a/lib/test_server/doc/src/Makefile +++ b/lib/test_server/doc/src/Makefile @@ -119,16 +119,16 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(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 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(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" release_spec: diff --git a/lib/test_server/doc/src/notes.xml b/lib/test_server/doc/src/notes.xml index d90ad2c4ed..3701066e56 100644 --- a/lib/test_server/doc/src/notes.xml +++ b/lib/test_server/doc/src/notes.xml @@ -32,6 +32,155 @@ <file>notes.xml</file> </header> +<section><title>Test_Server 3.5.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + After a test case timeout or abortion, the + end_per_testcase function executes on a new dedicated + process. The group leader for this process should be set + to the IO server for the test case, which was not done + properly. The result of this error was that no warnings + about end_per_testcase failing or timing out were ever + printed in the test case log. Also, help functions such + as e.g. test_server:stop_node/1, attempting to + synchronize with the IO server, would hang. The fault has + been corrected.</p> + <p> + Own Id: OTP-9666</p> + </item> + <item> + <p> + A deadlock situation could occur if Common Test is + forwarding error_handler printouts to Test Server at the + same time a new test case is starting. This error has + been fixed.</p> + <p> + Own Id: OTP-9894</p> + </item> + <item> + <p> + When a test case was killed because of a timetrap + timeout, the current location (suite, case and line) was + not printed correctly in the log files. This has been + corrected.</p> + <p> + Own Id: OTP-9930 Aux Id: seq12002 </p> + </item> + <item> + <p> + Test Server and Common Test would add new error handlers + with each test run and fail to remove previously added + ones. In the case of Test Server, this would only happen + if SASL was not running on the test node. This has been + fixed.</p> + <p> + Own Id: OTP-9941 Aux Id: seq12009 </p> + </item> + <item> + <p> + If a test case process was terminated due to an exit + signal from a linked process, Test Server failed to + report the correct name of the suite and case to the + framework. This has been corrected.</p> + <p> + Own Id: OTP-9958 Aux Id: OTP-9855 </p> + </item> + </list> + </section> + + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + A new optional feature has been introduced that enables + Common Test to generate priv_dir directory names that are + unique for each test case or config function. The name of + the option/flag is 'create_priv_dir' and it can be set to + value 'auto_per_run' (which is the default, existing, + behaviour), or 'auto_per_tc' or 'manual_per_tc'. If + 'auto_per_tc' is used, Test Server creates a dedicated + priv_dir automatically for each test case (which can be + very expensive in case of many and/or repeated cases). If + 'manual_per_tc' is used, the user needs to create the + priv_dir explicitly by calling the new function + ct:make_priv_dir/0.</p> + <p> + Own Id: OTP-9659 Aux Id: seq11930 </p> + </item> + <item> + <p> + A column for test case group name has been added to the + suite overview HTML log file.</p> + <p> + Own Id: OTP-9730 Aux Id: seq11952 </p> + </item> + <item> + <p> + It is now possible to use the post_end_per_testcase CT + hook function to print a comment for a test case in the + overview log file, even if the test case gets killed by a + timetrap or unknown exit signal, or if the + end_per_testcase function times out.</p> + <p> + Own Id: OTP-9855 Aux Id: seq11979 </p> + </item> + <item> + <p> + Common Test will now print error information (with a time + stamp) in the test case log file immediately when a test + case fails. This makes it easier to see when, in time, + the fault actually occured, and aid the job of locating + relevant trace and debug printouts in the log.</p> + <p> + Own Id: OTP-9904 Aux Id: seq11985, OTP-9900 </p> + </item> + <item> + <p> + Test Server has been modified to check the SASL + errlog_type parameter when receiving an error logger + event, so that it doesn't print reports of type that the + user has disabled.</p> + <p> + Own Id: OTP-9955 Aux Id: seq12013 </p> + </item> + <item> + <p> + If an application cannot be found by ts it is + automatically skipped when testing.</p> + <p> + Own Id: OTP-9971</p> + </item> + <item> + <p> + By specifying a user defined function ({M,F,A} or fun) as + timetrap value, either by means of an info function or by + calling ct:timetrap/1, it is now possible to set a + timetrap that will be triggered when the user function + returns.</p> + <p> + Own Id: OTP-9988 Aux Id: OTP-9501, seq11894 </p> + </item> + <item> + <p> + If the optional configuration functions init_per_suite/1 + and end_per_suite/1 are not implemented in the test + suite, local Common Test versions of these functions are + called instead, and will be displayed in the overview log + file. Any printouts made by the pre- or + post_init_per_suite and pre- or post_end_per_suite hook + functions are saved in the log files for these functions.</p> + <p> + Own Id: OTP-9992</p> + </item> + </list> + </section> + +</section> + <section><title>Test_Server 3.5</title> <section><title>Improvements and New Features</title> diff --git a/lib/test_server/src/Makefile b/lib/test_server/src/Makefile index 4bc51873c2..a75855eaab 100644 --- a/lib/test_server/src/Makefile +++ b/lib/test_server/src/Makefile @@ -124,22 +124,22 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DATA) $(INTERNAL_HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(INTERNAL_HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_tests_spec: opt - $(INSTALL_DIR) $(RELEASE_PATH)/test_server + $(INSTALL_DIR) "$(RELEASE_PATH)/test_server" $(INSTALL_DATA) $(ERL_FILES) $(TS_ERL_FILES) \ $(HRL_FILES) $(INTERNAL_HRL_FILES) $(TS_HRL_FILES) \ $(TS_TARGET_FILES) \ $(AUTOCONF_FILES) $(C_FILES) $(COVER_FILES) $(CONFIG) \ - $(RELEASE_PATH)/test_server - $(INSTALL_SCRIPT) $(PROGRAMS) $(RELEASE_PATH)/test_server + "$(RELEASE_PATH)/test_server" + $(INSTALL_SCRIPT) $(PROGRAMS) "$(RELEASE_PATH)/test_server" release_docs_spec: diff --git a/lib/test_server/src/configure.in b/lib/test_server/src/configure.in index 097853bcfc..77bc993ccd 100644 --- a/lib/test_server/src/configure.in +++ b/lib/test_server/src/configure.in @@ -108,7 +108,7 @@ AC_CHECK_HEADER(poll.h, AC_DEFINE(HAVE_POLL_H)) # for the system. AC_MSG_CHECKING([system version (for dynamic loading)]) -system=`uname -s`-`uname -r` +system=`./config.sub $host` AC_MSG_RESULT($system) # Step 2: check for existence of -ldl library. This is needed because @@ -119,10 +119,9 @@ AC_CHECK_LIB(dl, dlopen, have_dl=yes, have_dl=no) # Step 3: set configuration options based on system name and version. SHLIB_LDLIBS= - fullSrcDir=`cd $srcdir; pwd` case $system in - Linux*) + *-linux-*) SHLIB_CFLAGS="-fPIC" SHLIB_SUFFIX=".so" if test "$have_dl" = yes; then @@ -136,7 +135,7 @@ case $system in fi SHLIB_EXTRACT_ALL="" ;; - NetBSD-*|FreeBSD-*|OpenBSD-*|DragonFly*) + *-netbsd*|*-freebsd*|*-openbsd*|*-dragonfly*) # Not available on all versions: check for include file. AC_CHECK_HEADER(dlfcn.h, [ SHLIB_CFLAGS="-fpic" @@ -153,28 +152,21 @@ case $system in ]) SHLIB_EXTRACT_ALL="" ;; - SunOS-4*) - SHLIB_CFLAGS="-PIC" - SHLIB_LD="ld" - SHLIB_LDFLAGS="$LDFLAGS" - SHLIB_SUFFIX=".so" - SHLIB_EXTRACT_ALL="" - ;; - SunOS-5*|UNIX_SV-4.2*) + *-solaris2*|*-sysv4*) SHLIB_CFLAGS="-KPIC" SHLIB_LD="/usr/ccs/bin/ld" SHLIB_LDFLAGS="$LDFLAGS -G -z text" SHLIB_SUFFIX=".so" SHLIB_EXTRACT_ALL="-z allextract" ;; - Darwin*) + *darwin*) SHLIB_CFLAGS="-fno-common" SHLIB_LD="cc" SHLIB_LDFLAGS="$LDFLAGS -bundle -flat_namespace -undefined suppress" SHLIB_SUFFIX=".so" SHLIB_EXTRACT_ALL="" ;; - OSF1*) + *osf1*) SHLIB_CFLAGS="-fPIC" SHLIB_LD="ld" SHLIB_LDFLAGS="$LDFLAGS -shared" @@ -206,19 +198,19 @@ esac if test "$CC" = "gcc" -o `$CC -v 2>&1 | grep -c gcc` != "0" ; then case $system in - AIX-*) + *-aix) ;; - BSD/OS*) + *-bsd*) ;; - IRIX*) + *-irix) ;; - NetBSD-*|FreeBSD-*|OpenBSD-*) + *-netbsd|*-freebsd|*-openbsd) ;; - RISCos-*) + *-riscos) ;; - ULTRIX-4.*) + *ultrix4.*) ;; - Darwin*) + *darwin*) ;; *) SHLIB_CFLAGS="-fPIC" diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl index d7ce432786..17c5f5b253 100644 --- a/lib/test_server/src/test_server.erl +++ b/lib/test_server/src/test_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -35,6 +35,7 @@ -export([fail/0,fail/1,format/1,format/2,format/3]). -export([capture_start/0,capture_stop/0,capture_get/0]). -export([messages_get/0]). +-export([permit_io/2]). -export([hours/1,minutes/1,seconds/1,sleep/1,adjusted_sleep/1,timecall/3]). -export([timetrap_scale_factor/0,timetrap/1,get_timetrap_info/0, timetrap_cancel/1,timetrap_cancel/0]). @@ -49,7 +50,7 @@ -export([run_on_shielded_node/2]). -export([is_cover/0,is_debug/0,is_commercial/0]). --export([break/1,continue/0]). +-export([break/1,break/2,break/3,continue/0,continue/1]). %%% DEBUGGER INTERFACE %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -export([purify_new_leaks/0, purify_format/2, purify_new_fds_inuse/0, @@ -523,7 +524,7 @@ stick_all_sticky(Node,Sticky) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData) -> +%% run_test_case_apply(Mod,Func,Args,Name,RunInit,TimetrapData,RejectIoReqs) -> %% {Time,Value,Loc,Opts,Comment} | {died,Reason,unknown,Comment} %% %% Time = float() (seconds) @@ -558,8 +559,12 @@ stick_all_sticky(Node,Sticky) -> %% ScaleTimetrap indicates if test_server should attemp to automatically %% compensate timetraps for runtime delays introduced by e.g. tools like %% cover. +%% +%% RejectIoReqs (bool) is information about whether printouts to stdout +%% should be visible in the minor log file or not. -run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) -> +run_test_case_apply({CaseNum,Mod,Func,Args,Name, + RunInit,TimetrapData,RejectIoReqs}) -> purify_format("Test case #~w ~w:~w/1", [CaseNum, Mod, Func]), case os:getenv("TS_RUN_VALGRIND") of false -> @@ -570,17 +575,19 @@ run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit,TimetrapData}) -> end, test_server_h:testcase({Mod,Func,1}), ProcBef = erlang:system_info(process_count), - Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData), + Result = run_test_case_apply(Mod, Func, Args, Name, RunInit, + TimetrapData, RejectIoReqs), ProcAft = erlang:system_info(process_count), purify_new_leaks(), DetFail = get(test_server_detected_fail), {Result,DetFail,ProcBef,ProcAft}. -run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> +run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData, RejectIoReqs) -> case get(test_server_job_dir) of undefined -> %% i'm a local target - do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData); + do_run_test_case_apply(Mod, Func, Args, Name, RunInit, + TimetrapData, RejectIoReqs); JobDir -> %% i'm a remote target case Args of @@ -595,13 +602,14 @@ run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> Config2 = lists:keyreplace(priv_dir, 1, Config1, {priv_dir,TargetPrivDir}), do_run_test_case_apply(Mod, Func, [Config2], Name, RunInit, - TimetrapData); + TimetrapData, RejectIoReqs); _other -> do_run_test_case_apply(Mod, Func, Args, Name, RunInit, - TimetrapData) + TimetrapData, RejectIoReqs) end end. -do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> +do_run_test_case_apply(Mod, Func, Args, Name, RunInit, + TimetrapData, RejectIoReqs) -> {ok,Cwd} = file:get_cwd(), Args2Print = case Args of [Args1] when is_list(Args1) -> @@ -628,7 +636,8 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> end), group_leader(OldGLeader, self()), put(test_server_detected_fail, []), - run_test_case_msgloop(Ref, Pid, false, false, "", undefined). + run_test_case_msgloop(Ref, Pid, false, RejectIoReqs, false, "", + undefined, starting). %% Ugly bug (pre R5A): %% If this process (group leader of the test case) terminates before @@ -639,19 +648,37 @@ do_run_test_case_apply(Mod, Func, Args, Name, RunInit, TimetrapData) -> %% A test case is known to have failed if it returns {'EXIT', _} tuple, %% or sends a message {failed, File, Line} to it's group_leader %% -run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> +run_test_case_msgloop(Ref, Pid, CaptureStdout, RejectIoReqs, Terminate, + Comment, CurrConf, Status) -> %% NOTE: Keep job_proxy_msgloop/0 up to date when changes %% are made in this function! {Timeout,ReturnValue} = case Terminate of {true, ReturnVal} -> + %% stop any timetrap timers for the test case + %% that have been started by this process + timetrap_cancel_all(Pid, false), {20, ReturnVal}; false -> {infinity, should_never_appear} end, receive + {test_case_initialized,Pid} -> + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,running); + Abort = {abort_current_testcase,_,_} when Status == starting -> + %% we're in init phase, must must postpone this operation + %% until test case execution is in progress (or FW:init_tc + %% gets killed) + self() ! Abort, + erlang:yield(), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {abort_current_testcase,Reason,From} -> - Line = get_loc(Pid), + Line = case is_process_alive(Pid) of + true -> get_loc(Pid); + false -> unknown + end, Mon = erlang:monitor(process, Pid), exit(Pid,{testcase_aborted,Reason,Line}), erlang:yield(), @@ -665,76 +692,104 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> exit(Pid, kill), %% here's the only place we know Reason, so we save %% it as a comment, potentially replacing user data - Error = lists:flatten(io_lib:format("Aborted: ~p",[Reason])), + Error = lists:flatten(io_lib:format("Aborted: ~p", + [Reason])), Error1 = lists:flatten([string:strip(S,left) || - S <- string:tokens(Error,[$\n])]), + S <- string:tokens(Error, + [$\n])]), if length(Error1) > 63 -> string:substr(Error1,1,60) ++ "..."; true -> Error1 end end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,NewComment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + NewComment,CurrConf,Status); + {permit_io,FromPid} -> + put({permit_io,FromPid},true), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}} when is_list(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,io_lib,Func,[Format,Args]}} when is_atom(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,Bytes}} -> - run_test_case_msgloop_io( - ReplyAs,CaptureStdout,Bytes,From,put_chars), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Bytes,From,put_chars), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}} when is_list(Format) -> Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}} when is_list(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,unicode,io_lib,Func,[Format,Args]}} when is_atom(Format) -> Msg = unicode_to_latin1(catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,latin1,io_lib,Func,[Format,Args]}} when is_atom(Format) -> Msg = (catch io_lib:Func(Format,Args)), - run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,unicode,Bytes}} -> - run_test_case_msgloop_io( - ReplyAs,CaptureStdout,unicode_to_latin1(Bytes),From,put_chars), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + unicode_to_latin1(Bytes),From,put_chars), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {io_request,From,ReplyAs,{put_chars,latin1,Bytes}} -> - run_test_case_msgloop_io( - ReplyAs,CaptureStdout,Bytes,From,put_chars), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Bytes,From,put_chars), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); IoReq when element(1, IoReq) == io_request -> %% something else, just pass it on group_leader() ! IoReq, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {structured_io,ClientPid,Msg} -> output(Msg, ClientPid), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {capture,NewCapture} -> - run_test_case_msgloop(Ref,Pid,NewCapture,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,NewCapture,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {sync_apply,From,MFA} -> sync_local_or_remote_apply(false,From,MFA), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {sync_apply_proxy,Proxy,From,MFA} -> sync_local_or_remote_apply(Proxy,From,MFA), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {printout,Detail,Format,Args} -> print(Detail,Format,Args), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {comment,NewComment} -> NewComment1 = test_server_ctrl:to_string(NewComment), NewComment2 = test_server_sup:framework_call(format_comment, @@ -747,15 +802,20 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> Other -> Other end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate1,NewComment2,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate1, + NewComment2,CurrConf,Status); {read_comment,From} -> From ! {self(),read_comment,Comment}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {set_curr_conf,From,NewCurrConf} -> From ! {self(),set_curr_conf,ok}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,NewCurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,NewCurrConf,Status); {make_priv_dir,From} when CurrConf == undefined -> - From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}}; + From ! {self(),make_priv_dir,{error,no_priv_dir_in_config}}, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {make_priv_dir,From} -> Result = case proplists:get_value(priv_dir, element(2, CurrConf)) of @@ -772,10 +832,12 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> end end, From ! {self(),make_priv_dir,Result}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs,Terminate, + Comment,CurrConf,Status); {'EXIT',Pid,{Ref,Time,Value,Loc,Opts}} -> RetVal = {Time/1000000,Value,mod_loc(Loc),Opts,Comment}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},Comment,undefined); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + {true,RetVal},Comment,undefined,Status); {'EXIT',Pid,Reason} -> case Reason of {timetrap_timeout,TVal,Loc} -> @@ -786,36 +848,44 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> spawn_fw_call(FwMod,FwFunc,CurrConf,Pid, {framework_error,{timetrap,TVal}}, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,undefined); + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, + Terminate,Comment, + undefined,Status); Loc1 -> %% call end_per_testcase on a separate process, - %% only so that the user has a chance to clean up - %% after init_per_testcase, even after a timetrap timeout + %% only so that the user has a chance to + %% clean up after init_per_testcase, even after + %% a timetrap timeout NewCurrConf = case CurrConf of {{Mod,Func},Conf} -> EndConfPid = - call_end_conf(Mod,Func,Pid, - {timetrap_timeout,TVal}, - Loc1,[{tc_status, - {failed, - timetrap_timeout}}|Conf], - TVal), + call_end_conf( + Mod,Func,Pid, + {timetrap_timeout,TVal}, + Loc1,[{tc_status, + {failed, + timetrap_timeout}}|Conf], + TVal), {EndConfPid,{Mod,Func},Conf}; _ -> {Mod,Func} = get_mf(Loc1), - %% The framework functions mustn't execute on this - %% group leader process or io will cause deadlock, - %% so we spawn a dedicated process for the operation - %% and let the group leader go back to handle io. + %% The framework functions mustn't + %% execute on this group leader process + %% or io will cause deadlock, so we + %% spawn a dedicated process for the + %% operation and let the group leader + %% go back to handle io. spawn_fw_call(Mod,Func,CurrConf,Pid, {timetrap_timeout,TVal}, Loc1,self()), undefined end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,NewCurrConf) + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, + Terminate,Comment, + NewCurrConf,Status) end; {timetrap_timeout,TVal,Loc,InitOrEnd} -> case mod_loc(Loc) of @@ -830,7 +900,18 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> {timetrap_timeout,TVal}, Loc1,self()) end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); + {testcase_aborted,ErrorMsg={user_timetrap_error,_},AbortLoc} -> + %% user timetrap function caused exit + %% during start of test case + {Mod,Func} = get_mf(mod_loc(AbortLoc)), + spawn_fw_call(Mod,Func,CurrConf,Pid, + ErrorMsg,unknown,self()), + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, + Terminate,Comment, + undefined,Status); {testcase_aborted,AbortReason,AbortLoc} -> ErrorMsg = {testcase_aborted,AbortReason}, case mod_loc(AbortLoc) of @@ -839,33 +920,43 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> spawn_fw_call(FwMod,FwFunc,CurrConf,Pid, {framework_error,ErrorMsg}, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,undefined); + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, + Terminate,Comment, + undefined,Status); Loc1 -> - %% call end_per_testcase on a separate process, only so - %% that the user has a chance to clean up after init_per_testcase, - %% even after abortion + %% call end_per_testcase on a separate process, + %% only so that the user has a chance to clean up + %% after init_per_testcase, even after abortion NewCurrConf = case CurrConf of {{Mod,Func},Conf} -> - TVal = case lists:keysearch(default_timeout,1,Conf) of - {value,{default_timeout,Tmo}} -> Tmo; - _ -> ?DEFAULT_TIMETRAP_SECS*1000 - end, + TVal = + case lists:keysearch(default_timeout, + 1, + Conf) of + {value,{default_timeout,Tmo}} -> + Tmo; + _ -> + ?DEFAULT_TIMETRAP_SECS*1000 + end, EndConfPid = - call_end_conf(Mod,Func,Pid,ErrorMsg, - Loc1, - [{tc_status,{failed,ErrorMsg}}|Conf], - TVal), + call_end_conf( + Mod,Func,Pid, + ErrorMsg,Loc1, + [{tc_status, + {failed,ErrorMsg}}|Conf],TVal), {EndConfPid,{Mod,Func},Conf}; _ -> {Mod,Func} = get_mf(Loc1), - spawn_fw_call(Mod,Func,CurrConf,Pid,ErrorMsg, - Loc1,self()), + spawn_fw_call(Mod,Func,CurrConf,Pid, + ErrorMsg,Loc1,self()), undefined end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate, - Comment,NewCurrConf) + run_test_case_msgloop(Ref,Pid, + CaptureStdout,RejectIoReqs, + Terminate,Comment, + NewCurrConf,Status) end; killed -> %% result of an exit(TestCase,kill) call, which is the @@ -878,11 +969,14 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> spawn_fw_call(Mod,Func,CurrConf,Pid, testcase_aborted_or_killed, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); {fw_error,{FwMod,FwFunc,FwError}} -> - spawn_fw_call(FwMod,FwFunc,CurrConf,Pid,{framework_error,FwError}, + spawn_fw_call(FwMod,FwFunc,CurrConf,Pid, + {framework_error,FwError}, unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); _Other -> %% the testcase has terminated because of Reason (e.g. an exit %% because a linked process failed) @@ -890,17 +984,22 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> {MF,_} -> MF; _ -> {undefined,undefined} end, - spawn_fw_call(Mod,Func,CurrConf,Pid,Reason,unknown,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf) + spawn_fw_call(Mod,Func,CurrConf,Pid, + Reason,unknown,self()), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status) end; {EndConfPid,{call_end_conf,Data,_Result}} -> case CurrConf of {EndConfPid,{Mod,Func},_Conf} -> {_Mod,_Func,TCPid,TCExitReason,Loc} = Data, - spawn_fw_call(Mod,Func,CurrConf,TCPid,TCExitReason,Loc,self()), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,undefined); + spawn_fw_call(Mod,Func,CurrConf,TCPid, + TCExitReason,Loc,self()), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,undefined,Status); _ -> - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf) + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status) end; {_FwCallPid,fw_notify_done,{T,Value,Loc,Opts,AddToComment}} -> %% the framework has been notified, we're finished @@ -920,7 +1019,8 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> end, {T,Value,Loc,Opts,Comment1} end, - run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},Comment,undefined); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + {true,RetVal},Comment,undefined,Status); {'EXIT',_FwCallPid,{fw_notify_done,Func,Error}} -> %% a framework function failed CB = os:getenv("TEST_SERVER_FRAMEWORK"), @@ -931,37 +1031,88 @@ run_test_case_msgloop(Ref, Pid, CaptureStdout, Terminate, Comment, CurrConf) -> {list_to_atom(CB),Func} end, RetVal = {died,{framework_error,Loc,Error},Loc,"Framework error"}, - run_test_case_msgloop(Ref,Pid,CaptureStdout,{true,RetVal},Comment,undefined); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + {true,RetVal},Comment,undefined,Status); {failed,File,Line} -> put(test_server_detected_fail, [{File, Line}| get(test_server_detected_fail)]), - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); + + {user_timetrap,Pid,_TrapTime,StartTime,E={user_timetrap_error,_},_} -> + case update_user_timetraps(Pid, StartTime) of + proceed -> + self() ! {abort_current_testcase,E,Pid}; + ignore -> + ok + end, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); + {user_timetrap,Pid,TrapTime,StartTime,ElapsedTime,Scale} -> + %% a user timetrap is triggered, ignore it if new + %% timetrap has been started since + case update_user_timetraps(Pid, StartTime) of + proceed -> + TotalTime = if is_integer(TrapTime) -> + TrapTime + ElapsedTime; + true -> + TrapTime + end, + timetrap(TrapTime, TotalTime, Pid, Scale); + ignore -> + ok + end, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); + {timetrap_cancel_one,Handle,_From} -> + timetrap_cancel_one(Handle, false), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); + {timetrap_cancel_all,TCPid,_From} -> + timetrap_cancel_all(TCPid, false), + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); + {get_timetrap_info,TCPid,From} -> + Info = get_timetrap_info(TCPid, false), + From ! {self(),get_timetrap_info,Info}, + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); _Other when not is_tuple(_Other) -> %% ignore anything not generated by test server - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf); + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status); _Other when element(1, _Other) /= 'EXIT', element(1, _Other) /= started, element(1, _Other) /= finished, element(1, _Other) /= print -> %% ignore anything not generated by test server - run_test_case_msgloop(Ref,Pid,CaptureStdout,Terminate,Comment,CurrConf) + run_test_case_msgloop(Ref,Pid,CaptureStdout,RejectIoReqs, + Terminate,Comment,CurrConf,Status) after Timeout -> ReturnValue end. -run_test_case_msgloop_io(ReplyAs,CaptureStdout,Msg,From,Func) -> +run_test_case_msgloop_io(From,ReplyAs,CaptureStdout,RejectIoReqs, + Msg,From,Func) -> case Msg of {'EXIT',_} -> From ! {io_reply,ReplyAs,{error,Func}}; _ -> From ! {io_reply,ReplyAs,ok} end, - if CaptureStdout /= false -> - CaptureStdout ! {captured,Msg}; - true -> + Proceed = if RejectIoReqs -> get({permit_io,From}); + true -> true + end, + if Proceed -> + if CaptureStdout /= false -> + CaptureStdout ! {captured,Msg}; + true -> + ok + end, + output({minor,Msg},From); + true -> ok - end, - output({minor,Msg},From). + end. output(Msg,Sender) -> local_or_remote_apply({test_server_ctrl,output,[Msg,Sender]}). @@ -980,7 +1131,8 @@ call_end_conf(Mod,Func,TCPid,TCExitReason,Loc,Conf,TVal) -> {'EXIT',Why} -> timer:sleep(1), group_leader() ! {printout,12, - "WARNING! ~p:end_per_testcase(~p, ~p)" + "WARNING! " + "~p:end_per_testcase(~p, ~p)" " crashed!\n\tReason: ~p\n", [Mod,Func,Conf,Why]}; _ -> @@ -1096,13 +1248,18 @@ spawn_fw_call(FwMod,FwFunc,_,_Pid,{framework_error,FwError},_,SendTo) -> end, spawn_link(FwCall); -spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) -> +spawn_fw_call(Mod,Func,CurrConf,Pid,Error,Loc,SendTo) -> + {Mod1,Func1} = + case {Mod,Func,CurrConf} of + {undefined,undefined,{{M,F},_}} -> {M,F}; + _ -> {Mod,Func} + end, FwCall = fun() -> %% set group leader so that printouts/comments %% from the framework get printed in the logs group_leader(SendTo, self()), - case catch fw_error_notify(Mod,Func,[], + case catch fw_error_notify(Mod1,Func1,[], Error,Loc) of {'EXIT',FwErrorNotifyErr} -> exit({fw_notify_done,error_notification, @@ -1111,7 +1268,7 @@ spawn_fw_call(Mod,Func,_,Pid,Error,Loc,SendTo) -> ok end, Conf = [{tc_status,{failed,timetrap_timeout}}], - case catch do_end_tc_call(Mod,Func, Loc, + case catch do_end_tc_call(Mod1,Func1, Loc, {Pid,Error,[Conf]},Error) of {'EXIT',FwEndTCErr} -> exit({fw_notify_done,end_tc,FwEndTCErr}); @@ -1176,10 +1333,11 @@ run_test_case_eval(Mod, Func, Args0, Name, Ref, RunInit, TimetrapData, LogOpts, TCCallback) -> put(test_server_multiply_timetraps, TimetrapData), put(test_server_logopts, LogOpts), - + FWInitResult = test_server_sup:framework_call(init_tc,[?pl2a(Mod),Func,Args0], + {ok,Args0}), + group_leader() ! {test_case_initialized,self()}, {{Time,Value},Loc,Opts} = - case test_server_sup:framework_call(init_tc,[?pl2a(Mod),Func,Args0], - {ok,Args0}) of + case FWInitResult of {ok,Args} -> run_test_case_eval1(Mod, Func, Args, Name, RunInit, TCCallback); Error = {error,_Reason} -> @@ -1841,6 +1999,13 @@ messages_get() -> test_server_sup:messages_get([]). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% permit_io(GroupLeader, FromPid) -> ok +%% +%% Make sure proceeding IO from FromPid won't get rejected +permit_io(GroupLeader, FromPid) -> + GroupLeader ! {permit_io,FromPid}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% sleep(Time) -> ok %% Time = integer() | float() | infinity %% @@ -1943,31 +2108,40 @@ fail() -> %% Break a test case so part of the test can be done manually. %% Use continue/0 to continue. break(Comment) -> - case erase(test_server_timetraps) of - undefined -> ok; - List -> lists:foreach(fun({Ref,_,_}) -> - timetrap_cancel(Ref) - end, List) - end, + break(?MODULE, Comment). + +break(CBM, Comment) -> + break(CBM, '', Comment). + +break(CBM, TestCase, Comment) -> + timetrap_cancel(), + {TCName,CntArg,PName} = + if TestCase == '' -> + {"", "", test_server_break_process}; + true -> + Str = atom_to_list(TestCase), + {[32 | Str], Str, + list_to_atom("test_server_break_process_" ++ Str)} + end, io:format(user, "\n\n\n--- SEMIAUTOMATIC TESTING ---" - "\nThe test case executes on process ~w" + "\nThe test case~s executes on process ~w" "\n\n\n~s" "\n\n\n-----------------------------\n\n" - "Continue with --> test_server:continue().\n", - [self(),Comment]), - case whereis(test_server_break_process) of + "Continue with --> ~w:continue(~s).\n", + [TCName,self(),Comment,CBM,CntArg]), + case whereis(PName) of undefined -> - spawn_break_process(self()); + spawn_break_process(self(), PName); OldBreakProcess -> OldBreakProcess ! cancel, - spawn_break_process(self()) + spawn_break_process(self(), PName) end, receive continue -> ok end. -spawn_break_process(Pid) -> +spawn_break_process(Pid, PName) -> spawn(fun() -> - register(test_server_break_process,self()), + register(PName, self()), receive continue -> continue(Pid); cancel -> ok @@ -1976,13 +2150,19 @@ spawn_break_process(Pid) -> continue() -> case whereis(test_server_break_process) of - undefined -> - ok; - BreakProcess -> - BreakProcess ! continue + undefined -> ok; + BreakProcess -> BreakProcess ! continue end. -continue(Pid) -> +continue(TestCase) when is_atom(TestCase) -> + PName = list_to_atom("test_server_break_process_" ++ + atom_to_list(TestCase)), + case whereis(PName) of + undefined -> ok; + BreakProcess -> BreakProcess ! continue + end; + +continue(Pid) when is_pid(Pid) -> Pid ! continue. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -2020,26 +2200,56 @@ timetrap_scale_factor() -> %% %% Creates a time trap, that will kill the calling process if the %% trap is not cancelled with timetrap_cancel/1, within Timeout milliseconds. -timetrap(Timeout0) -> - Timeout = time_ms(Timeout0), - cancel_default_timetrap(), - case get(test_server_multiply_timetraps) of - undefined -> timetrap1(Timeout, true); - {undefined,false} -> timetrap1(Timeout, false); - {undefined,_} -> timetrap1(Timeout, true); - {infinity,_} -> infinity; - {_Int,_Scale} when Timeout == infinity -> infinity; - {Int,Scale} -> timetrap1(Timeout*Int, Scale) - end. +timetrap(Timeout) -> + MultAndScale = + case get(test_server_multiply_timetraps) of + undefined -> {fun(T) -> T end, true}; + {undefined,false} -> {fun(T) -> T end, false}; + {undefined,_} -> {fun(T) -> T end, true}; + {infinity,_} -> {fun(_) -> infinity end, false}; + {Int,Scale} -> {fun(infinity) -> infinity; + (T) -> T*Int end, Scale} + end, + timetrap(Timeout, Timeout, self(), MultAndScale). + +%% when the function is called from different process than +%% the test case, the test_server_multiply_timetraps data +%% is unknown and must be passed as argument +timetrap(Timeout, TCPid, MultAndScale) -> + timetrap(Timeout, Timeout, TCPid, MultAndScale). + +timetrap(Timeout0, TimeToReport0, TCPid, MultAndScale = {Multiplier,Scale}) -> + %% the time_ms call will either convert Timeout to ms or spawn a + %% user timetrap which sends the result to the IO server process + Timeout = time_ms(Timeout0, TCPid, MultAndScale), + Timeout1 = Multiplier(Timeout), + TimeToReport = if Timeout0 == TimeToReport0 -> + Timeout1; + true -> + %% only convert to ms, don't start a + %% user timetrap + time_ms_check(TimeToReport0) + end, + cancel_default_timetrap(self() == TCPid), + Handle = case Timeout1 of + infinity -> + infinity; + _ -> + spawn_link(test_server_sup,timetrap,[Timeout1,TimeToReport, + Scale,TCPid]) + end, + + %% ERROR! This sets dict on IO process instead of testcase process + %% if Timeout is return value from previous user timetrap!! -timetrap1(Timeout, Scale) -> - TCPid = self(), - Ref = spawn_link(test_server_sup,timetrap,[Timeout,Scale,TCPid]), case get(test_server_timetraps) of - undefined -> put(test_server_timetraps,[{Ref,TCPid,{Timeout,Scale}}]); - List -> put(test_server_timetraps,[{Ref,TCPid,{Timeout,Scale}}|List]) + undefined -> + put(test_server_timetraps,[{Handle,TCPid,{TimeToReport,Scale}}]); + List -> + List1 = lists:delete({infinity,TCPid,{infinity,false}}, List), + put(test_server_timetraps,[{Handle,TCPid,{TimeToReport,Scale}}|List1]) end, - Ref. + Handle. ensure_timetrap(Config) -> case get(test_server_timetraps) of @@ -2064,7 +2274,10 @@ ensure_timetrap(Config) -> put(test_server_default_timetrap, timetrap(seconds(DTmo))) end. -cancel_default_timetrap() -> +%% executing on IO process, no default timetrap ever set here +cancel_default_timetrap(false) -> + ok; +cancel_default_timetrap(true) -> case get(test_server_default_timetrap) of undefined -> ok; @@ -2082,75 +2295,175 @@ cancel_default_timetrap() -> error end. - -time_ms({hours,N}) -> hours(N); -time_ms({minutes,N}) -> minutes(N); -time_ms({seconds,N}) -> seconds(N); -time_ms({Other,_N}) -> +time_ms({hours,N}, _, _) -> hours(N); +time_ms({minutes,N}, _, _) -> minutes(N); +time_ms({seconds,N}, _, _) -> seconds(N); +time_ms({Other,_N}, _, _) -> format("=== ERROR: Invalid time specification: ~p. " "Should be seconds, minutes, or hours.~n", [Other]), exit({invalid_time_format,Other}); -time_ms(Ms) when is_integer(Ms) -> Ms; -time_ms(infinity) -> infinity; -time_ms(Fun) when is_function(Fun) -> - time_ms_apply(Fun); -time_ms({M,F,A}=MFA) when is_atom(M), is_atom(F), is_list(A) -> - time_ms_apply(MFA); -time_ms(Other) -> exit({invalid_time_format,Other}). - -time_ms_apply(Func) -> - time_ms_apply(Func, [5000,30000,60000,infinity]). - -time_ms_apply(Func, TOs) -> - Apply = fun() -> - case Func of - {M,F,A} -> - exit({self(),apply(M, F, A)}); - Fun -> - exit({self(),Fun()}) - end - end, - Pid = spawn(Apply), - Ref = monitor(process, Pid), - time_ms_wait(Func, Pid, Ref, TOs). - -time_ms_wait(Func, Pid, Ref, [TO|TOs]) -> - receive - {'DOWN',Ref,process,Pid,{Pid,Result}} -> - time_ms_check(Result); - {'DOWN',Ref,process,Pid,Error} -> - exit({timetrap_error,Error}) - after - TO -> - format("=== WARNING: No return from timetrap function ~p~n", [Func]), - time_ms_wait(Func, Pid, Ref, TOs) - end; -%% this clause will never execute if 'infinity' is in TOs list, that's ok! -time_ms_wait(Func, Pid, Ref, []) -> - demonitor(Ref), - exit(Pid, kill), - exit({timetrap_error,{no_return_from_timetrap_function,Func}}). +time_ms(Ms, _, _) when is_integer(Ms) -> Ms; +time_ms(infinity, _, _) -> infinity; +time_ms(Fun, TCPid, MultAndScale) when is_function(Fun) -> + time_ms_apply(Fun, TCPid, MultAndScale); +time_ms({M,F,A}=MFA, TCPid, MultAndScale) when is_atom(M), is_atom(F), is_list(A) -> + time_ms_apply(MFA, TCPid, MultAndScale); +time_ms(Other, _, _) -> exit({invalid_time_format,Other}). time_ms_check(MFA = {M,F,A}) when is_atom(M), is_atom(F), is_list(A) -> - exit({invalid_time_format,MFA}); + MFA; time_ms_check(Fun) when is_function(Fun) -> - exit({invalid_time_format,Fun}); + Fun; time_ms_check(Other) -> - time_ms(Other). + time_ms(Other, undefined, undefined). + +time_ms_apply(Func, TCPid, MultAndScale) -> + {_,GL} = process_info(TCPid, group_leader), + WhoAmI = self(), % either TC or IO server + T0 = now(), + UserTTSup = + spawn(fun() -> + user_timetrap_supervisor(Func, WhoAmI, TCPid, + GL, T0, MultAndScale) + end), + receive + {UserTTSup,infinity} -> + %% remember the user timetrap so that it can be cancelled + save_user_timetrap(TCPid, UserTTSup, T0), + %% we need to make sure the user timetrap function + %% gets time to execute and return + timetrap(infinity, TCPid, MultAndScale) + after 5000 -> + exit(UserTTSup, kill), + if WhoAmI /= GL -> + exit({user_timetrap_error,time_ms_apply}); + true -> + format("=== ERROR: User timetrap execution failed!", []), + ignore + end + end. + +user_timetrap_supervisor(Func, Spawner, TCPid, GL, T0, MultAndScale) -> + process_flag(trap_exit, true), + Spawner ! {self(),infinity}, + MonRef = monitor(process, TCPid), + UserTTSup = self(), + group_leader(GL, UserTTSup), + UserTT = spawn_link(fun() -> call_user_timetrap(Func, UserTTSup) end), + receive + {UserTT,Result} -> + demonitor(MonRef, [flush]), + Elapsed = trunc(timer:now_diff(now(), T0) / 1000), + try time_ms_check(Result) of + TimeVal -> + %% this is the new timetrap value to set (return value + %% from a fun or an MFA) + GL ! {user_timetrap,TCPid,TimeVal,T0,Elapsed,MultAndScale} + catch _:_ -> + %% when other than a legal timetrap value is returned + %% which will be the normal case for user timetraps + GL ! {user_timetrap,TCPid,0,T0,Elapsed,MultAndScale} + end; + {'EXIT',UserTT,Error} when Error /= normal -> + demonitor(MonRef, [flush]), + GL ! {user_timetrap,TCPid,0,T0,{user_timetrap_error,Error}, + MultAndScale}; + {'DOWN',MonRef,_,_,_} -> + demonitor(MonRef, [flush]), + exit(UserTT, kill) + end. + +call_user_timetrap(Func, Sup) when is_function(Func) -> + try Func() of + Result -> + Sup ! {self(),Result} + catch _:Error -> + exit({Error,erlang:get_stacktrace()}) + end; +call_user_timetrap({M,F,A}, Sup) -> + try apply(M,F,A) of + Result -> + Sup ! {self(),Result} + catch _:Error -> + exit({Error,erlang:get_stacktrace()}) + end. + +save_user_timetrap(TCPid, UserTTSup, StartTime) -> + %% save pid of user timetrap supervisor process so that + %% it may be stopped even before the timetrap func has returned + NewUserTT = {TCPid,{UserTTSup,StartTime}}, + case get(test_server_user_timetrap) of + undefined -> + put(test_server_user_timetrap, [NewUserTT]); + UserTTSups -> + case proplists:get_value(TCPid, UserTTSups) of + undefined -> + put(test_server_user_timetrap, + [NewUserTT | UserTTSups]); + PrevTTSup -> + %% remove prev user timetrap + remove_user_timetrap(PrevTTSup), + put(test_server_user_timetrap, + [NewUserTT | proplists:delete(TCPid, + UserTTSups)]) + end + end. + +update_user_timetraps(TCPid, StartTime) -> + %% called when a user timetrap is triggered + case get(test_server_user_timetrap) of + undefined -> + proceed; + UserTTs -> + case proplists:get_value(TCPid, UserTTs) of + {_UserTTSup,StartTime} -> % same timetrap + put(test_server_user_timetrap, + proplists:delete(TCPid, UserTTs)), + proceed; + {OtherUserTTSup,OtherStartTime} -> + case timer:now_diff(OtherStartTime, StartTime) of + Diff when Diff >= 0 -> + ignore; + _ -> + exit(OtherUserTTSup, kill), + put(test_server_user_timetrap, + proplists:delete(TCPid, UserTTs)), + proceed + end; + undefined -> + proceed + end + end. + +remove_user_timetrap(TTSup) -> + exit(TTSup, kill). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% timetrap_cancel(Handle) -> ok %% Handle = term() %% %% Cancels a time trap. -timetrap_cancel(infinity) -> - ok; timetrap_cancel(Handle) -> + timetrap_cancel_one(Handle, true). + +timetrap_cancel_one(infinity, _SendToServer) -> + ok; +timetrap_cancel_one(Handle, SendToServer) -> case get(test_server_timetraps) of - undefined -> ok; - [{Handle,_,_}] -> erase(test_server_timetraps); - Timers -> put(test_server_timetraps, - lists:keydelete(Handle, 1, Timers)) + undefined -> + ok; + [{Handle,_,_}] -> + erase(test_server_timetraps); + Timers -> + case lists:keysearch(Handle, 1, Timers) of + {value,_} -> + put(test_server_timetraps, + lists:keydelete(Handle, 1, Timers)); + false when SendToServer == true -> + group_leader() ! {timetrap_cancel_one,Handle,self()}; + false -> + ok + end end, test_server_sup:timetrap_cancel(Handle). @@ -2159,31 +2472,59 @@ timetrap_cancel(Handle) -> %% %% Cancels timetrap for current test case. timetrap_cancel() -> + timetrap_cancel_all(self(), true). + +timetrap_cancel_all(TCPid, SendToServer) -> case get(test_server_timetraps) of undefined -> ok; Timers -> - case lists:keysearch(self(), 2, Timers) of - {value,{Handle,_,_}} -> - timetrap_cancel(Handle); - _ -> + [timetrap_cancel_one(Handle, false) || + {Handle,Pid,_} <- Timers, Pid == TCPid] + end, + case get(test_server_user_timetrap) of + undefined -> + ok; + UserTTs -> + case proplists:get_value(TCPid, UserTTs) of + {UserTTSup,_StartTime} -> + remove_user_timetrap(UserTTSup), + put(test_server_user_timetrap, + proplists:delete(TCPid, UserTTs)); + undefined -> ok end - end. + end, + if SendToServer == true -> + group_leader() ! {timetrap_cancel_all,TCPid,self()}; + true -> + ok + end, + ok. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% get_timetrap_info() -> {Timeout,Scale} | undefined %% %% Read timetrap info for current test case get_timetrap_info() -> + get_timetrap_info(self(), true). + +get_timetrap_info(TCPid, SendToServer) -> case get(test_server_timetraps) of undefined -> undefined; Timers -> - case lists:keysearch(self(), 2, Timers) of - {value,{_,_,Info}} -> - Info; - _ -> + case [Info || {Handle,Pid,Info} <- Timers, + Pid == TCPid, Handle /= infinity] of + [I|_] -> + I; + [] when SendToServer == true -> + MsgLooper = group_leader(), + MsgLooper ! {get_timetrap_info,TCPid,self()}, + receive + {MsgLooper,get_timetrap_info,I} -> I + end; + [] -> undefined end end. diff --git a/lib/test_server/src/test_server_ctrl.erl b/lib/test_server/src/test_server_ctrl.erl index 50720a45c8..df2187bc04 100644 --- a/lib/test_server/src/test_server_ctrl.erl +++ b/lib/test_server/src/test_server_ctrl.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 @@ -162,7 +162,7 @@ -export([jobs/0, run_test/1, wait_finish/0, idle_notify/1, abort_current_testcase/1, abort/0]). -export([start_get_totals/1, stop_get_totals/0]). --export([get_levels/0, set_levels/3]). +-export([reject_io_reqs/1, get_levels/0, set_levels/3]). -export([multiply_timetraps/1, scale_timetraps/1, get_timetrap_parameters/0]). -export([create_priv_dir/1]). -export([cover/2, cover/3, cover/7, @@ -218,8 +218,9 @@ -define(auto_skip_color, "#FFA64D"). -define(user_skip_color, "#FF8000"). +-define(sortable_table_name, "SortableTable"). --record(state,{jobs=[],levels={1,19,10}, +-record(state,{jobs=[], levels={1,19,10}, reject_io_reqs=false, multiply_timetraps=1, scale_timetraps=true, create_priv_dir=auto_per_run, finish=false, target_info, trc=false, cover=false, wait_for_node=[], @@ -498,6 +499,9 @@ get_levels() -> set_levels(Show, Major, Minor) -> controller_call({set_levels,Show,Major,Minor}). +reject_io_reqs(Bool) -> + controller_call({reject_io_reqs,Bool}). + multiply_timetraps(N) -> controller_call({multiply_timetraps,N}). @@ -650,8 +654,8 @@ init([Param]) -> contact_main_target(local) -> %% When used by a general framework, global registration of %% test_server should not be required. - case os:getenv("TEST_SERVER_FRAMEWORK") of - FW when FW =:= false; FW =:= "undefined" -> + case get_fw_mod(undefined) of + undefined -> %% Local target! The global test_server process implemented by %% test_server.erl will not be started, so we simulate it by %% globally registering this process instead. @@ -815,6 +819,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) -> [SpecName,{State#state.multiply_timetraps, State#state.scale_timetraps}], LogDir, Name, State#state.levels, + State#state.reject_io_reqs, State#state.create_priv_dir, State#state.testcase_callback, ExtraTools1), NewJobs = [{Name,Pid}|State#state.jobs], @@ -825,6 +830,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) -> [SpecList,{State#state.multiply_timetraps, State#state.scale_timetraps}], LogDir, Name, State#state.levels, + State#state.reject_io_reqs, State#state.create_priv_dir, State#state.testcase_callback, ExtraTools1), NewJobs = [{Name,Pid}|State#state.jobs], @@ -843,6 +849,7 @@ handle_call({add_job,Dir,Name,TopCase,Skip}, _From, State) -> {State#state.multiply_timetraps, State#state.scale_timetraps}], LogDir, Name, State#state.levels, + State#state.reject_io_reqs, State#state.create_priv_dir, State#state.testcase_callback, ExtraTools1), NewJobs = [{Name,Pid}|State#state.jobs], @@ -975,6 +982,15 @@ handle_call({set_levels,Show,Major,Minor}, _From, State) -> {reply,ok,State#state{levels={Show,Major,Minor}}}; %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% handle_call({reject_io_reqs,Bool}, _, State) -> ok +%% Bool = bool() +%% +%% May be used to switch off stdout printouts to the minor log file + +handle_call({reject_io_reqs,Bool}, _From, State) -> + {reply,ok,State#state{reject_io_reqs=Bool}}; + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% handle_call({multiply_timetraps,N}, _, State) -> ok %% N = integer() | infinity %% @@ -1340,14 +1356,15 @@ kill_all_jobs([]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, CreatePrivDir, -%% TestCaseCallback, ExtraTools) -> Pid +%% spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, +%% CreatePrivDir, TestCaseCallback, ExtraTools) -> Pid %% Mod = atom() %% Func = atom() %% Args = [term(),...] %% Dir = string() %% Name = string() %% Levels = {integer(),integer(),integer()} +%% RejectIoReqs = bool() %% CreatePrivDir = auto_per_run | manual_per_tc | auto_per_tc %% TestCaseCallback = {CBMod,CBFunc} | undefined %% ExtraTools = [ExtraTool,...] @@ -1359,14 +1376,14 @@ kill_all_jobs([]) -> %% When the named function is done executing, a summary of the results %% is printed to the log files. -spawn_tester(Mod, Func, Args, Dir, Name, Levels, +spawn_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) -> spawn_link( - fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels, + fun() -> init_tester(Mod, Func, Args, Dir, Name, Levels, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) end). -init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, +init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, RejectIoReqs, CreatePrivDir, TCCallback, ExtraTools) -> process_flag(trap_exit, true), put(test_server_name, Name), @@ -1378,9 +1395,22 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, put(test_server_summary_level, SumLev), put(test_server_major_level, MajLev), put(test_server_minor_level, MinLev), + put(test_server_reject_io_reqs, RejectIoReqs), put(test_server_create_priv_dir, CreatePrivDir), put(test_server_random_seed, proplists:get_value(random_seed, ExtraTools)), put(test_server_testcase_callback, TCCallback), + case os:getenv("TEST_SERVER_FRAMEWORK") of + FW when FW =:= false; FW =:= "undefined" -> + put(test_server_framework, '$none'); + FW -> + put(test_server_framework_name, list_to_atom(FW)), + case os:getenv("TEST_SERVER_FRAMEWORK_NAME") of + FWName when FWName =:= false; FWName =:= "undefined" -> + put(test_server_framework_name, '$none'); + FWName -> + put(test_server_framework_name, list_to_atom(FWName)) + end + end, %% before first print, read and set logging options LogOpts = test_server_sup:framework_call(get_logopts, [], []), put(test_server_logopts, LogOpts), @@ -1412,8 +1442,10 @@ init_tester(Mod, Func, Args, Dir, Name, {SumLev,MajLev,MinLev}, end, OkN = get(test_server_ok), FailedN = get(test_server_failed), - print(html,"<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>" - "<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n", + print(html,"\n</tbody>\n<tfoot>\n" + "<tr><td></td><td><b>TOTAL</b></td><td></td><td></td><td></td>" + "<td>~.3fs</td><td><b>~s</b></td><td>~p Ok, ~p Failed~s of ~p</td></tr>\n" + "</tfoot>\n", [Time,SuccessStr,OkN,FailedN,SkipStr,OkN+FailedN+SkippedN]). %% timer:tc/3 @@ -1474,7 +1506,7 @@ stop_extra_tools([], _) -> %% Reads the named test suite specification file, and executes it. %% %% This function is meant to be called by a process created by -%% spawn_tester/7, which sets up some necessary dictionary values. +%% spawn_tester/10, which sets up some necessary dictionary values. do_spec(SpecName, TimetrapSpec) when is_list(SpecName) -> case file:consult(SpecName) of @@ -1523,7 +1555,7 @@ do_spec(SpecName, TimetrapSpec) when is_list(SpecName) -> %% should not be used. Use a configuration test case instead. %% %% This function is meant to be called by a process created by -%% spawn_tester/7, which sets up some necessary dictionary values. +%% spawn_tester/10, which sets up some necessary dictionary values. do_spec_list(TermList0, TimetrapSpec) -> Nodes = [], @@ -1690,7 +1722,7 @@ add_mod(Mod, Mods) -> %% configuration information into the log files. %% %% This function is meant to be called by a process created by -%% spawn_tester/7, which sets up some necessary dictionary values. +%% spawn_tester/10, which sets up some necessary dictionary values. do_test_cases(TopCases, SkipCases, Config, MultiplyTimetrap) when is_integer(MultiplyTimetrap); MultiplyTimetrap == infinity -> @@ -1700,11 +1732,7 @@ do_test_cases(TopCases, SkipCases, Config, TimetrapData) when is_list(TopCases), is_tuple(TimetrapData) -> {ok,TestDir} = start_log_file(), - FwMod = - case os:getenv("TEST_SERVER_FRAMEWORK") of - FW when FW =:= false; FW =:= "undefined" -> ?MODULE; - FW -> list_to_atom(FW) - end, + FwMod = get_fw_mod(?MODULE), case collect_all_cases(TopCases, SkipCases) of {error,Why} -> print(1, "Error starting: ~p", [Why]), @@ -1733,7 +1761,8 @@ do_test_cases(TopCases, SkipCases, test_server_sup:framework_call(report, [tests_start,{Test,N}]), {Header,Footer} = case test_server_sup:framework_call(get_html_wrapper, - [TestDescr,true,TestDir], "") of + [TestDescr,true,TestDir, + {[],[2,3,4,7,8],[1,6]}], "") of Empty when (Empty == "") ; (element(2,Empty) == "") -> put(basic_html, true), {["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", @@ -1795,14 +1824,15 @@ do_test_cases(TopCases, SkipCases, [?suitelog_name,?coverlog_name]), print(html, "<p>~s</p>\n" ++ - xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">", - "<table>") ++ - "<tr><th>Num</th><th>Module</th><th>Group</th>" ++ - "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++ - "<th>Comment</th></tr>\n", - [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>\n",[N]}, + xhtml("<table bgcolor=\"white\" border=\"3\" cellpadding=\"5\">", + ["<table id=\"",?sortable_table_name,"\">\n", + "<thead>\n"]) ++ + "<tr><th>Num</th><th>Module</th><th>Group</th>" ++ + "<th>Case</th><th>Log</th><th>Time</th><th>Result</th>" ++ + "<th>Comment</th></tr>\n</thead>\n<tbody>\n", + [print_if_known(N, {"<i>Executing <b>~p</b> test cases...</i>" ++ + xhtml("\n<br>\n", "\n<br />\n"),[N]}, {"",[]})]), - print(html, xhtml("<br>", "<br />")), print(major, "=cases ~p", [get(test_server_cases)]), print(major, "=user ~s", [TI#target_info.username]), @@ -1956,7 +1986,8 @@ start_minor_log_file1(Mod, Func, LogDir, AbsName) -> {Header,Footer} = case test_server_sup:framework_call(get_html_wrapper, [TestDescr,false, - filename:dirname(AbsName)], "") of + filename:dirname(AbsName), + undefined], "") of Empty when (Empty == "") ; (element(2,Empty) == "") -> put(basic_html, true), {["<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n", @@ -2086,7 +2117,7 @@ html_possibly_convert(Src, SrcInfo, Dest) -> Header = case test_server_sup:framework_call(get_html_wrapper, ["Module "++Src,false, - OutDir], "") of + OutDir,undefined], "") of Empty when (Empty == "") ; (element(2,Empty) == "") -> ["<!DOCTYPE HTML PUBLIC", "\"-//W3C//DTD HTML 3.2 Final//EN\">\n", @@ -2129,17 +2160,17 @@ add_init_and_end_per_suite([{make,_,_}=Case|Cases], LastMod, LastRef, FwMod) -> add_init_and_end_per_suite([{skip_case,{{Mod,all},_}}=Case|Cases], LastMod, LastRef, FwMod) when Mod =/= LastMod -> {PreCases, NextMod, NextRef} = - do_add_end_per_suite_and_skip(LastMod, LastRef, Mod), + do_add_end_per_suite_and_skip(LastMod, LastRef, Mod, FwMod), PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)]; add_init_and_end_per_suite([{skip_case,{{Mod,_},_}}=Case|Cases], LastMod, LastRef, FwMod) when Mod =/= LastMod -> {PreCases, NextMod, NextRef} = - do_add_init_and_end_per_suite(LastMod, LastRef, Mod), + do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod), PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)]; add_init_and_end_per_suite([{skip_case,{conf,_,{Mod,_},_}}=Case|Cases], LastMod, LastRef, FwMod) when Mod =/= LastMod -> {PreCases, NextMod, NextRef} = - do_add_init_and_end_per_suite(LastMod, LastRef, Mod), + do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod), PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)]; add_init_and_end_per_suite([{skip_case,_}=Case|Cases], LastMod, LastRef, FwMod) -> [Case|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)]; @@ -2151,7 +2182,7 @@ add_init_and_end_per_suite([{conf,Ref,Props,{FwMod,Func}}=Case|Cases], LastMod, case proplists:get_value(suite, Props) of Suite when Suite =/= undefined, Suite =/= LastMod -> {PreCases, NextMod, NextRef} = - do_add_init_and_end_per_suite(LastMod, LastRef, Suite), + do_add_init_and_end_per_suite(LastMod, LastRef, Suite, FwMod), Case1 = {conf,Ref,proplists:delete(suite,Props),{FwMod,Func}}, PreCases ++ [Case1|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)]; @@ -2161,19 +2192,19 @@ add_init_and_end_per_suite([{conf,Ref,Props,{FwMod,Func}}=Case|Cases], LastMod, add_init_and_end_per_suite([{conf,_,_,{Mod,_}}=Case|Cases], LastMod, LastRef, FwMod) when Mod =/= LastMod, Mod =/= FwMod -> {PreCases, NextMod, NextRef} = - do_add_init_and_end_per_suite(LastMod, LastRef, Mod), + do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod), PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)]; add_init_and_end_per_suite([{conf,_,_,_}=Case|Cases], LastMod, LastRef, FwMod) -> [Case|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)]; add_init_and_end_per_suite([{Mod,_}=Case|Cases], LastMod, LastRef, FwMod) when Mod =/= LastMod, Mod =/= FwMod -> {PreCases, NextMod, NextRef} = - do_add_init_and_end_per_suite(LastMod, LastRef, Mod), + do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod), PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)]; add_init_and_end_per_suite([{Mod,_,_}=Case|Cases], LastMod, LastRef, FwMod) when Mod =/= LastMod, Mod =/= FwMod -> {PreCases, NextMod, NextRef} = - do_add_init_and_end_per_suite(LastMod, LastRef, Mod), + do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod), PreCases ++ [Case|add_init_and_end_per_suite(Cases, NextMod, NextRef, FwMod)]; add_init_and_end_per_suite([Case|Cases], LastMod, LastRef, FwMod)-> [Case|add_init_and_end_per_suite(Cases, LastMod, LastRef, FwMod)]; @@ -2181,10 +2212,23 @@ add_init_and_end_per_suite([], _LastMod, undefined, _FwMod) -> []; add_init_and_end_per_suite([], _LastMod, skipped_suite, _FwMod) -> []; -add_init_and_end_per_suite([], LastMod, LastRef, _FwMod) -> - [{conf,LastRef,[],{LastMod,end_per_suite}}]. +add_init_and_end_per_suite([], LastMod, LastRef, FwMod) -> + %% we'll add end_per_suite here even if it's not exported + %% (and simply let the call fail if it's missing) + case erlang:function_exported(LastMod, end_per_suite, 1) of + true -> + [{conf,LastRef,[],{LastMod,end_per_suite}}]; + false -> + %% let's call a "fake" end_per_suite if it exists + case erlang:function_exported(FwMod, end_per_suite, 1) of + true -> + [{conf,LastRef,[{suite,LastMod}],{FwMod,end_per_suite}}]; + false -> + [{conf,LastRef,[],{LastMod,end_per_suite}}] + end + end. -do_add_init_and_end_per_suite(LastMod, LastRef, Mod) -> +do_add_init_and_end_per_suite(LastMod, LastRef, Mod, FwMod) -> case code:is_loaded(Mod) of false -> code:load_file(Mod); _ -> ok @@ -2195,7 +2239,16 @@ do_add_init_and_end_per_suite(LastMod, LastRef, Mod) -> Ref = make_ref(), {[{conf,Ref,[],{Mod,init_per_suite}}],Mod,Ref}; false -> - {[],Mod,undefined} + %% let's call a "fake" init_per_suite if it exists + case erlang:function_exported(FwMod, init_per_suite, 1) of + true -> + Ref = make_ref(), + {[{conf,Ref,[{suite,Mod}], + {FwMod,init_per_suite}}],Mod,Ref}; + false -> + {[],Mod,undefined} + end + end, Cases = if LastRef==undefined -> @@ -2203,20 +2256,44 @@ do_add_init_and_end_per_suite(LastMod, LastRef, Mod) -> LastRef==skipped_suite -> Init; true -> - %% Adding end_per_suite here without checking if the - %% function is actually exported. This is because a - %% conf case must have an end case - so if it doesn't - %% exist, it will only fail... - [{conf,LastRef,[],{LastMod,end_per_suite}}|Init] + %% we'll add end_per_suite here even if it's not exported + %% (and simply let the call fail if it's missing) + case erlang:function_exported(LastMod, end_per_suite, 1) of + true -> + [{conf,LastRef,[],{LastMod,end_per_suite}}|Init]; + false -> + %% let's call a "fake" end_per_suite if it exists + case erlang:function_exported(FwMod, end_per_suite, 1) of + true -> + [{conf,LastRef,[{suite,Mod}], + {FwMod,end_per_suite}}|Init]; + false -> + [{conf,LastRef,[],{LastMod,end_per_suite}}|Init] + end + end end, {Cases,NextMod,NextRef}. -do_add_end_per_suite_and_skip(LastMod, LastRef, Mod) -> +do_add_end_per_suite_and_skip(LastMod, LastRef, Mod, FwMod) -> case LastRef of No when No==undefined ; No==skipped_suite -> {[],Mod,skipped_suite}; _Ref -> - {[{conf,LastRef,[],{LastMod,end_per_suite}}],Mod,skipped_suite} + case erlang:function_exported(LastMod, end_per_suite, 1) of + true -> + {[{conf,LastRef,[],{LastMod,end_per_suite}}], + Mod,skipped_suite}; + false -> + case erlang:function_exported(FwMod, end_per_suite, 1) of + true -> + %% let's call "fake" end_per_suite if it exists + {[{conf,LastRef,[],{FwMod,end_per_suite}}], + Mod,skipped_suite}; + false -> + {[{conf,LastRef,[],{LastMod,end_per_suite}}], + Mod,skipped_suite} + end + end end. @@ -2777,12 +2854,13 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0, {failed,TcFail}]}] end, + SuiteName = proplists:get_value(suite, Props), case get(test_server_create_priv_dir) of auto_per_run -> % use common priv_dir TSDirs = [{priv_dir,get(test_server_priv_dir)}, - {data_dir,get_data_dir(Mod)}]; + {data_dir,get_data_dir(Mod, SuiteName)}]; _ -> - TSDirs = [{data_dir,get_data_dir(Mod)}] + TSDirs = [{data_dir,get_data_dir(Mod, SuiteName)}] end, ActualCfg = @@ -2794,7 +2872,8 @@ run_test_cases_loop([{conf,Ref,Props,{Mod,Func}}|_Cases]=Cs0, end, Mode0), update_config(hd(Config), TSDirs ++ [{tc_group_path,GroupPath} | CfgProps]) - end, + end, + CurrMode = curr_mode(Ref, Mode0, Mode), ConfCaseResult = run_test_case(Ref, 0, Mod, Func, [ActualCfg], skip_init, target, TimetrapData, CurrMode), @@ -2946,13 +3025,13 @@ run_test_cases_loop([{conf,_Ref,_Props,_X}=Conf|_Cases0], run_test_cases_loop([{Mod,Case}|Cases], Config, TimetrapData, Mode, Status) -> ActualCfg = - case get(test_server_create_priv_dir) of - auto_per_run -> - update_config(hd(Config), [{priv_dir,get(test_server_priv_dir)}, - {data_dir,get_data_dir(Mod)}]); - _ -> - update_config(hd(Config), [{data_dir,get_data_dir(Mod)}]) - end, + case get(test_server_create_priv_dir) of + auto_per_run -> + update_config(hd(Config), [{priv_dir,get(test_server_priv_dir)}, + {data_dir,get_data_dir(Mod)}]); + _ -> + update_config(hd(Config), [{data_dir,get_data_dir(Mod)}]) + end, run_test_cases_loop([{Mod,Case,[ActualCfg]}|Cases], Config, TimetrapData, Mode, Status); @@ -3117,13 +3196,20 @@ conf_start(Ref, Mode) -> false -> 0 end. + get_data_dir(Mod) -> - case code:which(Mod) of + get_data_dir(Mod, undefined). + +get_data_dir(Mod, Suite) -> + UseMod = if Suite == undefined -> Mod; + true -> Suite + end, + case code:which(UseMod) of non_existing -> print(12, "The module ~p is not loaded", [Mod]), []; FullPath -> - filename:dirname(FullPath) ++ "/" ++ cast_to_list(Mod) ++ + filename:dirname(FullPath) ++ "/" ++ cast_to_list(UseMod) ++ ?data_dir_suffix end. @@ -3289,7 +3375,7 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) -> print(major, "=started ~s", [lists:flatten(timestamp_get(""))]), print(major, "=result skipped: ~s", [Comment1]), print(2,"*** Skipping test case #~w ~p ***", [CaseNum,{Mod,Func}]), - TR = xhtml("<tr valign=\"top\">", ["<tr class=\"",odd_or_even(),"\">"]), + TR = xhtml("<tr valign=\"top\">", ["<tr class=\"",odd_or_even(),"\">"]), GroupName = case get_name(Mode) of undefined -> ""; Name -> cast_to_list(Name) @@ -3303,7 +3389,7 @@ skip_case1(Type, CaseNum, Mod, Func, Comment, Mode) -> "<td>" ++ Col0 ++ "0.000s" ++ Col1 ++ "</td>" "<td><font color=\"~s\">SKIPPED</font></td>" "<td>~s</td></tr>\n", - [num2str(CaseNum),Mod,GroupName,Func,ResultCol,Comment1]), + [num2str(CaseNum),fw_name(Mod),GroupName,Func,ResultCol,Comment1]), if CaseNum > 0 -> {US,AS} = get(test_server_skipped), case Type of @@ -3742,13 +3828,16 @@ run_test_case1(Ref, Num, Mod, Func, Args, RunInit, Where, "<td>" ++ Col0 ++ "~s" ++ Col1 ++ "</td>" "<td><a href=\"~s\">~p</a></td>" "<td><a href=\"~s#top\"><</a> <a href=\"~s#end\">></a></td>", - [num2str(Num),Mod,GroupName,MinorBase,Func,MinorBase,MinorBase]), + [num2str(Num),fw_name(Mod),GroupName,MinorBase,Func, + MinorBase,MinorBase]), do_if_parallel(Main, ok, fun erlang:yield/0), + + RejectIoReqs = get(test_server_reject_io_reqs), %% run the test case {Result,DetectedFail,ProcsBefore,ProcsAfter} = run_test_case_apply(Num, Mod, Func, [UpdatedArgs], get_name(Mode), - RunInit, Where, TimetrapData), + RunInit, Where, TimetrapData, RejectIoReqs), {Time,RetVal,Loc,Opts,Comment} = case Result of Normal={_Time,_RetVal,_Loc,_Opts,_Comment} -> Normal; @@ -4189,6 +4278,46 @@ progress(ok, _CaseNum, Mod, Func, _Loc, RetVal, Time, %%-------------------------------------------------------------------- %% various help functions +get_fw_mod(Mod) -> + case get(test_server_framework) of + undefined -> + case os:getenv("TEST_SERVER_FRAMEWORK") of + FW when FW =:= false; FW =:= "undefined" -> + Mod; + FW -> + list_to_atom(FW) + end; + '$none' -> Mod; + FW -> FW + end. + +fw_name(?MODULE) -> + test_server; +fw_name(Mod) -> + case get(test_server_framework_name) of + undefined -> + case get_fw_mod(undefined) of + undefined -> + Mod; + Mod -> + case os:getenv("TEST_SERVER_FRAMEWORK_NAME") of + FWName when FWName =:= false; FWName =:= "undefined" -> + Mod; + FWName -> + list_to_atom(FWName) + end; + _ -> + Mod + end; + '$none' -> + Mod; + FWName -> + case get_fw_mod(Mod) of + Mod -> FWName; + _ -> Mod + end + end. + if_auto_skip(Reason={failed,{_,init_per_testcase,_}}, True, _False) -> {Reason,True()}; if_auto_skip({_T,{skip,Reason={failed,{_,init_per_testcase,_}}},_Opts}, True, _False) -> @@ -4292,8 +4421,8 @@ get_font_style1(default) -> %% set to false. format_exception(Reason={_Error,Stack}) when is_list(Stack) -> - case os:getenv("TEST_SERVER_FRAMEWORK") of - FW when FW =:= false; FW =:= "undefined" -> + case get_fw_mod(undefined) of + undefined -> case application:get_env(test_server, format_exception) of {ok,false} -> {"~p",Reason}; @@ -4301,7 +4430,7 @@ format_exception(Reason={_Error,Stack}) when is_list(Stack) -> do_format_exception(Reason) end; FW -> - case application:get_env(list_to_atom(FW), format_exception) of + case application:get_env(FW, format_exception) of {ok,false} -> {"~p",Reason}; _ -> @@ -4327,7 +4456,7 @@ do_format_exception(Reason={Error,Stack}) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, -%% Where, TimetrapData) -> +%% Where, TimetrapData, RejectIoReqs) -> %% {{Time,RetVal,Loc,Opts,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} | %% {{died,Reason,unknown,Comment},DetectedFail,ProcessesBefore,ProcessesAfter} %% Name = atom() @@ -4346,19 +4475,21 @@ do_format_exception(Reason={Error,Stack}) -> %% sent over socket to target, and test_server runs the case and sends the %% result back over the socket. Else test_server runs the case directly on host. -run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host, TimetrapData) -> +run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, host, + TimetrapData, RejectIoReqs) -> test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit, - TimetrapData}); -run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, TimetrapData) -> + TimetrapData,RejectIoReqs}); +run_test_case_apply(CaseNum, Mod, Func, Args, Name, RunInit, target, + TimetrapData, RejectIoReqs) -> case get(test_server_ctrl_job_sock) of undefined -> %% local target test_server:run_test_case_apply({CaseNum,Mod,Func,Args,Name,RunInit, - TimetrapData}); + TimetrapData,RejectIoReqs}); JobSock -> %% remote target request(JobSock, {test_case,{CaseNum,Mod,Func,Args,Name,RunInit, - TimetrapData}}), + TimetrapData,RejectIoReqs}}), read_job_sock_loop(JobSock) end. @@ -4897,8 +5028,8 @@ collect_case([Case | Cases], St, Acc) -> collect_case(Cases, NewSt, Acc ++ FlatCases). collect_case_invoke(Mod, Case, MFA, St) -> - case os:getenv("TEST_SERVER_FRAMEWORK") of - FW when FW =:= false; FW =:= "undefined" -> + case get_fw_mod(undefined) of + undefined -> case catch apply(Mod, Case, [suite]) of {'EXIT',_} -> {ok,[MFA],St}; @@ -4906,7 +5037,9 @@ collect_case_invoke(Mod, Case, MFA, St) -> collect_subcases(Mod, Case, MFA, St, Suite) end; _ -> - Suite = test_server_sup:framework_call(get_suite, [?pl2a(Mod),Case], []), + Suite = test_server_sup:framework_call(get_suite, + [?pl2a(Mod),Case], + []), collect_subcases(Mod, Case, MFA, St, Suite) end. @@ -5060,7 +5193,9 @@ init_props(Props) -> end. keep_name(Props) -> - lists:filter(fun({name,_}) -> true; (_) -> false end, Props). + lists:filter(fun({name,_}) -> true; + ({suite,_}) -> true; + (_) -> false end, Props). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Target node handling functions %% diff --git a/lib/test_server/src/test_server_h.erl b/lib/test_server/src/test_server_h.erl index 6707f98109..fdeee59326 100644 --- a/lib/test_server/src/test_server_h.erl +++ b/lib/test_server/src/test_server_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2009. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/test_server/src/test_server_node.erl b/lib/test_server/src/test_server_node.erl index 1fd40d1dd9..6358efa764 100644 --- a/lib/test_server/src/test_server_node.erl +++ b/lib/test_server/src/test_server_node.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2011. All Rights Reserved. +%% Copyright Ericsson AB 2002-2012. 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 @@ -407,7 +407,7 @@ start_node_peer(SlaveName, OptList, From, TI) -> % Support for erl_crash_dump files.. CrashFile = filename:join([TI#target_info.test_server_dir, "erl_crash_dump."++cast_to_list(SlaveName)]), - CrashArgs = lists:concat([" -env ERL_CRASH_DUMP ",CrashFile," "]), + CrashArgs = lists:concat([" -env ERL_CRASH_DUMP \"",CrashFile,"\" "]), FailOnError = start_node_get_option_value(fail_on_error, OptList, true), Pa = TI#target_info.test_server_dir, Prog0 = start_node_get_option_value(erl, OptList, default), @@ -420,7 +420,7 @@ start_node_peer(SlaveName, OptList, From, TI) -> Cmd = lists:concat([Prog, " -detached ", TI#target_info.naming, " ", SlaveName, - " -pa ", Pa, + " -pa \"", Pa,"\"", NodeStarted, CrashArgs, " ", Args]), @@ -472,9 +472,9 @@ start_node_slave(SlaveName, OptList, From, TI) -> CrashFile = filename:join([TI#target_info.test_server_dir, "erl_crash_dump."++cast_to_list(SlaveName)]), - CrashArgs = lists:concat([" -env ERL_CRASH_DUMP ",CrashFile," "]), + CrashArgs = lists:concat([" -env ERL_CRASH_DUMP \"",CrashFile,"\" "]), Pa = TI#target_info.test_server_dir, - Args = lists:concat([" -pa ", Pa, " ", SuppliedArgs, CrashArgs]), + Args = lists:concat([" -pa \"", Pa, "\" ", SuppliedArgs, CrashArgs]), Prog0 = start_node_get_option_value(erl, OptList, default), Prog = pick_erl_program(Prog0), @@ -943,12 +943,23 @@ find_rel_suse_1(Rel, RootWc) -> end. find_rel_suse_2(Rel, RootWc) -> - Wc = RootWc ++ "_" ++ Rel, - case filelib:wildcard(Wc) of - [] -> - []; - [R|_] -> - [filename:join([R,"bin","erl"])] + RelDir = filename:dirname(RootWc), + Pat = filename:basename(RootWc ++ "_" ++ Rel) ++ ".*", + case file:list_dir(RelDir) of + {ok,Dirs} -> + case lists:filter(fun(Dir) -> + case re:run(Dir, Pat) of + nomatch -> false; + _ -> true + end + end, Dirs) of + [] -> + []; + [R|_] -> + [filename:join([RelDir,R,"bin","erl"])] + end; + _ -> + [] end. %% suse_release() -> VersionString | none. diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl index 875f45eea6..9d111ff769 100644 --- a/lib/test_server/src/test_server_sup.erl +++ b/lib/test_server/src/test_server_sup.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -21,7 +21,8 @@ %%% Purpose: Test server support functions. %%%------------------------------------------------------------------- -module(test_server_sup). --export([timetrap/2, timetrap/3, timetrap_cancel/1, capture_get/1, messages_get/1, +-export([timetrap/2, timetrap/3, timetrap/4, + timetrap_cancel/1, capture_get/1, messages_get/1, timecall/3, call_crash/5, app_test/2, check_new_crash_dumps/0, cleanup_crash_dumps/0, crash_dump_dir/0, tar_crash_dumps/0, get_username/0, get_os_family/0, @@ -44,9 +45,12 @@ %% delays during the test (e.g. if cover is running). timetrap(Timeout0, Pid) -> - timetrap(Timeout0, true, Pid). + timetrap(Timeout0, Timeout0, true, Pid). timetrap(Timeout0, Scale, Pid) -> + timetrap(Timeout0, Timeout0, Scale, Pid). + +timetrap(Timeout0, ReportTVal, Scale, Pid) -> process_flag(priority, max), Timeout = if not Scale -> Timeout0; true -> test_server:timetrap_scale_factor() * Timeout0 @@ -54,28 +58,36 @@ timetrap(Timeout0, Scale, Pid) -> TruncTO = trunc(Timeout), receive after TruncTO -> - MFLs = test_server:get_loc(Pid), - Mon = erlang:monitor(process, Pid), - Trap = - case get(test_server_init_or_end_conf) of - undefined -> - {timetrap_timeout,TruncTO,MFLs}; - InitOrEnd -> - {timetrap_timeout,TruncTO,MFLs,InitOrEnd} - end, - exit(Pid, Trap), - receive - {'DOWN', Mon, process, Pid, _} -> + case is_process_alive(Pid) of + true -> + TimeToReport = if Timeout0 == ReportTVal -> TruncTO; + true -> ReportTVal end, + MFLs = test_server:get_loc(Pid), + Mon = erlang:monitor(process, Pid), + Trap = + case get(test_server_init_or_end_conf) of + undefined -> + {timetrap_timeout,TimeToReport,MFLs}; + InitOrEnd -> + {timetrap_timeout,TimeToReport,MFLs,InitOrEnd} + end, + exit(Pid, Trap), + receive + {'DOWN', Mon, process, Pid, _} -> + ok + after 10000 -> + %% Pid is probably trapping exits, hit it harder... + catch error_logger:warning_msg( + "Testcase process ~p not " + "responding to timetrap " + "timeout:~n" + " ~p.~n" + "Killing testcase...~n", + [Pid, Trap]), + exit(Pid, kill) + end; + false -> ok - after 10000 -> - %% Pid is probably trapping exits, hit it harder... - catch error_logger:warning_msg("Testcase process ~p not " - "responding to timetrap " - "timeout:~n" - " ~p.~n" - "Killing testcase...~n", - [Pid, Trap]), - exit(Pid, kill) end end. @@ -88,8 +100,12 @@ timetrap_cancel(Handle) -> unlink(Handle), MonRef = erlang:monitor(process, Handle), exit(Handle, kill), - receive {'DOWN',MonRef,_,_,_} -> ok after 2000 -> ok end. - + receive {'DOWN',MonRef,_,_,_} -> ok + after + 2000 -> + erlang:demonitor(MonRef, [flush]), + ok + end. capture_get(Msgs) -> receive @@ -99,7 +115,6 @@ capture_get(Msgs) -> lists:reverse(Msgs) end. - messages_get(Msgs) -> receive Msg -> @@ -108,7 +123,6 @@ messages_get(Msgs) -> lists:reverse(Msgs) end. - timecall(M, F, A) -> Befor = erlang:now(), Val = apply(M, F, A), diff --git a/lib/test_server/src/ts.erl b/lib/test_server/src/ts.erl index 7e48a11f33..4899f38d2b 100644 --- a/lib/test_server/src/ts.erl +++ b/lib/test_server/src/ts.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. All Rights Reserved. %% %% The contents of this file are subject to the Erlang Public License, %% Version 1.1, (the "License"); you may not use this file except in @@ -27,9 +27,10 @@ -export([run/0, run/1, run/2, run/3, run/4, clean/0, clean/1, tests/0, tests/1, - install/0, install/1, install/2, index/0, + install/0, install/1, index/0, estone/0, estone/1, cross_cover_analyse/1, + compile_testcases/0, compile_testcases/1, help/0]). -export([i/0, l/1, r/0, r/1, r/2, r/3]). @@ -88,35 +89,25 @@ -define( install_help, [ - " ts:install() - Install TS for local target with no Options.\n" - " ts:install([Options])\n", - " - Install TS for local target with Options\n" - " ts:install({Architecture, Target_name})\n", - " - Install TS for a remote target architecture.\n", - " and target network name (e.g. {vxworks_cpu32, sauron}).\n", - " ts:install({Architecture, Target_name}, [Options])\n", - " - Install TS as above, and with Options.\n", + " ts:install() - Install TS with no Options.\n" + " ts:install([Options]) - Install TS with Options\n" "\n", "Installation options supported:\n", " {longnames, true} - Use fully qualified hostnames\n", - " {hosts, [HostList]}\n" - " - Use theese hosts for distributed testing.\n" " {verbose, Level} - Sets verbosity level for TS output (0,1,2), 0 is\n" " quiet(default).\n" - " {slavetargets, SlaveTarges}\n" - " - Available hosts for starting slave nodes for\n" - " platforms which cannot have more than one erlang\n" - " node per host.\n" - " {crossroot, TargetErlRoot}\n" - " - Erlang root directory on target host\n" - " Mandatory for remote targets\n" - " {master, {MasterHost, MasterCookie}}\n" - " - Master host and cookie for targets which are\n" - " started as slave nodes.\n" - " erl_boot_server must be started on master before\n" - " test is run.\n" - " Optional, default is controller host and then\n" - " erl_boot_server is started autmatically\n" + " {crossroot, ErlTop}\n" + " - Erlang root directory on build host, ~n" + " normally same value as $ERL_TOP\n" + " {crossenv, [{Key,Val}]}\n" + " - Environmentals used by test configure on build host\n" + " {crossflags, FlagsString}\n" + " - Flags used by test configure on build host\n" + " {xcomp, XCompFile}\n" + " - The xcomp file to use for cross compiling the~n" + " testcases. Using this option will override any~n" + " cross* configurations given to ts. Note that you~n" + " have to have a correct ERL_TOP as well.~n" ]). help() -> @@ -183,26 +174,24 @@ help(installed) -> " cover_details. Analyses modules specified in\n" " cross.cover.\n" " Level can be 'overview' or 'details'.\n", + " ts:compile_testcases()~n" + " ts:compile_testcases(Apps)~n" + " - Compile all testcases for usage in a cross ~n" + " compile environment." " \n" "Installation (already done):\n" ], show_help([H,?install_help]). show_help(H) -> - io:put_chars(lists:flatten(H)). + io:format(lists:flatten(H)). %% Installs tests. install() -> ts_install:install(install_local,[]). -install({Architecture, Target_name}) -> - ts_install:install({ts_lib:maybe_atom_to_list(Architecture), - ts_lib:maybe_atom_to_list(Target_name)}, []); install(Options) when is_list(Options) -> ts_install:install(install_local,Options). -install({Architecture, Target_name}, Options) when is_list(Options)-> - ts_install:install({ts_lib:maybe_atom_to_list(Architecture), - ts_lib:maybe_atom_to_list(Target_name)}, Options). %% Updates the local index page. @@ -728,3 +717,23 @@ cover_type(cover_details) -> details. do_load(Mod) -> code:purge(Mod), code:load_file(Mod). + + +compile_testcases() -> + compile_datadirs("../*/*_data"). + +compile_testcases(App) when is_atom(App) -> + compile_testcases([App]); +compile_testcases([App | T]) -> + compile_datadirs(io_lib:format("../~s_test/*_data", [App])), + compile_testcases(T); +compile_testcases([]) -> + ok. + +compile_datadirs(DataDirs) -> + {ok,Variables} = file:consult("variables"), + + lists:foreach(fun(Dir) -> + ts_lib:make_non_erlang(Dir, Variables) + end, + filelib:wildcard(DataDirs)). diff --git a/lib/test_server/src/ts.hrl b/lib/test_server/src/ts.hrl index 885a726c54..db804d23a1 100644 --- a/lib/test_server/src/ts.hrl +++ b/lib/test_server/src/ts.hrl @@ -28,6 +28,7 @@ -define(run_summary, "suite.summary"). -define(cover_total,"total_cover.log"). -define(variables, "variables"). +-define(cross_variables, "variables-cross"). -define(LF, [10]). % Newline in VxWorks script -define(CHAR_PER_LINE, 60). % Characters per VxWorks script building line -define(CROSS_COOKIE, "cross"). % cookie used when cross platform testing diff --git a/lib/test_server/src/ts_autoconf_win32.erl b/lib/test_server/src/ts_autoconf_win32.erl index 9103542fd2..258040b39e 100644 --- a/lib/test_server/src/ts_autoconf_win32.erl +++ b/lib/test_server/src/ts_autoconf_win32.erl @@ -67,6 +67,7 @@ system_type(Vars) -> {5,1,_} -> "Windows XP"; {5,2,_} -> "Windows 2003"; {6,0,_} -> "Windows Vista"; + {6,1,_} -> "Windows 7"; {_,_,_} -> "Windows NCC-1701-D" end; {win32, windows} -> diff --git a/lib/test_server/src/ts_erl_config.erl b/lib/test_server/src/ts_erl_config.erl index 5585e8ccd3..45d88016a4 100644 --- a/lib/test_server/src/ts_erl_config.erl +++ b/lib/test_server/src/ts_erl_config.erl @@ -128,15 +128,15 @@ erts_lib(Vars,OsType) -> ErtsLibInternal} end, [{erts_lib_include, - filename:nativename(ErtsLibInclude)}, + quote(filename:nativename(ErtsLibInclude))}, {erts_lib_include_generated, - filename:nativename(ErtsLibIncludeGenerated)}, + quote(filename:nativename(ErtsLibIncludeGenerated))}, {erts_lib_include_internal, - filename:nativename(ErtsLibIncludeInternal)}, + quote(filename:nativename(ErtsLibIncludeInternal))}, {erts_lib_include_internal_generated, - filename:nativename(ErtsLibIncludeInternalGenerated)}, - {erts_lib_path, filename:nativename(ErtsLibPath)}, - {erts_lib_internal_path, filename:nativename(ErtsLibInternalPath)}, + quote(filename:nativename(ErtsLibIncludeInternalGenerated))}, + {erts_lib_path, quote(filename:nativename(ErtsLibPath))}, + {erts_lib_internal_path, quote(filename:nativename(ErtsLibInternalPath))}, {erts_lib_multi_threaded, erts_lib_name(multi_threaded, OsType)}, {erts_lib_single_threaded, erts_lib_name(single_threaded, OsType)} | Vars]. @@ -145,13 +145,13 @@ erl_include(Vars) -> Include = case erl_root(Vars) of {installed, Root} -> - filename:join([Root, "usr", "include"]); + quote(filename:join([Root, "usr", "include"])); {srctree, Root, Target} -> - filename:join([Root, "erts", "emulator", "beam"]) - ++ " -I" ++ filename:join([Root, "erts", "emulator"]) + quote(filename:join([Root, "erts", "emulator", "beam"])) + ++ " -I" ++ quote(filename:join([Root, "erts", "emulator"])) ++ system_include(Root, Vars) - ++ " -I" ++ filename:join([Root, "erts", "include"]) - ++ " -I" ++ filename:join([Root, "erts", "include", Target]) + ++ " -I" ++ quote(filename:join([Root, "erts", "include"])) + ++ " -I" ++ quote(filename:join([Root, "erts", "include", Target])) end, [{erl_include, filename:nativename(Include)}|Vars]. @@ -163,7 +163,7 @@ system_include(Root, Vars) -> "VxWorks" -> "sys.vxworks"; _ -> "sys/unix" end, - " -I" ++ filename:nativename(filename:join([Root, "erts", "emulator", SysDir])). + " -I" ++ quote(filename:nativename(filename:join([Root, "erts", "emulator", SysDir]))). erl_interface(Vars,OsType) -> {Incl, {LibPath, MkIncl}} = @@ -220,20 +220,16 @@ erl_interface(Vars,OsType) -> _ -> "" % VxWorks end, - CrossCompile = case OsType of - vxworks -> "true"; - _ -> "false" - end, - [{erl_interface_libpath, filename:nativename(LibPath)}, + [{erl_interface_libpath, quote(filename:nativename(LibPath))}, {erl_interface_sock_libs, sock_libraries(OsType)}, {erl_interface_lib, Lib}, {erl_interface_eilib, Lib1}, {erl_interface_lib_drv, LibDrv}, {erl_interface_eilib_drv, Lib1Drv}, {erl_interface_threadlib, ThreadLib}, - {erl_interface_include, filename:nativename(Incl)}, - {erl_interface_mk_include, filename:nativename(MkIncl)}, - {erl_interface_cross_compile, CrossCompile} | Vars]. + {erl_interface_include, quote(filename:nativename(Incl))}, + {erl_interface_mk_include, quote(filename:nativename(MkIncl))} + | Vars]. ic(Vars, OsType) -> {ClassPath, LibPath, Incl} = @@ -250,10 +246,10 @@ ic(Vars, OsType) -> end, filename:join(Dir, "include")} end, - [{ic_classpath, filename:nativename(ClassPath)}, - {ic_libpath, filename:nativename(LibPath)}, + [{ic_classpath, quote(filename:nativename(ClassPath))}, + {ic_libpath, quote(filename:nativename(LibPath))}, {ic_lib, link_library("ic", OsType)}, - {ic_include_path, filename:nativename(Incl)}|Vars]. + {ic_include_path, quote(filename:nativename(Incl))}|Vars]. jinterface(Vars, _OsType) -> ClassPath = @@ -263,7 +259,7 @@ jinterface(Vars, _OsType) -> Dir -> filename:join([Dir, "priv", "OtpErlang.jar"]) end, - [{jinterface_classpath, filename:nativename(ClassPath)}|Vars]. + [{jinterface_classpath, quote(filename:nativename(ClassPath))}|Vars]. lib_dir(Vars, Lib) -> LibLibDir = case Lib of @@ -276,8 +272,6 @@ lib_dir(Vars, Lib) -> case {get_var(crossroot, Vars), LibLibDir} of {{error, _}, _} -> %no crossroot LibLibDir; - {_, {error, _}} -> %no lib - LibLibDir; {CrossRoot, _} -> %% XXX: Ugly. So ugly I won't comment it %% /Patrik @@ -299,18 +293,16 @@ lib_dir(Vars, Lib) -> end. erl_root(Vars) -> - Root = code:root_dir(), - case ts_lib:erlang_type() of + Root = case get_var(crossroot,Vars) of + {error, notfound} -> code:root_dir(); + CrossRoot -> CrossRoot + end, + case ts_lib:erlang_type(Root) of {srctree, _Version} -> Target = get_var(target, Vars), {srctree, Root, Target}; {_, _Version} -> - case get_var(crossroot,Vars) of - {error, notfound} -> - {installed, Root}; - CrossRoot -> - {installed, CrossRoot} - end + {installed, Root} end. @@ -362,10 +354,17 @@ ssl(Vars, _OsType) -> {error, bad_name} -> throw({cannot_find_app, ssl}); Dir -> - [{ssl_libdir, filename:nativename(Dir)}| Vars] + [{ssl_libdir, quote(filename:nativename(Dir))}| Vars] end. separators(Vars, {win32,_}) -> [{'DS',"\\"},{'PS',";"}|Vars]; separators(Vars, _) -> [{'DS',"/"},{'PS',":"}|Vars]. + +quote([$ |R]) -> + "\\ "++quote(R); +quote([C|R]) -> + [C|quote(R)]; +quote([]) -> + []. diff --git a/lib/test_server/src/ts_install.erl b/lib/test_server/src/ts_install.erl index 9703478f20..99ccfbc9bc 100644 --- a/lib/test_server/src/ts_install.erl +++ b/lib/test_server/src/ts_install.erl @@ -28,12 +28,25 @@ install(install_local, Options) -> install(os:type(), Options); install(TargetSystem, Options) -> - io:format("Running configure for cross architecture, network target name~n" - "~p~n", [TargetSystem]), - case autoconf(TargetSystem) of + case file:consult(?variables) of + {ok, Vars} -> + case proplists:get_value(cross,Vars) of + "yes" when Options == []-> + target_install(Vars); + _ -> + build_install(TargetSystem, Options) + end; + _ -> + build_install(TargetSystem, Options) + end. + + +build_install(TargetSystem, Options) -> + XComp = parse_xcomp_file(proplists:get_value(xcomp,Options)), + case autoconf(TargetSystem, XComp++Options) of {ok, Vars0} -> OsType = os_type(TargetSystem), - Vars1 = ts_erl_config:variables(merge(Vars0,Options),OsType), + Vars1 = ts_erl_config:variables(Vars0++XComp++Options,OsType), {Options1, Vars2} = add_vars(Vars1, Options), Vars3 = lists:flatten([Options1|Vars2]), write_terms(?variables, Vars3); @@ -45,32 +58,43 @@ os_type({unix,_}=OsType) -> OsType; os_type({win32,_}=OsType) -> OsType; os_type(_Other) -> vxworks. -merge(Vars,[]) -> - Vars; -merge(Vars,[{crossroot,X}| Tail]) -> - merge([{crossroot, X} | Vars], Tail); -merge(Vars,[_X | Tail]) -> - merge(Vars,Tail). +target_install(CrossVars) -> + io:format("Cross installation detected, skipping configure and data_dir make~n"), + case file:rename(?variables,?cross_variables) of + ok -> + ok; + _ -> + io:format("Could not find variables file from cross make~n"), + throw(cross_installation_failed) + end, + CPU = proplists:get_value('CPU',CrossVars), + OS = proplists:get_value(os,CrossVars), + {Options,Vars} = add_vars([{cross,"yes"},{'CPU',CPU},{os,OS}],[]), + Variables = lists:flatten([Options|Vars]), + write_terms(?variables, Variables). %% Autoconf for various platforms. %% unix uses the configure script %% win32 uses ts_autoconf_win32 %% VxWorks uses ts_autoconf_vxworks. -autoconf(TargetSystem) -> - case autoconf1(TargetSystem) of +autoconf(TargetSystem, XComp) -> + case autoconf1(TargetSystem, XComp) of ok -> autoconf2(file:read_file("conf_vars")); Error -> Error end. -autoconf1({win32, _}) -> +autoconf1({win32, _},[{cross,"no"}]) -> ts_autoconf_win32:configure(); -autoconf1({unix, _}) -> - unix_autoconf(); -autoconf1(Other) -> - ts_autoconf_vxworks:configure(Other). +autoconf1({unix, _},XCompFile) -> + unix_autoconf(XCompFile); +autoconf1(Other,[{cross,"no"}]) -> + ts_autoconf_vxworks:configure(Other); +autoconf1(_,_) -> + io:format("cross compilation not supported for that this platform~n"), + throw(cross_installation_failed). autoconf2({ok, Bin}) -> get_vars(binary_to_list(Bin), name, [], []); @@ -92,27 +116,40 @@ get_vars([], name, [], Result) -> get_vars(_, _, _, _) -> {error, fatal_bad_conf_vars}. -unix_autoconf() -> +unix_autoconf(XConf) -> Configure = filename:absname("configure"), - Args = case catch erlang:system_info(threads) of - false -> ""; - _ -> " --enable-shlib-thread-safety" - end - ++ case catch string:str(erlang:system_info(system_version), - "debug") > 0 of - false -> ""; - _ -> " --enable-debug-mode" - end, + Flags = proplists:get_value(crossflags,XConf,[]), + Env = proplists:get_value(crossenv,XConf,[]), + Host = get_xcomp_flag("host", Flags), + Build = get_xcomp_flag("build", Flags), + Threads = [" --enable-shlib-thread-safety" || + erlang:system_info(threads) /= false], + Debug = [" --enable-debug-mode" || + string:str(erlang:system_info(system_version),"debug") > 0], + Args = Host ++ Build ++ Threads ++ Debug, case filelib:is_file(Configure) of true -> - Env = macosx_cflags(), - Port = open_port({spawn, Configure ++ Args}, - [stream, eof, {env,Env}]), + OSXEnv = macosx_cflags(), + io:format("Running ~sEnv: ~p~n", + [lists:flatten(Configure ++ Args),Env++OSXEnv]), + Port = open_port({spawn, lists:flatten(["\"",Configure,"\"",Args])}, + [stream, eof, {env,Env++OSXEnv}]), ts_lib:print_data(Port); false -> {error, no_configure_script} end. + +get_xcomp_flag(Flag, Flags) -> + get_xcomp_flag(Flag, Flag, Flags). +get_xcomp_flag(Flag, Tag, Flags) -> + case proplists:get_value(Flag,Flags) of + undefined -> ""; + "guess" -> [" --",Tag,"=",os:cmd("$ERL_TOP/erts/autoconf/config.guess")]; + HostVal -> [" --",Tag,"=",HostVal] + end. + + macosx_cflags() -> case os:type() of {unix, darwin} -> @@ -125,10 +162,33 @@ macosx_cflags() -> [] end. +parse_xcomp_file(undefined) -> + [{cross,"no"}]; +parse_xcomp_file(Filepath) -> + {ok,Bin} = file:read_file(Filepath), + Lines = binary:split(Bin,<<"\n">>,[global,trim]), + {Envs,Flags} = parse_xcomp_file(Lines,[],[]), + [{cross,"yes"},{crossroot,os:getenv("ERL_TOP")}, + {crossenv,Envs},{crossflags,Flags}]. + +parse_xcomp_file([<<A:8,_/binary>> = Line|R],Envs,Flags) + when $A =< A, A =< $Z -> + [Var,Value] = binary:split(Line,<<"=">>), + parse_xcomp_file(R,[{binary_to_list(Var), + binary_to_list(Value)}|Envs],Flags); +parse_xcomp_file([<<"erl_xcomp_",Line/binary>>|R],Envs,Flags) -> + [Var,Value] = binary:split(Line,<<"=">>), + parse_xcomp_file(R,Envs,[{binary_to_list(Var), + binary_to_list(Value)}|Flags]); +parse_xcomp_file([_|R],Envs,Flags) -> + parse_xcomp_file(R,Envs,Flags); +parse_xcomp_file([],Envs,Flags) -> + {lists:reverse(Envs),lists:reverse(Flags)}. + write_terms(Name, Terms) -> case file:open(Name, [write]) of {ok, Fd} -> - Result = write_terms1(Fd, Terms), + Result = write_terms1(Fd, remove_duplicates(Terms)), file:close(Fd), Result; {error, Reason} -> @@ -141,6 +201,17 @@ write_terms1(Fd, [Term|Rest]) -> write_terms1(_, []) -> ok. +remove_duplicates(List) -> + lists:reverse( + lists:foldl(fun({Key,Val},Acc) -> + R = make_ref(), + case proplists:get_value(Key,Acc,R) of + R -> [{Key,Val}|Acc]; + _Else -> + Acc + end + end,[],List)). + add_vars(Vars0, Opts0) -> {Opts,LongNames} = case lists:keymember(longnames, 1, Opts0) of @@ -209,12 +280,11 @@ platform(Vars) -> LC = lock_checking(), MT = modified_timing(), AsyncThreads = async_threads(), - HeapType = heap_type_label(), Debug = debug(), CpuBits = word_size(), Common = lists:concat([Hostname,"/",OsType,"/",CpuType,CpuBits,LinuxDist, Schedulers,BindType,KP,IOTHR,LC,MT,AsyncThreads, - HeapType,Debug,ExtraLabel]), + Debug,ExtraLabel]), PlatformId = lists:concat([ErlType, " ", Version, Common]), PlatformLabel = ErlType ++ Common, PlatformFilename = platform_as_filename(PlatformId), @@ -272,12 +342,6 @@ hostname() -> "/localhost" end. -heap_type_label() -> - case catch erlang:system_info(heap_type) of - hybrid -> "/Hybrid"; - _ -> "" %private - end. - async_threads() -> case catch erlang:system_info(threads) of true -> "/A"++integer_to_list(erlang:system_info(thread_pool_size)); diff --git a/lib/test_server/src/ts_install_cth.erl b/lib/test_server/src/ts_install_cth.erl index a41916fd0a..67f2df0cae 100644 --- a/lib/test_server/src/ts_install_cth.erl +++ b/lib/test_server/src/ts_install_cth.erl @@ -95,17 +95,12 @@ pre_init_per_suite(_Suite,Config,State) -> try {ok,Variables} = file:consult(filename:join(State#state.ts_conf_dir,"variables")), - - %% Make the stuff in all_SUITE_data if it exists - AllDir = filename:join(DataDir,"../all_SUITE_data"), - case filelib:is_dir(AllDir) of - true -> - make_non_erlang(AllDir,Variables); - false -> - ok + case proplists:get_value(cross,Variables) of + "yes" -> + ct:log("Not making data dir as tests have been cross compiled"); + _ -> + ts_lib:make_non_erlang(DataDir, Variables) end, - - make_non_erlang(DataDir, Variables), {add_node_name(Config, State), State} catch Error:Reason -> @@ -219,39 +214,6 @@ terminate(_State) -> %%% ============================================================================ %%% Local functions %%% ============================================================================ -%% Configure and run all the Makefiles in the data dirs of the suite -%% in question -make_non_erlang(DataDir, Variables) -> - {ok,CurrWD} = file:get_cwd(), - try - file:set_cwd(DataDir), - MakeCommand = proplists:get_value(make_command,Variables), - - FirstMakefile = filename:join(DataDir,"Makefile.first"), - case filelib:is_regular(FirstMakefile) of - true -> - ct:log("Making ~p",[FirstMakefile]), - ok = ts_make:make( - MakeCommand, DataDir, filename:basename(FirstMakefile)); - false -> - ok - end, - - MakefileSrc = filename:join(DataDir,"Makefile.src"), - MakefileDest = filename:join(DataDir,"Makefile"), - case filelib:is_regular(MakefileSrc) of - true -> - ok = ts_lib:subst_file(MakefileSrc,MakefileDest,Variables), - ct:log("Making ~p",[MakefileDest]), - ok = ts_make:make([{makefile,"Makefile"},{data_dir,DataDir} - | Variables]); - false -> - ok - end - after - file:set_cwd(CurrWD), - timer:sleep(100) - end. %% Add a nodename to config if it does not exist add_node_name(Config, State) -> diff --git a/lib/test_server/src/ts_lib.erl b/lib/test_server/src/ts_lib.erl index 2f0a4ea8c0..d521d2beda 100644 --- a/lib/test_server/src/ts_lib.erl +++ b/lib/test_server/src/ts_lib.erl @@ -24,10 +24,12 @@ %% Avoid warning for local function error/1 clashing with autoimported BIF. -compile({no_auto_import,[error/1]}). -export([error/1, var/2, erlang_type/0, + erlang_type/1, initial_capital/1, interesting_logs/1, specs/1, suites/2, last_test/1, force_write_file/2, force_delete/1, subst_file/3, subst/2, print_data/1, + make_non_erlang/2, maybe_atom_to_list/1, progress/4 ]). @@ -73,8 +75,10 @@ progress(Vars, Level, Format, Args) -> %% Returns: {Type, Version} where Type is otp|src erlang_type() -> + erlang_type(code:root_dir()). +erlang_type(RootDir) -> {_, Version} = init:script_id(), - RelDir = filename:join(code:root_dir(), "releases"), % Only in installed + RelDir = filename:join(RootDir, "releases"), % Only in installed case filelib:is_file(RelDir) of true -> {otp,Version}; % installed OTP false -> {srctree,Version} % source code tree @@ -333,3 +337,45 @@ maybe_atom_to_list(To_list) when is_list(To_list) -> maybe_atom_to_list(To_list) when is_atom(To_list)-> atom_to_list(To_list). + +%% Configure and run all the Makefiles in the data dir of the suite +%% in question +make_non_erlang(DataDir, Variables) -> + %% Make the stuff in all_SUITE_data if it exists + AllDir = filename:join(DataDir,"../all_SUITE_data"), + case filelib:is_dir(AllDir) of + true -> + make_non_erlang_do(AllDir,Variables); + false -> + ok + end, + make_non_erlang_do(DataDir, Variables). + +make_non_erlang_do(DataDir, Variables) -> + try + MakeCommand = proplists:get_value(make_command,Variables), + + FirstMakefile = filename:join(DataDir,"Makefile.first"), + case filelib:is_regular(FirstMakefile) of + true -> + io:format("Making ~p",[FirstMakefile]), + ok = ts_make:make( + MakeCommand, DataDir, filename:basename(FirstMakefile)); + false -> + ok + end, + + MakefileSrc = filename:join(DataDir,"Makefile.src"), + MakefileDest = filename:join(DataDir,"Makefile"), + case filelib:is_regular(MakefileSrc) of + true -> + ok = ts_lib:subst_file(MakefileSrc,MakefileDest,Variables), + io:format("Making ~p",[MakefileDest]), + ok = ts_make:make([{makefile,"Makefile"},{data_dir,DataDir} + | Variables]); + false -> + ok + end + after + timer:sleep(100) %% maybe unnecessary now when we don't do set_cwd anymore + end. diff --git a/lib/test_server/src/ts_make.erl b/lib/test_server/src/ts_make.erl index 3df66111a3..1d8ef230c7 100644 --- a/lib/test_server/src/ts_make.erl +++ b/lib/test_server/src/ts_make.erl @@ -25,12 +25,12 @@ %% Functions to be called from make test cases. make(Config) when is_list(Config) -> - DataDir = ?config(data_dir, Config), - Makefile = ?config(makefile, Config), - Make = ?config(make_command, Config), + DataDir = proplists:get_value(data_dir, Config), + Makefile = proplists:get_value(makefile, Config), + Make = proplists:get_value(make_command, Config), case make(Make, DataDir, Makefile) of ok -> ok; - {error,Reason} -> ?t:fail({make_failed,Reason}) + {error,Reason} -> exit({make_failed,Reason}) end. unmake(Config) when is_list(Config) -> @@ -85,7 +85,7 @@ run_make_script({win32, _}, Make, Dir, Makefile) -> {"run_make.bat", ".\\run_make", ["@echo off\r\n", - "cd ", filename:nativename(Dir), "\r\n", + "cd \"", filename:nativename(Dir), "\"\r\n", Make, " -f ", Makefile, " \r\n", "if errorlevel 1 echo *error*\r\n", "if not errorlevel 1 echo *ok*\r\n"]}; @@ -93,7 +93,7 @@ run_make_script({unix, _}, Make, Dir, Makefile) -> {"run_make", "/bin/sh ./run_make", ["#!/bin/sh\n", - "cd ", Dir, "\n", + "cd \"", Dir, "\"\n", Make, " -f ", Makefile, " 2>&1\n", "case $? in\n", " 0) echo '*ok*';;\n", diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl index 885a3c9b96..a61028e4bc 100644 --- a/lib/test_server/src/ts_run.erl +++ b/lib/test_server/src/ts_run.erl @@ -229,7 +229,7 @@ make_command(Vars, Spec, State) -> %% uncomment the line below to disable exception formatting %% " -test_server_format_exception false", " -boot start_sasl -sasl errlog_type error", - " -pz ",Cwd, + " -pz \"",Cwd,"\"", " -ct_test_vars ",TestVars, " -eval \"file:set_cwd(\\\"",TestDir,"\\\")\" " " -eval \"ct:run_test(", diff --git a/lib/test_server/test/Makefile b/lib/test_server/test/Makefile index 198440bb17..b2ac95afaa 100644 --- a/lib/test_server/test/Makefile +++ b/lib/test_server/test/Makefile @@ -83,10 +83,10 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(COVERFILE) $(RELSYSDIR) - $(INSTALL_DATA) test_server_test_lib.hrl test_server.spec test_server.cover $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(EMAKEFILE) $(ERL_FILES) $(COVERFILE) "$(RELSYSDIR)" + $(INSTALL_DATA) test_server_test_lib.hrl test_server.spec test_server.cover "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/toolbar/doc/src/Makefile b/lib/toolbar/doc/src/Makefile index 76147c111e..af907859d7 100644 --- a/lib/toolbar/doc/src/Makefile +++ b/lib/toolbar/doc/src/Makefile @@ -102,14 +102,14 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/toolbar/doc/src/notes.xml b/lib/toolbar/doc/src/notes.xml index ac6ad533fc..b901e6c8ff 100644 --- a/lib/toolbar/doc/src/notes.xml +++ b/lib/toolbar/doc/src/notes.xml @@ -31,6 +31,26 @@ <p>This document describes the changes made to the Toolbar application.</p> +<section><title>Toolbar 1.4.2.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>The GS applications is now deprecated and will be + removed in the R16 release. The following GS-based + applications have been superseded by the Observer + application and will removed in R16: Appmon, Pman, + Tv.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9907</p> + </item> + </list> + </section> + +</section> + <section><title>Toolbar 1.4.2.1</title> <section><title>Improvements and New Features</title> diff --git a/lib/toolbar/doc/src/toolbar.xml b/lib/toolbar/doc/src/toolbar.xml index 3ad0b4eb78..2580de2242 100644 --- a/lib/toolbar/doc/src/toolbar.xml +++ b/lib/toolbar/doc/src/toolbar.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1996</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/toolbar/doc/src/toolbar_chapter.xml b/lib/toolbar/doc/src/toolbar_chapter.xml index 4ea2101218..ebd47ef2fe 100644 --- a/lib/toolbar/doc/src/toolbar_chapter.xml +++ b/lib/toolbar/doc/src/toolbar_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1996</year><year>2009</year> + <year>1996</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/toolbar/src/Makefile b/lib/toolbar/src/Makefile index 14e1451609..91ce305e26 100644 --- a/lib/toolbar/src/Makefile +++ b/lib/toolbar/src/Makefile @@ -84,10 +84,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/toolbar/src/canvasbutton.erl b/lib/toolbar/src/canvasbutton.erl index 7613253efe..81e0ed43bb 100644 --- a/lib/toolbar/src/canvasbutton.erl +++ b/lib/toolbar/src/canvasbutton.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/toolbar/src/toolbar.erl b/lib/toolbar/src/toolbar.erl index b78df15700..b37c323008 100644 --- a/lib/toolbar/src/toolbar.erl +++ b/lib/toolbar/src/toolbar.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/toolbar/src/toolbar_graphics.erl b/lib/toolbar/src/toolbar_graphics.erl index b442d7ff06..c4c4273c63 100644 --- a/lib/toolbar/src/toolbar_graphics.erl +++ b/lib/toolbar/src/toolbar_graphics.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2009. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/toolbar/src/toolbar_toolconfig.erl b/lib/toolbar/src/toolbar_toolconfig.erl index 6fb56cb1bd..479033fa22 100644 --- a/lib/toolbar/src/toolbar_toolconfig.erl +++ b/lib/toolbar/src/toolbar_toolconfig.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-2012. 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 diff --git a/lib/toolbar/vsn.mk b/lib/toolbar/vsn.mk index b2b0764877..9fbfec871f 100644 --- a/lib/toolbar/vsn.mk +++ b/lib/toolbar/vsn.mk @@ -1,4 +1,4 @@ -TOOLBAR_VSN = 1.4.2.1 +TOOLBAR_VSN = 1.4.2.2 diff --git a/lib/tools/c_src/Makefile.in b/lib/tools/c_src/Makefile.in index b8c4aed6e2..0382d3228d 100644 --- a/lib/tools/c_src/Makefile.in +++ b/lib/tools/c_src/Makefile.in @@ -188,11 +188,11 @@ include ../vsn.mk RELSYSDIR = $(RELEASE_PATH)/lib/tools-$(TOOLS_VSN) release_spec: all - $(INSTALL_DIR) $(RELSYSDIR)/c_src - $(INSTALL_DATA) $(EMEM_SRCS) $(EMEM_HEADERS) $(RELSYSDIR)/c_src + $(INSTALL_DIR) "$(RELSYSDIR)/c_src" + $(INSTALL_DATA) $(EMEM_SRCS) $(EMEM_HEADERS) "$(RELSYSDIR)/c_src" ifneq ($(PROGS),) - $(INSTALL_DIR) $(RELSYSDIR)/bin - $(INSTALL_PROGRAM) $(PROGS) $(RELSYSDIR)/bin + $(INSTALL_DIR) "$(RELSYSDIR)/bin" + $(INSTALL_PROGRAM) $(PROGS) "$(RELSYSDIR)/bin" endif release_docs_spec: diff --git a/lib/tools/c_src/erl_memory.c b/lib/tools/c_src/erl_memory.c index 5239176d03..86e84723b1 100644 --- a/lib/tools/c_src/erl_memory.c +++ b/lib/tools/c_src/erl_memory.c @@ -1,7 +1,7 @@ /* * %CopyrightBegin% * - * Copyright Ericsson AB 2003-2010. All Rights Reserved. + * Copyright Ericsson AB 2003-2012. 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 diff --git a/lib/tools/doc/src/Makefile b/lib/tools/doc/src/Makefile index 433f123ae5..a76ce98ad6 100644 --- a/lib/tools/doc/src/Makefile +++ b/lib/tools/doc/src/Makefile @@ -121,14 +121,14 @@ clean clean_docs: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/tools/doc/src/eprof.xml b/lib/tools/doc/src/eprof.xml index 8b614d8860..1c5e38109b 100644 --- a/lib/tools/doc/src/eprof.xml +++ b/lib/tools/doc/src/eprof.xml @@ -67,9 +67,9 @@ <p><c>Rootset</c> is a list of pids and registered names.</p> <p>The function returns <c>profiling</c> if tracing could be enabled for all processes in <c>Rootset</c>, or <c>error</c> otherwise.</p> - <p>A pattern can be selected to narrow the profiling. For instance ca a specific - module be selected and only the code processes executes in that module will be - profiled.</p> + <p>A pattern can be selected to narrow the profiling. For instance a + specific module can be selected, and only the code executed in that + module will be profiled.</p> </desc> </func> <func> @@ -147,8 +147,8 @@ </type> <desc> <p>This function ensures that the results displayed by - <c>analyze/0,1,2</c> are printed both to - the file <c>File</c> and the screen.</p> + <c>analyze/0,1,2</c> are printed both to the file + <c>File</c> and the screen.</p> </desc> </func> <func> diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml index e24e1c5977..f4da132102 100644 --- a/lib/tools/doc/src/notes.xml +++ b/lib/tools/doc/src/notes.xml @@ -30,6 +30,31 @@ </header> <p>This document describes the changes made to the Tools application.</p> +<section><title>Tools 2.6.7</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Makefiles in erts, hipe and tools have been corrected to + enable parallel make, i.e MAKEFLAGS=-jX where X is the + parallelity number. As a result of this dependencies were + corrected since that is what is needed for parallel make + to work.</p> + <p> + Own Id: OTP-9857 Aux Id: OTP-9451 </p> + </item> + <item> + <p>Minor suppressions and fixes of compilation + warnings</p> + <p> + Own Id: OTP-10016</p> + </item> + </list> + </section> + +</section> + <section><title>Tools 2.6.6.6</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/tools/emacs/Makefile b/lib/tools/emacs/Makefile index 8533488463..e5ed56b412 100644 --- a/lib/tools/emacs/Makefile +++ b/lib/tools/emacs/Makefile @@ -71,9 +71,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/emacs + $(INSTALL_DIR) "$(RELSYSDIR)/emacs" $(INSTALL_DATA) $(EL_FILES) $(README_FILES) $(TEST_FILES) \ - $(RELSYSDIR)/emacs + "$(RELSYSDIR)/emacs" ifeq ($(DOCTYPE),pdf) release_docs_spec: @@ -82,7 +82,7 @@ ifeq ($(DOCTYPE),ps) release_docs_spec: else release_docs_spec: docs - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN_FILES) $(RELEASE_PATH)/man/man3 + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN_FILES) "$(RELEASE_PATH)/man/man3" endif endif diff --git a/lib/tools/emacs/erlang.el b/lib/tools/emacs/erlang.el index bc7a190fb4..8f98d6c85c 100644 --- a/lib/tools/emacs/erlang.el +++ b/lib/tools/emacs/erlang.el @@ -1516,7 +1516,7 @@ Other commands: ;; A dollar sign right before the double quote that ends a ;; string is not a character escape. ;; - ;; And a "string" has with a double quote not escaped by a + ;; And a "string" consists of a double quote not escaped by a ;; dollar sign, any number of non-backslash non-newline ;; characters or escaped backslashes, a dollar sign ;; (otherwise we wouldn't care) and a double quote. This @@ -1525,6 +1525,8 @@ Other commands: ;; know whether matching started inside a string: limiting ;; search to a single line keeps things sane. . (("\\(?:^\\|[^$]\\)\"\\(?:[^\"\n]\\|\\\\\"\\)*\\(\\$\\)\"" 1 "w") + ;; Likewise for atoms + ("\\(?:^\\|[^$]\\)'\\(?:[^'\n]\\|\\\\'\\)*\\(\\$\\)'" 1 "w") ;; And the dollar sign in $\" escapes two characters, not ;; just one. ("\\(\\$\\)\\\\\\\"" 1 "'")))))) @@ -2986,18 +2988,52 @@ This assumes that the preceding expression is either simple (forward-sexp (- arg)) (let ((col (current-column))) (skip-chars-backward " \t") - ;; Needed to match the colon in "'foo':'bar'". - (if (not (memq (preceding-char) '(?# ?:))) - col - ;; Special hack to handle: (note line break) - ;; [#myrecord{ - ;; foo = foo}] - (or - (ignore-errors - (backward-char 1) - (forward-sexp -1) - (current-column)) - col))))) + ;; Special hack to handle: (note line break) + ;; [#myrecord{ + ;; foo = foo}] + ;; where the call (forward-sexp -1) will fail when point is at the `#'. + (or + (ignore-errors + ;; Needed to match the colon in "'foo':'bar'". + (cond ((eq (preceding-char) ?:) + (backward-char 1) + (forward-sexp -1) + (current-column)) + ((eq (preceding-char) ?#) + ;; We may now be at: + ;; - either a construction of a new record + ;; - or update of a record, in which case we want + ;; the column of the expression to be updated. + ;; + ;; To see which of the two cases we are at, we first + ;; move an expression backwards, check for keywords, + ;; then immediately an expression forwards. Moving + ;; backwards skips past tokens like `,' or `->', but + ;; when moving forwards again, we won't skip past such + ;; tokens. We use this: if, after having moved + ;; forwards, we're back where we started, then it was + ;; a record update. + ;; The check for keywords is to detect cases like: + ;; case Something of #record_construction{...} + (backward-char 1) + (let ((record-start (point)) + (record-start-col (current-column))) + (forward-sexp -1) + (let ((preceding-expr-col (current-column)) + ;; white space definition according to erl_scan + (white-space "\000-\040\200-\240")) + (if (erlang-at-keyword) + ;; The (forward-sexp -1) call moved past a keyword + (1+ record-start-col) + (forward-sexp 1) + (skip-chars-forward white-space record-start) + ;; Are we back where we started? If so, it was an update. + (if (= (point) record-start) + preceding-expr-col + (goto-char record-start) + (1+ (current-column))))))) + (t col))) + col)))) (defun erlang-indent-parenthesis (stack-position) (let ((previous (erlang-indent-find-preceding-expr))) diff --git a/lib/tools/emacs/test.erl.indented b/lib/tools/emacs/test.erl.indented index 2948ccf1b5..45d9593000 100644 --- a/lib/tools/emacs/test.erl.indented +++ b/lib/tools/emacs/test.erl.indented @@ -215,6 +215,11 @@ highlighting(X) % Function definitions should be highlighted "char $in string", atom, + 'atom$', atom, 'atom$', atom, + 'atom\$', atom, + + 'char $in atom', atom, + $[, ${, $\\, atom, ?MACRO_1, ?MACRO_2(foo), @@ -657,3 +662,41 @@ indent_comprehensions() -> foo() -> [#foo{ foo = foo}]. + +%% Record indentation +some_function_with_a_very_long_name() -> + #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b}, + case dummy_function_with_a_very_very_long_name(x) of + #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b} -> + ok; + Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b} -> + Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b}; + #xyz{ + a=1, + b=2} -> + ok + end. + +another_function_with_a_very_very_long_name() -> + #rec{ + field1=1, + field2=1}. + +some_function_name_xyz(xyzzy, #some_record{ + field1=Field1, + field2=Field2}) -> + SomeVariable = f(#'Some-long-record-name'{ + field_a = 1, + 'inter-xyz-parameters' = + #'Some-other-very-long-record-name'{ + field2 = Field1, + field2 = Field2}}), + {ok, SomeVariable}. diff --git a/lib/tools/emacs/test.erl.orig b/lib/tools/emacs/test.erl.orig index 1221c5655e..e123150dd1 100644 --- a/lib/tools/emacs/test.erl.orig +++ b/lib/tools/emacs/test.erl.orig @@ -215,6 +215,11 @@ highlighting(X) % Function definitions should be highlighted "char $in string", atom, + 'atom$', atom, 'atom$', atom, + 'atom\$', atom, + + 'char $in atom', atom, + $[, ${, $\\, atom, ?MACRO_1, ?MACRO_2(foo), @@ -657,3 +662,41 @@ ok. foo() -> [#foo{ foo = foo}]. + +%% Record indentation +some_function_with_a_very_long_name() -> + #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b}, + case dummy_function_with_a_very_very_long_name(x) of + #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b} -> + ok; + Var = #'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b} -> + Var#'a-long-record-name-like-it-sometimes-is-with-asn.1-records'{ + field1=a, + field2=b}; + #xyz{ + a=1, + b=2} -> + ok + end. + +another_function_with_a_very_very_long_name() -> + #rec{ + field1=1, + field2=1}. + +some_function_name_xyz(xyzzy, #some_record{ + field1=Field1, + field2=Field2}) -> + SomeVariable = f(#'Some-long-record-name'{ + field_a = 1, + 'inter-xyz-parameters' = + #'Some-other-very-long-record-name'{ + field2 = Field1, + field2 = Field2}}), + {ok, SomeVariable}. diff --git a/lib/tools/examples/Makefile b/lib/tools/examples/Makefile index 9fb8434633..fc14ee54d6 100644 --- a/lib/tools/examples/Makefile +++ b/lib/tools/examples/Makefile @@ -50,7 +50,7 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/examples - $(INSTALL_DATA) $(EXAMPLE_FILES) $(RELSYSDIR)/examples + $(INSTALL_DIR) "$(RELSYSDIR)/examples" + $(INSTALL_DATA) $(EXAMPLE_FILES) "$(RELSYSDIR)/examples" release_docs_spec: diff --git a/lib/tools/priv/Makefile b/lib/tools/priv/Makefile index 6fea580c00..b32ba5820d 100644 --- a/lib/tools/priv/Makefile +++ b/lib/tools/priv/Makefile @@ -58,9 +58,9 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TOOL_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(HTDOCS_FILES) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TOOL_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/tools/src/Makefile b/lib/tools/src/Makefile index 360f4f8f29..4c0cc8cde7 100644 --- a/lib/tools/src/Makefile +++ b/lib/tools/src/Makefile @@ -105,10 +105,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(YRL_FILE) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \ - $(RELSYSDIR)/ebin + "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/tools/src/fprof.erl b/lib/tools/src/fprof.erl index 8165a6c13a..4cbb910f11 100644 --- a/lib/tools/src/fprof.erl +++ b/lib/tools/src/fprof.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 @@ -89,9 +89,7 @@ dbg(_, _, _) -> apply({M, F}, Args) when is_atom(M), is_atom(F), is_list(Args) -> - Arity = length(Args), - Function = fun M:F/Arity, - apply_1(Function, Args, []); + apply_1(M, F, Args, []); apply(Fun, Args) when is_function(Fun), is_list(Args) -> apply_1(Fun, Args, []); @@ -99,26 +97,25 @@ apply(A, B) -> erlang:error(badarg, [A, B]). apply(M, F, Args) when is_atom(M), is_atom(F), is_list(Args) -> - apply_1({M, F}, Args, []); + apply_1(M, F, Args, []); apply({M, F}, Args, Options) when is_atom(M), is_atom(F), is_list(Args), is_list(Options) -> - Arity = length(Args), - Function = fun M:F/Arity, - apply_1(Function, Args, Options); + apply_1(M, F, Args, Options); apply(Fun, Args, Options) when is_function(Fun), is_list(Args), is_list(Options) -> apply_1(Fun, Args, Options); apply(A, B, C) -> erlang:error(badarg, [A, B, C]). -apply(Module, Function, Args, Options) - when is_atom(Module), is_atom(Function), is_list(Args), is_list(Options) -> - Arity = length(Args), - Fun = fun Module:Function/Arity, - apply_1(Fun, Args, Options); +apply(M, F, Args, Options) + when is_atom(M), is_atom(F), is_list(Args), is_list(Options) -> + apply_1(M, F, Args, Options); apply(A, B, C, D) -> erlang:error(badarg, [A, B, C, D]). +apply_1(M, F, Args, Options) -> + Arity = length(Args), + apply_1(fun M:F/Arity, Args, Options). apply_1(Function, Args, Options) -> {[_, Procs, Continue], Options_1} = diff --git a/lib/tools/src/xref_utils.erl b/lib/tools/src/xref_utils.erl index 9d4a175d88..680563e9df 100644 --- a/lib/tools/src/xref_utils.erl +++ b/lib/tools/src/xref_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2010. All Rights Reserved. +%% Copyright Ericsson AB 2000-2012. 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 @@ -141,7 +141,7 @@ is_string([], _) -> is_string(Term, C) -> is_string1(Term, C). -is_string1([H | T], C) when H > C, H < 127 -> +is_string1([H | T], C) when H > C -> is_string1(T, C); is_string1([], _) -> true; diff --git a/lib/tools/test/Makefile b/lib/tools/test/Makefile index 8019b7269f..b9a42041a2 100644 --- a/lib/tools/test/Makefile +++ b/lib/tools/test/Makefile @@ -84,11 +84,11 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: make_emakefile - $(INSTALL_DIR) $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" $(INSTALL_DATA) $(SPEC_FILES) $(COVER_FILE) $(EMAKEFILE) \ - $(ERL_FILES) $(RELSYSDIR) - chmod -R u+w $(RELSYSDIR) - @tar cf - *_SUITE_data | (cd $(RELSYSDIR); tar xf -) + $(ERL_FILES) "$(RELSYSDIR)" + chmod -R u+w "$(RELSYSDIR)" + @tar cf - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) release_docs_spec: diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index 576d7e261c..0cd53a05db 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -574,14 +574,7 @@ otp_5418(Config) when is_list(Config) -> ok. -otp_6115(suite) -> []; otp_6115(Config) when is_list(Config) -> - case erlang:system_info(heap_type) of - hybrid -> {skip,"Hybrid-heap emulator doesn't keep track of funs"}; - _ -> otp_6115_1(Config) - end. - -otp_6115_1(Config) -> ?line {ok, CWD} = file:get_cwd(), ?line Dir = filename:join(?config(data_dir, Config), otp_6115), ?line ok = file:set_cwd(Dir), diff --git a/lib/tools/test/fprof_SUITE.erl b/lib/tools/test/fprof_SUITE.erl index f491c1e227..1bc4c11b5d 100644 --- a/lib/tools/test/fprof_SUITE.erl +++ b/lib/tools/test/fprof_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-2012. 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 diff --git a/lib/tools/test/xref_SUITE.erl b/lib/tools/test/xref_SUITE.erl index 78e49044a5..fd3e111d8d 100644 --- a/lib/tools/test/xref_SUITE.erl +++ b/lib/tools/test/xref_SUITE.erl @@ -53,7 +53,7 @@ analyze/1, basic/1, md/1, q/1, variables/1, unused_locals/1]). -export([ - format_error/1, otp_7423/1, otp_7831/1]). + format_error/1, otp_7423/1, otp_7831/1, otp_10192/1]). -import(lists, [append/2, flatten/1, keysearch/3, member/2, sort/1, usort/1]). @@ -86,7 +86,7 @@ groups() -> fun_mfa_r14, fun_mfa_vars, qlc]}, {analyses, [], [analyze, basic, md, q, variables, unused_locals]}, - {misc, [], [format_error, otp_7423, otp_7831]}]. + {misc, [], [format_error, otp_7423, otp_7831, otp_10192]}]. init_per_suite(Config) -> init(Config). @@ -2515,6 +2515,18 @@ otp_7831(Conf) when is_list(Conf) -> ?line xref:stop(Pid2), ok. +otp_10192(suite) -> []; +otp_10192(doc) -> + ["OTP-10192. Allow filenames with character codes greater than 126."]; +otp_10192(Conf) when is_list(Conf) -> + PrivDir = ?privdir, + {ok, _Pid} = xref:start(s), + Dir = filename:join(PrivDir, "�"), + ok = file:make_dir(Dir), + {ok, []} = xref:add_directory(s, Dir), + xref:stop(s), + ok. + %%% %%% Utilities %%% diff --git a/lib/tools/vsn.mk b/lib/tools/vsn.mk index 269d3d7773..788ee12900 100644 --- a/lib/tools/vsn.mk +++ b/lib/tools/vsn.mk @@ -1 +1 @@ -TOOLS_VSN = 2.6.6.6 +TOOLS_VSN = 2.6.8 diff --git a/lib/tv/doc/src/Makefile b/lib/tv/doc/src/Makefile index 21478d0931..e36dd84b73 100644 --- a/lib/tv/doc/src/Makefile +++ b/lib/tv/doc/src/Makefile @@ -125,14 +125,14 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/tv/doc/src/notes.xml b/lib/tv/doc/src/notes.xml index 97c99e6202..9217c018e9 100644 --- a/lib/tv/doc/src/notes.xml +++ b/lib/tv/doc/src/notes.xml @@ -30,6 +30,26 @@ </header> <p>This document describes the changes made to the TV application.</p> +<section><title>TV 2.1.4.9</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>The GS applications is now deprecated and will be + removed in the R16 release. The following GS-based + applications have been superseded by the Observer + application and will removed in R16: Appmon, Pman, + Tv.</p> + <p> + *** POTENTIAL INCOMPATIBILITY ***</p> + <p> + Own Id: OTP-9907</p> + </item> + </list> + </section> + +</section> + <section><title>TV 2.1.4.8</title> <section><title>Improvements and New Features</title> diff --git a/lib/tv/doc/src/table_visualizer_chapter.xml b/lib/tv/doc/src/table_visualizer_chapter.xml index dbfd322945..8b757f82f3 100644 --- a/lib/tv/doc/src/table_visualizer_chapter.xml +++ b/lib/tv/doc/src/table_visualizer_chapter.xml @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>1997</year><year>2009</year> + <year>1997</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/tv/doc/src/tv.xml b/lib/tv/doc/src/tv.xml index 83bbdfc052..05dd60b34a 100644 --- a/lib/tv/doc/src/tv.xml +++ b/lib/tv/doc/src/tv.xml @@ -5,7 +5,7 @@ <header> <copyright> <year>1997</year> - <year>2011</year> + <year>2012</year> <holder>Ericsson AB, All Rights Reserved</holder> </copyright> <legalnotice> diff --git a/lib/tv/priv/Makefile b/lib/tv/priv/Makefile index 69e3e32c8a..73ea8f4ee9 100644 --- a/lib/tv/priv/Makefile +++ b/lib/tv/priv/Makefile @@ -64,8 +64,8 @@ clean: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) $(TOOLBAR_FILES) $(RELSYSDIR)/priv + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) $(TOOLBAR_FILES) "$(RELSYSDIR)/priv" release_docs_spec: diff --git a/lib/tv/src/Makefile b/lib/tv/src/Makefile index 457b9d38c4..f078e5dee1 100644 --- a/lib/tv/src/Makefile +++ b/lib/tv/src/Makefile @@ -126,10 +126,10 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/tv/src/tv_db.erl b/lib/tv/src/tv_db.erl index 179b75c2e6..75537418b3 100644 --- a/lib/tv/src/tv_db.erl +++ b/lib/tv/src/tv_db.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_db_search.erl b/lib/tv/src/tv_db_search.erl index 6ae999253a..edfa57df04 100644 --- a/lib/tv/src/tv_db_search.erl +++ b/lib/tv/src/tv_db_search.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 diff --git a/lib/tv/src/tv_etsread.erl b/lib/tv/src/tv_etsread.erl index b3e38f9d45..32f111c9a1 100644 --- a/lib/tv/src/tv_etsread.erl +++ b/lib/tv/src/tv_etsread.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_info.erl b/lib/tv/src/tv_info.erl index 941286362c..c744888c38 100644 --- a/lib/tv/src/tv_info.erl +++ b/lib/tv/src/tv_info.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_ip.erl b/lib/tv/src/tv_ip.erl index 2d3ada878a..9f66917362 100644 --- a/lib/tv/src/tv_ip.erl +++ b/lib/tv/src/tv_ip.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_main.erl b/lib/tv/src/tv_main.erl index fbf56971f9..b6ffbd7c49 100644 --- a/lib/tv/src/tv_main.erl +++ b/lib/tv/src/tv_main.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 diff --git a/lib/tv/src/tv_new_table.erl b/lib/tv/src/tv_new_table.erl index d31b9a4ee2..779835d78a 100644 --- a/lib/tv/src/tv_new_table.erl +++ b/lib/tv/src/tv_new_table.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 diff --git a/lib/tv/src/tv_nodewin.erl b/lib/tv/src/tv_nodewin.erl index 9030ed930f..8376c4a7aa 100644 --- a/lib/tv/src/tv_nodewin.erl +++ b/lib/tv/src/tv_nodewin.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 diff --git a/lib/tv/src/tv_pb.erl b/lib/tv/src/tv_pb.erl index fa3dcde919..81e7e7e2d1 100644 --- a/lib/tv/src/tv_pb.erl +++ b/lib/tv/src/tv_pb.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pb_funcs.erl b/lib/tv/src/tv_pb_funcs.erl index 0670d2795f..24d1120597 100644 --- a/lib/tv/src/tv_pb_funcs.erl +++ b/lib/tv/src/tv_pb_funcs.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pc.erl b/lib/tv/src/tv_pc.erl index fcb7aba3a7..e0612e13eb 100644 --- a/lib/tv/src/tv_pc.erl +++ b/lib/tv/src/tv_pc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pc_menu_handling.erl b/lib/tv/src/tv_pc_menu_handling.erl index 5d411b106e..64a06743b7 100644 --- a/lib/tv/src/tv_pc_menu_handling.erl +++ b/lib/tv/src/tv_pc_menu_handling.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pd.erl b/lib/tv/src/tv_pd.erl index 6694ea22a3..6c38148fdd 100644 --- a/lib/tv/src/tv_pd.erl +++ b/lib/tv/src/tv_pd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pd_display.erl b/lib/tv/src/tv_pd_display.erl index dab442e28e..804180da16 100644 --- a/lib/tv/src/tv_pd_display.erl +++ b/lib/tv/src/tv_pd_display.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pd_frames.erl b/lib/tv/src/tv_pd_frames.erl index d18dcaf70d..aab40e2bba 100644 --- a/lib/tv/src/tv_pd_frames.erl +++ b/lib/tv/src/tv_pd_frames.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pd_scale.erl b/lib/tv/src/tv_pd_scale.erl index 2f98c3183f..04af2bb981 100644 --- a/lib/tv/src/tv_pd_scale.erl +++ b/lib/tv/src/tv_pd_scale.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pg.erl b/lib/tv/src/tv_pg.erl index 0b36b18212..7194cfb756 100644 --- a/lib/tv/src/tv_pg.erl +++ b/lib/tv/src/tv_pg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pg_gridfcns.erl b/lib/tv/src/tv_pg_gridfcns.erl index e47dac28a8..d037b42294 100644 --- a/lib/tv/src/tv_pg_gridfcns.erl +++ b/lib/tv/src/tv_pg_gridfcns.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2010. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_poll_dialog.erl b/lib/tv/src/tv_poll_dialog.erl index 4bf49f44f1..e33d398898 100644 --- a/lib/tv/src/tv_poll_dialog.erl +++ b/lib/tv/src/tv_poll_dialog.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pw.erl b/lib/tv/src/tv_pw.erl index bd6d06e241..5f88c9f1fd 100644 --- a/lib/tv/src/tv_pw.erl +++ b/lib/tv/src/tv_pw.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_pw_window.erl b/lib/tv/src/tv_pw_window.erl index 0cd241a031..81f29dc41c 100644 --- a/lib/tv/src/tv_pw_window.erl +++ b/lib/tv/src/tv_pw_window.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2009. All Rights Reserved. +%% Copyright Ericsson AB 1997-2012. 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 diff --git a/lib/tv/src/tv_rec_edit.erl b/lib/tv/src/tv_rec_edit.erl index f6c09ebc67..3f9ea8b5f5 100644 --- a/lib/tv/src/tv_rec_edit.erl +++ b/lib/tv/src/tv_rec_edit.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 diff --git a/lib/tv/src/tv_utils.erl b/lib/tv/src/tv_utils.erl index 9c7458d302..799e3994cb 100644 --- a/lib/tv/src/tv_utils.erl +++ b/lib/tv/src/tv_utils.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2009. All Rights Reserved. +%% Copyright Ericsson AB 1998-2012. 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 diff --git a/lib/tv/vsn.mk b/lib/tv/vsn.mk index 756aae2096..1bb8ca30f8 100644 --- a/lib/tv/vsn.mk +++ b/lib/tv/vsn.mk @@ -1 +1 @@ -TV_VSN = 2.1.4.8 +TV_VSN = 2.1.4.9 diff --git a/lib/typer/src/Makefile b/lib/typer/src/Makefile index 620b3ebb69..c3fae0ad42 100644 --- a/lib/typer/src/Makefile +++ b/lib/typer/src/Makefile @@ -101,10 +101,10 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src + $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(YRL_FILES) \ - $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(INSTALL_FILES) $(RELSYSDIR)/ebin + "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(INSTALL_FILES) "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/typer/src/typer.erl b/lib/typer/src/typer.erl index 6392f5765f..1e40b8926e 100644 --- a/lib/typer/src/typer.erl +++ b/lib/typer/src/typer.erl @@ -83,6 +83,7 @@ start() -> {Args, Analysis} = process_cl_args(), %% io:format("Args: ~p\n", [Args]), %% io:format("Analysis: ~p\n", [Analysis]), + Timer = dialyzer_timing:init(false), TrustedFiles = filter_fd(Args#args.trusted, [], fun is_erl_file/1), Analysis2 = extract(Analysis, TrustedFiles), All_Files = get_all_files(Args), @@ -91,6 +92,7 @@ start() -> Analysis4 = collect_info(Analysis3), %% io:format("Final: ~p\n", [Analysis4#analysis.fms]), TypeInfo = get_type_info(Analysis4), + dialyzer_timing:stop(Timer), show_or_annotate(TypeInfo), %% io:format("\nTyper analysis finished\n"), erlang:halt(0). @@ -181,7 +183,6 @@ get_type_info(#analysis{callgraph = CallGraph, remove_external(CallGraph, PLT) -> {StrippedCG0, Ext} = dialyzer_callgraph:remove_external(CallGraph), - StrippedCG = dialyzer_callgraph:finalize(StrippedCG0), case get_external(Ext, PLT) of [] -> ok; Externals -> @@ -192,7 +193,7 @@ remove_external(CallGraph, PLT) -> _ -> msg(io_lib:format(" Unknown types: ~p\n", [ExtTypes])) end end, - StrippedCG. + StrippedCG0. -spec get_external([{mfa(), mfa()}], plt()) -> [mfa()]. @@ -901,8 +902,9 @@ analyze_core_tree(Core, Records, SpecInfo, CbInfo, ExpTypes, Analysis, File) -> MergedExpTypes = sets:union(ExpTypes, OldExpTypes), CS6 = dialyzer_codeserver:insert_temp_exported_types(MergedExpTypes, CS5), Ex_Funcs = [{0,F,A} || {_,_,{F,A}} <- cerl:module_exports(Tree)], - TmpCG = Analysis#analysis.callgraph, - CG = dialyzer_callgraph:scan_core_tree(Tree, TmpCG), + CG = Analysis#analysis.callgraph, + {V, E} = dialyzer_callgraph:scan_core_tree(Tree, CG), + dialyzer_callgraph:add_edges(E, V, CG), Fun = fun analyze_one_function/2, All_Defs = cerl:module_defs(Tree), Acc = lists:foldl(Fun, #tmpAcc{file = File, module = Module}, All_Defs), diff --git a/lib/webtool/doc/src/Makefile b/lib/webtool/doc/src/Makefile index 7b86752738..32269e9424 100644 --- a/lib/webtool/doc/src/Makefile +++ b/lib/webtool/doc/src/Makefile @@ -112,16 +112,16 @@ debug opt: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man1 - $(INSTALL_DATA) $(MAN1_FILES) $(RELEASE_PATH)/man/man1 - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DATA) $(MAN1_FILES) "$(RELEASE_PATH)/man/man1" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/webtool/priv/Makefile b/lib/webtool/priv/Makefile index 6e1c6606fe..6af997c2bb 100644 --- a/lib/webtool/priv/Makefile +++ b/lib/webtool/priv/Makefile @@ -67,14 +67,14 @@ docs: include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DIR) $(RELSYSDIR)/priv/root - $(INSTALL_DIR) $(RELSYSDIR)/priv/root/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/root/doc - $(INSTALL_DATA) $(HTDOCS_FILES) $(RELSYSDIR)/priv/root/doc - $(INSTALL_DATA) $(WEBSERVER_CONFIG_FILES) $(RELSYSDIR)/priv/root/conf - $(INSTALL_DIR) $(RELSYSDIR)/priv/bin - $(INSTALL_SCRIPT) $(SCRIPTS) $(RELSYSDIR)/priv/bin + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/root" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/root/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/root/doc" + $(INSTALL_DATA) $(HTDOCS_FILES) "$(RELSYSDIR)/priv/root/doc" + $(INSTALL_DATA) $(WEBSERVER_CONFIG_FILES) "$(RELSYSDIR)/priv/root/conf" + $(INSTALL_DIR) "$(RELSYSDIR)/priv/bin" + $(INSTALL_SCRIPT) $(SCRIPTS) "$(RELSYSDIR)/priv/bin" release_docs_spec: diff --git a/lib/webtool/src/Makefile b/lib/webtool/src/Makefile index 62845cd370..45e8b2a6f0 100644 --- a/lib/webtool/src/Makefile +++ b/lib/webtool/src/Makefile @@ -87,11 +87,11 @@ $(APPUP_TARGET): $(APPUP_SRC) ../vsn.mk include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" $(INSTALL_DATA) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) \ - $(RELSYSDIR)/ebin + "$(RELSYSDIR)/ebin" release_docs_spec: diff --git a/lib/wx/api_gen/gl_gen.erl b/lib/wx/api_gen/gl_gen.erl index ce0cb922e9..331ba32ba4 100644 --- a/lib/wx/api_gen/gl_gen.erl +++ b/lib/wx/api_gen/gl_gen.erl @@ -190,14 +190,17 @@ parse_define([#xmlElement{name=initializer,content=[#xmlText{value=V}]}|_],Def,_ try case Val0 of "0x" ++ Val1 -> - Val = http_util:hexlist_to_integer(Val1), - Def#def{val=Val, type=hex}; + _ = http_util:hexlist_to_integer(Val1), + Def#def{val=Val1, type=hex}; _ -> Val = list_to_integer(Val0), Def#def{val=Val, type=int} end - catch _:_ -> - Def#def{val=Val0, type=string} + catch _:_ -> + case catch list_to_float(Val0) of + {'EXIT', _} -> Def#def{val=Val0, type=string}; + _ -> Def#def{val=Val0, type=float_str} + end end; parse_define([_|R], D, Opts) -> parse_define(R, D, Opts); diff --git a/lib/wx/api_gen/gl_gen_erl.erl b/lib/wx/api_gen/gl_gen_erl.erl index f77b2d8e24..25f89e4ad4 100644 --- a/lib/wx/api_gen/gl_gen_erl.erl +++ b/lib/wx/api_gen/gl_gen_erl.erl @@ -54,8 +54,10 @@ glu_defines(Defs) -> gen_define(#def{name=N, val=Val, type=int}) -> w("-define(~s, ~p).~n", [N,Val]); +gen_define(#def{name=N, val=Val, type=float_str}) -> + w("-define(~s, ~s).~n", [N,Val]); gen_define(#def{name=N, val=Val, type=hex}) -> - w("-define(~s, ~.16#).~n", [N,Val]); + w("-define(~s, 16#~s).~n", [N,Val]); gen_define(#def{name=N, val=Val, type=string}) -> w("-define(~s, ?~s).~n", [N,Val]); gen_define(#def{name="GLEXT_64_TYPES"++_, val=undefined, type=undefined}) -> diff --git a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl index 23a34225ca..c5802af679 100644 --- a/lib/wx/api_gen/wx_extra/wxEvtHandler.erl +++ b/lib/wx/api_gen/wx_extra/wxEvtHandler.erl @@ -61,7 +61,7 @@ connect(This, EventType) -> %% {userData, term()} An erlang term that will be sent with the event. Default: []. -spec connect(This::wxEvtHandler(), EventType::wxEventType(), [Option]) -> ok when Option :: {id, integer()} | {lastId, integer()} | {skip, boolean()} | - {callback, function()} | {userData, term()}. + callback | {callback, function()} | {userData, term()}. connect(This=#wx_ref{type=ThisT}, EventType, Options) -> EvH = parse_opts(Options, #evh{et=EventType}), ?CLASS(ThisT,wxEvtHandler), diff --git a/lib/wx/api_gen/wx_gen_cpp.erl b/lib/wx/api_gen/wx_gen_cpp.erl index 69e7510a95..2209e4a53b 100644 --- a/lib/wx/api_gen/wx_gen_cpp.erl +++ b/lib/wx/api_gen/wx_gen_cpp.erl @@ -350,8 +350,12 @@ declare_type(N,false,_,#type{name="wxDateTime"}) -> w(" wxDateTime ~s;~n", [N]); declare_type(N,false,_,#type{name="wxColour"}) -> w(" wxColour ~s;~n", [N]); +declare_type(N,false,_,#type{name=Type, base=int, ref=reference}) -> + w(" ~s ~s;~n", [Type,N]); declare_type(N,false,_,#type{name=Type, base=int64, ref=reference}) -> w(" ~s ~s;~n", [Type,N]); +declare_type(N,false,_,#type{base={comp,_,_},single=true,name=Type,ref=reference}) -> + w(" ~s ~s;~n", [Type,N]); declare_type(N,true,Def,#type{base=Base,single=true,name=Type,by_val=true}) when Base =:= int; Base =:= long; Base =:= float; Base =:= double; Base =:= bool -> w(" ~s ~s=~s;~n", [Type,N,Def]); @@ -812,6 +816,7 @@ call_arg(#param{name=N,in=false,type=#type{by_val=false, single=true}}) -> "&" + call_arg(#param{name=N,def=Def,type=#type{base={comp,_,_},ref={pointer,1},single=true}}) when Def =:= none -> "&" ++N; +call_arg(#param{name=N,type=#type{base=int, ref=reference, single=true}}) -> "*" ++ N; call_arg(#param{name=N,type=#type{by_val=false}}) -> N; call_arg(#param{name=N,type={merged,_,#type{base={class,_},single=true, by_val=ByVal, @@ -888,7 +893,7 @@ build_return_vals(Type,Ps) -> build_ret_types(void,Ps) -> Calc = fun(#param{name=N,in=False,type=T}, Free) when False =/= true -> - case build_ret(N, False, T) of + case build_ret(N, {arg, False}, T) of ok -> Free; Other -> [Other|Free] end; @@ -896,12 +901,12 @@ build_ret_types(void,Ps) -> end, lists:foldl(Calc, [], Ps); build_ret_types(Type,Ps) -> - Free = case build_ret("Result", out, Type) of + Free = case build_ret("Result", {ret, out}, Type) of ok -> []; FreeStr -> [FreeStr] end, Calc = fun(#param{name=N,in=False,type=T}, FreeAcc) when False =/= true -> - case build_ret(N, False, T) of + case build_ret(N, {arg, False}, T) of ok -> FreeAcc; FreeMe -> [FreeMe|FreeAcc] end; @@ -927,13 +932,13 @@ build_ret(Name,_,#type{base={enum,_Type},single=true}) -> w(" rt.addInt(~s);~n",[Name]); build_ret(Name,_,#type{base={comp,_,{record, _}},single=true}) -> w(" rt.add(~s);~n", [Name]); -build_ret(Name,_,#type{base={comp,_,_},single=true, ref=reference}) -> +build_ret(Name,{ret,_},#type{base={comp,_,_},single=true, ref=reference}) -> w(" rt.add((*~s));~n",[Name]); build_ret(Name,_,#type{base={comp,_,_},single=true}) -> w(" rt.add(~s);~n",[Name]); build_ret(Name,_,#type{base=bool,single=true,by_val=true}) -> w(" rt.addBool(~s);~n",[Name]); -build_ret(Name,both,#type{base=int,single=true,mod=M}) -> +build_ret(Name,{arg, both},#type{base=int,single=true,mod=M}) -> case lists:member(unsigned, M) of true -> w(" rt.addUint(*~s);~n",[Name]); false -> w(" rt.addInt(*~s);~n",[Name]) diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl index aac57586bc..a8f23575f3 100644 --- a/lib/wx/api_gen/wx_gen_erl.erl +++ b/lib/wx/api_gen/wx_gen_erl.erl @@ -177,7 +177,18 @@ parents_check([Parent|Ps]) -> w("parent_class(~s) -> true;~n",[Parent]), parents_check(Ps). -check_class(#type{base={class,"wx"}}) -> ok; +check_class(#type{name="wxObject", base={class,"wx"}}) -> + "wx:wx_object()"; +check_class(#type{name="wxIconLocation", base={class,"wx"}}) -> + "wx:wx_object()"; +check_class(#type{name="wxToolBarToolBase", base={class,"wx"}, mod=Mod}) -> + %% Implement this some day + "wx:wx_object()"; +check_class(#type{name="wxValidator", base={class,"wx"}, mod=Mod}) -> + %% Implement this some day + "wx:wx_object()"; +check_class(#type{name=Name, base={class,"wx"}}) -> + exit({class, Name}); check_class(#type{base={class,Name},xml=Xml}) -> case get({class,Name}) of undefined -> @@ -190,12 +201,15 @@ check_class(#type{base={class,Name},xml=Xml}) -> _ -> ?warning("~s:~s: Class ~p used but not defined~n (see ~p)~n", [get(current_class),get(current_func),Name, Xml]) - end; + end, + "wx:wx_object()"; _ -> ?warning("~s:~s: Class ~p used is enum~n", - [get(current_class),get(current_func),Name]) + [get(current_class),get(current_func),Name]), + exit(class_enum) end; - _ -> ok + _ -> + Name ++ ":" ++ Name ++ "()" end. gen_export(#class{name=Class,abstract=Abs},Ms0) -> @@ -749,8 +763,8 @@ doc_arg_type3(#type{base=string}, in) -> "unicode:chardata()"; doc_arg_type3(#type{base=string}, out) -> "unicode:charlist()"; doc_arg_type3(#type{name="wxChar", single=S},in) when S =/= true -> "unicode:chardata()"; doc_arg_type3(#type{name="wxChar", single=S},out) when S =/= true -> "unicode:charlist()"; -doc_arg_type3(#type{name="wxArrayString"},in) -> "[unicode:chardata()]"; -doc_arg_type3(#type{name="wxArrayString"},out) -> "[unicode:charlist()]"; +doc_arg_type3(#type{name="wxArrayString"},in) -> "unicode:chardata()"; +doc_arg_type3(#type{name="wxArrayString"},out) -> "unicode:charlist()"; doc_arg_type3(#type{name="wxDateTime"}, _) -> "wx:wx_datetime()"; doc_arg_type3(#type{name="wxArtClient"}, _) -> "unicode:chardata()"; doc_arg_type3(#type{base=int}, _) -> "integer()"; @@ -766,21 +780,21 @@ doc_arg_type3(#type{base=eventType}, _) -> "atom()"; doc_arg_type3(#type{base={ref,N}}, _) -> N++"()"; doc_arg_type3(#type{base={term,_N}}, _) -> "term()"; doc_arg_type3(T=#type{base={class,N}}, _) -> - check_class(T), - case get(current_class) of - N -> N ++ "()"; - _ -> N++":" ++ N++"()" + ClassType = check_class(T), + Current = get(current_class), + if N =:= Current -> N ++ "()"; + true -> ClassType end; doc_arg_type3({merged,_,T1=#type{base={class,N1}},_,_,T2=#type{base={class,N2}},_}, _) -> - check_class(T1), - check_class(T2), + CT1 = check_class(T1), + CT2 = check_class(T2), Curr = get(current_class), if - N1 =:= Curr, N2 =:= Curr -> N1++"() | "++ N2++"()"; - N1 =:= Curr -> N1++"() | "++ N2++":" ++ N2++"()"; - N2 =:= Curr -> N1++":" ++ N1++"() | "++ N2++"()"; + N1 =:= Curr, N2 =:= Curr -> N1++"()"; + N1 =:= Curr -> N1++"() | "++ CT2; + N2 =:= Curr -> CT1 ++ " | "++ N2++"()"; true -> - N1++":" ++ N1++"() | "++ N2++":" ++ N2++"()" + CT1 ++ " | " ++ CT2 end; %% doc_arg_type3(#type{base={enum,{_,N}}}, _) -> uppercase(N); %% doc_arg_type3(#type{base={enum,N}}, _) -> uppercase(N); @@ -1010,12 +1024,13 @@ enum_name(Name) -> gen_enums_ints() -> %% open_write("../include/wx.hrl"), opened in gen_event_recs w("~n%% Hardcoded Records~n", []), - w("-record(wxMouseState, {x, y, %% integer()~n" - " leftDown, middleDown, rightDown, %% bool()~n" - " controlDown, shiftDown, altDown, metaDown, cmdDown %% bool()~n" + w("-record(wxMouseState, {x :: integer(), y :: integer(),~n" + " leftDown :: boolean(), middleDown :: boolean, rightDown :: boolean, ~n" + " controlDown :: boolean(), shiftDown :: boolean(),~n" + " altDown :: boolean(), metaDown :: boolean(), cmdDown :: boolean()~n" " }).~n", []), w("-record(wxHtmlLinkInfo, {~n" - " href, target %% unicode:chardata()~n" + " href :: unicode:chardata(), target :: unicode:chardata()~n" " }).~n", []), w("~n%% Hardcoded Defines~n", []), Enums = [E || {{enum,_},E = #enum{as_atom=false}} <- get()], @@ -1045,16 +1060,16 @@ build_enum_ints(#enum{from=From, vals=Vals},Done) -> Format = fun(#const{name="wxEVT_" ++ _}) -> ignore; %% Ignore event macros they are not valid in our event model - (#const{name=Name,val=Value,is_const=true}) when is_integer(Value) -> + (#const{name=Name,val=Value,is_const=true}) when is_number(Value) -> w("-define(~s, ~p).~n", [enum_name(Name),Value]); - (#const{name=Name,val=Value,is_const=false}) when is_integer(Value) -> + (#const{name=Name,val=Value,is_const=false}) when is_number(Value) -> w("-define(~s, wxe_util:get_const(~s)).~n", [enum_name(Name),enum_name(Name)]); (#const{name=Name,val={Str,0}}) -> case string:tokens(Str, " |()") of [Token] -> - w("-define(~s, ?~s).~n", [enum_name(Name),Token]); + w("-define(~s, ~s).~n", [enum_name(Name),const_value(Token)]); Tokens -> - Def = args(fun(T) -> [$?|T] end, " bor ", Tokens), + Def = args(fun(T) -> const_value(T) end, " bor ", Tokens), w("-define(~s, (~s)).~n", [enum_name(Name),Def]) end; (#const{name=Name,val={Str,N}}) -> @@ -1079,6 +1094,17 @@ build_enum_ints(#enum{from=From, vals=Vals},Done) -> end, lists:foldl(Write, Done, Vals). +const_value(V) when is_integer(V) -> integer_to_list(V); +const_value(V = "16#" ++ IntList) -> + _ = http_util:hexlist_to_integer(IntList), %% ASSERT + V; +const_value(V0) -> + try + _ = list_to_integer(V0), + V0 + catch _:_ -> [$?|V0] + end. + gen_event_recs() -> open_write("../include/wx.hrl"), erl_copyright(), diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf index 383addfe3f..1f6225ce60 100644 --- a/lib/wx/api_gen/wxapi.conf +++ b/lib/wx/api_gen/wxapi.conf @@ -776,10 +776,12 @@ 'GetEditControl', 'GetImageList','GetItem','GetItemBackgroundColour', 'GetItemCount',{'GetItemData', [{return, {base,int}}]}, - 'GetItemFont','GetItemPosition','GetItemRect', - 'GetItemSpacing','GetItemState','GetItemText','GetItemTextColour', + 'GetItemFont', {'GetItemPosition', [{"pos", out}]},{'GetItemRect',[{"rect", out}]}, + 'GetItemSpacing','GetItemState', + 'GetItemText','GetItemTextColour', 'GetNextItem','GetSelectedItemCount','GetTextColour','GetTopItem', - 'GetViewRect',{'HitTest',[{"pSubItem",nowhere}]},'InsertColumn','InsertItem', + 'GetViewRect',{'HitTest',[{"pSubItem",nowhere}, {"flags", in}]}, + 'InsertColumn','InsertItem', %%'OnGetItemAttr', 'OnGetItemImage','OnGetItemText', 'RefreshItem','RefreshItems','ScrollList', 'SetBackgroundColour','SetColumn','SetColumnWidth','SetImageList','SetItem', diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in index ce7f4bf53e..1497ac4d16 100644 --- a/lib/wx/c_src/Makefile.in +++ b/lib/wx/c_src/Makefile.in @@ -176,10 +176,10 @@ $(TARGET_DIR)/erl_gl$(SO_EXT): $(GL_OBJECTS) # ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/priv - $(INSTALL_DATA) ../priv/erlang-logo32.png $(RELSYSDIR)/priv/ - $(INSTALL_DATA) ../priv/erlang-logo64.png $(RELSYSDIR)/priv/ - $(INSTALL_PROGRAM) $(TARGET_DIR)/wxe_driver$(SO_EXT) $(RELSYSDIR)/priv/ - $(INSTALL_PROGRAM) $(TARGET_DIR)/erl_gl$(SO_EXT) $(RELSYSDIR)/priv/ + $(INSTALL_DIR) "$(RELSYSDIR)/priv" + $(INSTALL_DATA) ../priv/erlang-logo32.png "$(RELSYSDIR)/priv/" + $(INSTALL_DATA) ../priv/erlang-logo64.png "$(RELSYSDIR)/priv/" + $(INSTALL_PROGRAM) $(TARGET_DIR)/wxe_driver$(SO_EXT) "$(RELSYSDIR)/priv/" + $(INSTALL_PROGRAM) $(TARGET_DIR)/erl_gl$(SO_EXT) "$(RELSYSDIR)/priv/" release_docs_spec: diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp index c81b3c88c0..15012011ed 100644 --- a/lib/wx/c_src/gen/wxe_funcs.cpp +++ b/lib/wx/c_src/gen/wxe_funcs.cpp @@ -15420,25 +15420,21 @@ case wxListCtrl_GetItemFont: { // wxListCtrl::GetItemFont break; } case wxListCtrl_GetItemPosition: { // wxListCtrl::GetItemPosition + wxPoint pos; wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4; int * item = (int *) bp; bp += 4; - int * posX = (int *) bp; bp += 4; - int * posY = (int *) bp; bp += 4; - wxPoint pos = wxPoint(*posX,*posY); if(!This) throw wxe_badarg(0); bool Result = This->GetItemPosition((long) *item,pos); rt.addBool(Result); + rt.add(pos); + rt.addTupleCount(2); break; } case wxListCtrl_GetItemRect: { // wxListCtrl::GetItemRect + wxRect rect; int code=wxLIST_RECT_BOUNDS; wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4; int * item = (int *) bp; bp += 4; - int * rectX = (int *) bp; bp += 4; - int * rectY = (int *) bp; bp += 4; - int * rectW = (int *) bp; bp += 4; - int * rectH = (int *) bp; bp += 4; - wxRect rect = wxRect(*rectX,*rectY,*rectW,*rectH); while( * (int*) bp) { switch (* (int*) bp) { case 1: {bp += 4; code = (int)*(int *) bp; bp += 4; @@ -15447,6 +15443,8 @@ case wxListCtrl_GetItemRect: { // wxListCtrl::GetItemRect if(!This) throw wxe_badarg(0); bool Result = This->GetItemRect((long) *item,rect,code); rt.addBool(Result); + rt.add(rect); + rt.addTupleCount(2); break; } case wxListCtrl_GetItemSpacing: { // wxListCtrl::GetItemSpacing @@ -15528,16 +15526,14 @@ case wxListCtrl_GetViewRect: { // wxListCtrl::GetViewRect break; } case wxListCtrl_HitTest: { // wxListCtrl::HitTest - int flags; wxListCtrl *This = (wxListCtrl *) getPtr(bp,memenv); bp += 4; int * pointX = (int *) bp; bp += 4; int * pointY = (int *) bp; bp += 4; wxPoint point = wxPoint(*pointX,*pointY); + int * flags = (int *) bp; bp += 4; if(!This) throw wxe_badarg(0); - long Result = This->HitTest(point,flags); + long Result = This->HitTest(point,*flags); rt.addInt(Result); - rt.addInt(flags); - rt.addTupleCount(2); break; } case wxListCtrl_InsertColumn_2: { // wxListCtrl::InsertColumn diff --git a/lib/wx/configure.in b/lib/wx/configure.in index b27e114a3d..8e5f696bc7 100755 --- a/lib/wx/configure.in +++ b/lib/wx/configure.in @@ -571,7 +571,7 @@ if test X"$WX_HAVE_STATIC_LIBS" = X"true" ; then LIBS=$WX_LIBS_STATIC fi -AC_LINK_IFELSE([ +AC_LINK_IFELSE([AC_LANG_SOURCE([ #ifdef WIN32 # include <windows.h> # include <gl/gl.h> @@ -583,6 +583,7 @@ AC_LINK_IFELSE([ #include "wx/wx.h" #include "wx/stc/stc.h" #include "wx/glcanvas.h" + ]) class MyApp : public wxApp { diff --git a/lib/wx/doc/src/Makefile b/lib/wx/doc/src/Makefile index 834f887076..4e6727b6a6 100644 --- a/lib/wx/doc/src/Makefile +++ b/lib/wx/doc/src/Makefile @@ -95,13 +95,13 @@ ref_man.xml: ref_man.xml.src @echo "</application>" >> ref_man.xml @echo -$(ErlMods:%.erl=%.xml): +$(ErlMods:%.erl=%.xml): ../../src/$(@:%.xml=%.erl) escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) -preprocess true -sort_functions false ../../src/$(@:%.xml=%.erl) -$(GenMods:%.erl=%.xml): +$(GenMods:%.erl=%.xml): ../../src/gen/$(@:%.xml=%.erl) escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) -i ../../src -preprocess true -sort_functions false ../../src/gen/$(@:%.xml=%.erl) -$(XML_CHAPTER_FILES): +$(XML_CHAPTER_FILES): ../overview.edoc escript $(DOCGEN)/priv/bin/xml_from_edoc.escript -def vsn $(VSN) -chapter ../overview.edoc debug opt: @@ -120,14 +120,14 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3_FILES) $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3_FILES) "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/wx/doc/src/notes.xml b/lib/wx/doc/src/notes.xml index 4a94227a55..1ba7e62d7d 100644 --- a/lib/wx/doc/src/notes.xml +++ b/lib/wx/doc/src/notes.xml @@ -31,6 +31,23 @@ <p>This document describes the changes made to the wxErlang application.</p> +<section><title>Wx 0.99.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p>Fix errors in wxDC and wxGraphicsContext api.</p> + <p>Add wxTaskBarIcon.</p> <p>Add + wxStyledTextControl:setEdgeMode/2.</p> <p>Add type and + specs for all functions and records.</p> + <p> + Own Id: OTP-9947</p> + </item> + </list> + </section> + +</section> + <section><title>Wx 0.99.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/wx/examples/demo/Makefile b/lib/wx/examples/demo/Makefile index 872723fb98..c60e9c3d50 100644 --- a/lib/wx/examples/demo/Makefile +++ b/lib/wx/examples/demo/Makefile @@ -69,7 +69,7 @@ docs: run: opt erl -smp -detached -pa $(TOPDIR)/ebin -s demo -EXRELSYSDIR = $(RELSYSDIR)/examples/demo +EXRELSYSDIR = "$(RELSYSDIR)/examples/demo" include $(ERL_TOP)/make/otp_release_targets.mk docs: diff --git a/lib/wx/examples/simple/Makefile b/lib/wx/examples/simple/Makefile index 0178aea890..3f2bf01556 100644 --- a/lib/wx/examples/simple/Makefile +++ b/lib/wx/examples/simple/Makefile @@ -40,7 +40,7 @@ docs: run: opt erl -smp -detached -pa $(TOPDIR)/ebin -s hello -EXRELSYSDIR = $(RELSYSDIR)/examples/simple +EXRELSYSDIR = "$(RELSYSDIR)/examples/simple" include $(ERL_TOP)/make/otp_release_targets.mk docs: diff --git a/lib/wx/examples/sudoku/Makefile b/lib/wx/examples/sudoku/Makefile index 89919ff50c..ec900f37e2 100644 --- a/lib/wx/examples/sudoku/Makefile +++ b/lib/wx/examples/sudoku/Makefile @@ -40,7 +40,7 @@ docs: run: opt erl -smp -detached -pa $(TOPDIR)/ebin -s sudoku -EXRELSYSDIR = $(RELSYSDIR)/examples/sudoku +EXRELSYSDIR = "$(RELSYSDIR)/examples/sudoku" include $(ERL_TOP)/make/otp_release_targets.mk docs: diff --git a/lib/wx/examples/xrc/Makefile b/lib/wx/examples/xrc/Makefile index 341fdbb2cc..9a976628a7 100644 --- a/lib/wx/examples/xrc/Makefile +++ b/lib/wx/examples/xrc/Makefile @@ -49,7 +49,7 @@ docs: run: opt erl -smp -detached -pa $(TOPDIR)/ebin -s xrc -EXRELSYSDIR = $(RELSYSDIR)/examples/xrc +EXRELSYSDIR = "$(RELSYSDIR)/examples/xrc" include $(ERL_TOP)/make/otp_release_targets.mk docs: diff --git a/lib/wx/include/gl.hrl b/lib/wx/include/gl.hrl index c643ae0962..9004a8aa31 100644 --- a/lib/wx/include/gl.hrl +++ b/lib/wx/include/gl.hrl @@ -37,16 +37,16 @@ -define(GL_3_BYTES, 16#1408). -define(GL_4_BYTES, 16#1409). -define(GL_DOUBLE, 16#140A). --define(GL_POINTS, 16#0). --define(GL_LINES, 16#1). --define(GL_LINE_LOOP, 16#2). --define(GL_LINE_STRIP, 16#3). --define(GL_TRIANGLES, 16#4). --define(GL_TRIANGLE_STRIP, 16#5). --define(GL_TRIANGLE_FAN, 16#6). --define(GL_QUADS, 16#7). --define(GL_QUAD_STRIP, 16#8). --define(GL_POLYGON, 16#9). +-define(GL_POINTS, 16#0000). +-define(GL_LINES, 16#0001). +-define(GL_LINE_LOOP, 16#0002). +-define(GL_LINE_STRIP, 16#0003). +-define(GL_TRIANGLES, 16#0004). +-define(GL_TRIANGLE_STRIP, 16#0005). +-define(GL_TRIANGLE_FAN, 16#0006). +-define(GL_QUADS, 16#0007). +-define(GL_QUAD_STRIP, 16#0008). +-define(GL_POLYGON, 16#0009). -define(GL_VERTEX_ARRAY, 16#8074). -define(GL_NORMAL_ARRAY, 16#8075). -define(GL_COLOR_ARRAY, 16#8076). @@ -87,35 +87,35 @@ -define(GL_T2F_N3F_V3F, 16#2A2B). -define(GL_T2F_C4F_N3F_V3F, 16#2A2C). -define(GL_T4F_C4F_N3F_V4F, 16#2A2D). --define(GL_MATRIX_MODE, 16#BA0). +-define(GL_MATRIX_MODE, 16#0BA0). -define(GL_MODELVIEW, 16#1700). -define(GL_PROJECTION, 16#1701). -define(GL_TEXTURE, 16#1702). --define(GL_POINT_SMOOTH, 16#B10). --define(GL_POINT_SIZE, 16#B11). --define(GL_POINT_SIZE_GRANULARITY, 16#B13). --define(GL_POINT_SIZE_RANGE, 16#B12). --define(GL_LINE_SMOOTH, 16#B20). --define(GL_LINE_STIPPLE, 16#B24). --define(GL_LINE_STIPPLE_PATTERN, 16#B25). --define(GL_LINE_STIPPLE_REPEAT, 16#B26). --define(GL_LINE_WIDTH, 16#B21). --define(GL_LINE_WIDTH_GRANULARITY, 16#B23). --define(GL_LINE_WIDTH_RANGE, 16#B22). +-define(GL_POINT_SMOOTH, 16#0B10). +-define(GL_POINT_SIZE, 16#0B11). +-define(GL_POINT_SIZE_GRANULARITY, 16#0B13). +-define(GL_POINT_SIZE_RANGE, 16#0B12). +-define(GL_LINE_SMOOTH, 16#0B20). +-define(GL_LINE_STIPPLE, 16#0B24). +-define(GL_LINE_STIPPLE_PATTERN, 16#0B25). +-define(GL_LINE_STIPPLE_REPEAT, 16#0B26). +-define(GL_LINE_WIDTH, 16#0B21). +-define(GL_LINE_WIDTH_GRANULARITY, 16#0B23). +-define(GL_LINE_WIDTH_RANGE, 16#0B22). -define(GL_POINT, 16#1B00). -define(GL_LINE, 16#1B01). -define(GL_FILL, 16#1B02). --define(GL_CW, 16#900). --define(GL_CCW, 16#901). --define(GL_FRONT, 16#404). --define(GL_BACK, 16#405). --define(GL_POLYGON_MODE, 16#B40). --define(GL_POLYGON_SMOOTH, 16#B41). --define(GL_POLYGON_STIPPLE, 16#B42). --define(GL_EDGE_FLAG, 16#B43). --define(GL_CULL_FACE, 16#B44). --define(GL_CULL_FACE_MODE, 16#B45). --define(GL_FRONT_FACE, 16#B46). +-define(GL_CW, 16#0900). +-define(GL_CCW, 16#0901). +-define(GL_FRONT, 16#0404). +-define(GL_BACK, 16#0405). +-define(GL_POLYGON_MODE, 16#0B40). +-define(GL_POLYGON_SMOOTH, 16#0B41). +-define(GL_POLYGON_STIPPLE, 16#0B42). +-define(GL_EDGE_FLAG, 16#0B43). +-define(GL_CULL_FACE, 16#0B44). +-define(GL_CULL_FACE_MODE, 16#0B45). +-define(GL_FRONT_FACE, 16#0B46). -define(GL_POLYGON_OFFSET_FACTOR, 16#8038). -define(GL_POLYGON_OFFSET_UNITS, 16#2A00). -define(GL_POLYGON_OFFSET_POINT, 16#2A01). @@ -123,25 +123,25 @@ -define(GL_POLYGON_OFFSET_FILL, 16#8037). -define(GL_COMPILE, 16#1300). -define(GL_COMPILE_AND_EXECUTE, 16#1301). --define(GL_LIST_BASE, 16#B32). --define(GL_LIST_INDEX, 16#B33). --define(GL_LIST_MODE, 16#B30). --define(GL_NEVER, 16#200). --define(GL_LESS, 16#201). --define(GL_EQUAL, 16#202). --define(GL_LEQUAL, 16#203). --define(GL_GREATER, 16#204). --define(GL_NOTEQUAL, 16#205). --define(GL_GEQUAL, 16#206). --define(GL_ALWAYS, 16#207). --define(GL_DEPTH_TEST, 16#B71). --define(GL_DEPTH_BITS, 16#D56). --define(GL_DEPTH_CLEAR_VALUE, 16#B73). --define(GL_DEPTH_FUNC, 16#B74). --define(GL_DEPTH_RANGE, 16#B70). --define(GL_DEPTH_WRITEMASK, 16#B72). +-define(GL_LIST_BASE, 16#0B32). +-define(GL_LIST_INDEX, 16#0B33). +-define(GL_LIST_MODE, 16#0B30). +-define(GL_NEVER, 16#0200). +-define(GL_LESS, 16#0201). +-define(GL_EQUAL, 16#0202). +-define(GL_LEQUAL, 16#0203). +-define(GL_GREATER, 16#0204). +-define(GL_NOTEQUAL, 16#0205). +-define(GL_GEQUAL, 16#0206). +-define(GL_ALWAYS, 16#0207). +-define(GL_DEPTH_TEST, 16#0B71). +-define(GL_DEPTH_BITS, 16#0D56). +-define(GL_DEPTH_CLEAR_VALUE, 16#0B73). +-define(GL_DEPTH_FUNC, 16#0B74). +-define(GL_DEPTH_RANGE, 16#0B70). +-define(GL_DEPTH_WRITEMASK, 16#0B72). -define(GL_DEPTH_COMPONENT, 16#1902). --define(GL_LIGHTING, 16#B50). +-define(GL_LIGHTING, 16#0B50). -define(GL_LIGHT0, 16#4000). -define(GL_LIGHT1, 16#4001). -define(GL_LIGHT2, 16#4002). @@ -164,85 +164,85 @@ -define(GL_SPOT_DIRECTION, 16#1204). -define(GL_AMBIENT_AND_DIFFUSE, 16#1602). -define(GL_COLOR_INDEXES, 16#1603). --define(GL_LIGHT_MODEL_TWO_SIDE, 16#B52). --define(GL_LIGHT_MODEL_LOCAL_VIEWER, 16#B51). --define(GL_LIGHT_MODEL_AMBIENT, 16#B53). --define(GL_FRONT_AND_BACK, 16#408). --define(GL_SHADE_MODEL, 16#B54). +-define(GL_LIGHT_MODEL_TWO_SIDE, 16#0B52). +-define(GL_LIGHT_MODEL_LOCAL_VIEWER, 16#0B51). +-define(GL_LIGHT_MODEL_AMBIENT, 16#0B53). +-define(GL_FRONT_AND_BACK, 16#0408). +-define(GL_SHADE_MODEL, 16#0B54). -define(GL_FLAT, 16#1D00). -define(GL_SMOOTH, 16#1D01). --define(GL_COLOR_MATERIAL, 16#B57). --define(GL_COLOR_MATERIAL_FACE, 16#B55). --define(GL_COLOR_MATERIAL_PARAMETER, 16#B56). --define(GL_NORMALIZE, 16#BA1). +-define(GL_COLOR_MATERIAL, 16#0B57). +-define(GL_COLOR_MATERIAL_FACE, 16#0B55). +-define(GL_COLOR_MATERIAL_PARAMETER, 16#0B56). +-define(GL_NORMALIZE, 16#0BA1). -define(GL_CLIP_PLANE0, 16#3000). -define(GL_CLIP_PLANE1, 16#3001). -define(GL_CLIP_PLANE2, 16#3002). -define(GL_CLIP_PLANE3, 16#3003). -define(GL_CLIP_PLANE4, 16#3004). -define(GL_CLIP_PLANE5, 16#3005). --define(GL_ACCUM_RED_BITS, 16#D58). --define(GL_ACCUM_GREEN_BITS, 16#D59). --define(GL_ACCUM_BLUE_BITS, 16#D5A). --define(GL_ACCUM_ALPHA_BITS, 16#D5B). --define(GL_ACCUM_CLEAR_VALUE, 16#B80). --define(GL_ACCUM, 16#100). --define(GL_ADD, 16#104). --define(GL_LOAD, 16#101). --define(GL_MULT, 16#103). --define(GL_RETURN, 16#102). --define(GL_ALPHA_TEST, 16#BC0). --define(GL_ALPHA_TEST_REF, 16#BC2). --define(GL_ALPHA_TEST_FUNC, 16#BC1). --define(GL_BLEND, 16#BE2). --define(GL_BLEND_SRC, 16#BE1). --define(GL_BLEND_DST, 16#BE0). +-define(GL_ACCUM_RED_BITS, 16#0D58). +-define(GL_ACCUM_GREEN_BITS, 16#0D59). +-define(GL_ACCUM_BLUE_BITS, 16#0D5A). +-define(GL_ACCUM_ALPHA_BITS, 16#0D5B). +-define(GL_ACCUM_CLEAR_VALUE, 16#0B80). +-define(GL_ACCUM, 16#0100). +-define(GL_ADD, 16#0104). +-define(GL_LOAD, 16#0101). +-define(GL_MULT, 16#0103). +-define(GL_RETURN, 16#0102). +-define(GL_ALPHA_TEST, 16#0BC0). +-define(GL_ALPHA_TEST_REF, 16#0BC2). +-define(GL_ALPHA_TEST_FUNC, 16#0BC1). +-define(GL_BLEND, 16#0BE2). +-define(GL_BLEND_SRC, 16#0BE1). +-define(GL_BLEND_DST, 16#0BE0). -define(GL_ZERO, 16#0). -define(GL_ONE, 16#1). --define(GL_SRC_COLOR, 16#300). --define(GL_ONE_MINUS_SRC_COLOR, 16#301). --define(GL_SRC_ALPHA, 16#302). --define(GL_ONE_MINUS_SRC_ALPHA, 16#303). --define(GL_DST_ALPHA, 16#304). --define(GL_ONE_MINUS_DST_ALPHA, 16#305). --define(GL_DST_COLOR, 16#306). --define(GL_ONE_MINUS_DST_COLOR, 16#307). --define(GL_SRC_ALPHA_SATURATE, 16#308). +-define(GL_SRC_COLOR, 16#0300). +-define(GL_ONE_MINUS_SRC_COLOR, 16#0301). +-define(GL_SRC_ALPHA, 16#0302). +-define(GL_ONE_MINUS_SRC_ALPHA, 16#0303). +-define(GL_DST_ALPHA, 16#0304). +-define(GL_ONE_MINUS_DST_ALPHA, 16#0305). +-define(GL_DST_COLOR, 16#0306). +-define(GL_ONE_MINUS_DST_COLOR, 16#0307). +-define(GL_SRC_ALPHA_SATURATE, 16#0308). -define(GL_FEEDBACK, 16#1C01). -define(GL_RENDER, 16#1C00). -define(GL_SELECT, 16#1C02). --define(GL_2D, 16#600). --define(GL_3D, 16#601). --define(GL_3D_COLOR, 16#602). --define(GL_3D_COLOR_TEXTURE, 16#603). --define(GL_4D_COLOR_TEXTURE, 16#604). --define(GL_POINT_TOKEN, 16#701). --define(GL_LINE_TOKEN, 16#702). --define(GL_LINE_RESET_TOKEN, 16#707). --define(GL_POLYGON_TOKEN, 16#703). --define(GL_BITMAP_TOKEN, 16#704). --define(GL_DRAW_PIXEL_TOKEN, 16#705). --define(GL_COPY_PIXEL_TOKEN, 16#706). --define(GL_PASS_THROUGH_TOKEN, 16#700). --define(GL_FEEDBACK_BUFFER_POINTER, 16#DF0). --define(GL_FEEDBACK_BUFFER_SIZE, 16#DF1). --define(GL_FEEDBACK_BUFFER_TYPE, 16#DF2). --define(GL_SELECTION_BUFFER_POINTER, 16#DF3). --define(GL_SELECTION_BUFFER_SIZE, 16#DF4). --define(GL_FOG, 16#B60). --define(GL_FOG_MODE, 16#B65). --define(GL_FOG_DENSITY, 16#B62). --define(GL_FOG_COLOR, 16#B66). --define(GL_FOG_INDEX, 16#B61). --define(GL_FOG_START, 16#B63). --define(GL_FOG_END, 16#B64). +-define(GL_2D, 16#0600). +-define(GL_3D, 16#0601). +-define(GL_3D_COLOR, 16#0602). +-define(GL_3D_COLOR_TEXTURE, 16#0603). +-define(GL_4D_COLOR_TEXTURE, 16#0604). +-define(GL_POINT_TOKEN, 16#0701). +-define(GL_LINE_TOKEN, 16#0702). +-define(GL_LINE_RESET_TOKEN, 16#0707). +-define(GL_POLYGON_TOKEN, 16#0703). +-define(GL_BITMAP_TOKEN, 16#0704). +-define(GL_DRAW_PIXEL_TOKEN, 16#0705). +-define(GL_COPY_PIXEL_TOKEN, 16#0706). +-define(GL_PASS_THROUGH_TOKEN, 16#0700). +-define(GL_FEEDBACK_BUFFER_POINTER, 16#0DF0). +-define(GL_FEEDBACK_BUFFER_SIZE, 16#0DF1). +-define(GL_FEEDBACK_BUFFER_TYPE, 16#0DF2). +-define(GL_SELECTION_BUFFER_POINTER, 16#0DF3). +-define(GL_SELECTION_BUFFER_SIZE, 16#0DF4). +-define(GL_FOG, 16#0B60). +-define(GL_FOG_MODE, 16#0B65). +-define(GL_FOG_DENSITY, 16#0B62). +-define(GL_FOG_COLOR, 16#0B66). +-define(GL_FOG_INDEX, 16#0B61). +-define(GL_FOG_START, 16#0B63). +-define(GL_FOG_END, 16#0B64). -define(GL_LINEAR, 16#2601). --define(GL_EXP, 16#800). --define(GL_EXP2, 16#801). --define(GL_LOGIC_OP, 16#BF1). --define(GL_INDEX_LOGIC_OP, 16#BF1). --define(GL_COLOR_LOGIC_OP, 16#BF2). --define(GL_LOGIC_OP_MODE, 16#BF0). +-define(GL_EXP, 16#0800). +-define(GL_EXP2, 16#0801). +-define(GL_LOGIC_OP, 16#0BF1). +-define(GL_INDEX_LOGIC_OP, 16#0BF1). +-define(GL_COLOR_LOGIC_OP, 16#0BF2). +-define(GL_LOGIC_OP_MODE, 16#0BF0). -define(GL_CLEAR, 16#1500). -define(GL_SET, 16#150F). -define(GL_COPY, 16#1503). @@ -259,32 +259,32 @@ -define(GL_AND_INVERTED, 16#1504). -define(GL_OR_REVERSE, 16#150B). -define(GL_OR_INVERTED, 16#150D). --define(GL_STENCIL_BITS, 16#D57). --define(GL_STENCIL_TEST, 16#B90). --define(GL_STENCIL_CLEAR_VALUE, 16#B91). --define(GL_STENCIL_FUNC, 16#B92). --define(GL_STENCIL_VALUE_MASK, 16#B93). --define(GL_STENCIL_FAIL, 16#B94). --define(GL_STENCIL_PASS_DEPTH_FAIL, 16#B95). --define(GL_STENCIL_PASS_DEPTH_PASS, 16#B96). --define(GL_STENCIL_REF, 16#B97). --define(GL_STENCIL_WRITEMASK, 16#B98). +-define(GL_STENCIL_BITS, 16#0D57). +-define(GL_STENCIL_TEST, 16#0B90). +-define(GL_STENCIL_CLEAR_VALUE, 16#0B91). +-define(GL_STENCIL_FUNC, 16#0B92). +-define(GL_STENCIL_VALUE_MASK, 16#0B93). +-define(GL_STENCIL_FAIL, 16#0B94). +-define(GL_STENCIL_PASS_DEPTH_FAIL, 16#0B95). +-define(GL_STENCIL_PASS_DEPTH_PASS, 16#0B96). +-define(GL_STENCIL_REF, 16#0B97). +-define(GL_STENCIL_WRITEMASK, 16#0B98). -define(GL_STENCIL_INDEX, 16#1901). -define(GL_KEEP, 16#1E00). -define(GL_REPLACE, 16#1E01). -define(GL_INCR, 16#1E02). -define(GL_DECR, 16#1E03). -define(GL_NONE, 16#0). --define(GL_LEFT, 16#406). --define(GL_RIGHT, 16#407). --define(GL_FRONT_LEFT, 16#400). --define(GL_FRONT_RIGHT, 16#401). --define(GL_BACK_LEFT, 16#402). --define(GL_BACK_RIGHT, 16#403). --define(GL_AUX0, 16#409). --define(GL_AUX1, 16#40A). --define(GL_AUX2, 16#40B). --define(GL_AUX3, 16#40C). +-define(GL_LEFT, 16#0406). +-define(GL_RIGHT, 16#0407). +-define(GL_FRONT_LEFT, 16#0400). +-define(GL_FRONT_RIGHT, 16#0401). +-define(GL_BACK_LEFT, 16#0402). +-define(GL_BACK_RIGHT, 16#0403). +-define(GL_AUX0, 16#0409). +-define(GL_AUX1, 16#040A). +-define(GL_AUX2, 16#040B). +-define(GL_AUX3, 16#040C). -define(GL_COLOR_INDEX, 16#1900). -define(GL_RED, 16#1903). -define(GL_GREEN, 16#1904). @@ -292,161 +292,161 @@ -define(GL_ALPHA, 16#1906). -define(GL_LUMINANCE, 16#1909). -define(GL_LUMINANCE_ALPHA, 16#190A). --define(GL_ALPHA_BITS, 16#D55). --define(GL_RED_BITS, 16#D52). --define(GL_GREEN_BITS, 16#D53). --define(GL_BLUE_BITS, 16#D54). --define(GL_INDEX_BITS, 16#D51). --define(GL_SUBPIXEL_BITS, 16#D50). --define(GL_AUX_BUFFERS, 16#C00). --define(GL_READ_BUFFER, 16#C02). --define(GL_DRAW_BUFFER, 16#C01). --define(GL_DOUBLEBUFFER, 16#C32). --define(GL_STEREO, 16#C33). +-define(GL_ALPHA_BITS, 16#0D55). +-define(GL_RED_BITS, 16#0D52). +-define(GL_GREEN_BITS, 16#0D53). +-define(GL_BLUE_BITS, 16#0D54). +-define(GL_INDEX_BITS, 16#0D51). +-define(GL_SUBPIXEL_BITS, 16#0D50). +-define(GL_AUX_BUFFERS, 16#0C00). +-define(GL_READ_BUFFER, 16#0C02). +-define(GL_DRAW_BUFFER, 16#0C01). +-define(GL_DOUBLEBUFFER, 16#0C32). +-define(GL_STEREO, 16#0C33). -define(GL_BITMAP, 16#1A00). -define(GL_COLOR, 16#1800). -define(GL_DEPTH, 16#1801). -define(GL_STENCIL, 16#1802). --define(GL_DITHER, 16#BD0). +-define(GL_DITHER, 16#0BD0). -define(GL_RGB, 16#1907). -define(GL_RGBA, 16#1908). --define(GL_MAX_LIST_NESTING, 16#B31). --define(GL_MAX_EVAL_ORDER, 16#D30). --define(GL_MAX_LIGHTS, 16#D31). --define(GL_MAX_CLIP_PLANES, 16#D32). --define(GL_MAX_TEXTURE_SIZE, 16#D33). --define(GL_MAX_PIXEL_MAP_TABLE, 16#D34). --define(GL_MAX_ATTRIB_STACK_DEPTH, 16#D35). --define(GL_MAX_MODELVIEW_STACK_DEPTH, 16#D36). --define(GL_MAX_NAME_STACK_DEPTH, 16#D37). --define(GL_MAX_PROJECTION_STACK_DEPTH, 16#D38). --define(GL_MAX_TEXTURE_STACK_DEPTH, 16#D39). --define(GL_MAX_VIEWPORT_DIMS, 16#D3A). --define(GL_MAX_CLIENT_ATTRIB_STACK_DEPTH, 16#D3B). --define(GL_ATTRIB_STACK_DEPTH, 16#BB0). --define(GL_CLIENT_ATTRIB_STACK_DEPTH, 16#BB1). --define(GL_COLOR_CLEAR_VALUE, 16#C22). --define(GL_COLOR_WRITEMASK, 16#C23). --define(GL_CURRENT_INDEX, 16#B01). --define(GL_CURRENT_COLOR, 16#B00). --define(GL_CURRENT_NORMAL, 16#B02). --define(GL_CURRENT_RASTER_COLOR, 16#B04). --define(GL_CURRENT_RASTER_DISTANCE, 16#B09). --define(GL_CURRENT_RASTER_INDEX, 16#B05). --define(GL_CURRENT_RASTER_POSITION, 16#B07). --define(GL_CURRENT_RASTER_TEXTURE_COORDS, 16#B06). --define(GL_CURRENT_RASTER_POSITION_VALID, 16#B08). --define(GL_CURRENT_TEXTURE_COORDS, 16#B03). --define(GL_INDEX_CLEAR_VALUE, 16#C20). --define(GL_INDEX_MODE, 16#C30). --define(GL_INDEX_WRITEMASK, 16#C21). --define(GL_MODELVIEW_MATRIX, 16#BA6). --define(GL_MODELVIEW_STACK_DEPTH, 16#BA3). --define(GL_NAME_STACK_DEPTH, 16#D70). --define(GL_PROJECTION_MATRIX, 16#BA7). --define(GL_PROJECTION_STACK_DEPTH, 16#BA4). --define(GL_RENDER_MODE, 16#C40). --define(GL_RGBA_MODE, 16#C31). --define(GL_TEXTURE_MATRIX, 16#BA8). --define(GL_TEXTURE_STACK_DEPTH, 16#BA5). --define(GL_VIEWPORT, 16#BA2). --define(GL_AUTO_NORMAL, 16#D80). --define(GL_MAP1_COLOR_4, 16#D90). --define(GL_MAP1_INDEX, 16#D91). --define(GL_MAP1_NORMAL, 16#D92). --define(GL_MAP1_TEXTURE_COORD_1, 16#D93). --define(GL_MAP1_TEXTURE_COORD_2, 16#D94). --define(GL_MAP1_TEXTURE_COORD_3, 16#D95). --define(GL_MAP1_TEXTURE_COORD_4, 16#D96). --define(GL_MAP1_VERTEX_3, 16#D97). --define(GL_MAP1_VERTEX_4, 16#D98). --define(GL_MAP2_COLOR_4, 16#DB0). --define(GL_MAP2_INDEX, 16#DB1). --define(GL_MAP2_NORMAL, 16#DB2). --define(GL_MAP2_TEXTURE_COORD_1, 16#DB3). --define(GL_MAP2_TEXTURE_COORD_2, 16#DB4). --define(GL_MAP2_TEXTURE_COORD_3, 16#DB5). --define(GL_MAP2_TEXTURE_COORD_4, 16#DB6). --define(GL_MAP2_VERTEX_3, 16#DB7). --define(GL_MAP2_VERTEX_4, 16#DB8). --define(GL_MAP1_GRID_DOMAIN, 16#DD0). --define(GL_MAP1_GRID_SEGMENTS, 16#DD1). --define(GL_MAP2_GRID_DOMAIN, 16#DD2). --define(GL_MAP2_GRID_SEGMENTS, 16#DD3). --define(GL_COEFF, 16#A00). --define(GL_ORDER, 16#A01). --define(GL_DOMAIN, 16#A02). --define(GL_PERSPECTIVE_CORRECTION_HINT, 16#C50). --define(GL_POINT_SMOOTH_HINT, 16#C51). --define(GL_LINE_SMOOTH_HINT, 16#C52). --define(GL_POLYGON_SMOOTH_HINT, 16#C53). --define(GL_FOG_HINT, 16#C54). +-define(GL_MAX_LIST_NESTING, 16#0B31). +-define(GL_MAX_EVAL_ORDER, 16#0D30). +-define(GL_MAX_LIGHTS, 16#0D31). +-define(GL_MAX_CLIP_PLANES, 16#0D32). +-define(GL_MAX_TEXTURE_SIZE, 16#0D33). +-define(GL_MAX_PIXEL_MAP_TABLE, 16#0D34). +-define(GL_MAX_ATTRIB_STACK_DEPTH, 16#0D35). +-define(GL_MAX_MODELVIEW_STACK_DEPTH, 16#0D36). +-define(GL_MAX_NAME_STACK_DEPTH, 16#0D37). +-define(GL_MAX_PROJECTION_STACK_DEPTH, 16#0D38). +-define(GL_MAX_TEXTURE_STACK_DEPTH, 16#0D39). +-define(GL_MAX_VIEWPORT_DIMS, 16#0D3A). +-define(GL_MAX_CLIENT_ATTRIB_STACK_DEPTH, 16#0D3B). +-define(GL_ATTRIB_STACK_DEPTH, 16#0BB0). +-define(GL_CLIENT_ATTRIB_STACK_DEPTH, 16#0BB1). +-define(GL_COLOR_CLEAR_VALUE, 16#0C22). +-define(GL_COLOR_WRITEMASK, 16#0C23). +-define(GL_CURRENT_INDEX, 16#0B01). +-define(GL_CURRENT_COLOR, 16#0B00). +-define(GL_CURRENT_NORMAL, 16#0B02). +-define(GL_CURRENT_RASTER_COLOR, 16#0B04). +-define(GL_CURRENT_RASTER_DISTANCE, 16#0B09). +-define(GL_CURRENT_RASTER_INDEX, 16#0B05). +-define(GL_CURRENT_RASTER_POSITION, 16#0B07). +-define(GL_CURRENT_RASTER_TEXTURE_COORDS, 16#0B06). +-define(GL_CURRENT_RASTER_POSITION_VALID, 16#0B08). +-define(GL_CURRENT_TEXTURE_COORDS, 16#0B03). +-define(GL_INDEX_CLEAR_VALUE, 16#0C20). +-define(GL_INDEX_MODE, 16#0C30). +-define(GL_INDEX_WRITEMASK, 16#0C21). +-define(GL_MODELVIEW_MATRIX, 16#0BA6). +-define(GL_MODELVIEW_STACK_DEPTH, 16#0BA3). +-define(GL_NAME_STACK_DEPTH, 16#0D70). +-define(GL_PROJECTION_MATRIX, 16#0BA7). +-define(GL_PROJECTION_STACK_DEPTH, 16#0BA4). +-define(GL_RENDER_MODE, 16#0C40). +-define(GL_RGBA_MODE, 16#0C31). +-define(GL_TEXTURE_MATRIX, 16#0BA8). +-define(GL_TEXTURE_STACK_DEPTH, 16#0BA5). +-define(GL_VIEWPORT, 16#0BA2). +-define(GL_AUTO_NORMAL, 16#0D80). +-define(GL_MAP1_COLOR_4, 16#0D90). +-define(GL_MAP1_INDEX, 16#0D91). +-define(GL_MAP1_NORMAL, 16#0D92). +-define(GL_MAP1_TEXTURE_COORD_1, 16#0D93). +-define(GL_MAP1_TEXTURE_COORD_2, 16#0D94). +-define(GL_MAP1_TEXTURE_COORD_3, 16#0D95). +-define(GL_MAP1_TEXTURE_COORD_4, 16#0D96). +-define(GL_MAP1_VERTEX_3, 16#0D97). +-define(GL_MAP1_VERTEX_4, 16#0D98). +-define(GL_MAP2_COLOR_4, 16#0DB0). +-define(GL_MAP2_INDEX, 16#0DB1). +-define(GL_MAP2_NORMAL, 16#0DB2). +-define(GL_MAP2_TEXTURE_COORD_1, 16#0DB3). +-define(GL_MAP2_TEXTURE_COORD_2, 16#0DB4). +-define(GL_MAP2_TEXTURE_COORD_3, 16#0DB5). +-define(GL_MAP2_TEXTURE_COORD_4, 16#0DB6). +-define(GL_MAP2_VERTEX_3, 16#0DB7). +-define(GL_MAP2_VERTEX_4, 16#0DB8). +-define(GL_MAP1_GRID_DOMAIN, 16#0DD0). +-define(GL_MAP1_GRID_SEGMENTS, 16#0DD1). +-define(GL_MAP2_GRID_DOMAIN, 16#0DD2). +-define(GL_MAP2_GRID_SEGMENTS, 16#0DD3). +-define(GL_COEFF, 16#0A00). +-define(GL_ORDER, 16#0A01). +-define(GL_DOMAIN, 16#0A02). +-define(GL_PERSPECTIVE_CORRECTION_HINT, 16#0C50). +-define(GL_POINT_SMOOTH_HINT, 16#0C51). +-define(GL_LINE_SMOOTH_HINT, 16#0C52). +-define(GL_POLYGON_SMOOTH_HINT, 16#0C53). +-define(GL_FOG_HINT, 16#0C54). -define(GL_DONT_CARE, 16#1100). -define(GL_FASTEST, 16#1101). -define(GL_NICEST, 16#1102). --define(GL_SCISSOR_BOX, 16#C10). --define(GL_SCISSOR_TEST, 16#C11). --define(GL_MAP_COLOR, 16#D10). --define(GL_MAP_STENCIL, 16#D11). --define(GL_INDEX_SHIFT, 16#D12). --define(GL_INDEX_OFFSET, 16#D13). --define(GL_RED_SCALE, 16#D14). --define(GL_RED_BIAS, 16#D15). --define(GL_GREEN_SCALE, 16#D18). --define(GL_GREEN_BIAS, 16#D19). --define(GL_BLUE_SCALE, 16#D1A). --define(GL_BLUE_BIAS, 16#D1B). --define(GL_ALPHA_SCALE, 16#D1C). --define(GL_ALPHA_BIAS, 16#D1D). --define(GL_DEPTH_SCALE, 16#D1E). --define(GL_DEPTH_BIAS, 16#D1F). --define(GL_PIXEL_MAP_S_TO_S_SIZE, 16#CB1). --define(GL_PIXEL_MAP_I_TO_I_SIZE, 16#CB0). --define(GL_PIXEL_MAP_I_TO_R_SIZE, 16#CB2). --define(GL_PIXEL_MAP_I_TO_G_SIZE, 16#CB3). --define(GL_PIXEL_MAP_I_TO_B_SIZE, 16#CB4). --define(GL_PIXEL_MAP_I_TO_A_SIZE, 16#CB5). --define(GL_PIXEL_MAP_R_TO_R_SIZE, 16#CB6). --define(GL_PIXEL_MAP_G_TO_G_SIZE, 16#CB7). --define(GL_PIXEL_MAP_B_TO_B_SIZE, 16#CB8). --define(GL_PIXEL_MAP_A_TO_A_SIZE, 16#CB9). --define(GL_PIXEL_MAP_S_TO_S, 16#C71). --define(GL_PIXEL_MAP_I_TO_I, 16#C70). --define(GL_PIXEL_MAP_I_TO_R, 16#C72). --define(GL_PIXEL_MAP_I_TO_G, 16#C73). --define(GL_PIXEL_MAP_I_TO_B, 16#C74). --define(GL_PIXEL_MAP_I_TO_A, 16#C75). --define(GL_PIXEL_MAP_R_TO_R, 16#C76). --define(GL_PIXEL_MAP_G_TO_G, 16#C77). --define(GL_PIXEL_MAP_B_TO_B, 16#C78). --define(GL_PIXEL_MAP_A_TO_A, 16#C79). --define(GL_PACK_ALIGNMENT, 16#D05). --define(GL_PACK_LSB_FIRST, 16#D01). --define(GL_PACK_ROW_LENGTH, 16#D02). --define(GL_PACK_SKIP_PIXELS, 16#D04). --define(GL_PACK_SKIP_ROWS, 16#D03). --define(GL_PACK_SWAP_BYTES, 16#D00). --define(GL_UNPACK_ALIGNMENT, 16#CF5). --define(GL_UNPACK_LSB_FIRST, 16#CF1). --define(GL_UNPACK_ROW_LENGTH, 16#CF2). --define(GL_UNPACK_SKIP_PIXELS, 16#CF4). --define(GL_UNPACK_SKIP_ROWS, 16#CF3). --define(GL_UNPACK_SWAP_BYTES, 16#CF0). --define(GL_ZOOM_X, 16#D16). --define(GL_ZOOM_Y, 16#D17). +-define(GL_SCISSOR_BOX, 16#0C10). +-define(GL_SCISSOR_TEST, 16#0C11). +-define(GL_MAP_COLOR, 16#0D10). +-define(GL_MAP_STENCIL, 16#0D11). +-define(GL_INDEX_SHIFT, 16#0D12). +-define(GL_INDEX_OFFSET, 16#0D13). +-define(GL_RED_SCALE, 16#0D14). +-define(GL_RED_BIAS, 16#0D15). +-define(GL_GREEN_SCALE, 16#0D18). +-define(GL_GREEN_BIAS, 16#0D19). +-define(GL_BLUE_SCALE, 16#0D1A). +-define(GL_BLUE_BIAS, 16#0D1B). +-define(GL_ALPHA_SCALE, 16#0D1C). +-define(GL_ALPHA_BIAS, 16#0D1D). +-define(GL_DEPTH_SCALE, 16#0D1E). +-define(GL_DEPTH_BIAS, 16#0D1F). +-define(GL_PIXEL_MAP_S_TO_S_SIZE, 16#0CB1). +-define(GL_PIXEL_MAP_I_TO_I_SIZE, 16#0CB0). +-define(GL_PIXEL_MAP_I_TO_R_SIZE, 16#0CB2). +-define(GL_PIXEL_MAP_I_TO_G_SIZE, 16#0CB3). +-define(GL_PIXEL_MAP_I_TO_B_SIZE, 16#0CB4). +-define(GL_PIXEL_MAP_I_TO_A_SIZE, 16#0CB5). +-define(GL_PIXEL_MAP_R_TO_R_SIZE, 16#0CB6). +-define(GL_PIXEL_MAP_G_TO_G_SIZE, 16#0CB7). +-define(GL_PIXEL_MAP_B_TO_B_SIZE, 16#0CB8). +-define(GL_PIXEL_MAP_A_TO_A_SIZE, 16#0CB9). +-define(GL_PIXEL_MAP_S_TO_S, 16#0C71). +-define(GL_PIXEL_MAP_I_TO_I, 16#0C70). +-define(GL_PIXEL_MAP_I_TO_R, 16#0C72). +-define(GL_PIXEL_MAP_I_TO_G, 16#0C73). +-define(GL_PIXEL_MAP_I_TO_B, 16#0C74). +-define(GL_PIXEL_MAP_I_TO_A, 16#0C75). +-define(GL_PIXEL_MAP_R_TO_R, 16#0C76). +-define(GL_PIXEL_MAP_G_TO_G, 16#0C77). +-define(GL_PIXEL_MAP_B_TO_B, 16#0C78). +-define(GL_PIXEL_MAP_A_TO_A, 16#0C79). +-define(GL_PACK_ALIGNMENT, 16#0D05). +-define(GL_PACK_LSB_FIRST, 16#0D01). +-define(GL_PACK_ROW_LENGTH, 16#0D02). +-define(GL_PACK_SKIP_PIXELS, 16#0D04). +-define(GL_PACK_SKIP_ROWS, 16#0D03). +-define(GL_PACK_SWAP_BYTES, 16#0D00). +-define(GL_UNPACK_ALIGNMENT, 16#0CF5). +-define(GL_UNPACK_LSB_FIRST, 16#0CF1). +-define(GL_UNPACK_ROW_LENGTH, 16#0CF2). +-define(GL_UNPACK_SKIP_PIXELS, 16#0CF4). +-define(GL_UNPACK_SKIP_ROWS, 16#0CF3). +-define(GL_UNPACK_SWAP_BYTES, 16#0CF0). +-define(GL_ZOOM_X, 16#0D16). +-define(GL_ZOOM_Y, 16#0D17). -define(GL_TEXTURE_ENV, 16#2300). -define(GL_TEXTURE_ENV_MODE, 16#2200). --define(GL_TEXTURE_1D, 16#DE0). --define(GL_TEXTURE_2D, 16#DE1). +-define(GL_TEXTURE_1D, 16#0DE0). +-define(GL_TEXTURE_2D, 16#0DE1). -define(GL_TEXTURE_WRAP_S, 16#2802). -define(GL_TEXTURE_WRAP_T, 16#2803). -define(GL_TEXTURE_MAG_FILTER, 16#2800). -define(GL_TEXTURE_MIN_FILTER, 16#2801). -define(GL_TEXTURE_ENV_COLOR, 16#2201). --define(GL_TEXTURE_GEN_S, 16#C60). --define(GL_TEXTURE_GEN_T, 16#C61). --define(GL_TEXTURE_GEN_R, 16#C62). --define(GL_TEXTURE_GEN_Q, 16#C63). +-define(GL_TEXTURE_GEN_S, 16#0C60). +-define(GL_TEXTURE_GEN_T, 16#0C61). +-define(GL_TEXTURE_GEN_R, 16#0C62). +-define(GL_TEXTURE_GEN_Q, 16#0C63). -define(GL_TEXTURE_GEN_MODE, 16#2500). -define(GL_TEXTURE_BORDER_COLOR, 16#1004). -define(GL_TEXTURE_WIDTH, 16#1000). @@ -482,33 +482,33 @@ -define(GL_VERSION, 16#1F02). -define(GL_EXTENSIONS, 16#1F03). -define(GL_NO_ERROR, 16#0). --define(GL_INVALID_ENUM, 16#500). --define(GL_INVALID_VALUE, 16#501). --define(GL_INVALID_OPERATION, 16#502). --define(GL_STACK_OVERFLOW, 16#503). --define(GL_STACK_UNDERFLOW, 16#504). --define(GL_OUT_OF_MEMORY, 16#505). --define(GL_CURRENT_BIT, 16#1). --define(GL_POINT_BIT, 16#2). --define(GL_LINE_BIT, 16#4). --define(GL_POLYGON_BIT, 16#8). --define(GL_POLYGON_STIPPLE_BIT, 16#10). --define(GL_PIXEL_MODE_BIT, 16#20). --define(GL_LIGHTING_BIT, 16#40). --define(GL_FOG_BIT, 16#80). --define(GL_DEPTH_BUFFER_BIT, 16#100). --define(GL_ACCUM_BUFFER_BIT, 16#200). --define(GL_STENCIL_BUFFER_BIT, 16#400). --define(GL_VIEWPORT_BIT, 16#800). --define(GL_TRANSFORM_BIT, 16#1000). --define(GL_ENABLE_BIT, 16#2000). --define(GL_COLOR_BUFFER_BIT, 16#4000). --define(GL_HINT_BIT, 16#8000). --define(GL_EVAL_BIT, 16#10000). --define(GL_LIST_BIT, 16#20000). --define(GL_TEXTURE_BIT, 16#40000). --define(GL_SCISSOR_BIT, 16#80000). --define(GL_ALL_ATTRIB_BITS, 16#FFFFF). +-define(GL_INVALID_ENUM, 16#0500). +-define(GL_INVALID_VALUE, 16#0501). +-define(GL_INVALID_OPERATION, 16#0502). +-define(GL_STACK_OVERFLOW, 16#0503). +-define(GL_STACK_UNDERFLOW, 16#0504). +-define(GL_OUT_OF_MEMORY, 16#0505). +-define(GL_CURRENT_BIT, 16#00000001). +-define(GL_POINT_BIT, 16#00000002). +-define(GL_LINE_BIT, 16#00000004). +-define(GL_POLYGON_BIT, 16#00000008). +-define(GL_POLYGON_STIPPLE_BIT, 16#00000010). +-define(GL_PIXEL_MODE_BIT, 16#00000020). +-define(GL_LIGHTING_BIT, 16#00000040). +-define(GL_FOG_BIT, 16#00000080). +-define(GL_DEPTH_BUFFER_BIT, 16#00000100). +-define(GL_ACCUM_BUFFER_BIT, 16#00000200). +-define(GL_STENCIL_BUFFER_BIT, 16#00000400). +-define(GL_VIEWPORT_BIT, 16#00000800). +-define(GL_TRANSFORM_BIT, 16#00001000). +-define(GL_ENABLE_BIT, 16#00002000). +-define(GL_COLOR_BUFFER_BIT, 16#00004000). +-define(GL_HINT_BIT, 16#00008000). +-define(GL_EVAL_BIT, 16#00010000). +-define(GL_LIST_BIT, 16#00020000). +-define(GL_TEXTURE_BIT, 16#00040000). +-define(GL_SCISSOR_BIT, 16#00080000). +-define(GL_ALL_ATTRIB_BITS, 16#000FFFFF). -define(GL_PROXY_TEXTURE_1D, 16#8063). -define(GL_PROXY_TEXTURE_2D, 16#8064). -define(GL_TEXTURE_PRIORITY, 16#8066). @@ -549,8 +549,8 @@ -define(GL_RGB10_A2, 16#8059). -define(GL_RGBA12, 16#805A). -define(GL_RGBA16, 16#805B). --define(GL_CLIENT_PIXEL_STORE_BIT, 16#1). --define(GL_CLIENT_VERTEX_ARRAY_BIT, 16#2). +-define(GL_CLIENT_PIXEL_STORE_BIT, 16#00000001). +-define(GL_CLIENT_VERTEX_ARRAY_BIT, 16#00000002). -define(GL_ALL_CLIENT_ATTRIB_BITS, 16#FFFFFFFF). -define(GL_CLIENT_ALL_ATTRIB_BITS, 16#FFFFFFFF). -define(GL_RESCALE_NORMAL, 16#803A). @@ -578,10 +578,10 @@ -define(GL_TEXTURE_MAX_LOD, 16#813B). -define(GL_TEXTURE_BASE_LEVEL, 16#813C). -define(GL_TEXTURE_MAX_LEVEL, 16#813D). --define(GL_SMOOTH_POINT_SIZE_RANGE, 16#B12). --define(GL_SMOOTH_POINT_SIZE_GRANULARITY, 16#B13). --define(GL_SMOOTH_LINE_WIDTH_RANGE, 16#B22). --define(GL_SMOOTH_LINE_WIDTH_GRANULARITY, 16#B23). +-define(GL_SMOOTH_POINT_SIZE_RANGE, 16#0B12). +-define(GL_SMOOTH_POINT_SIZE_GRANULARITY, 16#0B13). +-define(GL_SMOOTH_LINE_WIDTH_RANGE, 16#0B22). +-define(GL_SMOOTH_LINE_WIDTH_GRANULARITY, 16#0B23). -define(GL_ALIASED_POINT_SIZE_RANGE, 16#846D). -define(GL_ALIASED_LINE_WIDTH_RANGE, 16#846E). -define(GL_PACK_SKIP_IMAGES, 16#806B). @@ -808,14 +808,14 @@ -define(GL_UNSIGNED_SHORT_15_1_MESA, 16#8753). -define(GL_UNSIGNED_SHORT_1_15_REV_MESA, 16#8754). -define(GL_MESA_program_debug, 1). --define(GL_FRAGMENT_PROGRAM_POSITION_MESA, 16#8BB0). --define(GL_FRAGMENT_PROGRAM_CALLBACK_MESA, 16#8BB1). --define(GL_FRAGMENT_PROGRAM_CALLBACK_FUNC_MESA, 16#8BB2). --define(GL_FRAGMENT_PROGRAM_CALLBACK_DATA_MESA, 16#8BB3). --define(GL_VERTEX_PROGRAM_POSITION_MESA, 16#8BB4). --define(GL_VERTEX_PROGRAM_CALLBACK_MESA, 16#8BB5). --define(GL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA, 16#8BB6). --define(GL_VERTEX_PROGRAM_CALLBACK_DATA_MESA, 16#8BB7). +-define(GL_FRAGMENT_PROGRAM_POSITION_MESA, 16#8bb0). +-define(GL_FRAGMENT_PROGRAM_CALLBACK_MESA, 16#8bb1). +-define(GL_FRAGMENT_PROGRAM_CALLBACK_FUNC_MESA, 16#8bb2). +-define(GL_FRAGMENT_PROGRAM_CALLBACK_DATA_MESA, 16#8bb3). +-define(GL_VERTEX_PROGRAM_POSITION_MESA, 16#8bb4). +-define(GL_VERTEX_PROGRAM_CALLBACK_MESA, 16#8bb5). +-define(GL_VERTEX_PROGRAM_CALLBACK_FUNC_MESA, 16#8bb6). +-define(GL_VERTEX_PROGRAM_CALLBACK_DATA_MESA, 16#8bb7). -define(GL_MESA_texture_array, 1). -define(GL_TEXTURE_1D_ARRAY_EXT, 16#8C18). -define(GL_PROXY_TEXTURE_1D_ARRAY_EXT, 16#8C19). @@ -1034,14 +1034,14 @@ -define(GL_CLIP_DISTANCE5, 16#3005). -define(GL_CLIP_DISTANCE6, 16#3006). -define(GL_CLIP_DISTANCE7, 16#3007). --define(GL_MAX_CLIP_DISTANCES, 16#D32). +-define(GL_MAX_CLIP_DISTANCES, 16#0D32). -define(GL_MAJOR_VERSION, 16#821B). -define(GL_MINOR_VERSION, 16#821C). -define(GL_NUM_EXTENSIONS, 16#821D). -define(GL_CONTEXT_FLAGS, 16#821E). -define(GL_COMPRESSED_RED, 16#8225). -define(GL_COMPRESSED_RG, 16#8226). --define(GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, 16#1). +-define(GL_CONTEXT_FLAG_FORWARD_COMPATIBLE_BIT, 16#0001). -define(GL_RGBA32F, 16#8814). -define(GL_RGB32F, 16#8815). -define(GL_RGBA16F, 16#881A). @@ -1159,12 +1159,12 @@ -define(GL_SIGNED_NORMALIZED, 16#8F9C). -define(GL_PRIMITIVE_RESTART, 16#8F9D). -define(GL_PRIMITIVE_RESTART_INDEX, 16#8F9E). --define(GL_CONTEXT_CORE_PROFILE_BIT, 16#1). --define(GL_CONTEXT_COMPATIBILITY_PROFILE_BIT, 16#2). --define(GL_LINES_ADJACENCY, 16#A). --define(GL_LINE_STRIP_ADJACENCY, 16#B). --define(GL_TRIANGLES_ADJACENCY, 16#C). --define(GL_TRIANGLE_STRIP_ADJACENCY, 16#D). +-define(GL_CONTEXT_CORE_PROFILE_BIT, 16#00000001). +-define(GL_CONTEXT_COMPATIBILITY_PROFILE_BIT, 16#00000002). +-define(GL_LINES_ADJACENCY, 16#000A). +-define(GL_LINE_STRIP_ADJACENCY, 16#000B). +-define(GL_TRIANGLES_ADJACENCY, 16#000C). +-define(GL_TRIANGLE_STRIP_ADJACENCY, 16#000D). -define(GL_PROGRAM_POINT_SIZE, 16#8642). -define(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS, 16#8C29). -define(GL_FRAMEBUFFER_ATTACHMENT_LAYERED, 16#8DA7). @@ -1551,7 +1551,7 @@ -define(GL_DEPTH_COMPONENT32F, 16#8CAC). -define(GL_DEPTH32F_STENCIL8, 16#8CAD). -define(GL_FLOAT_32_UNSIGNED_INT_24_8_REV, 16#8DAD). --define(GL_INVALID_FRAMEBUFFER_OPERATION, 16#506). +-define(GL_INVALID_FRAMEBUFFER_OPERATION, 16#0506). -define(GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, 16#8210). -define(GL_FRAMEBUFFER_ATTACHMENT_COMPONENT_TYPE, 16#8211). -define(GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE, 16#8212). @@ -1632,10 +1632,10 @@ -define(GL_TEXTURE_LUMINANCE_TYPE, 16#8C14). -define(GL_TEXTURE_INTENSITY_TYPE, 16#8C15). -define(GL_FRAMEBUFFER_SRGB, 16#8DB9). --define(GL_LINES_ADJACENCY_ARB, 16#A). --define(GL_LINE_STRIP_ADJACENCY_ARB, 16#B). --define(GL_TRIANGLES_ADJACENCY_ARB, 16#C). --define(GL_TRIANGLE_STRIP_ADJACENCY_ARB, 16#D). +-define(GL_LINES_ADJACENCY_ARB, 16#000A). +-define(GL_LINE_STRIP_ADJACENCY_ARB, 16#000B). +-define(GL_TRIANGLES_ADJACENCY_ARB, 16#000C). +-define(GL_TRIANGLE_STRIP_ADJACENCY_ARB, 16#000D). -define(GL_PROGRAM_POINT_SIZE_ARB, 16#8642). -define(GL_MAX_GEOMETRY_TEXTURE_IMAGE_UNITS_ARB, 16#8C29). -define(GL_FRAMEBUFFER_ATTACHMENT_LAYERED_ARB, 16#8DA7). @@ -1652,12 +1652,12 @@ -define(GL_MAX_GEOMETRY_TOTAL_OUTPUT_COMPONENTS_ARB, 16#8DE1). -define(GL_HALF_FLOAT, 16#140B). -define(GL_VERTEX_ATTRIB_ARRAY_DIVISOR_ARB, 16#88FE). --define(GL_MAP_READ_BIT, 16#1). --define(GL_MAP_WRITE_BIT, 16#2). --define(GL_MAP_INVALIDATE_RANGE_BIT, 16#4). --define(GL_MAP_INVALIDATE_BUFFER_BIT, 16#8). --define(GL_MAP_FLUSH_EXPLICIT_BIT, 16#10). --define(GL_MAP_UNSYNCHRONIZED_BIT, 16#20). +-define(GL_MAP_READ_BIT, 16#0001). +-define(GL_MAP_WRITE_BIT, 16#0002). +-define(GL_MAP_INVALIDATE_RANGE_BIT, 16#0004). +-define(GL_MAP_INVALIDATE_BUFFER_BIT, 16#0008). +-define(GL_MAP_FLUSH_EXPLICIT_BIT, 16#0010). +-define(GL_MAP_UNSYNCHRONIZED_BIT, 16#0020). -define(GL_TEXTURE_BUFFER_ARB, 16#8C2A). -define(GL_MAX_TEXTURE_BUFFER_SIZE_ARB, 16#8C2B). -define(GL_TEXTURE_BINDING_BUFFER_ARB, 16#8C2C). @@ -1722,7 +1722,7 @@ -define(GL_UNIFORM_BLOCK_REFERENCED_BY_VERTEX_SHADER, 16#8A44). -define(GL_UNIFORM_BLOCK_REFERENCED_BY_GEOMETRY_SHADER, 16#8A45). -define(GL_UNIFORM_BLOCK_REFERENCED_BY_FRAGMENT_SHADER, 16#8A46). --define(GL_INVALID_INDEX, 16#FFFFFFFF). +-define(GL_INVALID_INDEX, 16#FFFFFFFFu). -define(GL_COPY_READ_BUFFER, 16#8F36). -define(GL_COPY_WRITE_BUFFER, 16#8F37). -define(GL_DEPTH_CLAMP, 16#864F). @@ -1744,8 +1744,8 @@ -define(GL_TIMEOUT_EXPIRED, 16#911B). -define(GL_CONDITION_SATISFIED, 16#911C). -define(GL_WAIT_FAILED, 16#911D). --define(GL_SYNC_FLUSH_COMMANDS_BIT, 16#1). --define(GL_TIMEOUT_IGNORED, 16#FFFFFFFFFFFFFFFF). +-define(GL_SYNC_FLUSH_COMMANDS_BIT, 16#00000001). +-define(GL_TIMEOUT_IGNORED, 16#FFFFFFFFFFFFFFFFull). -define(GL_SAMPLE_POSITION, 16#8E50). -define(GL_SAMPLE_MASK, 16#8E51). -define(GL_SAMPLE_MASK_VALUE, 16#8E52). @@ -1828,7 +1828,7 @@ -define(GL_MAX_SUBROUTINE_UNIFORM_LOCATIONS, 16#8DE8). -define(GL_NUM_COMPATIBLE_SUBROUTINES, 16#8E4A). -define(GL_COMPATIBLE_SUBROUTINES, 16#8E4B). --define(GL_PATCHES, 16#E). +-define(GL_PATCHES, 16#000E). -define(GL_PATCH_VERTICES, 16#8E72). -define(GL_PATCH_DEFAULT_INNER_LEVEL, 16#8E73). -define(GL_PATCH_DEFAULT_OUTER_LEVEL, 16#8E74). @@ -1884,11 +1884,11 @@ -define(GL_PROGRAM_BINARY_LENGTH, 16#8741). -define(GL_NUM_PROGRAM_BINARY_FORMATS, 16#87FE). -define(GL_PROGRAM_BINARY_FORMATS, 16#87FF). --define(GL_VERTEX_SHADER_BIT, 16#1). --define(GL_FRAGMENT_SHADER_BIT, 16#2). --define(GL_GEOMETRY_SHADER_BIT, 16#4). --define(GL_TESS_CONTROL_SHADER_BIT, 16#8). --define(GL_TESS_EVALUATION_SHADER_BIT, 16#10). +-define(GL_VERTEX_SHADER_BIT, 16#00000001). +-define(GL_FRAGMENT_SHADER_BIT, 16#00000002). +-define(GL_GEOMETRY_SHADER_BIT, 16#00000004). +-define(GL_TESS_CONTROL_SHADER_BIT, 16#00000008). +-define(GL_TESS_EVALUATION_SHADER_BIT, 16#00000010). -define(GL_ALL_SHADER_BITS, 16#FFFFFFFF). -define(GL_PROGRAM_SEPARABLE, 16#8258). -define(GL_ACTIVE_PROGRAM, 16#8259). @@ -1923,7 +1923,7 @@ -define(GL_DEBUG_SEVERITY_HIGH_ARB, 16#9146). -define(GL_DEBUG_SEVERITY_MEDIUM_ARB, 16#9147). -define(GL_DEBUG_SEVERITY_LOW_ARB, 16#9148). --define(GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB, 16#4). +-define(GL_CONTEXT_FLAG_ROBUST_ACCESS_BIT_ARB, 16#00000004). -define(GL_LOSE_CONTEXT_ON_RESET_ARB, 16#8252). -define(GL_GUILTY_CONTEXT_RESET_ARB, 16#8253). -define(GL_INNOCENT_CONTEXT_RESET_ARB, 16#8254). @@ -1969,18 +1969,18 @@ -define(GL_ACTIVE_ATOMIC_COUNTER_BUFFERS, 16#92D9). -define(GL_UNIFORM_ATOMIC_COUNTER_BUFFER_INDEX, 16#92DA). -define(GL_UNSIGNED_INT_ATOMIC_COUNTER, 16#92DB). --define(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT, 16#1). --define(GL_ELEMENT_ARRAY_BARRIER_BIT, 16#2). --define(GL_UNIFORM_BARRIER_BIT, 16#4). --define(GL_TEXTURE_FETCH_BARRIER_BIT, 16#8). --define(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT, 16#20). --define(GL_COMMAND_BARRIER_BIT, 16#40). --define(GL_PIXEL_BUFFER_BARRIER_BIT, 16#80). --define(GL_TEXTURE_UPDATE_BARRIER_BIT, 16#100). --define(GL_BUFFER_UPDATE_BARRIER_BIT, 16#200). --define(GL_FRAMEBUFFER_BARRIER_BIT, 16#400). --define(GL_TRANSFORM_FEEDBACK_BARRIER_BIT, 16#800). --define(GL_ATOMIC_COUNTER_BARRIER_BIT, 16#1000). +-define(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT, 16#00000001). +-define(GL_ELEMENT_ARRAY_BARRIER_BIT, 16#00000002). +-define(GL_UNIFORM_BARRIER_BIT, 16#00000004). +-define(GL_TEXTURE_FETCH_BARRIER_BIT, 16#00000008). +-define(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT, 16#00000020). +-define(GL_COMMAND_BARRIER_BIT, 16#00000040). +-define(GL_PIXEL_BUFFER_BARRIER_BIT, 16#00000080). +-define(GL_TEXTURE_UPDATE_BARRIER_BIT, 16#00000100). +-define(GL_BUFFER_UPDATE_BARRIER_BIT, 16#00000200). +-define(GL_FRAMEBUFFER_BARRIER_BIT, 16#00000400). +-define(GL_TRANSFORM_FEEDBACK_BARRIER_BIT, 16#00000800). +-define(GL_ATOMIC_COUNTER_BARRIER_BIT, 16#00001000). -define(GL_ALL_BARRIER_BITS, 16#FFFFFFFF). -define(GL_MAX_IMAGE_UNITS, 16#8F38). -define(GL_MAX_COMBINED_IMAGE_UNITS_AND_FRAGMENT_OUTPUTS, 16#8F39). @@ -2337,8 +2337,8 @@ -define(GL_FRAMEZOOM_SGIX, 16#818B). -define(GL_FRAMEZOOM_FACTOR_SGIX, 16#818C). -define(GL_MAX_FRAMEZOOM_FACTOR_SGIX, 16#818D). --define(GL_TEXTURE_DEFORMATION_BIT_SGIX, 16#1). --define(GL_GEOMETRY_DEFORMATION_BIT_SGIX, 16#2). +-define(GL_TEXTURE_DEFORMATION_BIT_SGIX, 16#00000001). +-define(GL_GEOMETRY_DEFORMATION_BIT_SGIX, 16#00000002). -define(GL_GEOMETRY_DEFORMATION_SGIX, 16#8194). -define(GL_TEXTURE_DEFORMATION_SGIX, 16#8195). -define(GL_DEFORMATIONS_MASK_SGIX, 16#8196). @@ -2377,24 +2377,24 @@ -define(GL_VERTEX_CONSISTENT_HINT_PGI, 16#1A22B). -define(GL_MATERIAL_SIDE_HINT_PGI, 16#1A22C). -define(GL_MAX_VERTEX_HINT_PGI, 16#1A22D). --define(GL_COLOR3_BIT_PGI, 16#10000). --define(GL_COLOR4_BIT_PGI, 16#20000). --define(GL_EDGEFLAG_BIT_PGI, 16#40000). --define(GL_INDEX_BIT_PGI, 16#80000). --define(GL_MAT_AMBIENT_BIT_PGI, 16#100000). --define(GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI, 16#200000). --define(GL_MAT_DIFFUSE_BIT_PGI, 16#400000). --define(GL_MAT_EMISSION_BIT_PGI, 16#800000). --define(GL_MAT_COLOR_INDEXES_BIT_PGI, 16#1000000). --define(GL_MAT_SHININESS_BIT_PGI, 16#2000000). --define(GL_MAT_SPECULAR_BIT_PGI, 16#4000000). --define(GL_NORMAL_BIT_PGI, 16#8000000). +-define(GL_COLOR3_BIT_PGI, 16#00010000). +-define(GL_COLOR4_BIT_PGI, 16#00020000). +-define(GL_EDGEFLAG_BIT_PGI, 16#00040000). +-define(GL_INDEX_BIT_PGI, 16#00080000). +-define(GL_MAT_AMBIENT_BIT_PGI, 16#00100000). +-define(GL_MAT_AMBIENT_AND_DIFFUSE_BIT_PGI, 16#00200000). +-define(GL_MAT_DIFFUSE_BIT_PGI, 16#00400000). +-define(GL_MAT_EMISSION_BIT_PGI, 16#00800000). +-define(GL_MAT_COLOR_INDEXES_BIT_PGI, 16#01000000). +-define(GL_MAT_SHININESS_BIT_PGI, 16#02000000). +-define(GL_MAT_SPECULAR_BIT_PGI, 16#04000000). +-define(GL_NORMAL_BIT_PGI, 16#08000000). -define(GL_TEXCOORD1_BIT_PGI, 16#10000000). -define(GL_TEXCOORD2_BIT_PGI, 16#20000000). -define(GL_TEXCOORD3_BIT_PGI, 16#40000000). -define(GL_TEXCOORD4_BIT_PGI, 16#80000000). --define(GL_VERTEX23_BIT_PGI, 16#4). --define(GL_VERTEX4_BIT_PGI, 16#8). +-define(GL_VERTEX23_BIT_PGI, 16#00000004). +-define(GL_VERTEX4_BIT_PGI, 16#00000008). -define(GL_PREFER_DOUBLEBUFFER_HINT_PGI, 16#1A1F8). -define(GL_CONSERVE_MEMORY_HINT_PGI, 16#1A1FD). -define(GL_RECLAIM_MEMORY_HINT_PGI, 16#1A1FE). @@ -2591,9 +2591,9 @@ -define(GL_TEXTURE_CONSTANT_DATA_SUNX, 16#81D6). -define(GL_GLOBAL_ALPHA_SUN, 16#81D9). -define(GL_GLOBAL_ALPHA_FACTOR_SUN, 16#81DA). --define(GL_RESTART_SUN, 16#1). --define(GL_REPLACE_MIDDLE_SUN, 16#2). --define(GL_REPLACE_OLDEST_SUN, 16#3). +-define(GL_RESTART_SUN, 16#0001). +-define(GL_REPLACE_MIDDLE_SUN, 16#0002). +-define(GL_REPLACE_OLDEST_SUN, 16#0003). -define(GL_TRIANGLE_LIST_SUN, 16#81D7). -define(GL_REPLACEMENT_CODE_SUN, 16#81D8). -define(GL_REPLACEMENT_CODE_ARRAY_SUN, 16#85C0). @@ -3112,19 +3112,19 @@ -define(GL_SWIZZLE_STQ_DQ_ATI, 16#8979). -define(GL_SWIZZLE_STRQ_ATI, 16#897A). -define(GL_SWIZZLE_STRQ_DQ_ATI, 16#897B). --define(GL_RED_BIT_ATI, 16#1). --define(GL_GREEN_BIT_ATI, 16#2). --define(GL_BLUE_BIT_ATI, 16#4). --define(GL_2X_BIT_ATI, 16#1). --define(GL_4X_BIT_ATI, 16#2). --define(GL_8X_BIT_ATI, 16#4). --define(GL_HALF_BIT_ATI, 16#8). --define(GL_QUARTER_BIT_ATI, 16#10). --define(GL_EIGHTH_BIT_ATI, 16#20). --define(GL_SATURATE_BIT_ATI, 16#40). --define(GL_COMP_BIT_ATI, 16#2). --define(GL_NEGATE_BIT_ATI, 16#4). --define(GL_BIAS_BIT_ATI, 16#8). +-define(GL_RED_BIT_ATI, 16#00000001). +-define(GL_GREEN_BIT_ATI, 16#00000002). +-define(GL_BLUE_BIT_ATI, 16#00000004). +-define(GL_2X_BIT_ATI, 16#00000001). +-define(GL_4X_BIT_ATI, 16#00000002). +-define(GL_8X_BIT_ATI, 16#00000004). +-define(GL_HALF_BIT_ATI, 16#00000008). +-define(GL_QUARTER_BIT_ATI, 16#00000010). +-define(GL_EIGHTH_BIT_ATI, 16#00000020). +-define(GL_SATURATE_BIT_ATI, 16#00000040). +-define(GL_COMP_BIT_ATI, 16#00000002). +-define(GL_NEGATE_BIT_ATI, 16#00000004). +-define(GL_BIAS_BIT_ATI, 16#00000008). -define(GL_PN_TRIANGLES_ATI, 16#87F0). -define(GL_MAX_PN_TRIANGLES_TESSELATION_LEVEL_ATI, 16#87F1). -define(GL_PN_TRIANGLES_POINT_MODE_ATI, 16#87F2). @@ -3409,7 +3409,7 @@ -define(GL_MAX_PROGRAM_IF_DEPTH_NV, 16#88F6). -define(GL_MAX_PROGRAM_LOOP_DEPTH_NV, 16#88F7). -define(GL_MAX_PROGRAM_LOOP_COUNT_NV, 16#88F8). --define(GL_INVALID_FRAMEBUFFER_OPERATION_EXT, 16#506). +-define(GL_INVALID_FRAMEBUFFER_OPERATION_EXT, 16#0506). -define(GL_MAX_RENDERBUFFER_SIZE_EXT, 16#84E8). -define(GL_FRAMEBUFFER_BINDING_EXT, 16#8CA6). -define(GL_RENDERBUFFER_BINDING_EXT, 16#8CA7). @@ -3506,10 +3506,10 @@ -define(GL_MAX_PROGRAM_RESULT_COMPONENTS_NV, 16#8909). -define(GL_MAX_PROGRAM_GENERIC_ATTRIBS_NV, 16#8DA5). -define(GL_MAX_PROGRAM_GENERIC_RESULTS_NV, 16#8DA6). --define(GL_LINES_ADJACENCY_EXT, 16#A). --define(GL_LINE_STRIP_ADJACENCY_EXT, 16#B). --define(GL_TRIANGLES_ADJACENCY_EXT, 16#C). --define(GL_TRIANGLE_STRIP_ADJACENCY_EXT, 16#D). +-define(GL_LINES_ADJACENCY_EXT, 16#000A). +-define(GL_LINE_STRIP_ADJACENCY_EXT, 16#000B). +-define(GL_TRIANGLES_ADJACENCY_EXT, 16#000C). +-define(GL_TRIANGLE_STRIP_ADJACENCY_EXT, 16#000D). -define(GL_GEOMETRY_PROGRAM_NV, 16#8C26). -define(GL_MAX_PROGRAM_OUTPUT_VERTICES_NV, 16#8C27). -define(GL_MAX_PROGRAM_TOTAL_OUTPUT_COMPONENTS_NV, 16#8C28). @@ -3891,18 +3891,18 @@ -define(GL_UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY_EXT, 16#906C). -define(GL_MAX_IMAGE_SAMPLES_EXT, 16#906D). -define(GL_IMAGE_BINDING_FORMAT_EXT, 16#906E). --define(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT, 16#1). --define(GL_ELEMENT_ARRAY_BARRIER_BIT_EXT, 16#2). --define(GL_UNIFORM_BARRIER_BIT_EXT, 16#4). --define(GL_TEXTURE_FETCH_BARRIER_BIT_EXT, 16#8). --define(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT, 16#20). --define(GL_COMMAND_BARRIER_BIT_EXT, 16#40). --define(GL_PIXEL_BUFFER_BARRIER_BIT_EXT, 16#80). --define(GL_TEXTURE_UPDATE_BARRIER_BIT_EXT, 16#100). --define(GL_BUFFER_UPDATE_BARRIER_BIT_EXT, 16#200). --define(GL_FRAMEBUFFER_BARRIER_BIT_EXT, 16#400). --define(GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT, 16#800). --define(GL_ATOMIC_COUNTER_BARRIER_BIT_EXT, 16#1000). +-define(GL_VERTEX_ATTRIB_ARRAY_BARRIER_BIT_EXT, 16#00000001). +-define(GL_ELEMENT_ARRAY_BARRIER_BIT_EXT, 16#00000002). +-define(GL_UNIFORM_BARRIER_BIT_EXT, 16#00000004). +-define(GL_TEXTURE_FETCH_BARRIER_BIT_EXT, 16#00000008). +-define(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT_EXT, 16#00000020). +-define(GL_COMMAND_BARRIER_BIT_EXT, 16#00000040). +-define(GL_PIXEL_BUFFER_BARRIER_BIT_EXT, 16#00000080). +-define(GL_TEXTURE_UPDATE_BARRIER_BIT_EXT, 16#00000100). +-define(GL_BUFFER_UPDATE_BARRIER_BIT_EXT, 16#00000200). +-define(GL_FRAMEBUFFER_BARRIER_BIT_EXT, 16#00000400). +-define(GL_TRANSFORM_FEEDBACK_BARRIER_BIT_EXT, 16#00000800). +-define(GL_ATOMIC_COUNTER_BARRIER_BIT_EXT, 16#00001000). -define(GL_ALL_BARRIER_BITS_EXT, 16#FFFFFFFF). -define(GL_DOUBLE_VEC2_EXT, 16#8FFC). -define(GL_DOUBLE_VEC3_EXT, 16#8FFD). @@ -3952,7 +3952,7 @@ -define(GL_FLOAT16_VEC2_NV, 16#8FF9). -define(GL_FLOAT16_VEC3_NV, 16#8FFA). -define(GL_FLOAT16_VEC4_NV, 16#8FFB). --define(GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV, 16#10). +-define(GL_SHADER_GLOBAL_ACCESS_BARRIER_BIT_NV, 16#00000010). -define(GL_MAX_PROGRAM_PATCH_ATTRIBS_NV, 16#86D8). -define(GL_TESS_CONTROL_PROGRAM_NV, 16#891E). -define(GL_TESS_EVALUATION_PROGRAM_NV, 16#891F). diff --git a/lib/wx/include/glu.hrl b/lib/wx/include/glu.hrl index a2ab55d054..dc55c5bb96 100644 --- a/lib/wx/include/glu.hrl +++ b/lib/wx/include/glu.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2010. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -174,4 +174,4 @@ -define(GLU_TESS_WINDING_POSITIVE, 100132). -define(GLU_TESS_WINDING_NEGATIVE, 100133). -define(GLU_TESS_WINDING_ABS_GEQ_TWO, 100134). --define(GLU_TESS_MAX_COORD, ?1.0e150). +-define(GLU_TESS_MAX_COORD, 1.0e150). diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl index fba99a1f07..62979908a6 100644 --- a/lib/wx/include/wx.hrl +++ b/lib/wx/include/wx.hrl @@ -316,12 +316,13 @@ -type wxEventType() :: wxAuiManagerEventType() | wxAuiNotebookEventType() | wxCalendarEventType() | wxChildFocusEventType() | wxCloseEventType() | wxColourPickerEventType() | wxCommandEventType() | wxContextMenuEventType() | wxDateEventType() | wxDisplayChangedEventType() | wxEraseEventType() | wxFileDirPickerEventType() | wxFocusEventType() | wxFontPickerEventType() | wxGridEventType() | wxHelpEventType() | wxHtmlLinkEventType() | wxIconizeEventType() | wxIdleEventType() | wxJoystickEventType() | wxKeyEventType() | wxListEventType() | wxMaximizeEventType() | wxMenuEventType() | wxMouseCaptureChangedEventType() | wxMouseEventType() | wxMoveEventType() | wxNavigationKeyEventType() | wxNcPaintEventType() | wxNotebookEventType() | wxPaintEventType() | wxPaletteChangedEventType() | wxQueryNewPaletteEventType() | wxSashEventType() | wxScrollEventType() | wxScrollWinEventType() | wxSetCursorEventType() | wxShowEventType() | wxSizeEventType() | wxSpinEventType() | wxSplitterEventType() | wxStyledTextEventType() | wxSysColourChangedEventType() | wxTaskBarIconEventType() | wxTreeEventType() | wxUpdateUIEventType() | wxWindowCreateEventType() | wxWindowDestroyEventType(). %% Hardcoded Records --record(wxMouseState, {x, y, %% integer() - leftDown, middleDown, rightDown, %% bool() - controlDown, shiftDown, altDown, metaDown, cmdDown %% bool() +-record(wxMouseState, {x :: integer(), y :: integer(), + leftDown :: boolean(), middleDown :: boolean, rightDown :: boolean, + controlDown :: boolean(), shiftDown :: boolean(), + altDown :: boolean(), metaDown :: boolean(), cmdDown :: boolean() }). -record(wxHtmlLinkInfo, { - href, target %% unicode:chardata() + href :: unicode:chardata(), target :: unicode:chardata() }). %% Hardcoded Defines @@ -984,7 +985,7 @@ -define(wxRESET, 16384). -define(wxBACKWARD, 8192). -define(wxFORWARD, 4096). --define(wxICON_MASK, (?16#00000100 bor ?16#00000200 bor ?16#00000400 bor ?16#00000800)). +-define(wxICON_MASK, (16#00000100 bor 16#00000200 bor 16#00000400 bor 16#00000800)). -define(wxICON_ASTERISK, ?wxICON_INFORMATION). -define(wxICON_STOP, ?wxICON_HAND). -define(wxICON_INFORMATION, 2048). diff --git a/lib/wx/src/Makefile b/lib/wx/src/Makefile index 16b425e6f0..777fb7d998 100644 --- a/lib/wx/src/Makefile +++ b/lib/wx/src/Makefile @@ -120,15 +120,15 @@ $(EBIN)/%.beam: $(EGEN)/%.erl $(HEADER_FILES) # ---------------------------------------------------- include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/src/gen - $(INSTALL_DATA) $(GEN_HRL) $(GEN_FILES) $(RELSYSDIR)/src/gen - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(EXT_HRL) $(RELSYSDIR)/include - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) $(RELSYSDIR)/ebin + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(HRL_FILES) $(ERL_FILES) "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/src/gen" + $(INSTALL_DATA) $(GEN_HRL) $(GEN_FILES) "$(RELSYSDIR)/src/gen" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(EXT_HRL) "$(RELSYSDIR)/include" + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" # $(INSTALL_DATA) ../$(ARCHIVE) $(RELEASE_PATH)/lib release_docs_spec: diff --git a/lib/wx/src/gen/wxAuiManager.erl b/lib/wx/src/gen/wxAuiManager.erl index 95dd5d64a5..71e851f706 100644 --- a/lib/wx/src/gen/wxAuiManager.erl +++ b/lib/wx/src/gen/wxAuiManager.erl @@ -119,7 +119,7 @@ detachPane(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef}) <<ThisRef:32/?UI,WindowRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxauimanager.html#wxauimanagergetallpanes">external documentation</a>. --spec getAllPanes(This) -> wxAuiPaneInfoArray:wxAuiPaneInfoArray() when +-spec getAllPanes(This) -> wx:wx_object() when This::wxAuiManager(). getAllPanes(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxAuiManager), diff --git a/lib/wx/src/gen/wxBitmapButton.erl b/lib/wx/src/gen/wxBitmapButton.erl index 79e51da801..ddddbbc1dd 100644 --- a/lib/wx/src/gen/wxBitmapButton.erl +++ b/lib/wx/src/gen/wxBitmapButton.erl @@ -101,7 +101,7 @@ new(Parent,Id,Bitmap) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -129,7 +129,7 @@ create(This,Parent,Id,Bitmap) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxBitmapButton), diff --git a/lib/wx/src/gen/wxButton.erl b/lib/wx/src/gen/wxButton.erl index ae78f53e6f..a27e5d91c2 100644 --- a/lib/wx/src/gen/wxButton.erl +++ b/lib/wx/src/gen/wxButton.erl @@ -99,7 +99,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -128,7 +128,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxButton), diff --git a/lib/wx/src/gen/wxCheckBox.erl b/lib/wx/src/gen/wxCheckBox.erl index 37bca77cae..6e30f14207 100644 --- a/lib/wx/src/gen/wxCheckBox.erl +++ b/lib/wx/src/gen/wxCheckBox.erl @@ -99,7 +99,7 @@ new(Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -127,7 +127,7 @@ create(This,Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxCheckBox), diff --git a/lib/wx/src/gen/wxCheckListBox.erl b/lib/wx/src/gen/wxCheckListBox.erl index 71199fed3b..382345abfa 100644 --- a/lib/wx/src/gen/wxCheckListBox.erl +++ b/lib/wx/src/gen/wxCheckListBox.erl @@ -104,9 +104,9 @@ new(Parent,Id) Parent::wxWindow:wxWindow(), Id::integer(), Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} - | {choices, [[unicode:chardata()]]} + | {choices, [unicode:chardata()]} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), diff --git a/lib/wx/src/gen/wxChoice.erl b/lib/wx/src/gen/wxChoice.erl index b9cac3926c..92b094036e 100644 --- a/lib/wx/src/gen/wxChoice.erl +++ b/lib/wx/src/gen/wxChoice.erl @@ -102,9 +102,9 @@ new(Parent,Id) Parent::wxWindow:wxWindow(), Id::integer(), Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} - | {choices, [[unicode:chardata()]]} + | {choices, [unicode:chardata()]} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -120,7 +120,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) %% @equiv create(This,Parent,Id,Pos,Size,Choices, []) -spec create(This, Parent, Id, Pos, Size, Choices) -> boolean() when - This::wxChoice(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]]. + This::wxChoice(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()]. create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) -> @@ -128,9 +128,9 @@ create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxchoice.html#wxchoicecreate">external documentation</a>. -spec create(This, Parent, Id, Pos, Size, Choices, [Option]) -> boolean() when - This::wxChoice(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], + This::wxChoice(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()], Option :: {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ThisT,wxChoice), diff --git a/lib/wx/src/gen/wxColourPickerCtrl.erl b/lib/wx/src/gen/wxColourPickerCtrl.erl index 5fd51eba15..1ba771695f 100644 --- a/lib/wx/src/gen/wxColourPickerCtrl.erl +++ b/lib/wx/src/gen/wxColourPickerCtrl.erl @@ -104,7 +104,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -133,7 +133,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxColourPickerCtrl), diff --git a/lib/wx/src/gen/wxComboBox.erl b/lib/wx/src/gen/wxComboBox.erl index b9224d18b9..4e6b247f67 100644 --- a/lib/wx/src/gen/wxComboBox.erl +++ b/lib/wx/src/gen/wxComboBox.erl @@ -105,9 +105,9 @@ new(Parent,Id) Option :: {value, unicode:chardata()} | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} - | {choices, [[unicode:chardata()]]} + | {choices, [unicode:chardata()]} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -124,7 +124,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) %% @equiv create(This,Parent,Id,Value,Pos,Size,Choices, []) -spec create(This, Parent, Id, Value, Pos, Size, Choices) -> boolean() when - This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]]. + This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()]. create(This,Parent,Id,Value,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Value),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) -> @@ -132,9 +132,9 @@ create(This,Parent,Id,Value,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxcombobox.html#wxcomboboxcreate">external documentation</a>. -spec create(This, Parent, Id, Value, Pos, Size, Choices, [Option]) -> boolean() when - This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], + This::wxComboBox(), Parent::wxWindow:wxWindow(), Id::integer(), Value::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()], Option :: {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Value,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_list(Value),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ThisT,wxComboBox), diff --git a/lib/wx/src/gen/wxControlWithItems.erl b/lib/wx/src/gen/wxControlWithItems.erl index 1e2f89dbf0..92632a1c31 100644 --- a/lib/wx/src/gen/wxControlWithItems.erl +++ b/lib/wx/src/gen/wxControlWithItems.erl @@ -103,7 +103,7 @@ append(#wx_ref{type=ThisT,ref=ThisRef},Item,ClientData) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxcontrolwithitems.html#wxcontrolwithitemsappend">external documentation</a>. -spec appendStrings(This, Strings) -> ok when - This::wxControlWithItems(), Strings::[[unicode:chardata()]]. + This::wxControlWithItems(), Strings::[unicode:chardata()]. appendStrings(#wx_ref{type=ThisT,ref=ThisRef},Strings) when is_list(Strings) -> ?CLASS(ThisT,wxControlWithItems), diff --git a/lib/wx/src/gen/wxDatePickerCtrl.erl b/lib/wx/src/gen/wxDatePickerCtrl.erl index ad66d1cf03..6ffc2ca3f5 100644 --- a/lib/wx/src/gen/wxDatePickerCtrl.erl +++ b/lib/wx/src/gen/wxDatePickerCtrl.erl @@ -104,7 +104,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), diff --git a/lib/wx/src/gen/wxDirPickerCtrl.erl b/lib/wx/src/gen/wxDirPickerCtrl.erl index 5bc79ca7a1..bbc169ae03 100644 --- a/lib/wx/src/gen/wxDirPickerCtrl.erl +++ b/lib/wx/src/gen/wxDirPickerCtrl.erl @@ -105,7 +105,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -136,7 +136,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxDirPickerCtrl), diff --git a/lib/wx/src/gen/wxEvtHandler.erl b/lib/wx/src/gen/wxEvtHandler.erl index cf4a72da5a..22c203392c 100644 --- a/lib/wx/src/gen/wxEvtHandler.erl +++ b/lib/wx/src/gen/wxEvtHandler.erl @@ -80,7 +80,7 @@ connect(This, EventType) -> %% {userData, term()} An erlang term that will be sent with the event. Default: []. -spec connect(This::wxEvtHandler(), EventType::wxEventType(), [Option]) -> ok when Option :: {id, integer()} | {lastId, integer()} | {skip, boolean()} | - {callback, function()} | {userData, term()}. + callback | {callback, function()} | {userData, term()}. connect(This=#wx_ref{type=ThisT}, EventType, Options) -> EvH = parse_opts(Options, #evh{et=EventType}), ?CLASS(ThisT,wxEvtHandler), diff --git a/lib/wx/src/gen/wxFileDataObject.erl b/lib/wx/src/gen/wxFileDataObject.erl index bcb30a5ece..435c795cdc 100644 --- a/lib/wx/src/gen/wxFileDataObject.erl +++ b/lib/wx/src/gen/wxFileDataObject.erl @@ -55,7 +55,7 @@ addFile(#wx_ref{type=ThisT,ref=ThisRef},Filename) <<ThisRef:32/?UI,(byte_size(Filename_UC)):32/?UI,(Filename_UC)/binary, 0:(((8- ((0+byte_size(Filename_UC)) band 16#7)) band 16#7))/unit:8>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxfiledataobject.html#wxfiledataobjectgetfilenames">external documentation</a>. --spec getFilenames(This) -> [[unicode:charlist()]] when +-spec getFilenames(This) -> [unicode:charlist()] when This::wxFileDataObject(). getFilenames(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxFileDataObject), diff --git a/lib/wx/src/gen/wxFileDialog.erl b/lib/wx/src/gen/wxFileDialog.erl index 3d65c92758..a257905795 100644 --- a/lib/wx/src/gen/wxFileDialog.erl +++ b/lib/wx/src/gen/wxFileDialog.erl @@ -138,7 +138,7 @@ getFilename(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxfiledialog.html#wxfiledialoggetfilenames">external documentation</a>. --spec getFilenames(This) -> [[unicode:charlist()]] when +-spec getFilenames(This) -> [unicode:charlist()] when This::wxFileDialog(). getFilenames(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxFileDialog), @@ -170,7 +170,7 @@ getPath(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxfiledialog.html#wxfiledialoggetpaths">external documentation</a>. --spec getPaths(This) -> [[unicode:charlist()]] when +-spec getPaths(This) -> [unicode:charlist()] when This::wxFileDialog(). getPaths(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxFileDialog), diff --git a/lib/wx/src/gen/wxFilePickerCtrl.erl b/lib/wx/src/gen/wxFilePickerCtrl.erl index f13c20f6c7..d19c8c00cb 100644 --- a/lib/wx/src/gen/wxFilePickerCtrl.erl +++ b/lib/wx/src/gen/wxFilePickerCtrl.erl @@ -106,7 +106,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -139,7 +139,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxFilePickerCtrl), diff --git a/lib/wx/src/gen/wxFontPickerCtrl.erl b/lib/wx/src/gen/wxFontPickerCtrl.erl index 2659d4e2e3..46c0dbab4d 100644 --- a/lib/wx/src/gen/wxFontPickerCtrl.erl +++ b/lib/wx/src/gen/wxFontPickerCtrl.erl @@ -105,7 +105,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -134,7 +134,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxFontPickerCtrl), diff --git a/lib/wx/src/gen/wxGLCanvas.erl b/lib/wx/src/gen/wxGLCanvas.erl index 3a9cb39020..46168374af 100644 --- a/lib/wx/src/gen/wxGLCanvas.erl +++ b/lib/wx/src/gen/wxGLCanvas.erl @@ -96,7 +96,7 @@ new(Parent) %% | {palette, wxPalette:wxPalette()}.<br /> %% -spec new(Parent, Shared) -> wxGLCanvas() when - Parent::wxWindow:wxWindow(), Shared::wxGLContext:wxGLContext() | wxGLCanvas(); + Parent::wxWindow:wxWindow(), Shared::wx:wx_object() | wxGLCanvas(); (Parent, [Option]) -> wxGLCanvas() when Parent::wxWindow:wxWindow(), Option :: {id, integer()} @@ -128,7 +128,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef}, Options) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxglcanvas.html#wxglcanvaswxglcanvas">external documentation</a>. -spec new(Parent, Shared, [Option]) -> wxGLCanvas() when - Parent::wxWindow:wxWindow(), Shared::wxGLContext:wxGLContext() | wxGLCanvas(), + Parent::wxWindow:wxWindow(), Shared::wx:wx_object() | wxGLCanvas(), Option :: {id, integer()} | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} @@ -159,7 +159,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},#wx_ref{type=SharedT,ref=SharedRef}, Opt <<ParentRef:32/?UI,SharedRef:32/?UI, BinOpt/binary>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxglcanvas.html#wxglcanvasgetcontext">external documentation</a>. --spec getContext(This) -> wxGLContext:wxGLContext() when +-spec getContext(This) -> wx:wx_object() when This::wxGLCanvas(). getContext(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxGLCanvas), diff --git a/lib/wx/src/gen/wxGauge.erl b/lib/wx/src/gen/wxGauge.erl index c35a2574f6..7f892355c7 100644 --- a/lib/wx/src/gen/wxGauge.erl +++ b/lib/wx/src/gen/wxGauge.erl @@ -99,7 +99,7 @@ new(Parent,Id,Range) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Range, Options) when is_integer(Id),is_integer(Range),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -126,7 +126,7 @@ create(This,Parent,Id,Range) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Range, Options) when is_integer(Id),is_integer(Range),is_list(Options) -> ?CLASS(ThisT,wxGauge), diff --git a/lib/wx/src/gen/wxGridBagSizer.erl b/lib/wx/src/gen/wxGridBagSizer.erl index 6a978efc85..e8a9ff6d76 100644 --- a/lib/wx/src/gen/wxGridBagSizer.erl +++ b/lib/wx/src/gen/wxGridBagSizer.erl @@ -98,7 +98,7 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) -> %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()}.<br /> +%% | {userData, wx:wx_object()}.<br /> %% -spec add(This, Width, Height) -> wxSizerItem:wxSizerItem() when This::wxGridBagSizer(), Width::integer(), Height::integer(); @@ -109,7 +109,7 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) -> Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. add(This,Width,Height) when is_record(This, wx_ref),is_integer(Width),is_integer(Height) -> @@ -143,13 +143,13 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef}, Options %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()};<br /> +%% | {userData, wx:wx_object()};<br /> %% (This, Window, Pos, [Option]) -> wxSizerItem:wxSizerItem() when<br /> %% This::wxGridBagSizer(), Window::wxWindow:wxWindow() | wxSizer:wxSizer(), Pos::{R::integer(), C::integer()},<br /> %% Option :: {span, {RS::integer(), CS::integer()}}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()}.<br /> +%% | {userData, wx:wx_object()}.<br /> %% -spec add(This, Width, Height, Pos) -> wxSizerItem:wxSizerItem() when This::wxGridBagSizer(), Width::integer(), Height::integer(), Pos::{R::integer(), C::integer()}; @@ -158,13 +158,13 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef}, Options Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}; + | {userData, wx:wx_object()}; (This, Window, Pos, [Option]) -> wxSizerItem:wxSizerItem() when This::wxGridBagSizer(), Window::wxWindow:wxWindow() | wxSizer:wxSizer(), Pos::{R::integer(), C::integer()}, Option :: {span, {RS::integer(), CS::integer()}} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. add(This,Width,Height,Pos={PosR,PosC}) when is_record(This, wx_ref),is_integer(Width),is_integer(Height),is_integer(PosR),is_integer(PosC) -> @@ -204,7 +204,7 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef},{PosR,Po Option :: {span, {RS::integer(), CS::integer()}} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. add(#wx_ref{type=ThisT,ref=ThisRef},Width,Height,{PosR,PosC}, Options) when is_integer(Width),is_integer(Height),is_integer(PosR),is_integer(PosC),is_list(Options) -> ?CLASS(ThisT,wxGridBagSizer), @@ -305,7 +305,7 @@ findItemAtPosition(#wx_ref{type=ThisT,ref=ThisRef},{PosR,PosC}) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxgridbagsizer.html#wxgridbagsizerfinditemwithdata">external documentation</a>. -spec findItemWithData(This, UserData) -> wxGBSizerItem:wxGBSizerItem() when - This::wxGridBagSizer(), UserData::wx:wx(). + This::wxGridBagSizer(), UserData::wx:wx_object(). findItemWithData(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=UserDataT,ref=UserDataRef}) -> ?CLASS(ThisT,wxGridBagSizer), ?CLASS(UserDataT,wx), diff --git a/lib/wx/src/gen/wxGridCellChoiceEditor.erl b/lib/wx/src/gen/wxGridCellChoiceEditor.erl index 6f7d7ceffb..6b037e01b3 100644 --- a/lib/wx/src/gen/wxGridCellChoiceEditor.erl +++ b/lib/wx/src/gen/wxGridCellChoiceEditor.erl @@ -41,7 +41,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}). -type wxGridCellChoiceEditor() :: wx:wx_object(). %% @equiv new(Choices, []) -spec new(Choices) -> wxGridCellChoiceEditor() when - Choices::[[unicode:chardata()]]. + Choices::[unicode:chardata()]. new(Choices) when is_list(Choices) -> @@ -49,7 +49,7 @@ new(Choices) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxgridcellchoiceeditor.html#wxgridcellchoiceeditorwxgridcellchoiceeditor">external documentation</a>. -spec new(Choices, [Option]) -> wxGridCellChoiceEditor() when - Choices::[[unicode:chardata()]], + Choices::[unicode:chardata()], Option :: {allowOthers, boolean()}. new(Choices, Options) when is_list(Choices),is_list(Options) -> diff --git a/lib/wx/src/gen/wxIcon.erl b/lib/wx/src/gen/wxIcon.erl index c726f2ed4c..0f31278732 100644 --- a/lib/wx/src/gen/wxIcon.erl +++ b/lib/wx/src/gen/wxIcon.erl @@ -50,13 +50,13 @@ new() -> %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxicon.html#wxiconwxicon">external documentation</a>. %% <br /> Also:<br /> %% new(Loc) -> wxIcon() when<br /> -%% Loc::wx:wx().<br /> +%% Loc::wx:wx_object().<br /> %% %%<br /> Type = ?wxBITMAP_TYPE_INVALID | ?wxBITMAP_TYPE_BMP | ?wxBITMAP_TYPE_BMP_RESOURCE | ?wxBITMAP_TYPE_RESOURCE | ?wxBITMAP_TYPE_ICO | ?wxBITMAP_TYPE_ICO_RESOURCE | ?wxBITMAP_TYPE_CUR | ?wxBITMAP_TYPE_CUR_RESOURCE | ?wxBITMAP_TYPE_XBM | ?wxBITMAP_TYPE_XBM_DATA | ?wxBITMAP_TYPE_XPM | ?wxBITMAP_TYPE_XPM_DATA | ?wxBITMAP_TYPE_TIF | ?wxBITMAP_TYPE_TIF_RESOURCE | ?wxBITMAP_TYPE_GIF | ?wxBITMAP_TYPE_GIF_RESOURCE | ?wxBITMAP_TYPE_PNG | ?wxBITMAP_TYPE_PNG_RESOURCE | ?wxBITMAP_TYPE_JPEG | ?wxBITMAP_TYPE_JPEG_RESOURCE | ?wxBITMAP_TYPE_PNM | ?wxBITMAP_TYPE_PNM_RESOURCE | ?wxBITMAP_TYPE_PCX | ?wxBITMAP_TYPE_PCX_RESOURCE | ?wxBITMAP_TYPE_PICT | ?wxBITMAP_TYPE_PICT_RESOURCE | ?wxBITMAP_TYPE_ICON | ?wxBITMAP_TYPE_ICON_RESOURCE | ?wxBITMAP_TYPE_ANI | ?wxBITMAP_TYPE_IFF | ?wxBITMAP_TYPE_TGA | ?wxBITMAP_TYPE_MACCURSOR | ?wxBITMAP_TYPE_MACCURSOR_RESOURCE | ?wxBITMAP_TYPE_ANY -spec new(Filename) -> wxIcon() when Filename::unicode:chardata(); (Loc) -> wxIcon() when - Loc::wx:wx(). + Loc::wx:wx_object(). new(Filename) when is_list(Filename) -> diff --git a/lib/wx/src/gen/wxListBox.erl b/lib/wx/src/gen/wxListBox.erl index bcaf5fad3b..fa4cbd01d5 100644 --- a/lib/wx/src/gen/wxListBox.erl +++ b/lib/wx/src/gen/wxListBox.erl @@ -102,9 +102,9 @@ new(Parent,Id) Parent::wxWindow:wxWindow(), Id::integer(), Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} - | {choices, [[unicode:chardata()]]} + | {choices, [unicode:chardata()]} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -120,7 +120,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) %% @equiv create(This,Parent,Id,Pos,Size,Choices, []) -spec create(This, Parent, Id, Pos, Size, Choices) -> boolean() when - This::wxListBox(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]]. + This::wxListBox(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()]. create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) -> @@ -128,9 +128,9 @@ create(This,Parent,Id,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistbox.html#wxlistboxcreate">external documentation</a>. -spec create(This, Parent, Id, Pos, Size, Choices, [Option]) -> boolean() when - This::wxListBox(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], + This::wxListBox(), Parent::wxWindow:wxWindow(), Id::integer(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()], Option :: {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ThisT,wxListBox), @@ -164,7 +164,7 @@ getSelections(#wx_ref{type=ThisT,ref=ThisRef}) -> %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistbox.html#wxlistboxinsertitems">external documentation</a>. -spec insertItems(This, Items, Pos) -> ok when - This::wxListBox(), Items::[[unicode:chardata()]], Pos::integer(). + This::wxListBox(), Items::[unicode:chardata()], Pos::integer(). insertItems(#wx_ref{type=ThisT,ref=ThisRef},Items,Pos) when is_list(Items),is_integer(Pos) -> ?CLASS(ThisT,wxListBox), @@ -184,7 +184,7 @@ isSelected(#wx_ref{type=ThisT,ref=ThisRef},N) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistbox.html#wxlistboxset">external documentation</a>. -spec set(This, Items) -> ok when - This::wxListBox(), Items::[[unicode:chardata()]]. + This::wxListBox(), Items::[unicode:chardata()]. set(#wx_ref{type=ThisT,ref=ThisRef},Items) when is_list(Items) -> ?CLASS(ThisT,wxListBox), diff --git a/lib/wx/src/gen/wxListCtrl.erl b/lib/wx/src/gen/wxListCtrl.erl index 16e8ae4508..a6288fc02a 100644 --- a/lib/wx/src/gen/wxListCtrl.erl +++ b/lib/wx/src/gen/wxListCtrl.erl @@ -34,10 +34,10 @@ deleteItem/2,destroy/1,editLabel/2,ensureVisible/2,findItem/3,findItem/4, getColumn/3,getColumnCount/1,getColumnWidth/2,getCountPerPage/1,getEditControl/1, getImageList/2,getItem/2,getItemBackgroundColour/2,getItemCount/1, - getItemData/2,getItemFont/2,getItemPosition/3,getItemRect/3,getItemRect/4, + getItemData/2,getItemFont/2,getItemPosition/2,getItemRect/2,getItemRect/3, getItemSpacing/1,getItemState/3,getItemText/2,getItemTextColour/2, getNextItem/2,getNextItem/3,getSelectedItemCount/1,getTextColour/1, - getTopItem/1,getViewRect/1,hitTest/2,insertColumn/3,insertColumn/4, + getTopItem/1,getViewRect/1,hitTest/3,insertColumn/3,insertColumn/4, insertItem/2,insertItem/3,insertItem/4,refreshItem/2,refreshItems/3, scrollList/3,setBackgroundColour/2,setColumn/3,setColumnWidth/3,setImageList/3, setItem/2,setItem/4,setItem/5,setItemBackgroundColour/3,setItemColumnImage/4, @@ -391,34 +391,37 @@ getItemFont(#wx_ref{type=ThisT,ref=ThisRef},Item) <<ThisRef:32/?UI,Item:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlgetitemposition">external documentation</a>. --spec getItemPosition(This, Item, Pos) -> boolean() when - This::wxListCtrl(), Item::integer(), Pos::{X::integer(), Y::integer()}. -getItemPosition(#wx_ref{type=ThisT,ref=ThisRef},Item,{PosX,PosY}) - when is_integer(Item),is_integer(PosX),is_integer(PosY) -> +-spec getItemPosition(This, Item) -> Result when + Result ::{Res ::boolean(), Pos::{X::integer(), Y::integer()}}, + This::wxListCtrl(), Item::integer(). +getItemPosition(#wx_ref{type=ThisT,ref=ThisRef},Item) + when is_integer(Item) -> ?CLASS(ThisT,wxListCtrl), wxe_util:call(?wxListCtrl_GetItemPosition, - <<ThisRef:32/?UI,Item:32/?UI,PosX:32/?UI,PosY:32/?UI>>). + <<ThisRef:32/?UI,Item:32/?UI>>). -%% @equiv getItemRect(This,Item,Rect, []) --spec getItemRect(This, Item, Rect) -> boolean() when - This::wxListCtrl(), Item::integer(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}. +%% @equiv getItemRect(This,Item, []) +-spec getItemRect(This, Item) -> Result when + Result ::{Res ::boolean(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}}, + This::wxListCtrl(), Item::integer(). -getItemRect(This,Item,Rect={RectX,RectY,RectW,RectH}) - when is_record(This, wx_ref),is_integer(Item),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH) -> - getItemRect(This,Item,Rect, []). +getItemRect(This,Item) + when is_record(This, wx_ref),is_integer(Item) -> + getItemRect(This,Item, []). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlgetitemrect">external documentation</a>. --spec getItemRect(This, Item, Rect, [Option]) -> boolean() when - This::wxListCtrl(), Item::integer(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}, +-spec getItemRect(This, Item, [Option]) -> Result when + Result :: {Res ::boolean(), Rect::{X::integer(), Y::integer(), W::integer(), H::integer()}}, + This::wxListCtrl(), Item::integer(), Option :: {code, integer()}. -getItemRect(#wx_ref{type=ThisT,ref=ThisRef},Item,{RectX,RectY,RectW,RectH}, Options) - when is_integer(Item),is_integer(RectX),is_integer(RectY),is_integer(RectW),is_integer(RectH),is_list(Options) -> +getItemRect(#wx_ref{type=ThisT,ref=ThisRef},Item, Options) + when is_integer(Item),is_list(Options) -> ?CLASS(ThisT,wxListCtrl), MOpts = fun({code, Code}, Acc) -> [<<1:32/?UI,Code:32/?UI>>|Acc]; (BadOpt, _) -> erlang:error({badoption, BadOpt}) end, BinOpt = list_to_binary(lists:foldl(MOpts, [<<0:32>>], Options)), wxe_util:call(?wxListCtrl_GetItemRect, - <<ThisRef:32/?UI,Item:32/?UI,RectX:32/?UI,RectY:32/?UI,RectW:32/?UI,RectH:32/?UI, BinOpt/binary>>). + <<ThisRef:32/?UI,Item:32/?UI, BinOpt/binary>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlgetitemspacing">external documentation</a>. -spec getItemSpacing(This) -> {W::integer(), H::integer()} when @@ -511,14 +514,13 @@ getViewRect(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlhittest">external documentation</a>. --spec hitTest(This, Point) -> Result when - Result ::{Res ::integer(), Flags::integer()}, - This::wxListCtrl(), Point::{X::integer(), Y::integer()}. -hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PointX,PointY}) - when is_integer(PointX),is_integer(PointY) -> +-spec hitTest(This, Point, Flags) -> integer() when + This::wxListCtrl(), Point::{X::integer(), Y::integer()}, Flags::integer(). +hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PointX,PointY},Flags) + when is_integer(PointX),is_integer(PointY),is_integer(Flags) -> ?CLASS(ThisT,wxListCtrl), wxe_util:call(?wxListCtrl_HitTest, - <<ThisRef:32/?UI,PointX:32/?UI,PointY:32/?UI>>). + <<ThisRef:32/?UI,PointX:32/?UI,PointY:32/?UI,Flags:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxlistctrl.html#wxlistctrlinsertcolumn">external documentation</a>. %% <br /> Also:<br /> diff --git a/lib/wx/src/gen/wxMultiChoiceDialog.erl b/lib/wx/src/gen/wxMultiChoiceDialog.erl index 04cbb832ea..6a6a6b833a 100644 --- a/lib/wx/src/gen/wxMultiChoiceDialog.erl +++ b/lib/wx/src/gen/wxMultiChoiceDialog.erl @@ -94,7 +94,7 @@ new() -> %% @equiv new(Parent,Message,Caption,Choices, []) -spec new(Parent, Message, Caption, Choices) -> wxMultiChoiceDialog() when - Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[[unicode:chardata()]]. + Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()]. new(Parent,Message,Caption,Choices) when is_record(Parent, wx_ref),is_list(Message),is_list(Caption),is_list(Choices) -> @@ -102,7 +102,7 @@ new(Parent,Message,Caption,Choices) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxmultichoicedialog.html#wxmultichoicedialogwxmultichoicedialog">external documentation</a>. -spec new(Parent, Message, Caption, Choices, [Option]) -> wxMultiChoiceDialog() when - Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[[unicode:chardata()]], + Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()], Option :: {style, integer()} | {pos, {X::integer(), Y::integer()}}. new(#wx_ref{type=ParentT,ref=ParentRef},Message,Caption,Choices, Options) diff --git a/lib/wx/src/gen/wxRadioBox.erl b/lib/wx/src/gen/wxRadioBox.erl index 1b3717de23..b7f52d7d9c 100644 --- a/lib/wx/src/gen/wxRadioBox.erl +++ b/lib/wx/src/gen/wxRadioBox.erl @@ -82,7 +82,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}). -type wxRadioBox() :: wx:wx_object(). %% @equiv new(Parent,Id,Title,Pos,Size,Choices, []) -spec new(Parent, Id, Title, Pos, Size, Choices) -> wxRadioBox() when - Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]]. + Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()]. new(Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) when is_record(Parent, wx_ref),is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) -> @@ -90,10 +90,10 @@ new(Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxradiobox.html#wxradioboxwxradiobox">external documentation</a>. -spec new(Parent, Id, Title, Pos, Size, Choices, [Option]) -> wxRadioBox() when - Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], + Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()], Option :: {majorDim, integer()} | {style, integer()} - | {val, wx:wx()}. + | {val, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -110,7 +110,7 @@ new(#wx_ref{type=ParentT,ref=ParentRef},Id,Title,{PosX,PosY},{SizeW,SizeH},Choic %% @equiv create(This,Parent,Id,Title,Pos,Size,Choices, []) -spec create(This, Parent, Id, Title, Pos, Size, Choices) -> boolean() when - This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]]. + This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()]. create(This,Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) when is_record(This, wx_ref),is_record(Parent, wx_ref),is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices) -> @@ -118,10 +118,10 @@ create(This,Parent,Id,Title,Pos={PosX,PosY},Size={SizeW,SizeH},Choices) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxradiobox.html#wxradioboxcreate">external documentation</a>. -spec create(This, Parent, Id, Title, Pos, Size, Choices, [Option]) -> boolean() when - This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[[unicode:chardata()]], + This::wxRadioBox(), Parent::wxWindow:wxWindow(), Id::integer(), Title::unicode:chardata(), Pos::{X::integer(), Y::integer()}, Size::{W::integer(), H::integer()}, Choices::[unicode:chardata()], Option :: {majorDim, integer()} | {style, integer()} - | {val, wx:wx()}. + | {val, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Title,{PosX,PosY},{SizeW,SizeH},Choices, Options) when is_integer(Id),is_list(Title),is_integer(PosX),is_integer(PosY),is_integer(SizeW),is_integer(SizeH),is_list(Choices),is_list(Options) -> ?CLASS(ThisT,wxRadioBox), diff --git a/lib/wx/src/gen/wxRadioButton.erl b/lib/wx/src/gen/wxRadioButton.erl index a013bd65e4..beb052b873 100644 --- a/lib/wx/src/gen/wxRadioButton.erl +++ b/lib/wx/src/gen/wxRadioButton.erl @@ -97,7 +97,7 @@ new(Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -125,7 +125,7 @@ create(This,Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxRadioButton), diff --git a/lib/wx/src/gen/wxScrollBar.erl b/lib/wx/src/gen/wxScrollBar.erl index 30272312ab..4370bd1635 100644 --- a/lib/wx/src/gen/wxScrollBar.erl +++ b/lib/wx/src/gen/wxScrollBar.erl @@ -98,7 +98,7 @@ new(Parent,Id) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -125,7 +125,7 @@ create(This,Parent,Id) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxScrollBar), diff --git a/lib/wx/src/gen/wxSingleChoiceDialog.erl b/lib/wx/src/gen/wxSingleChoiceDialog.erl index 6f96e283c8..db6b41ae75 100644 --- a/lib/wx/src/gen/wxSingleChoiceDialog.erl +++ b/lib/wx/src/gen/wxSingleChoiceDialog.erl @@ -94,7 +94,7 @@ new() -> %% @equiv new(Parent,Message,Caption,Choices, []) -spec new(Parent, Message, Caption, Choices) -> wxSingleChoiceDialog() when - Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[[unicode:chardata()]]. + Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()]. new(Parent,Message,Caption,Choices) when is_record(Parent, wx_ref),is_list(Message),is_list(Caption),is_list(Choices) -> @@ -102,7 +102,7 @@ new(Parent,Message,Caption,Choices) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsinglechoicedialog.html#wxsinglechoicedialogwxsinglechoicedialog">external documentation</a>. -spec new(Parent, Message, Caption, Choices, [Option]) -> wxSingleChoiceDialog() when - Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[[unicode:chardata()]], + Parent::wxWindow:wxWindow(), Message::unicode:chardata(), Caption::unicode:chardata(), Choices::[unicode:chardata()], Option :: {style, integer()} | {pos, {X::integer(), Y::integer()}}. new(#wx_ref{type=ParentT,ref=ParentRef},Message,Caption,Choices, Options) diff --git a/lib/wx/src/gen/wxSizer.erl b/lib/wx/src/gen/wxSizer.erl index 104e1bcdc9..7edc01aa2a 100644 --- a/lib/wx/src/gen/wxSizer.erl +++ b/lib/wx/src/gen/wxSizer.erl @@ -56,7 +56,7 @@ add(This,Window) %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()};<br /> +%% | {userData, wx:wx_object()};<br /> %% (This, Window, Flags) -> wxSizerItem:wxSizerItem() when<br /> %% This::wxSizer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags().<br /> %% @@ -67,7 +67,7 @@ add(This,Window) Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}; + | {userData, wx:wx_object()}; (This, Window, Flags) -> wxSizerItem:wxSizerItem() when This::wxSizer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags(). @@ -110,7 +110,7 @@ add(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef},#wx_ref{ Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. add(#wx_ref{type=ThisT,ref=ThisRef},Width,Height, Options) when is_integer(Width),is_integer(Height),is_list(Options) -> ?CLASS(ThisT,wxSizer), @@ -350,7 +350,7 @@ insert(#wx_ref{type=ThisT,ref=ThisRef},Index,#wx_ref{type=ItemT,ref=ItemRef}) %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()};<br /> +%% | {userData, wx:wx_object()};<br /> %% (This, Index, Window, Flags) -> wxSizerItem:wxSizerItem() when<br /> %% This::wxSizer(), Index::integer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags().<br /> %% @@ -361,7 +361,7 @@ insert(#wx_ref{type=ThisT,ref=ThisRef},Index,#wx_ref{type=ItemT,ref=ItemRef}) Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}; + | {userData, wx:wx_object()}; (This, Index, Window, Flags) -> wxSizerItem:wxSizerItem() when This::wxSizer(), Index::integer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags(). @@ -405,7 +405,7 @@ insert(#wx_ref{type=ThisT,ref=ThisRef},Index,#wx_ref{type=WindowT,ref=WindowRef} Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. insert(#wx_ref{type=ThisT,ref=ThisRef},Index,Width,Height, Options) when is_integer(Index),is_integer(Width),is_integer(Height),is_list(Options) -> ?CLASS(ThisT,wxSizer), @@ -497,7 +497,7 @@ prepend(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) -> %% Option :: {proportion, integer()}<br /> %% | {flag, integer()}<br /> %% | {border, integer()}<br /> -%% | {userData, wx:wx()};<br /> +%% | {userData, wx:wx_object()};<br /> %% (This, Window, Flags) -> wxSizerItem:wxSizerItem() when<br /> %% This::wxSizer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags().<br /> %% @@ -508,7 +508,7 @@ prepend(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ItemT,ref=ItemRef}) -> Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}; + | {userData, wx:wx_object()}; (This, Window, Flags) -> wxSizerItem:wxSizerItem() when This::wxSizer(), Window::wxWindow:wxWindow() | wxSizer(), Flags::wxSizerFlags:wxSizerFlags(). @@ -551,7 +551,7 @@ prepend(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=WindowT,ref=WindowRef},#wx_ Option :: {proportion, integer()} | {flag, integer()} | {border, integer()} - | {userData, wx:wx()}. + | {userData, wx:wx_object()}. prepend(#wx_ref{type=ThisT,ref=ThisRef},Width,Height, Options) when is_integer(Width),is_integer(Height),is_list(Options) -> ?CLASS(ThisT,wxSizer), diff --git a/lib/wx/src/gen/wxSizerItem.erl b/lib/wx/src/gen/wxSizerItem.erl index 22533500da..62655864d1 100644 --- a/lib/wx/src/gen/wxSizerItem.erl +++ b/lib/wx/src/gen/wxSizerItem.erl @@ -72,7 +72,7 @@ new(Width,Height,#wx_ref{type=FlagsT,ref=FlagsRef}) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsizeritem.html#wxsizeritemwxsizeritem">external documentation</a>. -spec new(Window, Proportion, Flag, Border, UserData) -> wxSizerItem() when - Window::wxWindow:wxWindow() | wxSizer:wxSizer(), Proportion::integer(), Flag::integer(), Border::integer(), UserData::wx:wx(). + Window::wxWindow:wxWindow() | wxSizer:wxSizer(), Proportion::integer(), Flag::integer(), Border::integer(), UserData::wx:wx_object(). new(#wx_ref{type=WindowT,ref=WindowRef},Proportion,Flag,Border,#wx_ref{type=UserDataT,ref=UserDataRef}) when is_integer(Proportion),is_integer(Flag),is_integer(Border) -> WindowOP = case ?CLASS_T(WindowT,wxWindow) of @@ -88,7 +88,7 @@ new(#wx_ref{type=WindowT,ref=WindowRef},Proportion,Flag,Border,#wx_ref{type=User %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsizeritem.html#wxsizeritemwxsizeritem">external documentation</a>. -spec new(Width, Height, Proportion, Flag, Border, UserData) -> wxSizerItem() when - Width::integer(), Height::integer(), Proportion::integer(), Flag::integer(), Border::integer(), UserData::wx:wx(). + Width::integer(), Height::integer(), Proportion::integer(), Flag::integer(), Border::integer(), UserData::wx:wx_object(). new(Width,Height,Proportion,Flag,Border,#wx_ref{type=UserDataT,ref=UserDataRef}) when is_integer(Width),is_integer(Height),is_integer(Proportion),is_integer(Flag),is_integer(Border) -> ?CLASS(UserDataT,wx), @@ -200,7 +200,7 @@ getSpacer(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxsizeritem.html#wxsizeritemgetuserdata">external documentation</a>. --spec getUserData(This) -> wx:wx() when +-spec getUserData(This) -> wx:wx_object() when This::wxSizerItem(). getUserData(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxSizerItem), diff --git a/lib/wx/src/gen/wxSlider.erl b/lib/wx/src/gen/wxSlider.erl index e951e559ca..459e9b9c35 100644 --- a/lib/wx/src/gen/wxSlider.erl +++ b/lib/wx/src/gen/wxSlider.erl @@ -99,7 +99,7 @@ new(Parent,Id,Value,MinValue,MaxValue) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Value,MinValue,MaxValue, Options) when is_integer(Id),is_integer(Value),is_integer(MinValue),is_integer(MaxValue),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -126,7 +126,7 @@ create(This,Parent,Id,Value,MinValue,MaxValue) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Value,MinValue,MaxValue, Options) when is_integer(Id),is_integer(Value),is_integer(MinValue),is_integer(MaxValue),is_list(Options) -> ?CLASS(ThisT,wxSlider), diff --git a/lib/wx/src/gen/wxStyledTextCtrl.erl b/lib/wx/src/gen/wxStyledTextCtrl.erl index 057e01ddd5..f6dc2176b7 100644 --- a/lib/wx/src/gen/wxStyledTextCtrl.erl +++ b/lib/wx/src/gen/wxStyledTextCtrl.erl @@ -252,7 +252,7 @@ addText(#wx_ref{type=ThisT,ref=ThisRef},Text) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxstyledtextctrl.html#wxstyledtextctrladdstyledtext">external documentation</a>. -spec addStyledText(This, Data) -> ok when - This::wxStyledTextCtrl(), Data::wxMemoryBuffer:wxMemoryBuffer(). + This::wxStyledTextCtrl(), Data::wx:wx_object(). addStyledText(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=DataT,ref=DataRef}) -> ?CLASS(ThisT,wxStyledTextCtrl), ?CLASS(DataT,wxMemoryBuffer), @@ -361,7 +361,7 @@ setSavePoint(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxstyledtextctrl.html#wxstyledtextctrlgetstyledtext">external documentation</a>. --spec getStyledText(This, StartPos, EndPos) -> wxMemoryBuffer:wxMemoryBuffer() when +-spec getStyledText(This, StartPos, EndPos) -> wx:wx_object() when This::wxStyledTextCtrl(), StartPos::integer(), EndPos::integer(). getStyledText(#wx_ref{type=ThisT,ref=ThisRef},StartPos,EndPos) when is_integer(StartPos),is_integer(EndPos) -> diff --git a/lib/wx/src/gen/wxTextCtrl.erl b/lib/wx/src/gen/wxTextCtrl.erl index 21ff062d5b..e1f82c40c3 100644 --- a/lib/wx/src/gen/wxTextCtrl.erl +++ b/lib/wx/src/gen/wxTextCtrl.erl @@ -106,7 +106,7 @@ new(Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -201,7 +201,7 @@ create(This,Parent,Id) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id, Options) when is_integer(Id),is_list(Options) -> ?CLASS(ThisT,wxTextCtrl), diff --git a/lib/wx/src/gen/wxToggleButton.erl b/lib/wx/src/gen/wxToggleButton.erl index 1d770b5ca5..ed2f564952 100644 --- a/lib/wx/src/gen/wxToggleButton.erl +++ b/lib/wx/src/gen/wxToggleButton.erl @@ -97,7 +97,7 @@ new(Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -125,7 +125,7 @@ create(This,Parent,Id,Label) Option :: {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef},Id,Label, Options) when is_integer(Id),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxToggleButton), diff --git a/lib/wx/src/gen/wxToolBar.erl b/lib/wx/src/gen/wxToolBar.erl index e55becb748..9401e30e20 100644 --- a/lib/wx/src/gen/wxToolBar.erl +++ b/lib/wx/src/gen/wxToolBar.erl @@ -86,7 +86,7 @@ parent_class(_Class) -> erlang:error({badtype, ?MODULE}). -type wxToolBar() :: wx:wx_object(). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddcontrol">external documentation</a>. --spec addControl(This, Control) -> wx:wx() when +-spec addControl(This, Control) -> wx:wx_object() when This::wxToolBar(), Control::wxControl:wxControl(). addControl(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ControlT,ref=ControlRef}) -> ?CLASS(ThisT,wxToolBar), @@ -95,7 +95,7 @@ addControl(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ControlT,ref=ControlRef} <<ThisRef:32/?UI,ControlRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddseparator">external documentation</a>. --spec addSeparator(This) -> wx:wx() when +-spec addSeparator(This) -> wx:wx_object() when This::wxToolBar(). addSeparator(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxToolBar), @@ -103,8 +103,8 @@ addSeparator(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. --spec addTool(This, Tool) -> wx:wx() when - This::wxToolBar(), Tool::wx:wx(). +-spec addTool(This, Tool) -> wx:wx_object() when + This::wxToolBar(), Tool::wx:wx_object(). addTool(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ToolT,ref=ToolRef}) -> ?CLASS(ThisT,wxToolBar), ?CLASS(ToolT,wx), @@ -112,7 +112,7 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ToolT,ref=ToolRef}) -> <<ThisRef:32/?UI,ToolRef:32/?UI>>). %% @equiv addTool(This,Toolid,Bitmap, []) --spec addTool(This, Toolid, Bitmap) -> wx:wx() when +-spec addTool(This, Toolid, Bitmap) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(). addTool(This,Toolid,Bitmap) @@ -121,19 +121,19 @@ addTool(This,Toolid,Bitmap) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. %% <br /> Also:<br /> -%% addTool(This, Toolid, Bitmap, BmpDisabled) -> wx:wx() when<br /> +%% addTool(This, Toolid, Bitmap, BmpDisabled) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap();<br /> -%% (This, Toolid, Bitmap, [Option]) -> wx:wx() when<br /> +%% (This, Toolid, Bitmap, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(),<br /> %% Option :: {shortHelpString, unicode:chardata()}<br /> %% | {longHelpString, unicode:chardata()}.<br /> %% %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec addTool(This, Toolid, Label, Bitmap) -> wx:wx() when +-spec addTool(This, Toolid, Label, Bitmap) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(); - (This, Toolid, Bitmap, BmpDisabled) -> wx:wx() when + (This, Toolid, Bitmap, BmpDisabled) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(); - (This, Toolid, Bitmap, [Option]) -> wx:wx() when + (This, Toolid, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), Option :: {shortHelpString, unicode:chardata()} | {longHelpString, unicode:chardata()}. @@ -158,28 +158,28 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRe %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. %% <br /> Also:<br /> -%% addTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx() when<br /> +%% addTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(),<br /> %% Option :: {shortHelp, unicode:chardata()}<br /> %% | {kind, wx:wx_enum()};<br /> -%% (This, Toolid, Bitmap, BmpDisabled, [Option]) -> wx:wx() when<br /> +%% (This, Toolid, Bitmap, BmpDisabled, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(),<br /> %% Option :: {toggle, boolean()}<br /> -%% | {clientData, wx:wx()}<br /> +%% | {clientData, wx:wx_object()}<br /> %% | {shortHelpString, unicode:chardata()}<br /> %% | {longHelpString, unicode:chardata()}.<br /> %% %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec addTool(This, Toolid, Label, Bitmap, BmpDisabled) -> wx:wx() when +-spec addTool(This, Toolid, Label, Bitmap, BmpDisabled) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(); - (This, Toolid, Label, Bitmap, [Option]) -> wx:wx() when + (This, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), Option :: {shortHelp, unicode:chardata()} | {kind, wx:wx_enum()}; - (This, Toolid, Bitmap, BmpDisabled, [Option]) -> wx:wx() when + (This, Toolid, Bitmap, BmpDisabled, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(), Option :: {toggle, boolean()} - | {clientData, wx:wx()} + | {clientData, wx:wx_object()} | {shortHelpString, unicode:chardata()} | {longHelpString, unicode:chardata()}. @@ -213,22 +213,22 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRe %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. %% <br /> Also:<br /> -%% addTool(This, Toolid, Label, Bitmap, BmpDisabled, [Option]) -> wx:wx() when<br /> +%% addTool(This, Toolid, Label, Bitmap, BmpDisabled, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(),<br /> %% Option :: {kind, wx:wx_enum()}<br /> %% | {shortHelp, unicode:chardata()}<br /> %% | {longHelp, unicode:chardata()}<br /> -%% | {data, wx:wx()}.<br /> +%% | {data, wx:wx_object()}.<br /> %% %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec addTool(This, Toolid, Bitmap, BmpDisabled, Toggle, XPos) -> wx:wx() when +-spec addTool(This, Toolid, Bitmap, BmpDisabled, Toggle, XPos) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(), Toggle::boolean(), XPos::integer(); - (This, Toolid, Label, Bitmap, BmpDisabled, [Option]) -> wx:wx() when + (This, Toolid, Label, Bitmap, BmpDisabled, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(), Option :: {kind, wx:wx_enum()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()} - | {data, wx:wx()}. + | {data, wx:wx_object()}. addTool(This,Toolid,Bitmap,BmpDisabled,Toggle,XPos) when is_record(This, wx_ref),is_integer(Toolid),is_record(Bitmap, wx_ref),is_record(BmpDisabled, wx_ref),is_boolean(Toggle),is_integer(XPos) -> @@ -249,10 +249,10 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=Bi <<ThisRef:32/?UI,Toolid:32/?UI,(byte_size(Label_UC)):32/?UI,(Label_UC)/binary, 0:(((8- ((4+byte_size(Label_UC)) band 16#7)) band 16#7))/unit:8,BitmapRef:32/?UI,BmpDisabledRef:32/?UI, BinOpt/binary>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddtool">external documentation</a>. --spec addTool(This, Toolid, Bitmap, BmpDisabled, Toggle, XPos, [Option]) -> wx:wx() when +-spec addTool(This, Toolid, Bitmap, BmpDisabled, Toggle, XPos, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), BmpDisabled::wxBitmap:wxBitmap(), Toggle::boolean(), XPos::integer(), Option :: {yPos, integer()} - | {clientData, wx:wx()} + | {clientData, wx:wx_object()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()}. addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRef},#wx_ref{type=BmpDisabledT,ref=BmpDisabledRef},Toggle,XPos, Options) @@ -270,7 +270,7 @@ addTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,#wx_ref{type=BitmapT,ref=BitmapRe <<ThisRef:32/?UI,Toolid:32/?UI,BitmapRef:32/?UI,BmpDisabledRef:32/?UI,(wxe_util:from_bool(Toggle)):32/?UI,XPos:32/?UI, BinOpt/binary>>). %% @equiv addCheckTool(This,Toolid,Label,Bitmap, []) --spec addCheckTool(This, Toolid, Label, Bitmap) -> wx:wx() when +-spec addCheckTool(This, Toolid, Label, Bitmap) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(). addCheckTool(This,Toolid,Label,Bitmap) @@ -278,12 +278,12 @@ addCheckTool(This,Toolid,Label,Bitmap) addCheckTool(This,Toolid,Label,Bitmap, []). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddchecktool">external documentation</a>. --spec addCheckTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx() when +-spec addCheckTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), Option :: {bmpDisabled, wxBitmap:wxBitmap()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()} - | {data, wx:wx()}. + | {data, wx:wx_object()}. addCheckTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Toolid),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxToolBar), @@ -299,7 +299,7 @@ addCheckTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,r <<ThisRef:32/?UI,Toolid:32/?UI,(byte_size(Label_UC)):32/?UI,(Label_UC)/binary, 0:(((8- ((4+byte_size(Label_UC)) band 16#7)) band 16#7))/unit:8,BitmapRef:32/?UI, 0:32,BinOpt/binary>>). %% @equiv addRadioTool(This,Toolid,Label,Bitmap, []) --spec addRadioTool(This, Toolid, Label, Bitmap) -> wx:wx() when +-spec addRadioTool(This, Toolid, Label, Bitmap) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(). addRadioTool(This,Toolid,Label,Bitmap) @@ -307,12 +307,12 @@ addRadioTool(This,Toolid,Label,Bitmap) addRadioTool(This,Toolid,Label,Bitmap, []). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbaraddradiotool">external documentation</a>. --spec addRadioTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx() when +-spec addRadioTool(This, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), Option :: {bmpDisabled, wxBitmap:wxBitmap()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()} - | {data, wx:wx()}. + | {data, wx:wx_object()}. addRadioTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Toolid),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxToolBar), @@ -355,7 +355,7 @@ enableTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid,Enable) <<ThisRef:32/?UI,Toolid:32/?UI,(wxe_util:from_bool(Enable)):32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarfindbyid">external documentation</a>. --spec findById(This, Toolid) -> wx:wx() when +-spec findById(This, Toolid) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(). findById(#wx_ref{type=ThisT,ref=ThisRef},Toolid) when is_integer(Toolid) -> @@ -373,7 +373,7 @@ findControl(#wx_ref{type=ThisT,ref=ThisRef},Toolid) <<ThisRef:32/?UI,Toolid:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarfindtoolforposition">external documentation</a>. --spec findToolForPosition(This, X, Y) -> wx:wx() when +-spec findToolForPosition(This, X, Y) -> wx:wx_object() when This::wxToolBar(), X::integer(), Y::integer(). findToolForPosition(#wx_ref{type=ThisT,ref=ThisRef},X,Y) when is_integer(X),is_integer(Y) -> @@ -467,7 +467,7 @@ getToolState(#wx_ref{type=ThisT,ref=ThisRef},Toolid) <<ThisRef:32/?UI,Toolid:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinsertcontrol">external documentation</a>. --spec insertControl(This, Pos, Control) -> wx:wx() when +-spec insertControl(This, Pos, Control) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Control::wxControl:wxControl(). insertControl(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=ControlT,ref=ControlRef}) when is_integer(Pos) -> @@ -477,7 +477,7 @@ insertControl(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=ControlT,ref=Cont <<ThisRef:32/?UI,Pos:32/?UI,ControlRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinsertseparator">external documentation</a>. --spec insertSeparator(This, Pos) -> wx:wx() when +-spec insertSeparator(This, Pos) -> wx:wx_object() when This::wxToolBar(), Pos::integer(). insertSeparator(#wx_ref{type=ThisT,ref=ThisRef},Pos) when is_integer(Pos) -> @@ -486,8 +486,8 @@ insertSeparator(#wx_ref{type=ThisT,ref=ThisRef},Pos) <<ThisRef:32/?UI,Pos:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinserttool">external documentation</a>. --spec insertTool(This, Pos, Tool) -> wx:wx() when - This::wxToolBar(), Pos::integer(), Tool::wx:wx(). +-spec insertTool(This, Pos, Tool) -> wx:wx_object() when + This::wxToolBar(), Pos::integer(), Tool::wx:wx_object(). insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=ToolT,ref=ToolRef}) when is_integer(Pos) -> ?CLASS(ThisT,wxToolBar), @@ -496,7 +496,7 @@ insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,#wx_ref{type=ToolT,ref=ToolRef}) <<ThisRef:32/?UI,Pos:32/?UI,ToolRef:32/?UI>>). %% @equiv insertTool(This,Pos,Toolid,Bitmap, []) --spec insertTool(This, Pos, Toolid, Bitmap) -> wx:wx() when +-spec insertTool(This, Pos, Toolid, Bitmap) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(). insertTool(This,Pos,Toolid,Bitmap) @@ -505,22 +505,22 @@ insertTool(This,Pos,Toolid,Bitmap) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinserttool">external documentation</a>. %% <br /> Also:<br /> -%% insertTool(This, Pos, Toolid, Bitmap, [Option]) -> wx:wx() when<br /> +%% insertTool(This, Pos, Toolid, Bitmap, [Option]) -> wx:wx_object() when<br /> %% This::wxToolBar(), Pos::integer(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(),<br /> %% Option :: {bmpDisabled, wxBitmap:wxBitmap()}<br /> %% | {toggle, boolean()}<br /> -%% | {clientData, wx:wx()}<br /> +%% | {clientData, wx:wx_object()}<br /> %% | {shortHelp, unicode:chardata()}<br /> %% | {longHelp, unicode:chardata()}.<br /> %% %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec insertTool(This, Pos, Toolid, Label, Bitmap) -> wx:wx() when +-spec insertTool(This, Pos, Toolid, Label, Bitmap) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(); - (This, Pos, Toolid, Bitmap, [Option]) -> wx:wx() when + (This, Pos, Toolid, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Toolid::integer(), Bitmap::wxBitmap:wxBitmap(), Option :: {bmpDisabled, wxBitmap:wxBitmap()} | {toggle, boolean()} - | {clientData, wx:wx()} + | {clientData, wx:wx_object()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()}. @@ -543,13 +543,13 @@ insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,Toolid,#wx_ref{type=BitmapT,ref=B %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarinserttool">external documentation</a>. %%<br /> Kind = ?wxITEM_SEPARATOR | ?wxITEM_NORMAL | ?wxITEM_CHECK | ?wxITEM_RADIO | ?wxITEM_MAX --spec insertTool(This, Pos, Toolid, Label, Bitmap, [Option]) -> wx:wx() when +-spec insertTool(This, Pos, Toolid, Label, Bitmap, [Option]) -> wx:wx_object() when This::wxToolBar(), Pos::integer(), Toolid::integer(), Label::unicode:chardata(), Bitmap::wxBitmap:wxBitmap(), Option :: {bmpDisabled, wxBitmap:wxBitmap()} | {kind, wx:wx_enum()} | {shortHelp, unicode:chardata()} | {longHelp, unicode:chardata()} - | {clientData, wx:wx()}. + | {clientData, wx:wx_object()}. insertTool(#wx_ref{type=ThisT,ref=ThisRef},Pos,Toolid,Label,#wx_ref{type=BitmapT,ref=BitmapRef}, Options) when is_integer(Pos),is_integer(Toolid),is_list(Label),is_list(Options) -> ?CLASS(ThisT,wxToolBar), @@ -574,7 +574,7 @@ realize(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxtoolbar.html#wxtoolbarremovetool">external documentation</a>. --spec removeTool(This, Toolid) -> wx:wx() when +-spec removeTool(This, Toolid) -> wx:wx_object() when This::wxToolBar(), Toolid::integer(). removeTool(#wx_ref{type=ThisT,ref=ThisRef},Toolid) when is_integer(Toolid) -> diff --git a/lib/wx/src/gen/wxTreeCtrl.erl b/lib/wx/src/gen/wxTreeCtrl.erl index fef9916fff..dfa9e691ce 100644 --- a/lib/wx/src/gen/wxTreeCtrl.erl +++ b/lib/wx/src/gen/wxTreeCtrl.erl @@ -118,7 +118,7 @@ new(Parent) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. new(#wx_ref{type=ParentT,ref=ParentRef}, Options) when is_list(Options) -> ?CLASS(ParentT,wxWindow), @@ -235,7 +235,7 @@ create(This,Parent) | {pos, {X::integer(), Y::integer()}} | {size, {W::integer(), H::integer()}} | {style, integer()} - | {validator, wx:wx()}. + | {validator, wx:wx_object()}. create(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=ParentT,ref=ParentRef}, Options) when is_list(Options) -> ?CLASS(ThisT,wxTreeCtrl), diff --git a/lib/wx/src/gen/wxWindow.erl b/lib/wx/src/gen/wxWindow.erl index d31f489726..229633a106 100644 --- a/lib/wx/src/gen/wxWindow.erl +++ b/lib/wx/src/gen/wxWindow.erl @@ -526,7 +526,7 @@ getCursor(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxwindow.html#wxwindowgetdroptarget">external documentation</a>. --spec getDropTarget(This) -> wxDropTarget:wxDropTarget() when +-spec getDropTarget(This) -> wx:wx_object() when This::wxWindow(). getDropTarget(#wx_ref{type=ThisT,ref=ThisRef}) -> ?CLASS(ThisT,wxWindow), @@ -1384,7 +1384,7 @@ setOwnForegroundColour(#wx_ref{type=ThisT,ref=ThisRef},Colour) %% @doc See <a href="http://www.wxwidgets.org/manuals/stable/wx_wxwindow.html#wxwindowsetdroptarget">external documentation</a>. -spec setDropTarget(This, DropTarget) -> ok when - This::wxWindow(), DropTarget::wxDropTarget:wxDropTarget(). + This::wxWindow(), DropTarget::wx:wx_object(). setDropTarget(#wx_ref{type=ThisT,ref=ThisRef},#wx_ref{type=DropTargetT,ref=DropTargetRef}) -> ?CLASS(ThisT,wxWindow), ?CLASS(DropTargetT,wxDropTarget), diff --git a/lib/wx/src/wx.erl b/lib/wx/src/wx.erl index 32e9f5da3f..7d62305048 100644 --- a/lib/wx/src/wx.erl +++ b/lib/wx/src/wx.erl @@ -71,13 +71,17 @@ retain_memory/1, release_memory/1]). -export([demo/0]). --export_type([wx_colour/0, wx_datetime/0, wx_enum/0, wx_mouseState/0, wx_wxHtmlLinkInfo/0]). + +-export_type([wx_object/0, wx_env/0, wx_memory/0]). +-export_type([wx_colour/0, wx_colour4/0, wx_datetime/0, + wx_enum/0, wx_wxMouseState/0, wx_wxHtmlLinkInfo/0]). + -include("wxe.hrl"). -include("../include/wx.hrl"). --opaque wx_object() :: #wx_ref{}. %% Opaque object reference --opaque wx_env() :: #wx_env{}. %% Opaque process environment --opaque wx_memory() :: binary() | #wx_mem{}. %% Opaque memory reference +-type wx_object() :: #wx_ref{}. %% Opaque object reference +-type wx_env() :: #wx_env{}. %% Opaque process environment +-type wx_memory() :: binary() | #wx_mem{}. %% Opaque memory reference -type wx_colour4() :: {R::byte(),G::byte(),B::byte(), A::byte()}. -type wx_colour() :: {R::byte(),G::byte(),B::byte()} | wx_colour4(). @@ -85,7 +89,7 @@ -type wx_datetime() :: {{Year::integer(),Month::integer(),Day::integer()}, {Hour::integer(),Minute::integer(),Second::integer()}}. %% In Local Timezone --type wx_mouseState() :: #wxMouseState{}. %% See #wxMouseState{} defined in wx.hrl +-type wx_wxMouseState() :: #wxMouseState{}. %% See #wxMouseState{} defined in wx.hrl -type wx_enum() :: integer(). %% Constant defined in wx.hrl -type wx_wxHtmlLinkInfo() :: #wxHtmlLinkInfo{}. @@ -311,14 +315,15 @@ debug(Level) when is_integer(Level) -> end. %% @doc Starts a wxErlang demo if examples directory exists and is compiled --spec demo() -> ok. +-spec demo() -> ok | {error, atom()}. demo() -> Priv = code:priv_dir(wx), Demo = filename:join([filename:dirname(Priv),examples,demo]), Mod = list_to_atom("demo"), %% Fool xref tests case file:set_cwd(Demo) of ok -> - apply(Mod, start, []); + apply(Mod, start, []), + ok; _ -> {error, no_demo_dir} end. diff --git a/lib/wx/test/Makefile b/lib/wx/test/Makefile index ac8a4bdd60..836885a308 100644 --- a/lib/wx/test/Makefile +++ b/lib/wx/test/Makefile @@ -53,8 +53,8 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) wx.spec wx.cover wx_test_lib.hrl $(ErlSrc) $(ErlTargets) $(RELSYSDIR) - $(INSTALL_SCRIPT) wxt $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) wx.spec wx.cover wx_test_lib.hrl $(ErlSrc) $(ErlTargets) "$(RELSYSDIR)" + $(INSTALL_SCRIPT) wxt "$(RELSYSDIR)" release_docs_spec: diff --git a/lib/wx/vsn.mk b/lib/wx/vsn.mk index 3f3e9422a8..719eb0daf0 100644 --- a/lib/wx/vsn.mk +++ b/lib/wx/vsn.mk @@ -1 +1 @@ -WX_VSN = 0.99.1 +WX_VSN = 0.99.2 diff --git a/lib/xmerl/doc/src/Makefile b/lib/xmerl/doc/src/Makefile index 100a2feb0a..4de06249df 100644 --- a/lib/xmerl/doc/src/Makefile +++ b/lib/xmerl/doc/src/Makefile @@ -161,15 +161,15 @@ info: include $(ERL_TOP)/make/otp_release_targets.mk release_docs_spec: docs - $(INSTALL_DIR) $(RELSYSDIR)/doc/pdf - $(INSTALL_DATA) $(TOP_PDF_FILE) $(RELSYSDIR)/doc/pdf - $(INSTALL_DIR) $(RELSYSDIR)/doc/html + $(INSTALL_DIR) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DATA) $(TOP_PDF_FILE) "$(RELSYSDIR)/doc/pdf" + $(INSTALL_DIR) "$(RELSYSDIR)/doc/html" $(INSTALL_DATA) $(HTMLDIR)/* \ - $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(EXAMPLE_FILES) $(HTML_EXAMPLE_FILES) $(HTML_STYLESHEET_FILES) $(RELSYSDIR)/doc/html - $(INSTALL_DATA) $(INFO_FILE) $(RELSYSDIR) - $(INSTALL_DIR) $(RELEASE_PATH)/man/man3 - $(INSTALL_DATA) $(MAN3DIR)/* $(RELEASE_PATH)/man/man3 + "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(EXAMPLE_FILES) $(HTML_EXAMPLE_FILES) $(HTML_STYLESHEET_FILES) "$(RELSYSDIR)/doc/html" + $(INSTALL_DATA) $(INFO_FILE) "$(RELSYSDIR)" + $(INSTALL_DIR) "$(RELEASE_PATH)/man/man3" + $(INSTALL_DATA) $(MAN3DIR)/* "$(RELEASE_PATH)/man/man3" release_spec: diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index 8734bd8771..bd12e688d0 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -31,6 +31,64 @@ <p>This document describes the changes made to the Xmerl application.</p> +<section><title>Xmerl 1.3.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a continuation bug when a new block of bytes is + to be read during parsing of a default declaration. </p> + <p> + Own Id: OTP-10063 Aux Id: seq12049 </p> + </item> + </list> + </section> + +</section> + +<section><title>Xmerl 1.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Add missing spaces in xmerl doc (Thanks to Ricardo + Catalinas Jim�nez)</p> + <p> + Own Id: OTP-9873</p> + </item> + <item> + <p> Fixed a continuation error in the sax parser and + added latin1 as recognized encoding (not only the + iso-8859 variants). </p> + <p> + Own Id: OTP-9961</p> + </item> + <item> + <p> Removed the unused file xmerl_xlink.hrl. Thanks to + Vlad Dumitrescu for informing us about it. </p> + <p> + Own Id: OTP-9965</p> + </item> + <item> + <p> xmerl couldn't handle comments inside a type + specification. </p> + <p> + Own Id: OTP-10023</p> + </item> + <item> + <p> Fix some small errors in the sax parser: error + message bug, removal of trailing blanks in DTD element + definitions and an documentation error of the startDTD + event in xmerl_sax_parser module. </p> + <p> + Own Id: OTP-10026</p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/xmerl/doc/src/xmerl_sax_parser.xml b/lib/xmerl/doc/src/xmerl_sax_parser.xml index 972023622e..eb22711a75 100644 --- a/lib/xmerl/doc/src/xmerl_sax_parser.xml +++ b/lib/xmerl/doc/src/xmerl_sax_parser.xml @@ -206,10 +206,15 @@ Report the end of a CDATA section. </item> - <tag><c>startDTD</c></tag> + <tag><c>{startDTD, Name, PublicId, SystemId}</c></tag> <item> Report the start of DTD declarations, it's reporting the start of the DOCTYPE declaration. If the document has no DOCTYPE declaration, this event will not be sent. + <list> + <item><c>Name = string()</c></item> + <item><c>PublicId = string()</c></item> + <item><c>SystemId = string()</c></item> + </list> </item> <tag><c>endDTD</c></tag> diff --git a/lib/xmerl/doc/src/xmerl_ug.xmlsrc b/lib/xmerl/doc/src/xmerl_ug.xmlsrc index 9ef8fbb0b9..8a0805020e 100644 --- a/lib/xmerl/doc/src/xmerl_ug.xmlsrc +++ b/lib/xmerl/doc/src/xmerl_ug.xmlsrc @@ -4,7 +4,7 @@ <chapter> <header> <copyright> - <year>2004</year><year>2009</year> + <year>2004</year><year>2012</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> diff --git a/lib/xmerl/include/xmerl_xlink.hrl b/lib/xmerl/include/xmerl_xlink.hrl deleted file mode 100644 index 375e244c23..0000000000 --- a/lib/xmerl/include/xmerl_xlink.hrl +++ /dev/null @@ -1,26 +0,0 @@ - - - -%% The following is a brief summary of the element types (columns) on -%% which the global attributes are allowed: -%% -%% simple extended locator arc resource title -%% type X X X X X X -%% href X X -%% role X X X X -%% title X X X X -%% show X X X -%% actuate X X X -%% from X -%% to X -%% --record(xlink, { - type, % simple | extended | locator | arc | resource | title - href, - role - title, - show, - actuate, - from, - to - }). diff --git a/lib/xmerl/src/Makefile b/lib/xmerl/src/Makefile index 7009b50f6c..ce1aa11fba 100644 --- a/lib/xmerl/src/Makefile +++ b/lib/xmerl/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2009. All Rights Reserved. +# Copyright Ericsson AB 2003-2012. 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 @@ -94,7 +94,6 @@ MODULES = $(EDOC_MODULES) \ HRL_FILES = \ ../include/xmerl.hrl \ - ../include/xmerl_xlink.hrl \ ../include/xmerl_xpath.hrl \ ../include/xmerl_xsd.hrl @@ -215,14 +214,14 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt - $(INSTALL_DIR) $(RELSYSDIR)/ebin - $(INSTALL_DATA) $(TARGET_FILES) $(RELSYSDIR)/ebin - $(INSTALL_DIR) $(RELSYSDIR)/src - $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(APP_SRC) $(APPUP_SRC) $(RELSYSDIR)/src - $(INSTALL_DATA) xmerl_xpath_parse.yrl $(RELSYSDIR)/src - $(INSTALL_DATA) xmerl_b64Bin.yrl $(RELSYSDIR)/src - $(INSTALL_DIR) $(RELSYSDIR)/include - $(INSTALL_DATA) $(HRL_FILES) $(RELSYSDIR)/include + $(INSTALL_DIR) "$(RELSYSDIR)/ebin" + $(INSTALL_DATA) $(TARGET_FILES) "$(RELSYSDIR)/ebin" + $(INSTALL_DIR) "$(RELSYSDIR)/src" + $(INSTALL_DATA) $(ERL_FILES) $(INTERNAL_HRL_FILES) $(APP_SRC) $(APPUP_SRC) "$(RELSYSDIR)/src" + $(INSTALL_DATA) xmerl_xpath_parse.yrl "$(RELSYSDIR)/src" + $(INSTALL_DATA) xmerl_b64Bin.yrl "$(RELSYSDIR)/src" + $(INSTALL_DIR) "$(RELSYSDIR)/include" + $(INSTALL_DATA) $(HRL_FILES) "$(RELSYSDIR)/include" release_docs_spec: diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl index 45e2a928ac..5c006aada2 100644 --- a/lib/xmerl/src/xmerl_sax_parser.erl +++ b/lib/xmerl/src/xmerl_sax_parser.erl @@ -1,7 +1,7 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2011. All Rights Reserved. +%% Copyright Ericsson AB 2008-2012. 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 @@ -217,7 +217,7 @@ check_encoding_option(E) when E==utf8; E=={utf16,little}; E=={utf16,big}; check_encoding_option(utf16) -> {utf16,big}; check_encoding_option(E) -> - {error, io_lib:format("Charcter set ~p not supported", [E])}. + {error, io_lib:format("Character set ~p not supported", [E])}. %%---------------------------------------------------------------------- %% Function: detect_charset(Xml, State) @@ -279,6 +279,7 @@ convert_encoding(Enc) -> %% Just for 7,8 bit + utf8 case string:to_lower(Enc) of "utf-8" -> utf8; "us-ascii" -> utf8; + "latin1" -> latin1; "iso-8859-1" -> latin1; % Handle all iso-8859 as latin1 "iso-8859-2" -> latin1; "iso-8859-3" -> latin1; diff --git a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc index c25cde0472..d38045f2a5 100644 --- a/lib/xmerl/src/xmerl_sax_parser_base.erlsrc +++ b/lib/xmerl/src/xmerl_sax_parser_base.erlsrc @@ -39,6 +39,9 @@ %% Internal exports %%---------------------------------------------------------------------- -export([ + cf/3, + cf/4, + cf/5 ]). %%---------------------------------------------------------------------- @@ -152,16 +155,16 @@ parse_xml_decl(?BYTE_ORDER_MARK_2, State) -> cf(?BYTE_ORDER_MARK_2, State, fun parse_xml_decl/2); parse_xml_decl(?BYTE_ORDER_MARK_REST(Rest), State) -> cf(Rest, State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<"), State) -> - cf(?STRING("<"), State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<?"), State) -> - cf(?STRING("<?"), State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<?x"), State) -> - cf(?STRING("<?x"), State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<?xm"), State) -> - cf(?STRING("<?xm"), State, fun parse_xml_decl/2); -parse_xml_decl(?STRING("<?xml"), State) -> - cf(?STRING("<?xml"), State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<?") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<?x") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<?xm") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); +parse_xml_decl(?STRING("<?xml") = Bytes, State) -> + cf(Bytes, State, fun parse_xml_decl/2); parse_xml_decl(?STRING_REST("<?xml", Rest1), State) -> parse_xml_decl_1(Rest1, State); parse_xml_decl(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State) when is_binary(Bytes) -> @@ -201,8 +204,8 @@ parse_xml_decl_1(Bytes, State) -> %%---------------------------------------------------------------------- parse_prolog(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_prolog/2); -parse_prolog(?STRING("<"), State) -> - cf(?STRING("<"), State, fun parse_prolog/2); +parse_prolog(?STRING("<") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog/2); parse_prolog(?STRING_REST("<?", Rest), State) -> {Rest1, State1} = parse_pi(Rest, State), parse_prolog(Rest1, State1); @@ -220,18 +223,18 @@ parse_prolog(Bytes, State) -> parse_prolog_1(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("D"), State) -> - cf(?STRING("D"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DO"), State) -> - cf(?STRING("DO"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DOC"), State) -> - cf(?STRING("DOC"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DOCT"), State) -> - cf(?STRING("DOCT"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DOCTY"), State) -> - cf(?STRING("DOCTY"), State, fun parse_prolog_1/2); -parse_prolog_1(?STRING("DOCTYP"), State) -> - cf(?STRING("DOCTYP"), State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("D") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DO") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DOC") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DOCT") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DOCTY") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); +parse_prolog_1(?STRING("DOCTYP") = Bytes, State) -> + cf(Bytes, State, fun parse_prolog_1/2); parse_prolog_1(?STRING_REST("DOCTYPE", Rest), State) -> {Rest1, State1} = parse_doctype(Rest, State), State2 = event_callback(endDTD, State1), @@ -509,10 +512,10 @@ parse_ns_name(Bytes, State, Prefix, Name) -> %%---------------------------------------------------------------------- parse_pi_data(?STRING_EMPTY, State, Acc) -> cf(?STRING_EMPTY, State, Acc, fun parse_pi_data/3); -parse_pi_data(?STRING("?"), State, Acc) -> - cf(?STRING("?"), State, Acc, fun parse_pi_data/3); -parse_pi_data(?STRING("\r"), State, Acc) -> - cf(?STRING("\r"), State, Acc, fun parse_pi_data/3); +parse_pi_data(?STRING("?") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_pi_data/3); +parse_pi_data(?STRING("\r") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_pi_data/3); parse_pi_data(?STRING_REST("?>", Rest), State, Acc) -> {lists:reverse(Acc), Rest, State}; parse_pi_data(?STRING_REST("\n", Rest), #xmerl_sax_parser_state{line_no=N} = State, Acc) -> @@ -541,23 +544,23 @@ parse_pi_data(Bytes, State, Acc) -> %%---------------------------------------------------------------------- parse_cdata(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_cdata/2); -parse_cdata(?STRING("["), State) -> - cf(?STRING("["), State, fun parse_cdata/2); -parse_cdata(?STRING("[C"), State) -> - cf(?STRING("[C"), State, fun parse_cdata/2); -parse_cdata(?STRING("[CD"), State) -> - cf(?STRING("[CD"), State, fun parse_cdata/2); -parse_cdata(?STRING("[CDA"), State) -> - cf(?STRING("[CDA"), State, fun parse_cdata/2); -parse_cdata(?STRING("[CDAT"), State) -> - cf(?STRING("[CDAT"), State, fun parse_cdata/2); -parse_cdata(?STRING("[CDATA"), State) -> - cf(?STRING("[CDATA"), State, fun parse_cdata/2); -parse_cdata(?STRING_REST("[CDATA[", Rest), State) -> - State1 = event_callback(startCDATA, State), +parse_cdata(?STRING("[") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[C") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[CD") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[CDA") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[CDAT") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING("[CDATA") = Bytes, State) -> + cf(Bytes, State, fun parse_cdata/2); +parse_cdata(?STRING_REST("[CDATA[", Rest), State) -> + State1 = event_callback(startCDATA, State), parse_cdata(Rest, State1, []); -parse_cdata(Bytes, State) -> - unicode_incomplete_check([Bytes, State, fun parse_cdata/2], +parse_cdata(Bytes, State) -> + unicode_incomplete_check([Bytes, State, fun parse_cdata/2], "expecting comment or CDATA"). @@ -571,12 +574,12 @@ parse_cdata(Bytes, State) -> %%---------------------------------------------------------------------- parse_cdata(?STRING_EMPTY, State, Acc) -> cf(?STRING_EMPTY, State, Acc, fun parse_cdata/3); -parse_cdata(?STRING("\r"), State, Acc) -> - cf(?STRING("\r"), State, Acc, fun parse_cdata/3); -parse_cdata(?STRING("]"), State, Acc) -> - cf(?STRING("]"), State, Acc, fun parse_cdata/3); -parse_cdata(?STRING("]]"), State, Acc) -> - cf(?STRING("]]"), State, Acc, fun parse_cdata/3); +parse_cdata(?STRING("\r") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_cdata/3); +parse_cdata(?STRING("]") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_cdata/3); +parse_cdata(?STRING("]]") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_cdata/3); parse_cdata(?STRING_REST("]]>", Rest), State, Acc) -> State1 = event_callback({characters, lists:reverse(Acc)}, State), State2 = event_callback(endCDATA, State1), @@ -607,12 +610,12 @@ parse_cdata(Bytes, State, Acc) -> %%---------------------------------------------------------------------- parse_comment(?STRING_EMPTY, State, Acc) -> cf(?STRING_EMPTY, State, Acc, fun parse_comment/3); -parse_comment(?STRING("\r"), State, Acc) -> - cf(?STRING("\r"), State, Acc, fun parse_comment/3); -parse_comment(?STRING("-"), State, Acc) -> - cf(?STRING("-"), State, Acc, fun parse_comment/3); -parse_comment(?STRING("--"), State, Acc) -> - cf(?STRING("--"), State, Acc, fun parse_comment/3); +parse_comment(?STRING("\r") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_comment/3); +parse_comment(?STRING("-") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_comment/3); +parse_comment(?STRING("--") = Bytes, State, Acc) -> + cf(Bytes, State, Acc, fun parse_comment/3); parse_comment(?STRING_REST("-->", Rest), State, Acc) -> State1 = event_callback({comment, lists:reverse(Acc)}, State), {Rest, State1}; @@ -710,8 +713,8 @@ parse_stag(Bytes, State) -> %%---------------------------------------------------------------------- parse_attributes(?STRING_EMPTY, State, CurrentTag) -> cf(?STRING_EMPTY, State, CurrentTag, fun parse_attributes/3); -parse_attributes(?STRING("/"), State, CurrentTag) -> - cf(?STRING("/"), State, CurrentTag, fun parse_attributes/3); +parse_attributes(?STRING("/") = Bytes, State, CurrentTag) -> + cf(Bytes, State, CurrentTag, fun parse_attributes/3); parse_attributes(?STRING_REST("/>", Rest), State, {Tag, AttList, NewNsList}) -> CompleteNsList = NewNsList ++ State#xmerl_sax_parser_state.ns, {Uri, LocalName, QName, Attributes} = fix_ns(Tag, AttList, CompleteNsList), @@ -759,7 +762,7 @@ parse_attributes(?STRING_UNBOUND_REST(C, Rest), State, {Tag, AttList, NsList}) - end end; false -> - ?fatal_error(State, "Invalid start character in attribute name: " ++ C) + ?fatal_error(State, "Invalid start character in attribute name: " ++ [C]) end; parse_attributes(Bytes, State, CurrentTag) -> unicode_incomplete_check([Bytes, State, CurrentTag, fun parse_attributes/3], @@ -908,20 +911,20 @@ parse_att_value(?STRING_EMPTY, State, undefined, Acc) -> {Acc, [], State}; %% stop clause when parsing references parse_att_value(?STRING_EMPTY, State, Stop, Acc) -> cf(?STRING_EMPTY, State, Stop, Acc, fun parse_att_value/4); -parse_att_value(?STRING("\r"), State, Stop, Acc) -> - cf(?STRING("\r"), State, Stop, Acc, fun parse_att_value/4); +parse_att_value(?STRING("\r") = Bytes, State, Stop, Acc) -> + cf(Bytes, State, Stop, Acc, fun parse_att_value/4); parse_att_value(?STRING_REST("\n", Rest), #xmerl_sax_parser_state{line_no=N} = State, Stop, Acc) -> parse_att_value(Rest, - State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); + State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); parse_att_value(?STRING_REST("\r\n", Rest), #xmerl_sax_parser_state{line_no=N} = State, Stop, Acc) -> parse_att_value(Rest, - State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); + State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); parse_att_value(?STRING_REST("\r", Rest), #xmerl_sax_parser_state{line_no=N} = State, Stop, Acc) -> parse_att_value(Rest, - State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); + State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); parse_att_value(?STRING_REST("\t", Rest), #xmerl_sax_parser_state{line_no=N} = State, Stop, Acc) -> parse_att_value(Rest, - State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); + State#xmerl_sax_parser_state{line_no=N+1}, Stop, [?space |Acc]); parse_att_value(?STRING_REST("&", Rest), State, Stop, Acc) -> {Ref, Rest1, State1} = parse_reference(Rest, State, true), case Ref of @@ -1043,17 +1046,17 @@ parse_content(?STRING_EMPTY, State, Acc, IgnorableWS) -> Other -> throw(Other) end; -parse_content(?STRING("\r"), State, Acc, IgnorableWS) -> - cf(?STRING("\r"), State, Acc, IgnorableWS, fun parse_content/4); -parse_content(?STRING("<"), State, Acc, IgnorableWS) -> - cf(?STRING("<"), State, Acc, IgnorableWS, fun parse_content/4); +parse_content(?STRING("\r") = Bytes, State, Acc, IgnorableWS) -> + cf(Bytes, State, Acc, IgnorableWS, fun parse_content/4); +parse_content(?STRING("<") = Bytes, State, Acc, IgnorableWS) -> + cf(Bytes, State, Acc, IgnorableWS, fun parse_content/4); parse_content(?STRING_REST("</", Rest), State, Acc, IgnorableWS) -> State1 = send_character_event(length(Acc), IgnorableWS, lists:reverse(Acc), State), parse_etag(Rest, State1); -parse_content(?STRING("<!"), State, _Acc, IgnorableWS) -> - cf(?STRING("<!"), State, [], IgnorableWS, fun parse_content/4); -parse_content(?STRING("<!-"), State, _Acc, IgnorableWS) -> - cf(?STRING("<!-"), State, [], IgnorableWS, fun parse_content/4); +parse_content(?STRING("<!") = Bytes, State, _Acc, IgnorableWS) -> + cf(Bytes, State, [], IgnorableWS, fun parse_content/4); +parse_content(?STRING("<!-") = Bytes, State, _Acc, IgnorableWS) -> + cf(Bytes, State, [], IgnorableWS, fun parse_content/4); parse_content(?STRING_REST("<!--", Rest), State, Acc, IgnorableWS) -> State1 = send_character_event(length(Acc), IgnorableWS, lists:reverse(Acc), State), {Rest1, State2} = parse_comment(Rest, State1, []), @@ -1224,8 +1227,8 @@ whitespace(Bytes, #xmerl_sax_parser_state{encoding=Enc} = State, Acc) when is_bi %%---------------------------------------------------------------------- parse_reference(?STRING_EMPTY, State, HaveToExist) -> cf(?STRING_EMPTY, State, HaveToExist, fun parse_reference/3); -parse_reference(?STRING("#"), State, HaveToExist) -> - cf(?STRING("#"), State, HaveToExist, fun parse_reference/3); +parse_reference(?STRING("#") = Bytes, State, HaveToExist) -> + cf(Bytes, State, HaveToExist, fun parse_reference/3); parse_reference(?STRING_REST("#x", Rest), State, _HaveToExist) -> {CharValue, RefString, Rest1, State1} = parse_hex(Rest, State, []), if @@ -1699,16 +1702,16 @@ parse_external_entity_1(?BYTE_ORDER_MARK_2, State) -> cf(?BYTE_ORDER_MARK_2, State, fun parse_external_entity_1/2); parse_external_entity_1(?BYTE_ORDER_MARK_REST(Rest), State) -> parse_external_entity_1(Rest, State); -parse_external_entity_1(?STRING("<"), State) -> - cf(?STRING("<"), State, fun parse_external_entity_1/2); -parse_external_entity_1(?STRING("<?"), State) -> - cf(?STRING("<?"), State, fun parse_external_entity_1/2); -parse_external_entity_1(?STRING("<?x"), State) -> - cf(?STRING("<?x"), State, fun parse_external_entity_1/2); -parse_external_entity_1(?STRING("<?xm"), State) -> - cf(?STRING("<?xm"), State, fun parse_external_entity_1/2); -parse_external_entity_1(?STRING("<?xml"), State) -> - cf(?STRING("<?xml"), State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<?") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<?x") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<?xm") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); +parse_external_entity_1(?STRING("<?xml") = Bytes, State) -> + cf(Bytes, State, fun parse_external_entity_1/2); parse_external_entity_1(?STRING_REST("<?xml", Rest) = Bytes, #xmerl_sax_parser_state{file_type=Type} = State) -> {Rest1, State1} = @@ -1778,29 +1781,29 @@ is_next_char_whitespace(Bytes, State) -> %%---------------------------------------------------------------------- parse_external_id(?STRING_EMPTY, State, OptionalSystemId) -> cf(?STRING_EMPTY, State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("S"), State,OptionalSystemId) -> - cf(?STRING("S"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("SY"), State, OptionalSystemId) -> - cf(?STRING("SY"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("SYS"), State, OptionalSystemId) -> - cf(?STRING("SYS"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("SYST"), State, OptionalSystemId) -> - cf(?STRING("SYST"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("SYSTE"), State, OptionalSystemId) -> - cf(?STRING("SYSTE"), State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("S") = Bytes, State,OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("SY") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("SYS") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("SYST") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("SYSTE") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); parse_external_id(?STRING_REST("SYSTEM", Rest), State, _) -> {SysId, Rest1, State1} = parse_system_id(Rest, State, false), {"", SysId, Rest1, State1}; -parse_external_id(?STRING("P"), State, OptionalSystemId) -> - cf(?STRING("P"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("PU"), State, OptionalSystemId) -> - cf(?STRING("PU"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("PUB"), State, OptionalSystemId) -> - cf(?STRING("PUB"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("PUBL"), State, OptionalSystemId) -> - cf(?STRING("PUBL"), State, OptionalSystemId, fun parse_external_id/3); -parse_external_id(?STRING("PUBLI"), State, OptionalSystemId) -> - cf(?STRING("PUBLI"), State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("P") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("PU") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("PUB") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("PUBL") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); +parse_external_id(?STRING("PUBLI") = Bytes, State, OptionalSystemId) -> + cf(Bytes, State, OptionalSystemId, fun parse_external_id/3); parse_external_id(?STRING_REST("PUBLIC", Rest), State, OptionalSystemId) -> parse_public_id(Rest, State, OptionalSystemId); parse_external_id(Bytes, State, OptionalSystemId) -> @@ -1920,70 +1923,70 @@ parse_doctype_decl(Bytes, State) -> parse_doctype_decl_1(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("E"), State) -> - cf(?STRING("E"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("EL"), State) -> - cf(?STRING("EL"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ELE"), State) -> - cf(?STRING("ELE"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ELEM"), State) -> - cf(?STRING("ELEM"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ELEME"), State) -> - cf(?STRING("ELEME"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ELEMEN"), State) -> - cf(?STRING("ELEMEN"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("E") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("EL") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ELE") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ELEM") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ELEME") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ELEMEN") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("ELEMENT", Rest), State) -> {Rest1, State1} = parse_element_decl(Rest, State), parse_doctype_decl(Rest1, State1); -parse_doctype_decl_1(?STRING("A"), State) -> - cf(?STRING("A"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("AT"), State) -> - cf(?STRING("AT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ATT"), State) -> - cf(?STRING("ATT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ATTL"), State) -> - cf(?STRING("ATTL"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ATTLI"), State) -> - cf(?STRING("ATTLI"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ATTLIS"), State) -> - cf(?STRING("ATTLIS"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("A") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("AT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ATT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ATTL") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ATTLI") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ATTLIS") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("ATTLIST", Rest), State) -> {Rest1, State1} = parse_att_list_decl(Rest, State), parse_doctype_decl(Rest1, State1); %% E clause not needed here because already taken care of above. -parse_doctype_decl_1(?STRING("EN"), State) -> - cf(?STRING("EN"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ENT"), State) -> - cf(?STRING("ENT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ENTI"), State) -> - cf(?STRING("ENTI"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("ENTIT"), State) -> - cf(?STRING("ENTIT"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("EN") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ENT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ENTI") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("ENTIT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("ENTITY", Rest), State) -> {Rest1, State1} = parse_entity_decl(Rest, State), parse_doctype_decl(Rest1, State1); -parse_doctype_decl_1(?STRING("N"), State) -> - cf(?STRING("N"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NO"), State) -> - cf(?STRING("NO"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOT"), State) -> - cf(?STRING("NOT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOTA"), State) -> - cf(?STRING("NOTA"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOTAT"), State) -> - cf(?STRING("NOTAT"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOTATI"), State) -> - cf(?STRING("NOTATI"), State, fun parse_doctype_decl_1/2); -parse_doctype_decl_1(?STRING("NOTATIO"), State) -> - cf(?STRING("NOTATIO"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("N") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NO") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOTA") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOTAT") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOTATI") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("NOTATIO") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("NOTATION", Rest), State) -> {Rest1, State1} = parse_notation_decl(Rest, State), parse_doctype_decl(Rest1, State1); -parse_doctype_decl_1(?STRING("-"), State) -> - cf(?STRING("-"), State, fun parse_doctype_decl_1/2); +parse_doctype_decl_1(?STRING("-") = Bytes, State) -> + cf(Bytes, State, fun parse_doctype_decl_1/2); parse_doctype_decl_1(?STRING_REST("--", Rest), State) -> {Rest1, State1} = parse_comment(Rest, State, []), parse_doctype_decl(Rest1, State1); @@ -2054,14 +2057,18 @@ parse_element_content(Bytes, State) -> parse_element_content_1(?STRING_EMPTY, State, Acc) -> cf(?STRING_EMPTY, State, Acc, fun parse_element_content_1/3); parse_element_content_1(?STRING_REST(">", Rest), State, Acc) -> - {lists:reverse(Acc), Rest, State}; + {lists:reverse(delete_leading_whitespace(Acc)), Rest, State}; parse_element_content_1(?STRING_UNBOUND_REST(C, Rest), State, Acc) -> parse_element_content_1(Rest, State, [C|Acc]); parse_element_content_1(Bytes, State, Acc) -> unicode_incomplete_check([Bytes, State, Acc, fun parse_element_content_1/3], undefined). - +delete_leading_whitespace([C |Acc]) when ?is_whitespace(C)-> + delete_leading_whitespace(Acc); +delete_leading_whitespace(Acc) -> + Acc. + %%---------------------------------------------------------------------- %% Function : parse_att_list_decl(Rest, State) -> Result %% Parameters: Rest = string() | binary() @@ -2257,52 +2264,52 @@ parse_default_decl(Bytes, State) -> %%---------------------------------------------------------------------- parse_default_decl_1(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_default_decl_1/2); -parse_default_decl_1(?STRING_REST("#", Rest), State) -> - case Rest of - ?STRING("R") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("RE") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQ") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQU") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQUI") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQUIR") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("REQUIRE") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING_REST("REQUIRED", Rest1) -> +parse_default_decl_1(?STRING_REST("#", _Rest) = Bytes, State) -> + case Bytes of + ?STRING("#R") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#RE") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQ") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQU") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQUI") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQUIR") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#REQUIRE") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING_REST("#REQUIRED", Rest1) -> {"#REQUIRED", undefined, Rest1, State}; - ?STRING("I") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IM") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IMP") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IMPL") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IMPLI") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("IMPLIE") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING_REST("IMPLIED", Rest1) -> + ?STRING("#I") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IM") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IMP") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IMPL") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IMPLI") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#IMPLIE") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING_REST("#IMPLIED", Rest1) -> {"#IMPLIED", undefined, Rest1, State}; - ?STRING("F") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("FI") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("FIX") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING("FIXE") -> - cf(Rest, State, fun parse_default_decl_1/2); - ?STRING_REST("FIXED", Rest1) -> + ?STRING("#F") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#FI") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#FIX") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING("#FIXE") -> + cf(Bytes, State, fun parse_default_decl_1/2); + ?STRING_REST("#FIXED", Rest1) -> parse_fixed(Rest1, State); _ -> - ?fatal_error(State, "REQUIRED, IMPLIED or FIXED expected") + ?fatal_error(State, "REQUIRED, IMPLIED or FIXED expected after #") end; parse_default_decl_1(?STRING_UNBOUND_REST(C, Rest), State) when C == $'; C == $" -> {DefaultValue, Rest1, State1} = parse_att_value(Rest, State, C, []), @@ -2553,14 +2560,14 @@ parse_ndata_decl(?STRING_EMPTY, State) -> cf(?STRING_EMPTY, State, fun parse_ndata_decl/2); parse_ndata_decl(?STRING_REST(">", Rest), State) -> {undefined, Rest, State}; -parse_ndata_decl(?STRING("N") = Rest, State) -> - cf(Rest, State, fun parse_ndata_decl/2); -parse_ndata_decl(?STRING("ND") = Rest, State) -> - cf(Rest, State, fun parse_ndata_decl/2); -parse_ndata_decl(?STRING("NDA") = Rest, State) -> - cf(Rest, State, fun parse_ndata_decl/2); -parse_ndata_decl(?STRING("NDAT") = Rest, State) -> - cf(Rest, State, fun parse_ndata_decl/2); +parse_ndata_decl(?STRING("N") = Bytes, State) -> + cf(Bytes, State, fun parse_ndata_decl/2); +parse_ndata_decl(?STRING("ND") = Bytes, State) -> + cf(Bytes, State, fun parse_ndata_decl/2); +parse_ndata_decl(?STRING("NDA") = Bytes, State) -> + cf(Bytes, State, fun parse_ndata_decl/2); +parse_ndata_decl(?STRING("NDAT") = Bytes, State) -> + cf(Bytes, State, fun parse_ndata_decl/2); parse_ndata_decl(?STRING_REST("NDATA", Rest), State) -> parse_ndata_decl_1(Rest, State); parse_ndata_decl(Bytes, State) -> diff --git a/lib/xmerl/src/xmerl_uri.erl b/lib/xmerl/src/xmerl_uri.erl index 1864651491..ed66dd78d4 100644 --- a/lib/xmerl/src/xmerl_uri.erl +++ b/lib/xmerl/src/xmerl_uri.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2011. All Rights Reserved. +%% Copyright Ericsson AB 2005-2012. 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 diff --git a/lib/xmerl/src/xmerl_xsd.erl b/lib/xmerl/src/xmerl_xsd.erl index ed0890f0d0..78d84d23a4 100644 --- a/lib/xmerl/src/xmerl_xsd.erl +++ b/lib/xmerl/src/xmerl_xsd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2011. All Rights Reserved. +%% Copyright Ericsson AB 2006-2012. 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 @@ -2338,6 +2338,8 @@ complexity([]) -> undefined; complexity([#xmlText{}|T]) -> complexity(T); +complexity([#xmlComment{}|T]) -> + complexity(T); complexity([H|T]) -> case kind(H) of simpleContent -> diff --git a/lib/xmerl/test/Makefile b/lib/xmerl/test/Makefile index 5a2a585841..1fdbb7ca0d 100644 --- a/lib/xmerl/test/Makefile +++ b/lib/xmerl/test/Makefile @@ -113,15 +113,15 @@ include $(ERL_TOP)/make/otp_release_targets.mk release_spec: opt release_tests_spec: opt - $(INSTALL_DIR) $(RELSYSDIR) - $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) $(RELSYSDIR) - $(INSTALL_DATA) $(SUITE_FILES) $(RELSYSDIR) - $(INSTALL_DATA) xmerl.spec xmerl.cover $(RELSYSDIR) - cp $(XML_FILES) $(RELSYSDIR) - @tar cfh - xmerl_SUITE_data | (cd $(RELSYSDIR); tar xf -) - @tar cfh - xmerl_std_SUITE_data | (cd $(RELSYSDIR); tar xf -) - @tar cfh - xmerl_xsd_SUITE_data | (cd $(RELSYSDIR); tar xf -) - @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 -R u+w $(RELSYSDIR) + $(INSTALL_DIR) "$(RELSYSDIR)" + $(INSTALL_DATA) $(ERL_FILES) $(HRL_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) $(SUITE_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) xmerl.spec xmerl.cover "$(RELSYSDIR)" + cp $(XML_FILES) "$(RELSYSDIR)" + @tar cfh - xmerl_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @tar cfh - xmerl_std_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @tar cfh - xmerl_xsd_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) + @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 -R u+w "$(RELSYSDIR)" diff --git a/lib/xmerl/vsn.mk b/lib/xmerl/vsn.mk index de47e3418b..599bc0b9d3 100644 --- a/lib/xmerl/vsn.mk +++ b/lib/xmerl/vsn.mk @@ -1 +1 @@ -XMERL_VSN = 1.3 +XMERL_VSN = 1.3.2 |