diff options
Diffstat (limited to 'lib')
217 files changed, 12668 insertions, 11749 deletions
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl index 045cd69af1..b6430134ab 100644 --- a/lib/asn1/test/asn1_SUITE.erl +++ b/lib/asn1/test/asn1_SUITE.erl @@ -34,7 +34,9 @@ %% Suite definition %%------------------------------------------------------------------------------ -suite() -> [{ct_hooks, [ts_install_cth]}]. +suite() -> + [{ct_hooks, [ts_install_cth]}, + {timetrap,{minutes,60}}]. all() -> [{group, compile}, @@ -194,18 +196,15 @@ end_per_group(_GroupName, Config) -> Config. init_per_testcase(Func, Config) -> - CaseDir = filename:join(?config(priv_dir, Config), Func), + CaseDir = filename:join(proplists:get_value(priv_dir, Config), Func), ok = filelib:ensure_dir(filename:join([CaseDir, dummy_file])), true = code:add_patha(CaseDir), - - Dog = case Func of - testRfcs -> ct:timetrap({minutes, 90}); - _ -> ct:timetrap({minutes, 60}) - end, - [{case_dir, CaseDir}, {watchdog, Dog}|Config]. + [{case_dir, CaseDir}|Config]. end_per_testcase(_Func, Config) -> - code:del_path(?config(case_dir, Config)). + CaseDir = proplists:get_value(case_dir, Config), + asn1_test_lib:rm_dirs([CaseDir]), + code:del_path(CaseDir). %%------------------------------------------------------------------------------ %% Test runners @@ -243,7 +242,7 @@ opts(Rule) when is_atom(Rule) -> []; opts({_Rule, Opts}) -> Opts. run_case(Config, Fun, Rule, Opts) -> - CaseDir = ?config(case_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), Dir = filename:join([CaseDir, join(Rule, Opts)]), ok = filelib:ensure_dir(filename:join([Dir, dummy_file])), replace_path(CaseDir, Dir), @@ -465,7 +464,7 @@ testSeqExtension(Config, Rule, Opts) -> "SeqExtension2"], Config, [Rule|Opts]), - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), testSeqExtension:main(Rule, DataDir, [Rule|Opts]). testSeqOptional(Config) -> test(Config, fun testSeqOptional/3). @@ -585,8 +584,8 @@ constraint_equivalence(Config, Rule, Opts) -> asn1_test_lib:compile(M, Config, [Rule|Opts]). constraint_equivalence_abs(Config) -> - DataDir = ?config(data_dir, Config), - CaseDir = ?config(case_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), Asn1Spec = "ConstraintEquivalence", Asn1Src = filename:join(DataDir, Asn1Spec), ok = asn1ct:compile(Asn1Src, [abs,{outdir,CaseDir}]), @@ -637,7 +636,7 @@ module_test(M0, Config, Rule, Opts) -> %% value for 'Filter' is not guaranteed to terminate. ok; M -> - TestOpts = [{i, ?config(case_dir, Config)}], + TestOpts = [{i, proplists:get_value(case_dir, Config)}], case asn1ct:test(M, TestOpts) of ok -> ok; @@ -768,7 +767,7 @@ testInfObjectClass(Config, Rule, Opts) -> testUniqueObjectSets(Config) -> test(Config, fun testUniqueObjectSets/3). testUniqueObjectSets(Config, Rule, Opts) -> - CaseDir = ?config(case_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), testUniqueObjectSets:main(CaseDir, Rule, Opts). testInfObjExtract(Config) -> test(Config, fun testInfObjExtract/3). @@ -993,6 +992,9 @@ testS1AP(Config, Rule, Opts) -> ok end. +testRfcs() -> + [{timetrap,{minutes,90}}]. + testRfcs(Config) -> test(Config, fun testRfcs/3, [{ber,[der]}]). testRfcs(Config, Rule, Opts) -> case erlang:system_info(system_architecture) of @@ -1136,7 +1138,7 @@ test_modules() -> "CCSNARG3"]. test_OTP_9688(Config) -> - PrivDir = ?config(case_dir, Config), + PrivDir = proplists:get_value(case_dir, Config), Data = " OTP-9688 DEFINITIONS ::= BEGIN @@ -1172,12 +1174,10 @@ testTimer_uper(Config) -> testTimer:go(). %% Test of multiple-line comment, OTP-8043 -testComment(suite) -> []; testComment(Config) -> asn1_test_lib:compile("Comment", Config, []), asn1_test_lib:roundtrip('Comment', 'Seq', {'Seq',12,true}). -testName2Number(suite) -> []; testName2Number(Config) -> N2NOptions = [{n2n,Type} || Type <- ['CauseMisc', 'CauseProtocol', 'CauseRadioNetwork', diff --git a/lib/asn1/test/asn1_SUITE_data/test_records.erl b/lib/asn1/test/asn1_SUITE_data/test_records.erl index 4c40623f1a..9fd07c1449 100644 --- a/lib/asn1/test/asn1_SUITE_data/test_records.erl +++ b/lib/asn1/test/asn1_SUITE_data/test_records.erl @@ -45,19 +45,19 @@ check_record_names({initiatingMessage, transactionID = _TransactionID, value = Value}}) -> - ?line ok = check_record_ProcedureID(ProcedureID), - ?line ok = check_record_Value(Value). + ok = check_record_ProcedureID(ProcedureID), + ok = check_record_Value(Value). check_record_ProcedureID(#'ProcedureID'{}) -> ok; check_record_ProcedureID(_) -> false. check_record_Value(#'ResourceStatusIndication'{protocolIEs = ProtocolIEs}) -> - ?line ok = check_record_ProtocolIEs(ProtocolIEs); + ok = check_record_ProtocolIEs(ProtocolIEs); check_record_Value(_) -> false. check_record_ProtocolIEs([#'ProtocolIE-Field'{value =IndicationType}|_]) -> - ?line ok = check_record_NFResourceStatusInd(IndicationType); + ok = check_record_NFResourceStatusInd(IndicationType); check_record_ProtocolIEs(_) -> false. check_record_NFResourceStatusInd({'no-Failure',#'No-Failure-ResourceStatusInd'{'local-Cell-InformationList'=[LCIPF]}}) -> @@ -65,13 +65,13 @@ check_record_NFResourceStatusInd({'no-Failure',#'No-Failure-ResourceStatusInd'{' check_record_NFResourceStatusInd(_) -> false. 'check_record_NFResourceStatusInd_ProtocolIE-Field'(#'ProtocolIE-Field'{value=LCI}) -> - ?line ok = check_record_LCInfoResourceStatusInd(LCI); + ok = check_record_LCInfoResourceStatusInd(LCI); 'check_record_NFResourceStatusInd_ProtocolIE-Field'(_) -> false. check_record_LCInfoResourceStatusInd(#'Local-Cell-InformationItem-ResourceStatusInd'{commonChannelsCapacityConsumptionLaw=[CCCCL],dedicatedChannelsCapacityConsumptionLaw=[DCCCL],'iE-Extensions' = [LCIRE]}) -> - ?line ok = check_record_CCCCL(CCCCL), - ?line ok = check_record_DCCCL(DCCCL), - ?line ok = check_record_LCIRE(LCIRE). + ok = check_record_CCCCL(CCCCL), + ok = check_record_DCCCL(DCCCL), + ok = check_record_LCIRE(LCIRE). check_record_CCCCL(#'CommonChannelsCapacityConsumptionLaw_SEQOF'{}) -> ok; diff --git a/lib/asn1/test/asn1_app_test.erl b/lib/asn1/test/asn1_app_test.erl index b93c99a4d8..028322f555 100644 --- a/lib/asn1/test/asn1_app_test.erl +++ b/lib/asn1/test/asn1_app_test.erl @@ -42,8 +42,6 @@ end_per_group(_GroupName, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -init_per_suite(suite) -> []; -init_per_suite(doc) -> []; init_per_suite(Config) when is_list(Config) -> case is_app(asn1) of {ok, AppFile} -> @@ -64,18 +62,13 @@ is_app(App) -> end. -end_per_suite(suite) -> []; -end_per_suite(doc) -> []; end_per_suite(Config) when is_list(Config) -> Config. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -fields(suite) -> - []; -fields(doc) -> - []; +%% . fields(Config) when is_list(Config) -> AppFile = key1search(app_file, Config), Fields = [vsn, description, modules, registered, applications], @@ -103,10 +96,7 @@ check_field(Name, AppFile, Missing) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -modules(suite) -> - []; -modules(doc) -> - []; +%% . modules(Config) when is_list(Config) -> AppFile = key1search(app_file, Config), Mods = key1search(modules, AppFile), @@ -176,10 +166,7 @@ extra_modules(Mods, [Mod|Ebins], Extra) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -exportall(suite) -> - []; -exportall(doc) -> - []; +%% . exportall(Config) when is_list(Config) -> AppFile = key1search(app_file, Config), Mods = key1search(modules, AppFile), @@ -209,10 +196,7 @@ check_export_all([Mod|Mods]) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -app_depend(suite) -> - []; -app_depend(doc) -> - []; +%% . app_depend(Config) when is_list(Config) -> AppFile = key1search(app_file, Config), Apps = key1search(applications, AppFile), diff --git a/lib/asn1/test/asn1_appup_test.erl b/lib/asn1/test/asn1_appup_test.erl index dd16c02ce8..54540e53cc 100644 --- a/lib/asn1/test/asn1_appup_test.erl +++ b/lib/asn1/test/asn1_appup_test.erl @@ -42,14 +42,10 @@ end_per_group(_GroupName, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -init_per_suite(suite) -> []; -init_per_suite(doc) -> []; init_per_suite(Config) when is_list(Config) -> Config. -end_per_suite(suite) -> []; -end_per_suite(doc) -> []; end_per_suite(Config) when is_list(Config) -> Config. diff --git a/lib/asn1/test/asn1_test_lib.erl b/lib/asn1/test/asn1_test_lib.erl index c12aa34cc7..1b4c3b3c77 100644 --- a/lib/asn1/test/asn1_test_lib.erl +++ b/lib/asn1/test/asn1_test_lib.erl @@ -21,6 +21,7 @@ -module(asn1_test_lib). -export([compile/3,compile_all/3,compile_erlang/3, + rm_dirs/1, hex_to_bin/1, match_value/2, parallel/0, @@ -34,8 +35,8 @@ run_dialyzer() -> compile(File, Config, Options) -> compile_all([File], Config, Options). compile_all(Files, Config, Options) -> - DataDir = ?config(data_dir, Config), - CaseDir = ?config(case_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), [compile_file(filename:join(DataDir, F), [{outdir, CaseDir}, debug_info|Options]) || F <- Files], @@ -99,12 +100,24 @@ compile_file(File, Options) -> end. compile_erlang(Mod, Config, Options) -> - DataDir = ?config(data_dir, Config), - CaseDir = ?config(case_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), M = list_to_atom(Mod), {ok, M} = compile:file(filename:join(DataDir, Mod), [report,{i,CaseDir},{outdir,CaseDir}|Options]). +rm_dirs([Dir|Dirs]) -> + {ok,L0} = file:list_dir(Dir), + L = [filename:join(Dir, F) || F <- L0], + IsDir = fun(F) -> filelib:is_dir(F) end, + {Subdirs,Files} = lists:partition(IsDir, L), + _ = [ok = file:delete(F) || F <- Files], + rm_dirs(Subdirs), + ok = file:del_dir(Dir), + rm_dirs(Dirs); +rm_dirs([]) -> + ok. + hex_to_bin(S) -> << <<(hex2num(C)):4>> || C <- S, C =/= $\s >>. diff --git a/lib/asn1/test/error_SUITE.erl b/lib/asn1/test/error_SUITE.erl index 13571a2bcf..6ce77d93fb 100644 --- a/lib/asn1/test/error_SUITE.erl +++ b/lib/asn1/test/error_SUITE.erl @@ -929,8 +929,8 @@ values(Config) -> run({Mod,Spec}, Config) -> Base = atom_to_list(Mod) ++ ".asn1", - File = filename:join(?config(priv_dir, Config), Base), - Include0 = filename:dirname(?config(data_dir, Config)), + File = filename:join(proplists:get_value(priv_dir, Config), Base), + Include0 = filename:dirname(proplists:get_value(data_dir, Config)), Include = filename:join(filename:dirname(Include0), "asn1_SUITE_data"), ok = file:write_file(File, Spec), asn1ct:compile(File, [{i, Include}]). diff --git a/lib/asn1/test/syntax_SUITE.erl b/lib/asn1/test/syntax_SUITE.erl index 3f45955b3c..442ec59c70 100644 --- a/lib/asn1/test/syntax_SUITE.erl +++ b/lib/asn1/test/syntax_SUITE.erl @@ -305,7 +305,7 @@ run(List, File, Config) -> run(List, File0, Config, Module) -> Base = File0 ++ ".asn1", - File = filename:join(?config(priv_dir, Config), Base), + File = filename:join(proplists:get_value(priv_dir, Config), Base), case run_1(List, Base, File, Module, 0) of 0 -> ok; Errors -> ?t:fail(Errors) diff --git a/lib/asn1/test/testContextSwitchingTypes.erl b/lib/asn1/test/testContextSwitchingTypes.erl index cfe83fbf8e..10012908a9 100644 --- a/lib/asn1/test/testContextSwitchingTypes.erl +++ b/lib/asn1/test/testContextSwitchingTypes.erl @@ -38,7 +38,7 @@ test(Config) -> check_EXTERNAL(enc_dec('T', ValT_4)), {ok,ValT2} = asn1ct:value('ContextSwitchingTypes', 'T', - [{i,?config(case_dir, Config)}]), + [{i,proplists:get_value(case_dir, Config)}]), io:format("ValT2 ~p~n",[ValT2]), check_EXTERNAL(enc_dec('T', ValT2)), diff --git a/lib/asn1/test/testMegaco.erl b/lib/asn1/test/testMegaco.erl index eff10762d2..0be798b962 100644 --- a/lib/asn1/test/testMegaco.erl +++ b/lib/asn1/test/testMegaco.erl @@ -32,13 +32,13 @@ compile(Config, Erule, Options) -> main(no_module,_) -> ok; main('OLD-MEDIA-GATEWAY-CONTROL',Config) -> - CaseDir = ?config(case_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), {ok,Msg} = asn1ct:value('OLD-MEDIA-GATEWAY-CONTROL','MegacoMessage', [{i, CaseDir}]), asn1_test_lib:roundtrip('OLD-MEDIA-GATEWAY-CONTROL', 'MegacoMessage', Msg), ok; main('MEDIA-GATEWAY-CONTROL'=Mod, Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Files = filelib:wildcard(filename:join([DataDir,megacomessages,"*.val"])), lists:foreach(fun(File) -> {ok,Bin} = file:read_file(File), diff --git a/lib/asn1/test/testMergeCompile.erl b/lib/asn1/test/testMergeCompile.erl index bd8b092b49..f0e68e07b7 100644 --- a/lib/asn1/test/testMergeCompile.erl +++ b/lib/asn1/test/testMergeCompile.erl @@ -66,16 +66,16 @@ main(Erule) -> mvrasn(Erule) -> case Erule of ber -> - ?line ok = test(isd), - ?line ok = test(isd2), - ?line ok = test(dsd), - ?line ok = test(ul_res), - ?line ok = test(seqofseq), - ?line ok = test('InsertSubscriberDataArg'); + ok = test(isd), + ok = test(isd2), + ok = test(dsd), + ok = test(ul_res), + ok = test(seqofseq), + ok = test('InsertSubscriberDataArg'); _ -> ok end, - ?line ok = test(mvrasn6,'InsertSubscriberDataArg'). + ok = test(mvrasn6,'InsertSubscriberDataArg'). test(isd)-> EncPdu = <<48,128,129,7,145,148,113,50,1,0,241,131,1,0,176,128,5,0, diff --git a/lib/asn1/test/testNBAPsystem.erl b/lib/asn1/test/testNBAPsystem.erl index 8fbcf9bd2b..1af283af42 100644 --- a/lib/asn1/test/testNBAPsystem.erl +++ b/lib/asn1/test/testNBAPsystem.erl @@ -92,23 +92,23 @@ compile(Config, Options) -> test(_Erule,Config) -> - ?line ok = enc_audit_req_msg(), - ?line ok = cell_setup_req_msg_test(), + ok = enc_audit_req_msg(), + ok = cell_setup_req_msg_test(), ticket_5812(Config). ticket_5812(Config) -> - ?line Msg = v_5812(), + Msg = v_5812(), {ok,B2} = 'NBAP-PDU-Discriptions':encode('NBAP-PDU', Msg), V = <<0,28,74,0,3,48,0,0,1,0,123,64,41,0,0,0,126,64,35,95,208,2,89,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,145,0,1,205,0,0,0,0,2,98,64,1,128>>, - ?line ok = compare(V,B2), + ok = compare(V,B2), {ok,Msg2} = 'NBAP-PDU-Discriptions':decode('NBAP-PDU', B2), - ?line ok = check_record_names(Msg2,Config). + ok = check_record_names(Msg2,Config). enc_audit_req_msg() -> Msg = {initiatingMessage, audit_req_msg()}, {ok,B} = 'NBAP-PDU-Discriptions':encode('NBAP-PDU', Msg), {ok,_Msg} = 'NBAP-PDU-Discriptions':decode('NBAP-PDU', B), - ?line {initiatingMessage, + {initiatingMessage, #'InitiatingMessage'{value=#'AuditRequest'{protocolIEs=[{_,114,ignore,_}], protocolExtensions = asn1_NOVALUE}}} = _Msg, io:format("Msg: ~n~P~n~n_Msg:~n~P~n",[Msg,15,_Msg,15]), @@ -285,8 +285,8 @@ compare(_,_) -> false. check_record_names(Msg,Config) -> - DataDir = ?config(data_dir,Config), - CaseDir = ?config(case_dir,Config), + DataDir = proplists:get_value(data_dir,Config), + CaseDir = proplists:get_value(case_dir,Config), {ok, test_records} = compile:file(filename:join([DataDir, "test_records"]), [{i, CaseDir}]), ok = test_records:'check_record_names_OTP-5812'(Msg). diff --git a/lib/asn1/test/testParameterizedInfObj.erl b/lib/asn1/test/testParameterizedInfObj.erl index 5d6500e0ad..d485ef137e 100644 --- a/lib/asn1/test/testParameterizedInfObj.erl +++ b/lib/asn1/test/testParameterizedInfObj.erl @@ -97,7 +97,7 @@ roundtrip(T, V) -> ranap(_Erule) -> PIEVal2 = [{'ProtocolIE-Field',4,ignore,{radioNetwork,'rab-pre-empted'}}], - ?line Val2 = + Val2 = #'InitiatingMessage'{procedureCode=1, criticality=ignore, value=#'Iu-ReleaseCommand'{protocolIEs=PIEVal2, @@ -121,8 +121,8 @@ param2(Config, Erule) -> {'ProtocolIE-Field',101,true}]}), %% Now remove the data after the extension mark in the object set. - DataDir = ?config(data_dir, Config), - CaseDir = ?config(case_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), Asn1SrcBase = "Param2.asn1", Asn1SrcFile0 = filename:join(DataDir, Asn1SrcBase), {ok,Src0} = file:read_file(Asn1SrcFile0), diff --git a/lib/asn1/test/testRfcs.erl b/lib/asn1/test/testRfcs.erl index e616d00563..da7333ef98 100644 --- a/lib/asn1/test/testRfcs.erl +++ b/lib/asn1/test/testRfcs.erl @@ -27,11 +27,11 @@ compile(Config, Erules, Options0) -> Options = [no_ok_wrapper|Options0], - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Specs0 = filelib:wildcard("*.asn1", filename:join(DataDir, rfcs)), Specs = [filename:join(rfcs, Spec) || Spec <- Specs0], 122 = length(Specs), - CaseDir = ?config(case_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), asn1_test_lib:compile_all(Specs, Config, [Erules,{i,CaseDir}|Options]). test() -> diff --git a/lib/asn1/test/testSSLspecs.erl b/lib/asn1/test/testSSLspecs.erl index fd58e930c0..9a0129d91a 100644 --- a/lib/asn1/test/testSSLspecs.erl +++ b/lib/asn1/test/testSSLspecs.erl @@ -26,8 +26,8 @@ -include_lib("common_test/include/ct.hrl"). compile(Config, Options) -> - DataDir = ?config(data_dir, Config), - CaseDir = ?config(case_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), NewOptions = [{i, DataDir}, {i, CaseDir}|Options], asn1_test_lib:compile_all(["SSL-PKIX", "PKIXAttributeCertificate"], @@ -44,16 +44,16 @@ compile(Config, Options) -> Config, NewOptions). compile_combined(Config, ber=Rule) -> - DataDir = ?config(data_dir, Config), - CaseDir = ?config(case_dir, Config), + DataDir = proplists:get_value(data_dir, Config), + CaseDir = proplists:get_value(case_dir, Config), Options = [{i, CaseDir}, {i, DataDir}, Rule, der, compact_bit_string, asn1config], ok = remove_db_files_combined(CaseDir), asn1_test_lib:compile("OTP-PKIX.set.asn", Config, Options). remove_db_files(Dir) -> - ?line ok = remove_db_file(Dir ++ "PKIX1Explicit93.asn1db"), - ?line ok = remove_db_file(Dir ++ "PKIX1Implicit93.asn1db"). + ok = remove_db_file(Dir ++ "PKIX1Explicit93.asn1db"), + ok = remove_db_file(Dir ++ "PKIX1Implicit93.asn1db"). remove_db_file(File) -> case file:delete(File) of ok -> @@ -65,23 +65,23 @@ remove_db_file(File) -> end. remove_db_files_combined(Dir) -> - ?line ok = remove_db_file(Dir ++ "OTP-PKIX.asn1db"), - ?line ok = remove_db_file(Dir ++ "SSL-PKIX.asn1db"), - ?line ok = remove_db_file(Dir ++ "PKIXAttributeCertificate.asn1db"), - ?line ok = remove_db_file(Dir ++ "PKIX1Algorithms88.asn1db"), - ?line ok = remove_db_file(Dir ++ "PKIX1Explicit88.asn1db"), - ?line ok = remove_db_file(Dir ++ "PKIX1Implicit88.asn1db"). + ok = remove_db_file(Dir ++ "OTP-PKIX.asn1db"), + ok = remove_db_file(Dir ++ "SSL-PKIX.asn1db"), + ok = remove_db_file(Dir ++ "PKIXAttributeCertificate.asn1db"), + ok = remove_db_file(Dir ++ "PKIX1Algorithms88.asn1db"), + ok = remove_db_file(Dir ++ "PKIX1Explicit88.asn1db"), + ok = remove_db_file(Dir ++ "PKIX1Implicit88.asn1db"). run(ber) -> run1(1). run1(6) -> - ?line f1(6), - ?line f2(6), - ?line transform4(ex(7)); + f1(6), + f2(6), + transform4(ex(7)); run1(N) -> - ?line f1(N), - ?line f2(N), + f1(N), + f2(N), run1(N+1). @@ -93,22 +93,22 @@ f2(N) -> transform1(ATAV) -> - ?line {ok, ATAVEnc} = 'PKIX1Explicit88':encode('AttributeTypeAndValue', + {ok, ATAVEnc} = 'PKIX1Explicit88':encode('AttributeTypeAndValue', ATAV), - ?line {ok, _ATAVDec} = 'SSL-PKIX':decode('AttributeTypeAndValue', + {ok, _ATAVDec} = 'SSL-PKIX':decode('AttributeTypeAndValue', ATAVEnc). transform2(ATAV) -> - ?line {ok, ATAVEnc} = 'PKIX1Explicit88':encode('AttributeTypeAndValue', + {ok, ATAVEnc} = 'PKIX1Explicit88':encode('AttributeTypeAndValue', ATAV), - ?line {ok, _ATAVDec} = 'PKIX1Explicit88':decode('AttributeTypeAndValue', + {ok, _ATAVDec} = 'PKIX1Explicit88':decode('AttributeTypeAndValue', ATAVEnc). transform4(ATAV) -> - ?line {ok, ATAVEnc} = 'PKIX1Explicit88':encode('Attribute', + {ok, ATAVEnc} = 'PKIX1Explicit88':encode('Attribute', ATAV), - ?line {ok, _ATAVDec} = 'PKIX1Explicit88':decode('Attribute', + {ok, _ATAVDec} = 'PKIX1Explicit88':decode('Attribute', ATAVEnc). @@ -144,8 +144,8 @@ ex(7) -> run_combined(ber) -> Cert = cert(), - ?line {ok,{'CertificatePKIX1Explicit88',{Type,UnDec},_,_}} = 'OTP-PKIX':decode_TBSCert_exclusive(Cert), - ?line {ok,_} = 'OTP-PKIX':decode_part(Type,UnDec), + {ok,{'CertificatePKIX1Explicit88',{Type,UnDec},_,_}} = 'OTP-PKIX':decode_TBSCert_exclusive(Cert), + {ok,_} = 'OTP-PKIX':decode_part(Type,UnDec), ok. cert() -> diff --git a/lib/asn1/test/testSeqOfIndefinite.erl b/lib/asn1/test/testSeqOfIndefinite.erl index fcc41edf82..2f66baf9a8 100644 --- a/lib/asn1/test/testSeqOfIndefinite.erl +++ b/lib/asn1/test/testSeqOfIndefinite.erl @@ -25,13 +25,13 @@ -include_lib("common_test/include/ct.hrl"). main() -> - ?line ok = test(isd), - ?line ok = test(isd2), - ?line ok = test(dsd), - ?line ok = test(ul_res), - ?line ok = test(prim), - ?line ok = test(seqofseq), - ?line ok = test('InsertSubscriberDataArg'). % OTP-4232 + ok = test(isd), + ok = test(isd2), + ok = test(dsd), + ok = test(ul_res), + ok = test(prim), + ok = test(seqofseq), + ok = test('InsertSubscriberDataArg'). % OTP-4232 test(isd)-> EncPdu = <<48,128,129,7,145,148,113,50,1,0,241,131,1,0,176,128,5,0, diff --git a/lib/asn1/test/testTCAP.erl b/lib/asn1/test/testTCAP.erl index c079ed9b73..422ae1f0fc 100644 --- a/lib/asn1/test/testTCAP.erl +++ b/lib/asn1/test/testTCAP.erl @@ -39,7 +39,6 @@ compile_asn1config(Config, Options) -> asn1_test_lib:compile_erlang("TCAPPackage_msg", Config, []). test(Erule,_Config) -> -% ?line OutDir = ?config(priv_dir,Config), %% testing OTP-4798, open type encoded with indefinite length {ok,_Res} = 'TCAPMessages-simple':decode('MessageType', val_OTP_4798(Erule)), @@ -49,20 +48,17 @@ test(Erule,_Config) -> val_OTP_4799(Erule)), %% testing vance shipley's problems. Parameterized object sets. - ?line Val3 = 'TCAPPackage_msg':val('PackageType',unidirectional), + Val3 = 'TCAPPackage_msg':val('PackageType',unidirectional), Res3 = enc_dec('PackageType', Val3), - ?line ok = 'TCAPPackage_msg':check_result('PackageType',unidirectional,Res3), -%% ?line io:format("Res3:~n~p~n~n",[Res3]), + ok = 'TCAPPackage_msg':check_result('PackageType',unidirectional,Res3), - ?line Val4 = 'TCAPPackage_msg':val('PackageType',abort), + Val4 = 'TCAPPackage_msg':val('PackageType',abort), Res4 = enc_dec('PackageType', Val4), - ?line ok = 'TCAPPackage_msg':check_result('PackageType',abort,Res4), -%% ?line io:format("Res4:~n~p~n~n",[Res4]), + ok = 'TCAPPackage_msg':check_result('PackageType',abort,Res4), - ?line Val5 = 'TCAPPackage_msg':val('PackageType',response), + Val5 = 'TCAPPackage_msg':val('PackageType',response), Res5 = enc_dec('PackageType', Val5), - ?line ok = 'TCAPPackage_msg':check_result('PackageType',response,Res5). -%% ?line io:format("Res5:~n~p~n~n",[Res5]). + ok = 'TCAPPackage_msg':check_result('PackageType',response,Res5). val_OTP_4798(ber) -> [100,129,176,73,4,57,3,17,80,107,42,40,40,6,7,0,17,134,5,1,1,1,160,29,97,27,128,2,7,128,161,9,6,7,4,0,0,1,0,14,2,162,3,2,1,0,163,5,161,3,2,1,0,108,128,162,120,2,1,0,48,115,2,1,56,48,128,48,34,4,16,203,87,215,196,217,93,235,90,64,131,106,145,39,26,25,236,4,4,197,241,81,112,4,8,78,225,34,196,215,212,200,0,48,34,4,16,145,125,27,67,42,144,6,161,207,112,55,75,200,191,191,28,4,4,226,219,242,123,4,8,72,46,130,28,206,178,168,0,48,34,4,16,1,8,20,29,70,160,218,160,125,188,244,174,113,115,253,245,4,4,26,5,90,160,4,8,252,75,149,98,153,224,140,0,0,0,0,0]; diff --git a/lib/asn1/test/test_compile_options.erl b/lib/asn1/test/test_compile_options.erl index e32840660a..ac74470537 100644 --- a/lib/asn1/test/test_compile_options.erl +++ b/lib/asn1/test/test_compile_options.erl @@ -39,50 +39,49 @@ wrong_path(Config) -> end. comp(Parent,Config) -> - DataDir = ?config(data_dir,Config), - OutDir = ?config(priv_dir,Config), - ?line Err=asn1ct:compile(DataDir++"NoImport",[{i,OutDir},{i,filename:join([DataDir,"subdir"])},{outdir,OutDir}]), + DataDir = proplists:get_value(data_dir,Config), + OutDir = proplists:get_value(priv_dir,Config), + Err=asn1ct:compile(DataDir++"NoImport",[{i,OutDir},{i,filename:join([DataDir,"subdir"])},{outdir,OutDir}]), Parent!Err. %% OTP-5701 path(Config) -> - DataDir = ?config(data_dir,Config), - OutDir = ?config(priv_dir,Config), + DataDir = proplists:get_value(data_dir,Config), + OutDir = proplists:get_value(priv_dir,Config), {ok,CWD} = file:get_cwd(), - ?line file:set_cwd(filename:join([DataDir,subdir])), + file:set_cwd(filename:join([DataDir,subdir])), ok = asn1ct:compile("../MyMerge.set.asn",[{outdir,OutDir}]), - ?line ok=outfiles_check(OutDir), - ?line outfiles_remove(OutDir), + ok=outfiles_check(OutDir), + outfiles_remove(OutDir), file:set_cwd(filename:join([DataDir,subdir,subsubdir])), ok = asn1ct:compile('../../MyMerge.set.asn',[{i,'..'},{outdir,OutDir}]), - ?line ok=outfiles_check(OutDir,outfiles2()), + ok=outfiles_check(OutDir,outfiles2()), file:set_cwd(CWD), ok. ticket_6143(Config) -> asn1_test_lib:compile("AA1", Config, []). noobj(Config) -> - DataDir = ?config(data_dir,Config), - OutDir = ?config(priv_dir,Config), + DataDir = proplists:get_value(data_dir,Config), + OutDir = proplists:get_value(priv_dir,Config), code:purge('P-Record'), file:delete(filename:join([OutDir,'P-Record.erl'])), file:delete(filename:join([OutDir,'P-Record.beam'])), - ?line ok=asn1ct:compile(filename:join([DataDir,"P-Record"]), + ok=asn1ct:compile(filename:join([DataDir,"P-Record"]), [noobj,{outdir,OutDir}]), -% ?line false = code:is_loaded('P-Record'), - ?line {ok,_} = file:read_file_info(filename:join([OutDir, + {ok,_} = file:read_file_info(filename:join([OutDir, "P-Record.erl"])), - ?line {error,enoent} = + {error,enoent} = file:read_file_info(filename:join([OutDir,"P-Record.beam"])), - ?line {ok,_} = c:c(filename:join([OutDir,'P-Record']), + {ok,_} = c:c(filename:join([OutDir,'P-Record']), [{i,OutDir},{outdir,OutDir}]), - ?line {file,_} = code:is_loaded('P-Record'), + {file,_} = code:is_loaded('P-Record'), code:purge('P-Record'), code:delete('P-Record'), @@ -94,43 +93,41 @@ noobj(Config) -> file:delete(filename:join([OutDir,'p_record.beam'])), ok = asn1ct:compile(filename:join([DataDir,"p_record.set.asn"]), [asn1config,ber,noobj,{outdir,OutDir}]), -%% ?line false = code:is_loaded('P-Record'), -%% ?line false = code:is_loaded('p_record'), - ?line {error,enoent} = + {error,enoent} = file:read_file_info(filename:join([OutDir,"P-Record.beam"])), - ?line {error,enoent} = + {error,enoent} = file:read_file_info(filename:join([OutDir,"P-Record.erl"])), - ?line {error,enoent} = + {error,enoent} = file:read_file_info(filename:join([OutDir,"p_record.beam"])), io:format("read_file_info: p_record.erl~n",[]), - ?line {ok,_} = + {ok,_} = file:read_file_info(filename:join([OutDir,"p_record.erl"])), io:format("c:c: p_record.erl~n",[]), - ?line {ok,_} = c:c(filename:join([OutDir,'p_record']), + {ok,_} = c:c(filename:join([OutDir,'p_record']), [{i,OutDir},{outdir,OutDir}]), io:format("code:is_loaded: p_record.erl~n",[]), - ?line {file,_} = code:is_loaded('p_record'), + {file,_} = code:is_loaded('p_record'), io:format("file:delete: p_record.erl~n",[]), file:delete(filename:join([OutDir,'p_record.erl'])), file:delete(filename:join([OutDir,'p_record.beam'])). verbose(Config) when is_list(Config) -> - DataDir = ?config(data_dir,Config), - OutDir = ?config(priv_dir,Config), + DataDir = proplists:get_value(data_dir,Config), + OutDir = proplists:get_value(priv_dir,Config), Asn1File = filename:join([DataDir,"Comment.asn"]), %% Test verbose compile - ?line test_server:capture_start(), - ?line ok = asn1ct:compile(Asn1File, [{i,DataDir},{outdir,OutDir},noobj,verbose]), - ?line test_server:capture_stop(), - ?line [Line0|_] = test_server:capture_get(), - ?line true = lists:prefix("Erlang ASN.1 compiler", Line0), + test_server:capture_start(), + ok = asn1ct:compile(Asn1File, [{i,DataDir},{outdir,OutDir},noobj,verbose]), + test_server:capture_stop(), + [Line0|_] = test_server:capture_get(), + true = lists:prefix("Erlang ASN.1 compiler", Line0), %% Test non-verbose compile - ?line test_server:capture_start(), - ?line ok = asn1ct:compile(Asn1File, [{i,DataDir},{outdir,OutDir},noobj]), - ?line test_server:capture_stop(), - ?line [] = test_server:capture_get(), + test_server:capture_start(), + ok = asn1ct:compile(Asn1File, [{i,DataDir},{outdir,OutDir},noobj]), + test_server:capture_stop(), + [] = test_server:capture_get(), ok. outfiles_check(OutDir) -> @@ -141,7 +138,7 @@ outfiles_check(_OutDir,[])-> ok; outfiles_check(OutDir,[H|T]) -> io:format("File: ~p~n",[filename:join([OutDir,H])]), - ?line {ok,_}=file:read_file_info(filename:join([OutDir,H])), + {ok,_}=file:read_file_info(filename:join([OutDir,H])), outfiles_check(OutDir,T). outfiles1() -> @@ -155,8 +152,8 @@ outfiles_remove(OutDir) -> outfiles1()). record_name_prefix(Config) -> - DataDir = ?config(data_dir,Config), - OutDir = ?config(priv_dir,Config), + DataDir = proplists:get_value(data_dir,Config), + OutDir = proplists:get_value(priv_dir,Config), ok = b_SeqIn(DataDir,OutDir), ok = a_SeqIn(DataDir,OutDir). @@ -165,15 +162,15 @@ b_SeqIn(DataDir,OutDir) -> [{record_name_prefix,"b_"},{outdir,OutDir}]), io:format("FileName: ~p~nOutDir:~p~n", [filename:join([DataDir,'b_SeqIn']),OutDir]), - ?line {ok,_} = compile:file(filename:join([DataDir,'b_SeqIn']), + {ok,_} = compile:file(filename:join([DataDir,'b_SeqIn']), [{i,OutDir}]), - ?line 'b_SeqIn' = b_SeqIn:record_name(), + 'b_SeqIn' = b_SeqIn:record_name(), ok. a_SeqIn(DataDir,OutDir) -> asn1ct:compile(filename:join([DataDir,'Seq']), [{record_name_prefix,"a_"},{outdir,OutDir}]), - ?line {ok,_} = compile:file(filename:join([DataDir,'a_SeqIn']), + {ok,_} = compile:file(filename:join([DataDir,'a_SeqIn']), [{i,OutDir}]), - ?line 'a_SeqIn' = a_SeqIn:record_name(), + 'a_SeqIn' = a_SeqIn:record_name(), ok. diff --git a/lib/asn1/test/test_modified_x420.erl b/lib/asn1/test/test_modified_x420.erl index 2e1426c00f..6cd9e0e33b 100644 --- a/lib/asn1/test/test_modified_x420.erl +++ b/lib/asn1/test/test_modified_x420.erl @@ -24,7 +24,7 @@ -include_lib("common_test/include/ct.hrl"). test(Config) -> - DataDir = ?config(data_dir,Config), + DataDir = proplists:get_value(data_dir,Config), Der = read_pem(filename:join([DataDir,modified_x420,"p7_signed_data.pem"])), {ok,{_,_,SignedData}} = 'PKCS7':decode( 'ContentInfo', Der), diff --git a/lib/asn1/test/test_partial_incomplete_decode.erl b/lib/asn1/test/test_partial_incomplete_decode.erl index d57aaaa1da..7c5cfab10a 100644 --- a/lib/asn1/test/test_partial_incomplete_decode.erl +++ b/lib/asn1/test/test_partial_incomplete_decode.erl @@ -67,7 +67,7 @@ test(Config) -> ok. test_megaco(Config) -> - DataDir = ?config(data_dir, Config), + DataDir = proplists:get_value(data_dir, Config), Files = filelib:wildcard(filename:join([DataDir,megacomessages,"*.val"])), Mod = 'MEDIA-GATEWAY-CONTROL', lists:foreach(fun(File) -> @@ -81,72 +81,72 @@ test_megaco(Config) -> exclusive_decode(Bin,F) -> Mod='MEDIA-GATEWAY-CONTROL', io:format("Encoding message: ~p~n",[F]), - ?line {ok,{_,_,{_,_VsnNo,{MsgMidKey,MsgMid},{MsgMBodyKey,MsgMBody}}}}= + {ok,{_,_,{_,_VsnNo,{MsgMidKey,MsgMid},{MsgMBodyKey,MsgMBody}}}}= Mod:decode_MegacoMessage_exclusive(Bin), - ?line {ok,_} = Mod:decode_part(MsgMidKey,MsgMid), - ?line {ok,_} = Mod:decode_part(MsgMBodyKey,MsgMBody), + {ok,_} = Mod:decode_part(MsgMidKey,MsgMid), + {ok,_} = Mod:decode_part(MsgMBodyKey,MsgMBody), ok. decode_parts('F',PartDecMsg) -> - ?line {fb,{'E',35,{NameE_b,ListBinE_b},false,{NameE_d,BinE_d}}} = PartDecMsg, - ?line {ok,[{'D',3,true}|_]} = 'PartialDecSeq':decode_part(NameE_b,ListBinE_b), - ?line {ok,{'D',3,true}} = 'PartialDecSeq':decode_part(NameE_b, + {fb,{'E',35,{NameE_b,ListBinE_b},false,{NameE_d,BinE_d}}} = PartDecMsg, + {ok,[{'D',3,true}|_]} = 'PartialDecSeq':decode_part(NameE_b,ListBinE_b), + {ok,{'D',3,true}} = 'PartialDecSeq':decode_part(NameE_b, hd(ListBinE_b)), - ?line {ok,{da,[{'A',16,{'D',17,true}}]}} = + {ok,{da,[{'A',16,{'D',17,true}}]}} = 'PartialDecSeq':decode_part(NameE_d,BinE_d), ok; decode_parts('F2',PartDecMsg) -> - ?line {fb,{'E',35,{E_bkey,E_b},false,{da,{E_d_akey,E_d_a}}}} = PartDecMsg, - ?line {ok,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}]} = 'PartialDecSeq':decode_part(E_bkey,E_b), - ?line {ok,[{'A',16,{'D',17,true}}]} = 'PartialDecSeq':decode_part(E_d_akey,E_d_a); + {fb,{'E',35,{E_bkey,E_b},false,{da,{E_d_akey,E_d_a}}}} = PartDecMsg, + {ok,[{'D',3,true},{'D',4,false},{'D',5,true},{'D',6,true},{'D',7,false},{'D',8,true},{'D',9,true},{'D',10,false},{'D',11,true},{'D',12,true},{'D',13,false},{'D',14,true}]} = 'PartialDecSeq':decode_part(E_bkey,E_b), + {ok,[{'A',16,{'D',17,true}}]} = 'PartialDecSeq':decode_part(E_d_akey,E_d_a); decode_parts('F3',PartDecMsg) -> - ?line {fb,{'E',10,{E_bkey,E_b},false,{dc,{'E_d_dc',13,true,{E_d_dc_dcckey,E_d_dc_dcc}}}}} = PartDecMsg, - ?line {ok,[{'D',11,true},{'D',12,false}]} = 'PartialDecSeq':decode_part(E_bkey,E_b), - ?line {ok,{'E_d_dc_dcc',14,15}} = 'PartialDecSeq':decode_part(E_d_dc_dcckey,E_d_dc_dcc); + {fb,{'E',10,{E_bkey,E_b},false,{dc,{'E_d_dc',13,true,{E_d_dc_dcckey,E_d_dc_dcc}}}}} = PartDecMsg, + {ok,[{'D',11,true},{'D',12,false}]} = 'PartialDecSeq':decode_part(E_bkey,E_b), + {ok,{'E_d_dc_dcc',14,15}} = 'PartialDecSeq':decode_part(E_d_dc_dcckey,E_d_dc_dcc); decode_parts('D',PartDecMsg) -> - ?line {'D',{NameD_a,BinD_a},true} = PartDecMsg, - ?line {ok,123} = 'PartialDecSeq':decode_part(NameD_a,BinD_a), + {'D',{NameD_a,BinD_a},true} = PartDecMsg, + {ok,123} = 'PartialDecSeq':decode_part(NameD_a,BinD_a), ok; decode_parts('A',PartDecMsg) -> - ?line {'A',12,{c,{'S',true,false}},{b,{NameA_c_b,BinA_c_b}}} = PartDecMsg, - ?line {ok,{'A_c_b',false,false}} = + {'A',12,{c,{'S',true,false}},{b,{NameA_c_b,BinA_c_b}}} = PartDecMsg, + {ok,{'A_c_b',false,false}} = 'PartialDecSeq2':decode_part(NameA_c_b,BinA_c_b), ok; decode_parts('GetRequest',PartDecMsg) -> - ?line {'GetRequest',true,false, + {'GetRequest',true,false, {'AcceptTypes',[html,'plain-text',gif,jpeg], {NameAcceptTypes_others,ListBinAcceptTypes_others}}, "IamfineThankYOu"} = PartDecMsg, - ?line {ok,["hell","othe","reho","peyo","uare","fine"]} = + {ok,["hell","othe","reho","peyo","uare","fine"]} = 'PartialDecMyHTTP':decode_part(NameAcceptTypes_others, ListBinAcceptTypes_others), - ?line {ok,"hell"} = + {ok,"hell"} = 'PartialDecMyHTTP':decode_part(NameAcceptTypes_others, hd(ListBinAcceptTypes_others)), ok; decode_parts('S1_1',PartDecMsg) -> - ?line {'S1',14,{'S2',false,12,{NameS2c,BinS2c}}, + {'S1',14,{'S2',false,12,{NameS2c,BinS2c}}, {_,{NameS1c_a,ListBinS1c_a}},{NameS1d,BinS1d}} = PartDecMsg, - ?line {ok,[{'S3',10,"PrintableString","OCTETSTRING", + {ok,[{'S3',10,"PrintableString","OCTETSTRING", [one,two,three,four]}|_Rest1]} = 'PartialDecSeq3':decode_part(NameS2c,BinS2c), - ?line {ok,[{'S3',10,"PrintableString","OCTETSTRING", + {ok,[{'S3',10,"PrintableString","OCTETSTRING", [one,two,three,four]}|_Rest2]} = 'PartialDecSeq3':decode_part(NameS1c_a,ListBinS1c_a), - ?line {ok,{'S3',10,"PrintableString","OCTETSTRING", + {ok,{'S3',10,"PrintableString","OCTETSTRING", [one,two,three,four]}} = 'PartialDecSeq3':decode_part(NameS1c_a,hd(ListBinS1c_a)), - ?line {ok,[{'Name',"Hans","HCA","Andersen"}|_Rest3]} = + {ok,[{'Name',"Hans","HCA","Andersen"}|_Rest3]} = 'PartialDecSeq3':decode_part(NameS1d,BinS1d), ok; decode_parts('S1_2',PartDecMsg) -> - ?line {'S1',14,{'S2',false,12,_S2c},S1c_b,{NameS1d,BinS1d}} = PartDecMsg, - ?line {b,{'C1_b',11,true, + {'S1',14,{'S2',false,12,_S2c},S1c_b,{NameS1d,BinS1d}} = PartDecMsg, + {b,{'C1_b',11,true, {'S4',{'Name',"Hans","HCA","Andersen"},"MSc"}}}=S1c_b, - ?line {ok,[{'Name',"Hans","HCA","Andersen"}|_Rest3]} = + {ok,[{'Name',"Hans","HCA","Andersen"}|_Rest3]} = 'PartialDecSeq3':decode_part(NameS1d,BinS1d), ok. diff --git a/lib/asn1/test/test_special_decode_performance.erl b/lib/asn1/test/test_special_decode_performance.erl index fbd53b8177..35c396575b 100644 --- a/lib/asn1/test/test_special_decode_performance.erl +++ b/lib/asn1/test/test_special_decode_performance.erl @@ -27,29 +27,29 @@ go(all) -> {Time_S_s,Time_S_e,Time_S_c}=go(10000,'PartialDecSeq'), {Time_MGC_s,Time_MGC_e,Time_MGC_c}=go(10000,'MEDIA-GATEWAY-CONTROL'), - ?line do_comment({Time_S_s,Time_MGC_s}, + do_comment({Time_S_s,Time_MGC_s}, {Time_S_e,Time_MGC_e}, {Time_S_c,Time_MGC_c}). go(N,Mod) -> {Type,Val} = val(Mod), {ok,B} = Mod:encode(Type, Val), - ?line go(Mod,B,N). + go(Mod,B,N). go(Mod,Bin,N) -> - ?line FsS = get_selective_funcs(Mod), - ?line FsE = get_exclusive_funcs(Mod), - ?line io:format("~nSize of value for module ~p: ~p bytes.~n~n",[Mod,size(Bin)]), - ?line Time_s=go1(selective,Mod,FsS,Bin,N,0), - ?line Time_e=go1(exclusive,Mod,FsE,Bin,N,0), - ?line Time_c=go1(common,Mod,[decode],Bin,N,0), - ?line {Time_s/length(FsS),Time_e/length(FsE),Time_c}. + FsS = get_selective_funcs(Mod), + FsE = get_exclusive_funcs(Mod), + io:format("~nSize of value for module ~p: ~p bytes.~n~n",[Mod,size(Bin)]), + Time_s=go1(selective,Mod,FsS,Bin,N,0), + Time_e=go1(exclusive,Mod,FsE,Bin,N,0), + Time_c=go1(common,Mod,[decode],Bin,N,0), + {Time_s/length(FsS),Time_e/length(FsE),Time_c}. go1(_,_,[],_,_,AccTime) -> - ?line AccTime; + AccTime; %% go1 for common decode go1(common,Mod,_,Bin,N,_) -> - ?line TT=get_top_type(Mod), + TT=get_top_type(Mod), {Time,Result} = timer:tc(fun() -> loop1(Mod, decode, TT, Bin, N) end), case Result of {ok,_R1} -> diff --git a/lib/asn1/test/test_undecoded_rest.erl b/lib/asn1/test/test_undecoded_rest.erl index ec0dfddb0d..f70953330b 100644 --- a/lib/asn1/test/test_undecoded_rest.erl +++ b/lib/asn1/test/test_undecoded_rest.erl @@ -29,7 +29,7 @@ test(Opts, Config) -> {ok,Msg} = asn1ct:value('P-Record', 'PersonnelRecord', - [{i,?config(case_dir, Config)}]), + [{i,proplists:get_value(case_dir, Config)}]), Bytes0 = encode(Opts, 'PersonnelRecord', Msg), Bytes1 = iolist_to_binary([Bytes0, <<55,55,55>>]), case proplists:get_bool(undec_rest, Opts) of diff --git a/lib/common_test/src/ct_logs.erl b/lib/common_test/src/ct_logs.erl index 1138acb839..77828a2186 100644 --- a/lib/common_test/src/ct_logs.erl +++ b/lib/common_test/src/ct_logs.erl @@ -1833,7 +1833,7 @@ make_all_runs_index(When) -> AbsName = ?abs(?all_runs_name), notify_and_lock_file(AbsName), if When == start -> ok; - true -> io:put_chars("Updating " ++ AbsName ++ "... ") + true -> io:put_chars("Updating " ++ AbsName ++ " ... ") end, %% check if log cache should be used, and if it exists @@ -2572,7 +2572,7 @@ update_tests_in_cache(TempData,LogCache=#log_cache{tests=Tests}) -> make_all_suites_index1(When, AbsIndexName, AllTestLogDirs) -> IndexName = ?index_name, if When == start -> ok; - true -> io:put_chars("Updating " ++ AbsIndexName ++ "... ") + true -> io:put_chars("Updating " ++ AbsIndexName ++ " ... ") end, case catch make_all_suites_index2(IndexName, AllTestLogDirs) of {'EXIT', Reason} -> diff --git a/lib/common_test/test_server/ts.config b/lib/common_test/test_server/ts.config index cf3d269616..d05e4885fc 100644 --- a/lib/common_test/test_server/ts.config +++ b/lib/common_test/test_server/ts.config @@ -44,3 +44,7 @@ % {295,0,0,0,0,0,0,1}, % ["dummy6-ip6"] % }}. + +%% Used by erl_interface tests +%% Known hostname with an unreachable ip +%{test_host_not_reachable, "ghost.mydomain.com"}. diff --git a/lib/common_test/test_server/ts_autoconf_win32.erl b/lib/common_test/test_server/ts_autoconf_win32.erl index 4e63960f8f..52e5ac8e69 100644 --- a/lib/common_test/test_server/ts_autoconf_win32.erl +++ b/lib/common_test/test_server/ts_autoconf_win32.erl @@ -139,15 +139,15 @@ visual_cxx(Vars) -> {"-MTd ", "-MDd ", "-LDd ", - "-debug -pdb:none ", + "-link -debug -pdb:none ", "-Z7 -DDEBUG", " "}; false -> {"-MT ", "-MD ", "-LD ", - " ", - " ", + "-Zi -link ", + "-Zi ", "-Ox "} end, WIN32 = "-D__WIN32__ ", @@ -158,7 +158,7 @@ visual_cxx(Vars) -> {'LD', CC}, {'SHLIB_LD', CC}, {'SHLIB_LDFLAGS', ERTS_THR_LIB ++ DLL}, - {'SHLIB_LDLIBS', "-link " ++ DBG_LINK ++ "kernel32.lib"}, + {'SHLIB_LDLIBS', DBG_LINK ++ "kernel32.lib"}, {'SHLIB_EXTRACT_ALL', ""}, {'CFLAGS', DEFAULT_THR_LIB ++ WIN32 ++ DBG_COMP}, {'EI_CFLAGS', DEFAULT_THR_LIB ++ WIN32 ++ DBG_COMP}, @@ -168,7 +168,7 @@ visual_cxx(Vars) -> {'DEFS', common_c_defs()}, {'SHLIB_SUFFIX', ".dll"}, {'ERTS_LIBS', ERTS_THR_LIB ++ LIBS}, - {'LIBS', DEFAULT_THR_LIB ++ "-link " ++ DBG_LINK ++ LIBS}, + {'LIBS', DEFAULT_THR_LIB ++ DBG_LINK ++ LIBS}, {obj,".obj"}, {exe, ".exe"}, {test_c_compiler, "{msc, undefined}"} diff --git a/lib/compiler/src/beam_asm.erl b/lib/compiler/src/beam_asm.erl index 84fe48921d..9a81537006 100644 --- a/lib/compiler/src/beam_asm.erl +++ b/lib/compiler/src/beam_asm.erl @@ -225,10 +225,14 @@ flatten_imports(Imps) -> list_to_binary(map(fun({M,F,A}) -> <<M:32,F:32,A:32>> end, Imps)). build_attributes(Opts, SourceFile, Attr, MD5) -> + Misc0 = case SourceFile of + [] -> []; + [_|_] -> [{source,SourceFile}] + end, Misc = case member(slim, Opts) of false -> {{Y,Mo,D},{H,Mi,S}} = erlang:universaltime(), - [{time,{Y,Mo,D,H,Mi,S}},{source,SourceFile}]; + [{time,{Y,Mo,D,H,Mi,S}}|Misc0]; true -> [] end, Compile = [{options,Opts},{version,?COMPILER_VSN}|Misc], diff --git a/lib/compiler/src/beam_block.erl b/lib/compiler/src/beam_block.erl index cee032f856..a8cfdffdf3 100644 --- a/lib/compiler/src/beam_block.erl +++ b/lib/compiler/src/beam_block.erl @@ -88,7 +88,9 @@ collect_block([I|Is]=Is0, Acc) -> case collect(I) of error -> {reverse(Acc),Is0}; Instr -> collect_block(Is, [Instr|Acc]) - end. + end; +collect_block([], Acc) -> + {reverse(Acc),[]}. collect({allocate,N,R}) -> {set,[],[],{alloc,R,{nozero,N,0,[]}}}; collect({allocate_zero,N,R}) -> {set,[],[],{alloc,R,{zero,N,0,[]}}}; diff --git a/lib/compiler/src/beam_split.erl b/lib/compiler/src/beam_split.erl index 6ffd0bfabd..c83c686953 100644 --- a/lib/compiler/src/beam_split.erl +++ b/lib/compiler/src/beam_split.erl @@ -47,6 +47,8 @@ split_block([{set,[R],[_,_,_]=As,{bif,is_record,{f,Lbl}}}|Is], Bl, Acc) -> split_block(Is, [], [{bif,is_record,{f,Lbl},As,R}|make_block(Bl, Acc)]); split_block([{set,[R],As,{bif,N,{f,Lbl}=Fail}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block(Is, [], [{bif,N,Fail,As,R}|make_block(Bl, Acc)]); +split_block([{set,[R],As,{bif,raise,{f,_}=Fail}}|Is], Bl, Acc) -> + split_block(Is, [], [{bif,raise,Fail,As,R}|make_block(Bl, Acc)]); split_block([{set,[R],As,{alloc,Live,{gc_bif,N,{f,Lbl}=Fail}}}|Is], Bl, Acc) when Lbl =/= 0 -> split_block(Is, [], [{gc_bif,N,Fail,Live,As,R}|make_block(Bl, Acc)]); diff --git a/lib/compiler/src/beam_validator.erl b/lib/compiler/src/beam_validator.erl index fbb03d7850..6877141885 100644 --- a/lib/compiler/src/beam_validator.erl +++ b/lib/compiler/src/beam_validator.erl @@ -509,6 +509,9 @@ valfun_4({bif,element,{f,Fail},[Pos,Tuple],Dst}, Vst0) -> TupleType = upgrade_tuple_type({tuple,[get_tuple_size(PosType)]}, TupleType0), Vst = set_type(TupleType, Tuple, Vst1), set_type_reg(term, Dst, Vst); +valfun_4({bif,raise,{f,0},Src,_Dst}, Vst) -> + validate_src(Src, Vst), + kill_state(Vst); valfun_4({bif,Op,{f,Fail},Src,Dst}, Vst0) -> validate_src(Src, Vst0), Vst = branch_state(Fail, Vst0), diff --git a/lib/compiler/src/beam_z.erl b/lib/compiler/src/beam_z.erl index fcd152e5ba..6c7f8543c2 100644 --- a/lib/compiler/src/beam_z.erl +++ b/lib/compiler/src/beam_z.erl @@ -24,6 +24,8 @@ -export([module/2]). +-import(lists, [dropwhile/2]). + module({Mod,Exp,Attr,Fs0,Lc}, _Opt) -> Fs = [function(F) || F <- Fs0], {ok,{Mod,Exp,Attr,Fs,Lc}}. @@ -51,6 +53,16 @@ undo_renames([{call,A,F},return|Is]) -> [{call_only,A,F}|undo_renames(Is)]; undo_renames([{call_ext,A,F},return|Is]) -> [{call_ext_only,A,F}|undo_renames(Is)]; +undo_renames([{bif,raise,_,_,_}=I|Is0]) -> + %% A minor optimization. Done here because: + %% (1) beam_jump may move or share 'raise' instructions, and that + %% may confuse beam_validator. + %% (2) beam_trim cannot do its optimization if the 'deallocate' + %% instruction after 'raise' has been removed. + Is = dropwhile(fun({label,_}) -> false; + (_) -> true + end, Is0), + [I|undo_renames(Is)]; undo_renames([I|Is]) -> [undo_rename(I)|undo_renames(Is)]; undo_renames([]) -> []. diff --git a/lib/compiler/src/compile.erl b/lib/compiler/src/compile.erl index 46917905de..65566df025 100644 --- a/lib/compiler/src/compile.erl +++ b/lib/compiler/src/compile.erl @@ -1328,7 +1328,7 @@ save_core_code(St) -> beam_asm(#compile{ifile=File,code=Code0, abstract_code=Abst,mod_options=Opts0}=St) -> - Source = filename:absname(File), + Source = paranoid_absname(File), Opts1 = lists:map(fun({debug_info_key,_}) -> {debug_info_key,'********'}; (Other) -> Other end, Opts0), @@ -1337,6 +1337,16 @@ beam_asm(#compile{ifile=File,code=Code0, {ok,Code} -> {ok,St#compile{code=Code,abstract_code=[]}} end. +paranoid_absname(""=File) -> + File; +paranoid_absname(File) -> + case file:get_cwd() of + {ok,Cwd} -> + filename:absname(File, Cwd); + _ -> + File + end. + test_native(#compile{options=Opts}) -> %% This test is done late, in case some other option has turned off native. %% 'native' given on the command line can be overridden by @@ -1681,6 +1691,7 @@ help(_) -> %% Compile entry point for erl_compile. compile(File0, _OutFile, Options) -> + pre_load(), File = shorten_filename(File0), case file(File, make_erl_options(Options)) of {ok,_Mod} -> ok; @@ -1745,3 +1756,46 @@ make_erl_options(Opts) -> end, Options ++ [report_errors, {cwd, Cwd}, {outdir, Outdir}| [{i, Dir} || Dir <- Includes]] ++ Specific. + +pre_load() -> + L = [beam_a, + beam_asm, + beam_block, + beam_bool, + beam_bs, + beam_bsm, + beam_clean, + beam_dead, + beam_dict, + beam_except, + beam_flatten, + beam_jump, + beam_opcodes, + beam_peep, + beam_receive, + beam_reorder, + beam_split, + beam_trim, + beam_type, + beam_utils, + beam_validator, + beam_z, + cerl, + cerl_clauses, + cerl_sets, + cerl_trees, + core_lib, + epp, + erl_bifs, + erl_expand_records, + erl_lint, + erl_parse, + erl_scan, + sys_core_dsetel, + sys_core_fold, + sys_pre_expand, + v3_codegen, + v3_core, + v3_kernel, + v3_life], + code:ensure_modules_loaded(L). diff --git a/lib/compiler/test/beam_validator_SUITE.erl b/lib/compiler/test/beam_validator_SUITE.erl index 4ac6802025..7c4e88ca3e 100644 --- a/lib/compiler/test/beam_validator_SUITE.erl +++ b/lib/compiler/test/beam_validator_SUITE.erl @@ -312,7 +312,7 @@ state_after_fault_in_catch(Config) when is_list(Config) -> no_exception_in_catch(Config) when is_list(Config) -> Errors = do_val(no_exception_in_catch, Config), [{{no_exception_in_catch,nested_of_1,4}, - {{move,{x,3},{x,0}},88,{uninitialized_reg,{x,3}}}}] = Errors, + {{move,{x,3},{x,0}},87,{uninitialized_reg,{x,3}}}}] = Errors, ok. undef_label(Config) when is_list(Config) -> diff --git a/lib/compiler/test/compile_SUITE.erl b/lib/compiler/test/compile_SUITE.erl index 8e0f06d3b4..e1db99b357 100644 --- a/lib/compiler/test/compile_SUITE.erl +++ b/lib/compiler/test/compile_SUITE.erl @@ -22,6 +22,7 @@ %% Tests compile:file/1 and compile:file/2 with various options. -include_lib("common_test/include/ct.hrl"). +-include_lib("stdlib/include/erl_compile.hrl"). -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, @@ -32,7 +33,7 @@ strict_record/1, missing_testheap/1, cover/1, env/1, core/1, asm/1, sys_pre_attributes/1, dialyzer/1, - warnings/1 + warnings/1, pre_load_check/1 ]). -export([init/3]). @@ -50,7 +51,7 @@ all() -> other_output, encrypted_abstr, strict_record, missing_testheap, cover, env, core, asm, - sys_pre_attributes, dialyzer, warnings]. + sys_pre_attributes, dialyzer, warnings, pre_load_check]. groups() -> []. @@ -126,13 +127,39 @@ forms_2(Config) when is_list(Config) -> Src = "/foo/bar", AbsSrc = filename:absname(Src), Anno = erl_anno:new(1), - {ok,simple,Binary} = compile:forms([{attribute,Anno,module,simple}], - [binary,{source,Src}]), - code:load_binary(simple, Src, Binary), - Info = simple:module_info(compile), + SimpleCode = [{attribute,Anno,module,simple}], + {ok,simple,Bin1} = compile:forms(SimpleCode, [binary,{source,Src}]), - %% Test that the proper source is returned. - AbsSrc = proplists:get_value(source, Info), + %% Load and test that the proper source is returned. + AbsSrc = forms_load_code(simple, Src, Bin1), + + %% Work in a deleted directory. + PrivDir = proplists:get_value(priv_dir, Config), + WorkDir = filename:join(PrivDir, ?FUNCTION_NAME), + ok = file:make_dir(WorkDir), + ok = file:set_cwd(WorkDir), + case os:type() of + {unix,_} -> os:cmd("rm -rf " ++ WorkDir); + _ -> ok + end, + {ok,simple,Bin2} = compile:forms(SimpleCode), + undefined = forms_load_code(simple, "ignore", Bin2), + + {ok,simple,Bin3} = compile:forms(SimpleCode, [{source,Src},report]), + case forms_load_code(simple, "ignore", Bin3) of + Src -> %Unix. + ok; + AbsSrc -> %Windows. + ok + end, + + ok. + + +forms_load_code(Mod, Src, Bin) -> + {module,Mod} = code:load_binary(Mod, Src, Bin), + Info = Mod:module_info(compile), + SourceOption = proplists:get_value(source, Info), %% Ensure that the options are not polluted with 'source'. [] = proplists:get_value(options, Info), @@ -140,7 +167,9 @@ forms_2(Config) when is_list(Config) -> %% Cleanup. true = code:delete(simple), false = code:purge(simple), - ok. + + SourceOption. + module_mismatch(Config) when is_list(Config) -> DataDir = proplists:get_value(data_dir, Config), @@ -881,6 +910,115 @@ do_warnings_2([], Next, F) -> do_warnings_1(Next, F). +%% Test that the compile:pre_load/0 function (used by 'erlc') +%% pre-loads the modules that are used by a typical compilation. + +pre_load_check(Config) -> + case test_server:is_cover() of + true -> + {skip,"Cover is running"}; + false -> + try + do_pre_load_check(Config) + after + dbg:stop_clear() + end + end. + +do_pre_load_check(Config) -> + DataDir = ?config(data_dir, Config), + Simple = filename:join(DataDir, "simple.erl"), + Big = filename:join(DataDir, "big.erl"), + {ok,_} = dbg:tracer(process, {fun pre_load_trace/2,[]}), + dbg:p(self(), call), + dbg:p(new, call), + {ok,_} = dbg:tpl({?MODULE,get_trace_data,0}, []), + {ok,_} = dbg:tp({code,ensure_modules_loaded,1}, []), + + %% Compile a simple module using the erl_compile interface + %% to find out the modules that are pre-loaded by + %% compile:pre_load/0. + Opts = #options{specific=[binary]}, + {ok,simple,_} = compile:compile(Simple, "", Opts), + [{code,ensure_modules_loaded,[PreLoaded0]}] = get_trace_data(), + PreLoaded1 = ordsets:from_list(PreLoaded0), + + %% Since 'compile' is the function doing the pre-loaded, + %% it is useless to include it in the list. + case ordsets:is_element(compile, PreLoaded1) of + true -> + io:put_chars("The 'compile' module should not be included " + "in the list of modules to be pre-loaded."), + ?t:fail(compile); + false -> + [] + end, + PreLoaded = ordsets:add_element(compile, PreLoaded1), + + %% Now unload all pre-loaded modules and all modules in + %% compiler application. Then compile a module to find + %% which modules that get loaded. + CompilerMods = compiler_modules(), + Unload = ordsets:union(ordsets:from_list(CompilerMods), PreLoaded), + _ = [begin + code:delete(M), + code:purge(M) + end || M <- Unload], + + {ok,_} = dbg:ctp({code,ensure_modules_loaded,1}), + {ok,_} = dbg:tp({code,ensure_loaded,1}, []), + {ok,big,_} = compile:file(Big, [binary]), + WasLoaded0 = get_trace_data(), + WasLoaded1 = [M || {code,ensure_loaded,[M]} <- WasLoaded0], + WasLoaded = ordsets:from_list(WasLoaded1), + + %% Check for modules that should have been pre-loaded. + case ordsets:subtract(WasLoaded, PreLoaded) of + [] -> + ok; + [_|_]=NotPreLoaded -> + io:format("The following modules were used " + "but not pre-loaded:\n~p\n", + [NotPreLoaded]), + ?t:fail({not_preload,NotPreLoaded}) + end, + + %% Check for modules that should not be pre-loaded. + case ordsets:subtract(PreLoaded, WasLoaded) of + [] -> + ok; + [_|_]=NotUsed -> + io:format("The following modules were pre-loaded" + " but not used:\n~p\n", + [NotUsed]), + ?t:fail({not_used,NotUsed}) + end, + + ok. + +get_trace_data() -> + %% Apparantely, doing a receive at the beginning of + %% a traced function can cause extra trace messages. + %% To avoid that, don't do the receive in this function. + do_get_trace_data(). + +do_get_trace_data() -> + receive + {trace_data,Data} -> Data + end. + +pre_load_trace({trace,Pid,call,{?MODULE,get_trace_data,[]}}, Acc) -> + Pid ! {trace_data,Acc}, + []; +pre_load_trace({trace,_,call,MFA}, Acc) -> + [MFA|Acc]. + +compiler_modules() -> + Wc = filename:join([code:lib_dir(compiler),"ebin","*.beam"]), + Ms = filelib:wildcard(Wc), + FN = filename, + [list_to_atom(FN:rootname(FN:basename(M), ".beam")) || M <- Ms]. + %%% %%% Utilities. %%% diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index 700f07cc9f..768922d57a 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -378,86 +378,96 @@ struct hmac_context static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context*); struct digest_type_t { - const char* type_str; - const EVP_MD* (*md_func)(void); /* NULL if notsup */ - ERL_NIF_TERM type_atom; + union { + const char* str; /* before init, NULL for end-of-table */ + ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ + }type; + union { + const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ + const EVP_MD* p; /* after init, NULL if notsup */ + }md; }; struct digest_type_t digest_types[] = { - {"md4", &EVP_md4}, - {"md5", &EVP_md5}, - {"ripemd160", &EVP_ripemd160}, - {"sha", &EVP_sha1}, - {"sha224", + {{"md4"}, {&EVP_md4}}, + {{"md5"}, {&EVP_md5}}, + {{"ripemd160"}, {&EVP_ripemd160}}, + {{"sha"}, {&EVP_sha1}}, + {{"sha224"}, #ifdef HAVE_SHA224 - &EVP_sha224 + {&EVP_sha224} #else - NULL + {NULL} #endif }, - {"sha256", + {{"sha256"}, #ifdef HAVE_SHA256 - &EVP_sha256 + {&EVP_sha256} #else - NULL + {NULL} #endif }, - {"sha384", + {{"sha384"}, #ifdef HAVE_SHA384 - &EVP_sha384 + {&EVP_sha384} #else - NULL + {NULL} #endif }, - {"sha512", + {{"sha512"}, #ifdef HAVE_SHA512 - &EVP_sha512 + {&EVP_sha512} #else - NULL + {NULL} #endif }, - {NULL} + {{NULL}} }; static struct digest_type_t* get_digest_type(ERL_NIF_TERM type); struct cipher_type_t { - const char* type_str; - const EVP_CIPHER* (*cipher_func)(void); /* NULL if notsup */ + union { + const char* str; /* before init */ + ERL_NIF_TERM atom; /* after init */ + }type; + union { + const EVP_CIPHER* (*funcp)(void); /* before init, NULL if notsup */ + const EVP_CIPHER* p; /* after init, NULL if notsup */ + }cipher; const size_t key_len; /* != 0 to also match on key_len */ - ERL_NIF_TERM type_atom; }; struct cipher_type_t cipher_types[] = { - {"rc2_cbc", &EVP_rc2_cbc}, - {"des_cbc", &EVP_des_cbc}, - {"des_cfb", &EVP_des_cfb8}, - {"des_ecb", &EVP_des_ecb}, - {"des_ede3_cbc", &EVP_des_ede3_cbc}, - {"des_ede3_cbf", + {{"rc2_cbc"}, {&EVP_rc2_cbc}}, + {{"des_cbc"}, {&EVP_des_cbc}}, + {{"des_cfb"}, {&EVP_des_cfb8}}, + {{"des_ecb"}, {&EVP_des_ecb}}, + {{"des_ede3_cbc"}, {&EVP_des_ede3_cbc}}, + {{"des_ede3_cbf"}, #ifdef HAVE_DES_ede3_cfb_encrypt - &EVP_des_ede3_cfb8 + {&EVP_des_ede3_cfb8} #else - NULL + {NULL} #endif }, - {"blowfish_cbc", &EVP_bf_cbc}, - {"blowfish_cfb64", &EVP_bf_cfb64}, - {"blowfish_ofb64", &EVP_bf_ofb}, - {"blowfish_ecb", &EVP_bf_ecb}, - {"aes_cbc", &EVP_aes_128_cbc, 16}, - {"aes_cbc", &EVP_aes_192_cbc, 24}, - {"aes_cbc", &EVP_aes_256_cbc, 32}, - {"aes_cbc128", &EVP_aes_128_cbc}, - {"aes_cbc256", &EVP_aes_256_cbc}, - {"aes_cfb8", &EVP_aes_128_cfb8}, - {"aes_cfb128", &EVP_aes_128_cfb128}, - {"aes_ecb", &EVP_aes_128_ecb, 16}, - {"aes_ecb", &EVP_aes_192_ecb, 24}, - {"aes_ecb", &EVP_aes_256_ecb, 32}, - {NULL} + {{"blowfish_cbc"}, {&EVP_bf_cbc}}, + {{"blowfish_cfb64"}, {&EVP_bf_cfb64}}, + {{"blowfish_ofb64"}, {&EVP_bf_ofb}}, + {{"blowfish_ecb"}, {&EVP_bf_ecb}}, + {{"aes_cbc"}, {&EVP_aes_128_cbc}, 16}, + {{"aes_cbc"}, {&EVP_aes_192_cbc}, 24}, + {{"aes_cbc"}, {&EVP_aes_256_cbc}, 32}, + {{"aes_cbc128"}, {&EVP_aes_128_cbc}}, + {{"aes_cbc256"}, {&EVP_aes_256_cbc}}, + {{"aes_cfb8"}, {&EVP_aes_128_cfb8}}, + {{"aes_cfb128"}, {&EVP_aes_128_cfb128}}, + {{"aes_ecb"}, {&EVP_aes_128_ecb}, 16}, + {{"aes_ecb"}, {&EVP_aes_192_ecb}, 24}, + {{"aes_ecb"}, {&EVP_aes_256_ecb}, 32}, + {{NULL}} }; static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len); @@ -829,6 +839,15 @@ static ERL_NIF_TERM info_lib(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] ver_term)); } +static ERL_NIF_TERM make_badarg_maybe(ErlNifEnv* env) +{ + ERL_NIF_TERM reason; + if (enif_has_pending_exception(env, &reason)) + return reason; /* dummy return value ignored */ + else + return enif_make_badarg(env); +} + static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) {/* (Type, Data) */ struct digest_type_t *digp = NULL; @@ -842,11 +861,11 @@ static ERL_NIF_TERM hash_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] !enif_inspect_iolist_as_binary(env, argv[1], &data)) { return enif_make_badarg(env); } - if (!digp->md_func) { + md = digp->md.p; + if (!md) { return atom_notsup; } - md = digp->md_func(); ret_size = (unsigned)EVP_MD_size(md); ASSERT(0 < ret_size && ret_size <= EVP_MAX_MD_SIZE); if (!EVP_Digest(data.data, data.size, @@ -872,12 +891,12 @@ static ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a if (!digp) { return enif_make_badarg(env); } - if (!digp->md_func) { + if (!digp->md.p) { return atom_notsup; } ctx = enif_alloc_resource(evp_md_ctx_rtype, sizeof(EVP_MD_CTX)); - if (!EVP_DigestInit(ctx, digp->md_func())) { + if (!EVP_DigestInit(ctx, digp->md.p)) { enif_release_resource(ctx); return atom_notsup; } @@ -946,11 +965,11 @@ static ERL_NIF_TERM hash_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a if (!digp) { return enif_make_badarg(env); } - if (!digp->md_func) { + if (!digp->md.p) { return atom_notsup; } - switch (EVP_MD_type(digp->md_func())) + switch (EVP_MD_type(digp->md.p)) { case NID_md4: ctx_size = MD4_CTX_LEN; @@ -1020,11 +1039,11 @@ static ERL_NIF_TERM hash_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM !enif_inspect_iolist_as_binary(env, argv[1], &data)) { return enif_make_badarg(env); } - if (!digp->md_func) { + if (!digp->md.p) { return atom_notsup; } - switch (EVP_MD_type(digp->md_func())) + switch (EVP_MD_type(digp->md.p)) { case NID_md4: ctx_size = MD4_CTX_LEN; @@ -1102,11 +1121,11 @@ static ERL_NIF_TERM hash_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM !enif_inspect_binary(env, tuple[1], &ctx)) { return enif_make_badarg(env); } - if (!digp->md_func) { + md = digp->md.p; + if (!md) { return atom_notsup; } - md = digp->md_func(); switch (EVP_MD_type(md)) { @@ -1186,8 +1205,8 @@ static ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] return enif_make_badarg(env); } - if (!digp->md_func || - !HMAC(digp->md_func(), + if (!digp->md.p || + !HMAC(digp->md.p, key.data, key.size, data.data, data.size, buff, &size)) { @@ -1229,7 +1248,7 @@ static ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a !enif_inspect_iolist_as_binary(env, argv[1], &key)) { return enif_make_badarg(env); } - if (!digp->md_func) { + if (!digp->md.p) { return atom_notsup; } @@ -1239,12 +1258,12 @@ static ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM a #if OPENSSL_VERSION_NUMBER >= 0x1000000fL // Check the return value of HMAC_Init: it may fail in FIPS mode // for disabled algorithms - if (!HMAC_Init(&obj->ctx, key.data, key.size, digp->md_func())) { + if (!HMAC_Init(&obj->ctx, key.data, key.size, digp->md.p)) { enif_release_resource(obj); return atom_notsup; } #else - HMAC_Init(&obj->ctx, key.data, key.size, digp->md_func()); + HMAC_Init(&obj->ctx, key.data, key.size, digp->md.p); #endif ret = enif_make_resource(env, obj); @@ -1323,7 +1342,8 @@ static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM || !enif_inspect_iolist_as_binary(env, argv[argc - 2], &text)) { return enif_make_badarg(env); } - if (!cipherp->cipher_func) { + cipher = cipherp->cipher.p; + if (!cipher) { return enif_raise_exception(env, atom_notsup); } @@ -1335,7 +1355,6 @@ static ERL_NIF_TERM block_crypt_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM return aes_cfb_8_crypt(env, argc-1, argv+1); } - cipher = cipherp->cipher_func(); ivec_size = EVP_CIPHER_iv_length(cipher); #ifdef HAVE_ECB_IVEC_BUG @@ -2110,27 +2129,31 @@ 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); + for (p = digest_types; p->type.str; p++) { + p->type.atom = enif_make_atom(env, p->type.str); + if (p->md.funcp) + p->md.p = p->md.funcp(); } - + p->type.atom = atom_false; /* end marker */ } static void init_cipher_types(ErlNifEnv* env) { struct cipher_type_t* p = cipher_types; - for (p = cipher_types; p->type_str; p++) { - p->type_atom = enif_make_atom(env, p->type_str); + for (p = cipher_types; p->type.str; p++) { + p->type.atom = enif_make_atom(env, p->type.str); + if (p->cipher.funcp) + p->cipher.p = p->cipher.funcp(); } - + p->type.atom = atom_false; /* end marker */ } 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) { + for (p = digest_types; p->type.atom != atom_false; p++) { + if (type == p->type.atom) { return p; } } @@ -2140,8 +2163,8 @@ static struct digest_type_t* get_digest_type(ERL_NIF_TERM type) static struct cipher_type_t* get_cipher_type(ERL_NIF_TERM type, size_t key_len) { struct cipher_type_t* p = NULL; - for (p = cipher_types; p->type_str; p++) { - if (type == p->type_atom && (!p->key_len || key_len == p->key_len)) { + for (p = cipher_types; p->type.atom != atom_false; p++) { + if (type == p->type.atom && (!p->key_len || key_len == p->key_len)) { return p; } } @@ -2166,12 +2189,12 @@ static ERL_NIF_TERM rsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM if (!digp) { return enif_make_badarg(env); } - if (!digp->md_func) { + md = digp->md.p; + if (!md) { return atom_notsup; } rsa = RSA_new(); - md = digp->md_func(); if (!enif_inspect_binary(env, argv[1], &digest_bin) || digest_bin.size != EVP_MD_size(md) @@ -2329,10 +2352,10 @@ static ERL_NIF_TERM rsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM ar if (!digp) { return enif_make_badarg(env); } - if (!digp->md_func) { + md = digp->md.p; + if (!md) { return atom_notsup; } - md = digp->md_func(); if (!enif_inspect_binary(env,argv[1],&digest_bin) || digest_bin.size != EVP_MD_size(md)) { @@ -2904,8 +2927,7 @@ static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg) EC_POINT *point = NULL; /* {Field, Prime, Point, Order, CoFactor} = Curve */ - if (enif_is_tuple(env, curve_arg) - && enif_get_tuple(env,curve_arg,&c_arity,&curve) + if (enif_get_tuple(env,curve_arg,&c_arity,&curve) && c_arity == 5 && get_bn_from_bin(env, curve[3], &bn_order) && (curve[4] != atom_none && get_bn_from_bin(env, curve[4], &cofactor))) { @@ -2942,9 +2964,11 @@ static EC_KEY* ec_key_new(ErlNifEnv* env, ERL_NIF_TERM curve_arg) /* create the EC_GROUP structure */ group = EC_GROUP_new_curve_GFp(p, a, b, NULL); -#if !defined(OPENSSL_NO_EC2M) - } else if (f_arity == 3 && field[0] == atom_characteristic_two_field) { +#if defined(OPENSSL_NO_EC2M) + enif_raise_exception(env, atom_notsup); + goto out_err; +#else /* {characteristic_two_field, M, Basis} */ int b_arity = -1; @@ -3221,7 +3245,7 @@ static ERL_NIF_TERM ec_key_generate(ErlNifEnv* env, int argc, const ERL_NIF_TERM badarg: if (key) EC_KEY_free(key); - return enif_make_badarg(env); + return make_badarg_maybe(env); #else return atom_notsup; #endif @@ -3241,10 +3265,10 @@ static ERL_NIF_TERM ecdsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM if (!digp) { return enif_make_badarg(env); } - if (!digp->md_func) { + md = digp->md.p; + if (!md) { return atom_notsup; } - md = digp->md_func(); len = EVP_MD_size(md); if (!enif_inspect_binary(env,argv[1],&digest_bin) @@ -3272,7 +3296,7 @@ static ERL_NIF_TERM ecdsa_sign_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM badarg: if (key) EC_KEY_free(key); - return enif_make_badarg(env); + return make_badarg_maybe(env); #else return atom_notsup; #endif @@ -3292,10 +3316,10 @@ static ERL_NIF_TERM ecdsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER if (!digp) { return enif_make_badarg(env); } - if (!digp->md_func) { + md = digp->md.p; + if (!md) { return atom_notsup; } - md = digp->md_func(); len = EVP_MD_size(md); if (!enif_inspect_binary(env, argv[1], &digest_bin) @@ -3314,7 +3338,7 @@ static ERL_NIF_TERM ecdsa_verify_nif(ErlNifEnv* env, int argc, const ERL_NIF_TER badarg: if (key) EC_KEY_free(key); - return enif_make_badarg(env); + return make_badarg_maybe(env); #else return atom_notsup; #endif @@ -3339,7 +3363,7 @@ static ERL_NIF_TERM ecdh_compute_key_nif(ErlNifEnv* env, int argc, const ERL_NIF EC_KEY *other_ecdh = NULL; if (!get_ec_key(env, argv[1], argv[2], atom_undefined, &key)) - return enif_make_badarg(env); + return make_badarg_maybe(env); group = EC_GROUP_dup(EC_KEY_get0_group(key)); priv_key = EC_KEY_get0_private_key(key); diff --git a/lib/crypto/test/old_crypto_SUITE.erl b/lib/crypto/test/old_crypto_SUITE.erl index 411a8cb82b..f57e9ff341 100644 --- a/lib/crypto/test/old_crypto_SUITE.erl +++ b/lib/crypto/test/old_crypto_SUITE.erl @@ -187,7 +187,9 @@ ldd_program() -> case os:find_executable("otool") of false -> none; Otool -> Otool ++ " -L" - end + end; + _ -> + none end; Ldd when is_list(Ldd) -> Ldd end. diff --git a/lib/eldap/src/Makefile b/lib/eldap/src/Makefile index 7df4844668..b79a537424 100644 --- a/lib/eldap/src/Makefile +++ b/lib/eldap/src/Makefile @@ -98,7 +98,7 @@ 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_DATA) $(ASN1_HRL) $(TARGET_FILES) $(APP_TARGET) $(APPUP_TARGET) "$(RELSYSDIR)/ebin" $(INSTALL_DIR) "$(RELSYSDIR)/src" $(INSTALL_DATA) $(ERL_FILES) "$(RELSYSDIR)/src" $(INSTALL_DIR) "$(RELSYSDIR)/asn1" diff --git a/lib/erl_interface/include/ei.h b/lib/erl_interface/include/ei.h index fd21965e97..948f89be85 100644 --- a/lib/erl_interface/include/ei.h +++ b/lib/erl_interface/include/ei.h @@ -121,8 +121,11 @@ #define ERL_SMALL_ATOM_UTF8_EXT 'w' #define ERL_REFERENCE_EXT 'e' #define ERL_NEW_REFERENCE_EXT 'r' +#define ERL_NEWER_REFERENCE_EXT 'Z' #define ERL_PORT_EXT 'f' +#define ERL_NEW_PORT_EXT 'Y' #define ERL_PID_EXT 'g' +#define ERL_NEW_PID_EXT 'X' #define ERL_SMALL_TUPLE_EXT 'h' #define ERL_LARGE_TUPLE_EXT 'i' #define ERL_NIL_EXT 'j' diff --git a/lib/erl_interface/include/erl_interface.h b/lib/erl_interface/include/erl_interface.h index 9502dc748e..c22f21af2b 100644 --- a/lib/erl_interface/include/erl_interface.h +++ b/lib/erl_interface/include/erl_interface.h @@ -211,14 +211,14 @@ typedef struct { Erl_Atom_data node; unsigned int number; unsigned int serial; - unsigned char creation; + unsigned int creation; } Erl_Pid; typedef struct { Erl_Header h; Erl_Atom_data node; unsigned int number; - unsigned char creation; + unsigned int creation; } Erl_Port; typedef struct { @@ -226,7 +226,7 @@ typedef struct { Erl_Atom_data node; int len; unsigned int n[3]; - unsigned char creation; + unsigned int creation; } Erl_Ref; typedef struct { diff --git a/lib/erl_interface/src/connect/ei_connect.c b/lib/erl_interface/src/connect/ei_connect.c index 248dabdec8..6dc51adee1 100644 --- a/lib/erl_interface/src/connect/ei_connect.c +++ b/lib/erl_interface/src/connect/ei_connect.c @@ -1342,7 +1342,8 @@ static int send_name_or_challenge(int fd, char *nodename, | DFLAG_NEW_FLOATS | DFLAG_SMALL_ATOM_TAGS | DFLAG_UTF8_ATOMS - | DFLAG_MAP_TAG)); + | DFLAG_MAP_TAG + | DFLAG_BIG_CREATION)); if (f_chall) put32be(s, challenge); memcpy(s, nodename, strlen(nodename)); diff --git a/lib/erl_interface/src/connect/ei_connect_int.h b/lib/erl_interface/src/connect/ei_connect_int.h index 80a46a73ee..0bcccaa84b 100644 --- a/lib/erl_interface/src/connect/ei_connect_int.h +++ b/lib/erl_interface/src/connect/ei_connect_int.h @@ -106,6 +106,7 @@ extern int h_errno; #define DFLAG_SMALL_ATOM_TAGS 0x4000 #define DFLAG_UTF8_ATOMS 0x10000 #define DFLAG_MAP_TAG 0x20000 +#define DFLAG_BIG_CREATION 0x40000 ei_cnode *ei_fd_to_cnode(int fd); int ei_distversion(int fd); diff --git a/lib/erl_interface/src/decode/decode_pid.c b/lib/erl_interface/src/decode/decode_pid.c index 12c5f0c7c4..e1055aa5c9 100644 --- a/lib/erl_interface/src/decode/decode_pid.c +++ b/lib/erl_interface/src/decode/decode_pid.c @@ -27,18 +27,22 @@ int ei_decode_pid(const char *buf, int *index, erlang_pid *p) { const char *s = buf + *index; const char *s0 = s; + const char tag = get8(s); - if (get8(s) != ERL_PID_EXT) return -1; + if (tag != ERL_PID_EXT && tag != ERL_NEW_PID_EXT) return -1; if (p) { if (get_atom(&s, p->node, NULL) < 0) return -1; p->num = get32be(s) & 0x7fff; /* 15 bits */ p->serial = get32be(s) & 0x1fff; /* 13 bits */ - p->creation = get8(s) & 0x03; /* 2 bits */ + if (tag == ERL_PID_EXT) + p->creation = get8(s) & 0x03; /* 2 bits */ + else + p->creation = get32be(s); /* 32 bits */ } else { if (get_atom(&s, NULL, NULL) < 0) return -1; - s+= 9; + s+= (tag == ERL_PID_EXT ? 9 : 12); } *index += s-s0; diff --git a/lib/erl_interface/src/decode/decode_port.c b/lib/erl_interface/src/decode/decode_port.c index 5a19b7bfaa..337648d803 100644 --- a/lib/erl_interface/src/decode/decode_port.c +++ b/lib/erl_interface/src/decode/decode_port.c @@ -26,17 +26,21 @@ int ei_decode_port(const char *buf, int *index, erlang_port *p) { const char *s = buf + *index; const char *s0 = s; + const char tag = get8(s); - if (get8(s) != ERL_PORT_EXT) return -1; + if (tag != ERL_PORT_EXT && tag != ERL_NEW_PORT_EXT) return -1; if (p) { if (get_atom(&s, p->node, NULL) < 0) return -1; p->id = get32be(s) & 0x0fffffff /* 28 bits */; - p->creation = get8(s) & 0x03; + if (tag == ERL_PORT_EXT) + p->creation = get8(s) & 0x03; + else + p->creation = get32be(s); } else { if (get_atom(&s, NULL, NULL) < 0) return -1; - s += 5; + s += (tag == ERL_PORT_EXT ? 5 : 8); } *index += s-s0; diff --git a/lib/erl_interface/src/decode/decode_ref.c b/lib/erl_interface/src/decode/decode_ref.c index b48675e043..c9b38c1c3b 100644 --- a/lib/erl_interface/src/decode/decode_ref.c +++ b/lib/erl_interface/src/decode/decode_ref.c @@ -28,8 +28,9 @@ int ei_decode_ref(const char *buf, int *index, erlang_ref *p) const char *s = buf + *index; const char *s0 = s; int count, i; + const char tag = get8(s); - switch (get8(s)) { + switch (tag) { case ERL_REFERENCE_EXT: if (p) { if (get_atom(&s, p->node, NULL) < 0) return -1; @@ -47,18 +48,23 @@ int ei_decode_ref(const char *buf, int *index, erlang_ref *p) return 0; break; - case ERL_NEW_REFERENCE_EXT: + case ERL_NEW_REFERENCE_EXT: + case ERL_NEWER_REFERENCE_EXT: + /* first the integer count */ count = get16be(s); if (p) { p->len = count; if (get_atom(&s, p->node, NULL) < 0) return -1; - p->creation = get8(s) & 0x03; + if (tag == ERL_NEW_REFERENCE_EXT) + p->creation = get8(s) & 0x03; + else + p->creation = get32be(s); } else { if (get_atom(&s, NULL, NULL) < 0) return -1; - s += 1; + s += (tag == ERL_NEW_REFERENCE_EXT ? 1 : 4); } /* finally the id integers */ diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c index 5a8020ba9f..0db315f09b 100644 --- a/lib/erl_interface/src/decode/decode_skip.c +++ b/lib/erl_interface/src/decode/decode_skip.c @@ -35,12 +35,15 @@ int ei_skip_term(const char* buf, int* index) NULL, NULL) < 0) return -1; break; case ERL_PID_EXT: + case ERL_NEW_PID_EXT: if (ei_decode_pid(buf, index, NULL) < 0) return -1; break; case ERL_PORT_EXT: + case ERL_NEW_PORT_EXT: if (ei_decode_port(buf, index, NULL) < 0) return -1; break; case ERL_NEW_REFERENCE_EXT: + case ERL_NEWER_REFERENCE_EXT: case ERL_REFERENCE_EXT: if (ei_decode_ref(buf, index, NULL) < 0) return -1; break; diff --git a/lib/erl_interface/src/encode/encode_pid.c b/lib/erl_interface/src/encode/encode_pid.c index d27d38d7c3..d14746b40f 100644 --- a/lib/erl_interface/src/encode/encode_pid.c +++ b/lib/erl_interface/src/encode/encode_pid.c @@ -24,7 +24,8 @@ int ei_encode_pid(char *buf, int *index, const erlang_pid *p) { - char *s = buf + *index; + char* s = buf + *index; + const char tag = (p->creation > 3) ? ERL_NEW_PID_EXT : ERL_PID_EXT; ++(*index); /* skip ERL_PID_EXT */ if (ei_encode_atom_len_as(buf, index, p->node, strlen(p->node), @@ -32,17 +33,21 @@ int ei_encode_pid(char *buf, int *index, const erlang_pid *p) return -1; if (buf) { - put8(s,ERL_PID_EXT); + put8(s, tag); s = buf + *index; /* now the integers */ put32be(s,p->num & 0x7fff); /* 15 bits */ put32be(s,p->serial & 0x1fff); /* 13 bits */ - put8(s,(p->creation & 0x03)); /* 2 bits */ + if (tag == ERL_PID_EXT) { + put8(s,(p->creation & 0x03)); /* 2 bits */ + } else { + put32be(s, p->creation); /* 32 bits */ + } } - *index += 4 + 4 + 1; + *index += 4 + 4 + (tag == ERL_PID_EXT ? 1 : 4); return 0; } diff --git a/lib/erl_interface/src/encode/encode_port.c b/lib/erl_interface/src/encode/encode_port.c index ea14f9fac0..eb464380c0 100644 --- a/lib/erl_interface/src/encode/encode_port.c +++ b/lib/erl_interface/src/encode/encode_port.c @@ -25,6 +25,7 @@ int ei_encode_port(char *buf, int *index, const erlang_port *p) { char *s = buf + *index; + const char tag = p->creation > 3 ? ERL_NEW_PORT_EXT : ERL_PORT_EXT; ++(*index); /* skip ERL_PORT_EXT */ if (ei_encode_atom_len_as(buf, index, p->node, strlen(p->node), ERLANG_UTF8, @@ -32,16 +33,19 @@ int ei_encode_port(char *buf, int *index, const erlang_port *p) return -1; } if (buf) { - put8(s,ERL_PORT_EXT); + put8(s, tag); s = buf + *index; /* now the integers */ put32be(s,p->id & 0x0fffffff /* 28 bits */); - put8(s,(p->creation & 0x03)); + if (tag == ERL_PORT_EXT) { + put8(s,(p->creation & 0x03)); + } else { + put32be(s, p->creation); + } } - - *index += 4 + 1; + *index += 4 + (tag == ERL_PORT_EXT ? 1 : 4); return 0; } diff --git a/lib/erl_interface/src/encode/encode_ref.c b/lib/erl_interface/src/encode/encode_ref.c index d0e7d4e05e..5ccfc32c6d 100644 --- a/lib/erl_interface/src/encode/encode_ref.c +++ b/lib/erl_interface/src/encode/encode_ref.c @@ -24,6 +24,7 @@ int ei_encode_ref(char *buf, int *index, const erlang_ref *p) { + const char tag = (p->creation > 3) ? ERL_NEWER_REFERENCE_EXT : ERL_NEW_REFERENCE_EXT; char *s = buf + *index; int i; @@ -36,7 +37,7 @@ int ei_encode_ref(char *buf, int *index, const erlang_ref *p) /* Always encode as an extended reference; all participating parties are now expected to be able to decode extended references. */ if (buf) { - put8(s,ERL_NEW_REFERENCE_EXT); + put8(s, tag); /* first, number of integers */ put16be(s, p->len); @@ -45,12 +46,15 @@ int ei_encode_ref(char *buf, int *index, const erlang_ref *p) s = buf + *index; /* now the integers */ - put8(s,(p->creation & 0x03)); + if (tag == ERL_NEW_REFERENCE_EXT) + put8(s,(p->creation & 0x03)); + else + put32be(s, p->creation); for (i = 0; i < p->len; i++) put32be(s,p->n[i]); } - *index += p->len*4 + 1; + *index += p->len*4 + (tag == ERL_NEW_REFERENCE_EXT ? 1 : 4); return 0; } diff --git a/lib/erl_interface/src/legacy/erl_eterm.c b/lib/erl_interface/src/legacy/erl_eterm.c index 7d0ce48929..e4b3b49c7d 100644 --- a/lib/erl_interface/src/legacy/erl_eterm.c +++ b/lib/erl_interface/src/legacy/erl_eterm.c @@ -285,12 +285,12 @@ ETERM *erl_mk_pid(const char *node, erl_errno = ENOMEM; return NULL; } - erl_mk_pid_helper(ep, number, serial, creation); + erl_mk_pid_helper(ep, number, serial, creation & 0x03); return ep; } void erl_mk_pid_helper(ETERM *ep, unsigned int number, - unsigned int serial, unsigned char creation) + unsigned int serial, unsigned int creation) { ERL_PID_NUMBER(ep) = number & 0x7fff; /* 15 bits */ if (ei_internal_use_r9_pids_ports()) { @@ -299,7 +299,7 @@ void erl_mk_pid_helper(ETERM *ep, unsigned int number, else { ERL_PID_SERIAL(ep) = serial & 0x1fff; /* 13 bits */ } - ERL_PID_CREATION(ep) = creation & 0x03; /* 2 bits */ + ERL_PID_CREATION(ep) = creation; /* 32 bits */ } /* @@ -326,7 +326,7 @@ ETERM *erl_mk_port(const char *node, return ep; } -void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned char creation) +void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned int creation) { if (ei_internal_use_r9_pids_ports()) { ERL_PORT_NUMBER(ep) = number & 0x3ffff; /* 18 bits */ @@ -334,7 +334,7 @@ void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned char creation) else { ERL_PORT_NUMBER(ep) = number & 0x0fffffff; /* 18 bits */ } - ERL_PORT_CREATION(ep) = creation & 0x03; /* 2 bits */ + ERL_PORT_CREATION(ep) = creation; /* 32 bits */ } /* @@ -344,7 +344,7 @@ ETERM *__erl_mk_reference (ETERM* t, const char *node, size_t len, unsigned int n[], - unsigned char creation) + unsigned int creation) { if (t == NULL) { if (node == NULL) return NULL; @@ -363,7 +363,7 @@ ETERM *__erl_mk_reference (ETERM* t, ERL_REF_NUMBERS(t)[0] = n[0] & 0x3ffff; /* 18 bits */ ERL_REF_NUMBERS(t)[1] = n[1]; ERL_REF_NUMBERS(t)[2] = n[2]; - ERL_REF_CREATION(t) = creation & 0x03; /* 2 bits */ + ERL_REF_CREATION(t) = creation; /* 32 bits */ return t; } diff --git a/lib/erl_interface/src/legacy/erl_eterm.h b/lib/erl_interface/src/legacy/erl_eterm.h index 661cacbf83..e2f3a90531 100644 --- a/lib/erl_interface/src/legacy/erl_eterm.h +++ b/lib/erl_interface/src/legacy/erl_eterm.h @@ -56,9 +56,9 @@ typedef struct _heapmark { } Erl_HeapMark; -void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned char creation); -void erl_mk_pid_helper(ETERM*, unsigned,unsigned, unsigned char); -ETERM * __erl_mk_reference(ETERM*, const char *, size_t, unsigned int n[], unsigned char); +void erl_mk_port_helper(ETERM* ep, unsigned number, unsigned int creation); +void erl_mk_pid_helper(ETERM*, unsigned,unsigned, unsigned int); +ETERM * __erl_mk_reference(ETERM*, const char *, size_t, unsigned int n[], unsigned int); int erl_current_fix_desc(void); #endif /* _ERL_ETERM_H */ diff --git a/lib/erl_interface/src/legacy/erl_marshal.c b/lib/erl_interface/src/legacy/erl_marshal.c index 8ea8e1c61d..3c212bf177 100644 --- a/lib/erl_interface/src/legacy/erl_marshal.c +++ b/lib/erl_interface/src/legacy/erl_marshal.c @@ -120,10 +120,13 @@ void erl_init_marshal(void) cmp_array[ERL_SMALL_ATOM_UTF8_EXT] = ERL_ATOM_CMP; cmp_array[ERL_REFERENCE_EXT] = ERL_REF_CMP; cmp_array[ERL_NEW_REFERENCE_EXT] = ERL_REF_CMP; + cmp_array[ERL_NEWER_REFERENCE_EXT]=ERL_REF_CMP; cmp_array[ERL_FUN_EXT] = ERL_FUN_CMP; cmp_array[ERL_NEW_FUN_EXT] = ERL_FUN_CMP; cmp_array[ERL_PORT_EXT] = ERL_PORT_CMP; + cmp_array[ERL_NEW_PORT_EXT] = ERL_PORT_CMP; cmp_array[ERL_PID_EXT] = ERL_PID_CMP; + cmp_array[ERL_NEW_PID_EXT] = ERL_PID_CMP; cmp_array[ERL_SMALL_TUPLE_EXT] = ERL_TUPLE_CMP; cmp_array[ERL_LARGE_TUPLE_EXT] = ERL_TUPLE_CMP; cmp_array[ERL_NIL_EXT] = ERL_NIL_CMP; @@ -304,8 +307,8 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) *(*ext)++ = ul & 0xff; return 0; - case ERL_PID: - *(*ext)++ = ERL_PID_EXT; + case ERL_PID: { + unsigned char* tagp = (*ext)++; /* First poke in node as an atom */ encode_atom(&ep->uval.pidval.node, ext); /* And then fill in the integer fields */ @@ -319,17 +322,29 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) *(*ext)++ = (i >> 16) &0xff; *(*ext)++ = (i >> 8) &0xff; *(*ext)++ = i &0xff; - *(*ext)++ = ERL_PID_CREATION(ep); + + i = ERL_PID_CREATION(ep); + if ((unsigned int)i <= 3) { + *tagp = ERL_PID_EXT; + *(*ext)++ = i; + } else { + *tagp = ERL_NEW_PID_EXT; + *(*ext)++ = (i >> 24) &0xff; + *(*ext)++ = (i >> 16) &0xff; + *(*ext)++ = (i >> 8) &0xff; + *(*ext)++ = i &0xff; + } return 0; + } case ERL_REF: { + unsigned char* tagp = (*ext)++; + int len, j; /* Always encode as an extended reference; all participating parties are now expected to be able to decode extended references. */ - *(*ext)++ = ERL_NEW_REFERENCE_EXT; - i = strlen((char *)ERL_REF_NODE(ep)); len = ERL_REF_LEN(ep); *(*ext)++ = (len >> 8) &0xff; @@ -337,7 +352,18 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) encode_atom(&ep->uval.refval.node, ext); - *(*ext)++ = ERL_REF_CREATION(ep); + i = ERL_REF_CREATION(ep); + if ((unsigned int)i <= 3) { + *tagp = ERL_NEW_REFERENCE_EXT; + *(*ext)++ = i; + } else { + *tagp = ERL_NEWER_REFERENCE_EXT; + *(*ext)++ = (i >> 24) &0xff; + *(*ext)++ = (i >> 16) &0xff; + *(*ext)++ = (i >> 8) &0xff; + *(*ext)++ = i &0xff; + } + /* Then the integer fields */ for (j = 0; j < ERL_REF_LEN(ep); j++) { i = ERL_REF_NUMBERS(ep)[j]; @@ -348,8 +374,8 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) } } return 0; - case ERL_PORT: - *(*ext)++ = ERL_PORT_EXT; + case ERL_PORT: { + unsigned char* tagp = (*ext)++; /* First poke in node as an atom */ encode_atom(&ep->uval.portval.node, ext); /* Then the integer fields */ @@ -358,8 +384,20 @@ int erl_encode_it(ETERM *ep, unsigned char **ext, int dist) *(*ext)++ = (i >> 16) &0xff; *(*ext)++ = (i >> 8) &0xff; *(*ext)++ = i &0xff; - *(*ext)++ = ERL_PORT_CREATION(ep); + + i = ERL_PORT_CREATION(ep); + if ((unsigned int)i <= 3) { + *tagp = ERL_PORT_EXT; + *(*ext)++ = i; + } else { + *tagp = ERL_NEW_PORT_EXT; + *(*ext)++ = (i >> 24) &0xff; + *(*ext)++ = (i >> 16) &0xff; + *(*ext)++ = (i >> 8) &0xff; + *(*ext)++ = i &0xff; + } return 0; + } case ERL_EMPTY_LIST: *(*ext)++ = ERL_NIL_EXT; break; @@ -698,12 +736,14 @@ static ETERM *erl_decode_it(unsigned char **ext) unsigned int u,sign; int i,j,arity; double ff; + unsigned char tag; /* Assume we are going to decode an integer */ ep = erl_alloc_eterm(ERL_INTEGER); ERL_COUNT(ep) = 1; - switch (*(*ext)++) + tag = *(*ext)++; + switch (tag) { case ERL_INTEGER_EXT: i = (int) (**ext << 24) | ((*ext)[1] << 16) | @@ -801,9 +841,10 @@ static ETERM *erl_decode_it(unsigned char **ext) return ep; case ERL_PID_EXT: + case ERL_NEW_PID_EXT: { unsigned int number, serial; - unsigned char creation; + unsigned int creation; ERL_TYPE(ep) = ERL_PID; if (read_atom(ext, &ep->uval.pidval.node) < 0) return NULL; @@ -815,7 +856,13 @@ static ETERM *erl_decode_it(unsigned char **ext) serial = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | ((*ext)[2]) << 8 | ((*ext)[3]); *ext += 4; - creation = *(*ext)++; + if (tag == ERL_PID_EXT) + creation = *(*ext)++; + else { + creation = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | + ((*ext)[2]) << 8 | ((*ext)[3]); + *ext += 4; + } erl_mk_pid_helper(ep, number, serial, creation); return ep; } @@ -836,11 +883,12 @@ static ETERM *erl_decode_it(unsigned char **ext) return ep; } - case ERL_NEW_REFERENCE_EXT: + case ERL_NEW_REFERENCE_EXT: + case ERL_NEWER_REFERENCE_EXT: { size_t cnt, i; unsigned int n[3]; - unsigned char creation; + unsigned int creation; ERL_TYPE(ep) = ERL_REF; cnt = ((*ext)[0] << 8) | (*ext)[1]; @@ -849,7 +897,13 @@ static ETERM *erl_decode_it(unsigned char **ext) if (read_atom(ext, &ep->uval.refval.node) < 0) return NULL; /* get the integers */ - creation = *(*ext)++; + if (tag == ERL_NEW_REFERENCE_EXT) + creation = *(*ext)++; + else { + creation = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | + ((*ext)[2]) << 8 | ((*ext)[3]); + *ext += 4; + } for(i = 0; i < cnt; i++) { n[i] = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | @@ -861,9 +915,10 @@ static ETERM *erl_decode_it(unsigned char **ext) } case ERL_PORT_EXT: + case ERL_NEW_PORT_EXT: { unsigned int number; - unsigned char creation; + unsigned int creation; ERL_TYPE(ep) = ERL_PORT; if (read_atom(ext, &ep->uval.portval.node) < 0) return NULL; @@ -872,7 +927,13 @@ static ETERM *erl_decode_it(unsigned char **ext) number = ((*ext)[0] << 24) | ((*ext)[1]) << 16 | ((*ext)[2]) << 8 | ((*ext)[3]); *ext += 4; - creation = *(*ext)++; + if (tag == ERL_PORT_EXT) + creation = *(*ext)++; + else { + creation = (((*ext)[0] << 24) | ((*ext)[1]) << 16 | + ((*ext)[2]) << 8 | ((*ext)[3])); + *ext += 4; + } erl_mk_port_helper(ep, number, creation); return ep; } @@ -1114,11 +1175,14 @@ unsigned char erl_ext_type(unsigned char *ext) case ERL_SMALL_ATOM_UTF8_EXT: return ERL_ATOM; case ERL_PID_EXT: + case ERL_NEW_PID_EXT: return ERL_PID; case ERL_PORT_EXT: + case ERL_NEW_PORT_EXT: return ERL_PORT; case ERL_REFERENCE_EXT: case ERL_NEW_REFERENCE_EXT: + case ERL_NEWER_REFERENCE_EXT: return ERL_REF; case ERL_NIL_EXT: return ERL_EMPTY_LIST; @@ -1167,9 +1231,12 @@ int erl_ext_size(unsigned char *t) case ERL_SMALL_ATOM_EXT: case ERL_SMALL_ATOM_UTF8_EXT: case ERL_PID_EXT: + case ERL_NEW_PID_EXT: case ERL_PORT_EXT: + case ERL_NEW_PORT_EXT: case ERL_REFERENCE_EXT: case ERL_NEW_REFERENCE_EXT: + case ERL_NEWER_REFERENCE_EXT: case ERL_NIL_EXT: case ERL_BINARY_EXT: case ERL_STRING_EXT: @@ -1240,8 +1307,9 @@ static int jump(unsigned char **ext) { int j,k,i=0; int n; + const int tag = *(*ext)++; - switch (*(*ext)++) { + switch (tag) { case ERL_VERSION_MAGIC: return jump(ext); case ERL_INTEGER_EXT: @@ -1257,22 +1325,29 @@ static int jump(unsigned char **ext) jump_atom(ext); break; case ERL_PID_EXT: - /* eat first atom */ if (!jump_atom(ext)) return 0; - *ext += 9; /* Two int's and the creation field */ + *ext += 4 + 4 + 1; break; + case ERL_NEW_PID_EXT: + if (!jump_atom(ext)) return 0; + *ext += 4 + 4 + 4; + break; case ERL_REFERENCE_EXT: case ERL_PORT_EXT: - /* first field is an atom */ if (!jump_atom(ext)) return 0; - *ext += 5; /* One int and the creation field */ + *ext += 4 + 1; break; + case ERL_NEW_PORT_EXT: + if (!jump_atom(ext)) return 0; + *ext += 4 + 4; + break; case ERL_NEW_REFERENCE_EXT: + case ERL_NEWER_REFERENCE_EXT: n = (**ext << 8) | (*ext)[1]; *ext += 2; /* first field is an atom */ if (!jump_atom(ext)) return 0; - *ext += 4*n+1; + *ext += 4*n + (tag == ERL_NEW_REFERENCE_EXT ? 1 : 4); break; case ERL_NIL_EXT: /* We just passed it... */ @@ -1605,7 +1680,6 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2) { int min, ret,i,j,k; double ff1, ff2; - unsigned char *tmp1, *tmp2; unsigned char tag1, tag2; if ( ((*e1)[0] == ERL_STRING_EXT) && ((*e2)[0] == ERL_LIST_EXT) ) { @@ -1649,47 +1723,68 @@ static int cmp_exe2(unsigned char **e1, unsigned char **e2) *e1 += i; *e2 += j; return ret; - case ERL_PID_EXT: { - unsigned char *n1 = *e1; - unsigned char *n2 = *e2; - CMP_EXT_SKIP_ATOM(*e1); CMP_EXT_SKIP_ATOM(*e2); - *e1 += 9; *e2 += 9; + case ERL_PID_EXT: + case ERL_NEW_PID_EXT: { + erlang_pid pid1, pid2; + unsigned char* buf1 = *e1 - 1; + unsigned char* buf2 = *e2 - 1; + int ix1 = 0, ix2 = 0; + + if (ei_decode_pid((char*)buf1, &ix1, &pid1) || + ei_decode_pid((char*)buf2, &ix2, &pid2)) + return CMP_EXT_ERROR_CODE; + + *e1 = buf1 + ix1; + *e2 = buf2 + ix2; /* First compare serials ... */ - tmp1 = *e1 - 5; tmp2 = *e2 - 5; - CMP_EXT_INT32_BE(tmp1, tmp2); + if (pid1.serial < pid2.serial) return -1; + else if (pid1.serial > pid2.serial) return 1; /* ... then ids ... */ - tmp1 -= 4; tmp2 -= 4; - CMP_EXT_INT32_BE(tmp1, tmp2); + if (pid1.num < pid2.num) return -1; + else if (pid1.num > pid2.num) return 1; /* ... then node names ... */ - ret = cmp_exe2(&n1, &n2); - if (ret != 0) - return ret; + j = strcmp(pid1.node, pid2.node); + if (j < 0) return -1; + else if (j > 0) return 1; /* ... and then finaly creations. */ - tmp1 += 8; tmp2 += 8; - if (*tmp1 != *tmp2) - return *tmp1 < *tmp2 ? -1 : 1; + if (pid1.creation < pid2.creation) return -1; + else if (pid1.creation > pid2.creation) return 1; + return 0; } case ERL_PORT_EXT: - /* First compare node names ... */ - if (!IS_ERL_ATOM(**e1) || !IS_ERL_ATOM(**e2)) - return CMP_EXT_ERROR_CODE; - ret = cmp_exe2(e1, e2); - *e1 += 5; *e2 += 5; - if (ret != 0) - return ret; + case ERL_NEW_PORT_EXT: { + erlang_port port1, port2; + unsigned char* buf1 = *e1 - 1; + unsigned char* buf2 = *e2 - 1; + int ix1 = 0, ix2 = 0; + + if (ei_decode_port((char*)buf1, &ix1, &port1) || + ei_decode_port((char*)buf2, &ix2, &port2)) + return CMP_EXT_ERROR_CODE; + + *e1 = buf1 + ix1; + *e2 = buf2 + ix2; + + /* First compare node names ... */ + j = strcmp(port1.node, port2.node); + if (j < 0) return -1; + else if (j > 0) return 1; + /* ... then creations ... */ - tmp1 = *e1 - 1; tmp2 = *e2 - 1; - if (*tmp1 != *tmp2) - return *tmp1 < *tmp2 ? -1 : 1; + if (port1.creation < port2.creation) return -1; + else if (port1.creation > port2.creation) return 1; + /* ... and then finaly ids. */ - tmp1 -= 4; tmp2 -= 4; - CMP_EXT_INT32_BE(tmp1, tmp2); - return 0; + if (port1.id < port2.id) return -1; + else if (port1.id > port2.id) return 1; + + return 0; + } case ERL_NIL_EXT: return 0; case ERL_LIST_EXT: i = (**e1 << 24) | ((*e1)[1] << 16) |((*e1)[2] << 8) | (*e1)[3]; diff --git a/lib/erl_interface/src/misc/ei_decode_term.c b/lib/erl_interface/src/misc/ei_decode_term.c index 088e061b39..63a7034508 100644 --- a/lib/erl_interface/src/misc/ei_decode_term.c +++ b/lib/erl_interface/src/misc/ei_decode_term.c @@ -33,7 +33,7 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term) { const char* s = buf + *index, * s0 = s; - int i, n, sign; + int n, sign; char c; if (term == NULL) return -1; @@ -47,47 +47,27 @@ int ei_decode_ei_term(const char* buf, int* index, ei_term* term) break; case ERL_FLOAT_EXT: case NEW_FLOAT_EXT: - return ei_decode_double(buf, index, &term->value.d_val); + return (ei_decode_double(buf, index, &term->value.d_val) < 0 + ? -1 : 1); case ERL_ATOM_EXT: case ERL_ATOM_UTF8_EXT: case ERL_SMALL_ATOM_EXT: case ERL_SMALL_ATOM_UTF8_EXT: - return ei_decode_atom(buf, index, term->value.atom_name); + return (ei_decode_atom(buf, index, term->value.atom_name) < 0 + ? -1 : 1); case ERL_REFERENCE_EXT: - /* first the nodename */ - if (get_atom(&s, term->value.ref.node, NULL) < 0) return -1; - /* now the numbers: num (4), creation (1) */ - term->value.ref.n[0] = get32be(s); - term->value.ref.len = 1; - term->value.ref.creation = get8(s) & 0x03; - break; case ERL_NEW_REFERENCE_EXT: - /* first the integer count */ - term->value.ref.len = get16be(s); - /* then the nodename */ - if (get_atom(&s, term->value.ref.node, NULL) < 0) return -1; - /* creation */ - term->value.ref.creation = get8(s) & 0x03; - /* finally the id integers */ - for (i = 0; (i<term->value.ref.len) && (i<3); i++) { - term->value.ref.n[i] = get32be(s); - } - if (term->value.ref.len > 3) { - s += 4 * (term->value.ref.len - 3); - } - break; + case ERL_NEWER_REFERENCE_EXT: + return (ei_decode_ref(buf, index, &term->value.ref) < 0 + ? -1 : 1); case ERL_PORT_EXT: - if (get_atom(&s, term->value.port.node, NULL) < 0) return -1; - term->value.port.id = get32be(s) & 0x0fffffff; /* 28 bits */; - term->value.port.creation = get8(s) & 0x03; - break; + case ERL_NEW_PORT_EXT: + return (ei_decode_port(buf, index, &term->value.port) < 0 + ? -1 : 1); case ERL_PID_EXT: - if (get_atom(&s, term->value.pid.node, NULL) < 0) return -1; - /* now the numbers: num (4), serial (4), creation (1) */ - term->value.pid.num = get32be(s) & 0x7fff; /* 15 bits */ - term->value.pid.serial = get32be(s) & 0x1fff; /* 13 bits */ - term->value.pid.creation = get8(s) & 0x03; /* 2 bits */ - break; + case ERL_NEW_PID_EXT: + return (ei_decode_pid(buf, index, &term->value.pid) < 0 + ? -1 : 1); case ERL_SMALL_TUPLE_EXT: term->arity = get8(s); break; diff --git a/lib/erl_interface/src/misc/ei_printterm.c b/lib/erl_interface/src/misc/ei_printterm.c index 55d18cb092..058de00de5 100644 --- a/lib/erl_interface/src/misc/ei_printterm.c +++ b/lib/erl_interface/src/misc/ei_printterm.c @@ -151,15 +151,18 @@ static int print_term(FILE* fp, ei_x_buff* x, } break; case ERL_PID_EXT: + case ERL_NEW_PID_EXT: if (ei_decode_pid(buf, index, &pid) < 0) goto err; ch_written += xprintf(fp, x, "<%s.%d.%d>", pid.node, pid.num, pid.serial); break; case ERL_PORT_EXT: + case ERL_NEW_PORT_EXT: if (ei_decode_port(buf, index, &port) < 0) goto err; ch_written += xprintf(fp, x, "#Port<%d.%d>", port.id, port.creation); break; case ERL_NEW_REFERENCE_EXT: + case ERL_NEWER_REFERENCE_EXT: case ERL_REFERENCE_EXT: if (ei_decode_ref(buf, index, &ref) < 0) goto err; ch_written += xprintf(fp, x, "#Ref<"); diff --git a/lib/erl_interface/src/misc/get_type.c b/lib/erl_interface/src/misc/get_type.c index cb326da941..aa69cd4d60 100644 --- a/lib/erl_interface/src/misc/get_type.c +++ b/lib/erl_interface/src/misc/get_type.c @@ -76,6 +76,15 @@ int ei_get_type_internal(const char *buf, const int *index, *len = get32be(s); /* #digit_bytes */ break; + case ERL_NEW_PID_EXT: + *type = ERL_PID_EXT; + break; + case ERL_NEW_PORT_EXT: + *type = ERL_PORT_EXT; + break; + case ERL_NEWER_REFERENCE_EXT: + *type = ERL_NEW_REFERENCE_EXT; + break; default: *len = 0; break; diff --git a/lib/erl_interface/src/misc/show_msg.c b/lib/erl_interface/src/misc/show_msg.c index 4140b48c32..81accab4b6 100644 --- a/lib/erl_interface/src/misc/show_msg.c +++ b/lib/erl_interface/src/misc/show_msg.c @@ -398,6 +398,7 @@ static void show_term(const char *termbuf, int *index, FILE *stream) break; case ERL_PID_EXT: + case ERL_NEW_PID_EXT: ei_decode_pid(termbuf,index,&pid); show_pid(stream,&pid); break; @@ -432,6 +433,7 @@ static void show_term(const char *termbuf, int *index, FILE *stream) case ERL_REFERENCE_EXT: case ERL_NEW_REFERENCE_EXT: + case ERL_NEWER_REFERENCE_EXT: ei_decode_ref(termbuf,index,&ref); fprintf(stream,"#Ref<%s",ref.node); for (i = 0; i < ref.len; i++) { @@ -441,6 +443,7 @@ static void show_term(const char *termbuf, int *index, FILE *stream) break; case ERL_PORT_EXT: + case ERL_NEW_PORT_EXT: ei_decode_port(termbuf,index,&port); fprintf(stream,"#Port<%s.%u.%u>",port.node,port.id,port.creation); break; diff --git a/lib/erl_interface/test/all_SUITE_data/ei_runner.c b/lib/erl_interface/test/all_SUITE_data/ei_runner.c index 92d01f1753..cd7a67c57c 100644 --- a/lib/erl_interface/test/all_SUITE_data/ei_runner.c +++ b/lib/erl_interface/test/all_SUITE_data/ei_runner.c @@ -198,8 +198,8 @@ void free_packet(char* packet) * ----- ---------------------------- * [$b|Bytes] {bytes, Bytes} * [$e] eot - * [$f] test_server:fail() - * [$f|Reason] test_server:fail(Reason) + * [$f] ct:fail() + * [$f|Reason] ct:fail(Reason) * [$t|EncodedTerm] {term, Term} * [$N] 'NULL' * [$m|Message] io:format("~s", [Message]) (otherwise ignored) @@ -211,7 +211,7 @@ void free_packet(char* packet) * you implement a test case entirely in C code. * * If the ok argument is zero, a [$f] reply will be sent to the - * Erlang side (causing test_server:fail() to be called); otherwise, + * Erlang side (causing ct:fail() to be called); otherwise, * the atom 'eot' will be sent to Erlang. * * If you need to provide more details on a failure, use the fail() function. @@ -251,16 +251,21 @@ do_report(file, line, ok) /* - * This function causes a call to test_server:fail(Reason) on the + * This function causes a call to ct:fail(Reason) on the * Erlang side. */ -void do_fail(char* file, int line, char* reason) +void do_fail(const char* file, int line, const char* reason, ...) { + va_list ap; char sbuf[2048]; + char* sp = sbuf; - sbuf[0] = 'f'; - sprintf(sbuf+1, "%s, line %d: %s", file, line, reason); + *sp++ = 'f'; + sp += sprintf(sp, "%s, line %d: ", file, line); + va_start(ap, reason); + sp += vsprintf(sp, reason, ap); + va_end(ap); reply(sbuf, 1+strlen(sbuf+1)); } diff --git a/lib/erl_interface/test/all_SUITE_data/ei_runner.h b/lib/erl_interface/test/all_SUITE_data/ei_runner.h index 875b8255b9..2608661303 100644 --- a/lib/erl_interface/test/all_SUITE_data/ei_runner.h +++ b/lib/erl_interface/test/all_SUITE_data/ei_runner.h @@ -52,10 +52,11 @@ void free_packet(char*); */ #define fail(reason) do_fail(__FILE__, __LINE__, reason) +#define fail1(reason, a1) do_fail(__FILE__, __LINE__, reason, a1) #define report(ok) do_report(__FILE__, __LINE__, ok) void do_report(char* file, int line, int ok); -void do_fail(char* file, int line, char* reason); +void do_fail(const char* file, int line, const char* reason, ...); void send_buffer(char* buf, int size); void message(char* format, ...); diff --git a/lib/erl_interface/test/all_SUITE_data/runner.c b/lib/erl_interface/test/all_SUITE_data/runner.c index 0c199953d1..42e8bb03e5 100644 --- a/lib/erl_interface/test/all_SUITE_data/runner.c +++ b/lib/erl_interface/test/all_SUITE_data/runner.c @@ -200,8 +200,8 @@ char *read_packet(int *len) * ----- ---------------------------- * [$b|Bytes] {bytes, Bytes} * [$e] eot - * [$f] test_server:fail() - * [$f|Reason] test_server:fail(Reason) + * [$f] ct:fail() + * [$f|Reason] ct:fail(Reason) * [$t|EncodedTerm] {term, Term} * [$N] 'NULL' * [$m|Message] io:format("~s", [Message]) (otherwise ignored) @@ -213,7 +213,7 @@ char *read_packet(int *len) * you implement a test case entirely in C code. * * If the ok argument is zero, a [$f] reply will be sent to the - * Erlang side (causing test_server:fail() to be called); otherwise, + * Erlang side (causing ct:fail() to be called); otherwise, * the atom 'eot' will be sent to Erlang. * * If you need to provide more details on a failure, use the fail() function. @@ -253,7 +253,7 @@ do_report(file, line, ok) /* - * This function causes a call to test_server:fail(Reason) on the + * This function causes a call to ct:fail(Reason) on the * Erlang side. */ diff --git a/lib/erl_interface/test/ei_accept_SUITE.erl b/lib/erl_interface/test/ei_accept_SUITE.erl index aae24ad152..e06ee762d7 100644 --- a/lib/erl_interface/test/ei_accept_SUITE.erl +++ b/lib/erl_interface/test/ei_accept_SUITE.erl @@ -24,94 +24,67 @@ -include_lib("common_test/include/ct.hrl"). -include("ei_accept_SUITE_data/ei_accept_test_cases.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2, - ei_accept/1, ei_threaded_accept/1]). +-export([all/0, suite/0, + ei_accept/1, ei_threaded_accept/1]). -import(runner, [get_term/1,send_term/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. all() -> [ei_accept, ei_threaded_accept]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?t:seconds(30)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - ei_accept(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), - - ?line Myname= hd(tl(string:tokens(atom_to_list(node()), "@"))), - ?line io:format("Myname ~p ~n", [Myname]), - ?line EINode= list_to_atom("c42@"++Myname), - ?line io:format("EINode ~p ~n", [EINode]), - ?line Self= self(), - ?line TermToSend= {call, Self, "Test"}, - ?line F= fun() -> - case waitfornode("c42",20) of - true -> - {any, EINode} ! TermToSend, - Self ! sent_ok; - false -> - Self ! never_published - end, - ok - end, - - ?line spawn(F), - ?line Port = 6543, - ?line {ok, Fd, _Node} = ei_accept(P, Port), - ?line TermReceived= ei_receive(P, Fd), - ?line io:format("Sent ~p received ~p ~n", [TermToSend, TermReceived]), - ?line TermToSend= TermReceived, - ?line receive - sent_ok -> - ok; - Unknown -> - io:format("~p ~n", [Unknown]) - after 1000 -> - io:format("timeout ~n") - end, - ?line runner:finish(P), + P = runner:start(?interpret), + 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), + + Myname = hd(tl(string:tokens(atom_to_list(node()), "@"))), + io:format("Myname ~p ~n", [Myname]), + EINode = list_to_atom("c42@"++Myname), + io:format("EINode ~p ~n", [EINode]), + Self = self(), + TermToSend= {call, Self, "Test"}, + F= fun() -> + case waitfornode("c42",20) of + true -> + {any, EINode} ! TermToSend, + Self ! sent_ok; + false -> + Self ! never_published + end, + ok + end, + + spawn(F), + Port = 6543, + {ok, Fd, _Node} = ei_accept(P, Port), + TermReceived= ei_receive(P, Fd), + io:format("Sent ~p received ~p ~n", [TermToSend, TermReceived]), + TermToSend= TermReceived, + receive + sent_ok -> + ok; + Unknown -> + io:format("~p ~n", [Unknown]) + after 1000 -> + io:format("timeout ~n") + end, + runner:finish(P), ok. ei_threaded_accept(Config) when is_list(Config) -> - ?line Einode = filename:join(?config(data_dir, Config), "eiaccnode"), - ?line N = 1, % 3, - ?line Host = atom_to_list(node()), - ?line Port = 6767, - ?line start_einode(Einode, N, Host, Port), - ?line io:format("started eiaccnode"), - %%?line spawn_link(fun() -> start_einode(Einode, N, Host, Port) end), - ?line TestServerPid = self(), - ?line [ spawn_link(fun() -> send_rec_einode(I, TestServerPid) end) - || I <- lists:seq(0, N-1) ], - ?line [ receive I -> ok end - || I <- lists:seq(0, N-1) ], + Einode = filename:join(proplists:get_value(data_dir, Config), "eiaccnode"), + N = 1, % 3, + Host = atom_to_list(node()), + Port = 6767, + start_einode(Einode, N, Host, Port), + io:format("started eiaccnode"), + %%spawn_link(fun() -> start_einode(Einode, N, Host, Port) end), + TestServerPid = self(), + [spawn_link(fun() -> send_rec_einode(I, TestServerPid) end) || I <- lists:seq(0, N-1)], + [receive I -> ok end || I <- lists:seq(0, N-1) ], ok. waitfornode(String,0) -> @@ -120,66 +93,61 @@ waitfornode(String,0) -> waitfornode(String,N) -> Registered = [X || {X,_} <- element(2,erl_epmd:names())], case lists:member(String,Registered) of - true -> - true; - false -> - timer:sleep(1000), - waitfornode(String,N-1) + true -> + true; + false -> + timer:sleep(1000), + waitfornode(String,N-1) end. send_rec_einode(N, TestServerPid) -> - ?line Myname= hd(tl(string:tokens(atom_to_list(node()), "@"))), - ?line FirstPart = "eiacc" ++ integer_to_list(N), - ?line EINode= list_to_atom(FirstPart ++ "@" ++ Myname), - ?line io:format("EINode ~p ~n", [EINode]), - ?line Self= self(), - ?line case waitfornode(FirstPart,20) of - true -> ok; - false -> test_server:fail({never_published,EINode}) - end, - ?line {any, EINode} ! Self, - ?line receive - {N,_}=X -> - ?line io:format("Received by ~s ~p~n", [EINode, X]), - ?line TestServerPid ! N, - ?line X - after 10000 -> - ?line test_server:fail(EINode) - end. + Myname= hd(tl(string:tokens(atom_to_list(node()), "@"))), + FirstPart = "eiacc" ++ integer_to_list(N), + EINode= list_to_atom(FirstPart ++ "@" ++ Myname), + io:format("EINode ~p ~n", [EINode]), + Self= self(), + case waitfornode(FirstPart,20) of + true -> ok; + false -> ct:fail({never_published,EINode}) + end, + {any, EINode} ! Self, + receive + {N,_}=X -> + io:format("Received by ~s ~p~n", [EINode, X]), + TestServerPid ! N, + X + after 10000 -> + ct:fail(EINode) + end. start_einode(Einode, N, Host, Port) -> Einodecmd = Einode ++ " " ++ atom_to_list(erlang:get_cookie()) - ++ " " ++ integer_to_list(N) ++ " " ++ Host ++ " " - ++ integer_to_list(Port) ++ " nothreads", + ++ " " ++ integer_to_list(N) ++ " " ++ Host ++ " " + ++ integer_to_list(Port) ++ " nothreads", io:format("Einodecmd ~p ~n", [Einodecmd]), - ?line open_port({spawn, Einodecmd}, []), + open_port({spawn, Einodecmd}, []), ok. - %%% Interface functions for ei (erl_interface) functions. ei_connect_init(P, Num, Cookie, Creation) -> send_command(P, ei_connect_init, [Num,Cookie,Creation]), case get_term(P) of - {term,Int} when is_integer(Int) -> Int + {term,Int} when is_integer(Int) -> Int end. ei_accept(P, PortNo) -> send_command(P, ei_accept, [PortNo]), case get_term(P) of - {term,{Fd, _, Node}} when Fd >= 0 -> {ok, Fd, Node}; - {term,{_Fd, Errno, _Node}} -> {error,Errno} + {term,{Fd, _, Node}} when Fd >= 0 -> {ok, Fd, Node}; + {term,{_Fd, Errno, _Node}} -> {error,Errno} end. ei_receive(P, Fd) -> send_command(P, ei_receive, [Fd]), - {term, T}= get_term(P), + {term, T} = get_term(P), T. send_command(P, Name, Args) -> runner:send_term(P, {Name,list_to_tuple(Args)}). - - - - diff --git a/lib/erl_interface/test/ei_connect_SUITE.erl b/lib/erl_interface/test/ei_connect_SUITE.erl index 249f76bcb6..66498deadc 100644 --- a/lib/erl_interface/test/ei_connect_SUITE.erl +++ b/lib/erl_interface/test/ei_connect_SUITE.erl @@ -24,173 +24,145 @@ -include_lib("common_test/include/ct.hrl"). -include("ei_connect_SUITE_data/ei_connect_test_cases.hrl"). --export([ - all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, - end_per_testcase/2, - - ei_send/1, - ei_reg_send/1, - ei_format_pid/1, - ei_rpc/1, - rpc_test/1, - ei_send_funs/1, - ei_threaded_send/1, - ei_set_get_tracelevel/1 - ]). +-export([all/0, suite/0, + ei_send/1, + ei_reg_send/1, + ei_format_pid/1, + ei_rpc/1, + rpc_test/1, + ei_send_funs/1, + ei_threaded_send/1, + ei_set_get_tracelevel/1]). -import(runner, [get_term/1,send_term/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. all() -> [ei_send, ei_reg_send, ei_rpc, ei_format_pid, ei_send_funs, ei_threaded_send, ei_set_get_tracelevel]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - -init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?t:minutes(0.25)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. - ei_send(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), - ?line {ok,Fd} = ei_connect(P, node()), + P = runner:start(?interpret), + 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), + {ok,Fd} = ei_connect(P, node()), - ?line ok = ei_send(P, Fd, self(), AMsg={a,message}), - ?line receive AMsg -> ok end, + ok = ei_send(P, Fd, self(), AMsg={a,message}), + receive AMsg -> ok end, - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + runner:send_eot(P), + runner:recv_eot(P), ok. ei_format_pid(Config) when is_list(Config) -> - ?line S = self(), - ?line P = runner:start(?interpret), - ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), - ?line {ok,Fd} = ei_connect(P, node()), + S = self(), + P = runner:start(?interpret), + 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), + {ok,Fd} = ei_connect(P, node()), - ?line ok = ei_format_pid(P, Fd, S), - ?line receive S -> ok end, + ok = ei_format_pid(P, Fd, S), + receive S -> ok end, - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + runner:send_eot(P), + runner:recv_eot(P), ok. ei_send_funs(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), - ?line {ok,Fd} = ei_connect(P, node()), - - ?line Fun1 = fun ei_send/1, - ?line Fun2 = fun(X) -> P, X, Fd, Fun1 end, - - ?line AMsg={Fun1,Fun2}, + P = runner:start(?interpret), + 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), + {ok,Fd} = ei_connect(P, node()), + + Fun1 = fun ei_send/1, + Fun2 = fun(X) -> P, X, Fd, Fun1 end, + + AMsg={Fun1,Fun2}, %%AMsg={wait_with_funs, new_dist_format}, - ?line ok = ei_send_funs(P, Fd, self(), AMsg), - ?line EIMsg = receive M -> M end, - ?line EIMsg = AMsg, + ok = ei_send_funs(P, Fd, self(), AMsg), + EIMsg = receive M -> M end, + EIMsg = AMsg, - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + runner:send_eot(P), + runner:recv_eot(P), ok. ei_reg_send(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), - ?line {ok,Fd} = ei_connect(P, node()), + P = runner:start(?interpret), + 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), + {ok,Fd} = ei_connect(P, node()), ARegName = a_strange_registred_name, - ?line register(ARegName, self()), - ?line ok = ei_reg_send(P, Fd, ARegName, AMsg={another,[strange],message}), - ?line receive AMsg -> ok end, + register(ARegName, self()), + ok = ei_reg_send(P, Fd, ARegName, AMsg={another,[strange],message}), + receive AMsg -> ok end, - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + runner:send_eot(P), + runner:recv_eot(P), ok. ei_threaded_send(Config) when is_list(Config) -> - ?line Einode = filename:join(?config(data_dir, Config), "einode"), - ?line N = 15, - ?line Host = atom_to_list(node()), - ?line TestServerPid = self(), - ?line [ spawn_link(fun() -> rec_einode(I, TestServerPid) end) - || I <- lists:seq(0, N-1) ], - ?line [ receive {I,registered} -> ok end - || I <- lists:seq(0, N-1) ], - ?line spawn_link(fun() -> start_einode(Einode, N, Host) end), - ?line [ receive I -> ok end - || I <- lists:seq(0, N-1) ], + Einode = filename:join(proplists:get_value(data_dir, Config), "einode"), + N = 15, + Host = atom_to_list(node()), + TestServerPid = self(), + [ spawn_link(fun() -> rec_einode(I, TestServerPid) end) + || I <- lists:seq(0, N-1) ], + [ receive {I,registered} -> ok end + || I <- lists:seq(0, N-1) ], + spawn_link(fun() -> start_einode(Einode, N, Host) end), + [ receive I -> ok end + || I <- lists:seq(0, N-1) ], ok. rec_einode(N, TestServerPid) -> - ?line Regname = list_to_atom("mth"++integer_to_list(N)), - ?line register(Regname, self()), - ?line TestServerPid ! {N, registered}, - ?line io:format("~p waiting~n", [Regname]), - ?line receive - X -> - ?line io:format("Received by ~s ~p~n", [Regname, X]), - ?line TestServerPid ! N, - ?line X - after 10000 -> - ?line test_server:fail(Regname) - end. + Regname = list_to_atom("mth"++integer_to_list(N)), + register(Regname, self()), + TestServerPid ! {N, registered}, + io:format("~p waiting~n", [Regname]), + receive + X -> + io:format("Received by ~s ~p~n", [Regname, X]), + TestServerPid ! N, + X + after 10000 -> + ct:fail(Regname) + end. start_einode(Einode, N, Host) -> Einodecmd = Einode ++ " " ++ atom_to_list(erlang:get_cookie()) - ++ " " ++ integer_to_list(N) ++ " " ++ Host, + ++ " " ++ integer_to_list(N) ++ " " ++ Host, io:format("Einodecmd ~p ~n", [Einodecmd]), - ?line open_port({spawn, Einodecmd}, []), + open_port({spawn, Einodecmd}, []), ok. ei_rpc(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), - ?line {ok,Fd} = ei_connect(P, node()), + P = runner:start(?interpret), + 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), + {ok,Fd} = ei_connect(P, node()), - ?line S= "Hej du glade!", SRev = lists:reverse(S), - ?line X = ei_rpc(P, Fd, self(), {?MODULE, rpc_test}, [SRev]), - ?line {term, S}= X, + S= "Hej du glade!", SRev = lists:reverse(S), + X = ei_rpc(P, Fd, self(), {?MODULE, rpc_test}, [SRev]), + {term, S}= X, - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + runner:send_eot(P), + runner:recv_eot(P), ok. ei_set_get_tracelevel(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line 5 = ei_set_get_tracelevel(P, 5), - ?line 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), - ?line {ok,Fd} = ei_connect(P, node()), + P = runner:start(?interpret), + 5 = ei_set_get_tracelevel(P, 5), + 0 = ei_connect_init(P, 42, erlang:get_cookie(), 0), + {ok,Fd} = ei_connect(P, node()), - ?line S= "Hej du glade!", SRev = lists:reverse(S), - ?line X = ei_rpc(P, Fd, self(), {?MODULE, rpc_test}, [SRev]), - ?line {term, S}= X, + S= "Hej du glade!", SRev = lists:reverse(S), + X = ei_rpc(P, Fd, self(), {?MODULE, rpc_test}, [SRev]), + {term, S}= X, - ?line 0 = ei_set_get_tracelevel(P, 0), + 0 = ei_set_get_tracelevel(P, 0), - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + runner:send_eot(P), + runner:recv_eot(P), ok. @@ -199,20 +171,20 @@ ei_set_get_tracelevel(Config) when is_list(Config) -> ei_connect_init(P, Num, Cookie, Creation) -> send_command(P, ei_connect_init, [Num,Cookie,Creation]), case get_term(P) of - {term,Int} when is_integer(Int) -> Int + {term,Int} when is_integer(Int) -> Int end. ei_connect(P, Node) -> send_command(P, ei_connect, [Node]), case get_term(P) of - {term,{Fd,_}} when Fd >= 0 -> {ok,Fd}; - {term,{-1,Errno}} -> {error,Errno} + {term,{Fd,_}} when Fd >= 0 -> {ok,Fd}; + {term,{-1,Errno}} -> {error,Errno} end. ei_set_get_tracelevel(P, Tracelevel) -> send_command(P, ei_set_get_tracelevel, [Tracelevel]), case get_term(P) of - {term,{tracelevel, Level}} when is_integer(Level) -> Level + {term,{tracelevel, Level}} when is_integer(Level) -> Level end. ei_send(P, Fd, To, Msg) -> @@ -238,12 +210,12 @@ ei_rpc(P, Fd, To, Func, Msg) -> get_send_result(P) -> case get_term(P) of - {term,{0,_}} -> ok; - {term,{1,_}} -> ok; - {term,{-1,Errno}} -> {error,Errno}; - {term,{Res,Errno}}-> - io:format("Return value: ~p\nerl_errno: ~p", [Res,Errno]), - ?t:fail(bad_return_value) + {term,{0,_}} -> ok; + {term,{1,_}} -> ok; + {term,{-1,Errno}} -> {error,Errno}; + {term,{Res,Errno}}-> + io:format("Return value: ~p\nerl_errno: ~p", [Res,Errno]), + ct:fail(bad_return_value) end. send_command(P, Name, Args) -> diff --git a/lib/erl_interface/test/ei_decode_SUITE.erl b/lib/erl_interface/test/ei_decode_SUITE.erl index 03c7083d56..1495a0d5d9 100644 --- a/lib/erl_interface/test/ei_decode_SUITE.erl +++ b/lib/erl_interface/test/ei_decode_SUITE.erl @@ -24,20 +24,15 @@ -include_lib("common_test/include/ct.hrl"). -include("ei_decode_SUITE_data/ei_decode_test_cases.hrl"). --export( - [ - all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, init_per_testcase/2, - end_per_testcase/2, - test_ei_decode_long/1, - test_ei_decode_ulong/1, - test_ei_decode_longlong/1, - test_ei_decode_ulonglong/1, - test_ei_decode_char/1, - test_ei_decode_nonoptimal/1, - test_ei_decode_misc/1, - test_ei_decode_utf8_atom/1 - ]). +-export([all/0, suite/0, + test_ei_decode_long/1, + test_ei_decode_ulong/1, + test_ei_decode_longlong/1, + test_ei_decode_ulonglong/1, + test_ei_decode_char/1, + test_ei_decode_nonoptimal/1, + test_ei_decode_misc/1, + test_ei_decode_utf8_atom/1]). suite() -> [{ct_hooks,[ts_install_cth]}]. @@ -47,27 +42,6 @@ all() -> test_ei_decode_char, test_ei_decode_nonoptimal, test_ei_decode_misc, test_ei_decode_utf8_atom]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - -init_per_testcase(_TC, Config) -> - Config. - -end_per_testcase(_RC, Config) -> - Config. - %% --------------------------------------------------------------------------- % NOTE: for historical reasons we don't pach as tight as we can, @@ -76,55 +50,51 @@ end_per_testcase(_RC, Config) -> %% ######################################################################## %% -test_ei_decode_long(suite) -> []; test_ei_decode_long(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_decode_long), + P = runner:start(?test_ei_decode_long), send_integers(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% ######################################################################## %% -test_ei_decode_ulong(suite) -> []; test_ei_decode_ulong(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_decode_ulong), + P = runner:start(?test_ei_decode_ulong), send_integers(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. % (*) In practical terms, other values may fit into the ext format % i32 is signed 32 bit on C side % u32 is unsigned 32 bit on C side - + %% ######################################################################## %% -test_ei_decode_longlong(suite) -> []; test_ei_decode_longlong(Config) when is_list(Config) -> case os:type() of - vxworks -> - {skip,"Skipped on VxWorks"}; - _ -> - ?line P = runner:start(?test_ei_decode_longlong), - send_integers2(P), - ?line runner:recv_eot(P), - ok + vxworks -> + {skip,"Skipped on VxWorks"}; + _ -> + P = runner:start(?test_ei_decode_longlong), + send_integers2(P), + runner:recv_eot(P), + ok end. %% ######################################################################## %% -test_ei_decode_ulonglong(suite) -> []; test_ei_decode_ulonglong(Config) when is_list(Config) -> case os:type() of - vxworks -> - {skip,"Skipped on VxWorks"}; - _ -> - ?line P = runner:start(?test_ei_decode_ulonglong), - send_integers2(P), - ?line runner:recv_eot(P), - ok + vxworks -> + {skip,"Skipped on VxWorks"}; + _ -> + P = runner:start(?test_ei_decode_ulonglong), + send_integers2(P), + runner:recv_eot(P), + ok end. @@ -133,38 +103,36 @@ test_ei_decode_ulonglong(Config) when is_list(Config) -> %% it is unsigned. %% FIXME maybe the API should change to use "unsigned char" to be clear?! -test_ei_decode_char(suite) -> []; test_ei_decode_char(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_decode_char), + P = runner:start(?test_ei_decode_char), - ?line send_term_as_binary(P,0), - ?line send_term_as_binary(P,16#7f), - ?line send_term_as_binary(P,16#ff), + send_term_as_binary(P,0), + send_term_as_binary(P,16#7f), + send_term_as_binary(P,16#ff), - ?line send_term_as_binary(P, []), % illegal type + send_term_as_binary(P, []), % illegal type - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% ######################################################################## %% -test_ei_decode_nonoptimal(suite) -> []; test_ei_decode_nonoptimal(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_decode_nonoptimal), + P = runner:start(?test_ei_decode_nonoptimal), send_non_optimal_pos(P), % decode_char send_non_optimal(P), % decode_long send_non_optimal_pos(P), % decode_ulong case os:type() of - vxworks -> - ok; - _ -> - send_non_optimal(P), % decode_longlong - send_non_optimal_pos(P) % decode_ulonglong + vxworks -> + ok; + _ -> + send_non_optimal(P), % decode_longlong + send_non_optimal_pos(P) % decode_ulonglong end, - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. @@ -173,82 +141,81 @@ send_non_optimal(P) -> send_non_optimal_neg(P). send_non_optimal_pos(P) -> - ?line send_raw(P, <<131,97,42>>), - ?line send_raw(P, <<131,98,42:32>>), - ?line send_raw(P, <<131,110,1,0,42>>), - ?line send_raw(P, <<131,110,2,0,42,0>>), - ?line send_raw(P, <<131,110,4,0,42,0,0,0>>), - ?line send_raw(P, <<131,111,0,0,0,1,0,42>>), - ?line send_raw(P, <<131,111,0,0,0,2,0,42,0>>), - ?line send_raw(P, <<131,111,0,0,0,3,0,42,0,0>>), - ?line send_raw(P, <<131,111,0,0,0,6,0,42,0,0,0,0,0>>), + send_raw(P, <<131,97,42>>), + send_raw(P, <<131,98,42:32>>), + send_raw(P, <<131,110,1,0,42>>), + send_raw(P, <<131,110,2,0,42,0>>), + send_raw(P, <<131,110,4,0,42,0,0,0>>), + send_raw(P, <<131,111,0,0,0,1,0,42>>), + send_raw(P, <<131,111,0,0,0,2,0,42,0>>), + send_raw(P, <<131,111,0,0,0,3,0,42,0,0>>), + send_raw(P, <<131,111,0,0,0,6,0,42,0,0,0,0,0>>), ok. send_non_optimal_neg(P) -> -% ?line send_raw(P, <<131,97,-42>>), - ?line send_raw(P, <<131,98,-42:32>>), - ?line send_raw(P, <<131,110,1,1,42>>), - ?line send_raw(P, <<131,110,2,1,42,0>>), - ?line send_raw(P, <<131,110,4,1,42,0,0,0>>), - ?line send_raw(P, <<131,111,0,0,0,1,1,42>>), - ?line send_raw(P, <<131,111,0,0,0,2,1,42,0>>), - ?line send_raw(P, <<131,111,0,0,0,3,1,42,0,0>>), - ?line send_raw(P, <<131,111,0,0,0,6,1,42,0,0,0,0,0>>), + % send_raw(P, <<131,97,-42>>), + send_raw(P, <<131,98,-42:32>>), + send_raw(P, <<131,110,1,1,42>>), + send_raw(P, <<131,110,2,1,42,0>>), + send_raw(P, <<131,110,4,1,42,0,0,0>>), + send_raw(P, <<131,111,0,0,0,1,1,42>>), + send_raw(P, <<131,111,0,0,0,2,1,42,0>>), + send_raw(P, <<131,111,0,0,0,3,1,42,0,0>>), + send_raw(P, <<131,111,0,0,0,6,1,42,0,0,0,0,0>>), ok. %% ######################################################################## %% -test_ei_decode_misc(suite) -> []; test_ei_decode_misc(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_decode_misc), + P = runner:start(?test_ei_decode_misc), - ?line send_term_as_binary(P,0.0), - ?line send_term_as_binary(P,-1.0), - ?line send_term_as_binary(P,1.0), + send_term_as_binary(P,0.0), + send_term_as_binary(P,-1.0), + send_term_as_binary(P,1.0), - ?line send_term_as_binary(P,false), - ?line send_term_as_binary(P,true), + send_term_as_binary(P,false), + send_term_as_binary(P,true), - ?line send_term_as_binary(P,foo), - ?line send_term_as_binary(P,''), - ?line send_term_as_binary(P,'ÅÄÖåäö'), + send_term_as_binary(P,foo), + send_term_as_binary(P,''), + send_term_as_binary(P,'ÅÄÖåäö'), - ?line send_term_as_binary(P,"foo"), - ?line send_term_as_binary(P,""), - ?line send_term_as_binary(P,"ÅÄÖåäö"), + send_term_as_binary(P,"foo"), + send_term_as_binary(P,""), + send_term_as_binary(P,"ÅÄÖåäö"), - ?line send_term_as_binary(P,<<"foo">>), - ?line send_term_as_binary(P,<<>>), - ?line send_term_as_binary(P,<<"ÅÄÖåäö">>), + send_term_as_binary(P,<<"foo">>), + send_term_as_binary(P,<<>>), + send_term_as_binary(P,<<"ÅÄÖåäö">>), -% ?line send_term_as_binary(P,{}), -% ?line send_term_as_binary(P,[]), + % send_term_as_binary(P,{}), + % send_term_as_binary(P,[]), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% ######################################################################## %% test_ei_decode_utf8_atom(Config) -> - ?line P = runner:start(?test_ei_decode_utf8_atom), + P = runner:start(?test_ei_decode_utf8_atom), send_utf8_atom_as_binary(P,"Ã¥"), send_utf8_atom_as_binary(P,"ä"), send_term_as_binary(P,'ö'), send_term_as_binary(P,'õ'), - - ?line send_utf8_atom_as_binary(P,[1758]), - ?line send_utf8_atom_as_binary(P,[1758,1758]), - ?line send_utf8_atom_as_binary(P,[1758,1758,1758]), - ?line send_utf8_atom_as_binary(P,[1758,1758,1758,1758]), + + send_utf8_atom_as_binary(P,[1758]), + send_utf8_atom_as_binary(P,[1758,1758]), + send_utf8_atom_as_binary(P,[1758,1758,1758]), + send_utf8_atom_as_binary(P,[1758,1758,1758,1758]), send_utf8_atom_as_binary(P,"a"), send_utf8_atom_as_binary(P,"b"), send_term_as_binary(P,'c'), send_term_as_binary(P,'d'), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. @@ -264,77 +231,77 @@ send_utf8_atom_as_binary(Port, String) -> Port ! {self(), {command, term_to_binary(uc_atup(String))}}. send_integers(P) -> - ?line send_term_as_binary(P,0), % SMALL_INTEGER_EXT smallest - ?line send_term_as_binary(P,255), % SMALL_INTEGER_EXT largest - ?line send_term_as_binary(P,256), % INTEGER_EXT smallest pos (*) - ?line send_term_as_binary(P,-1), % INTEGER_EXT largest neg - - ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT old largest (28 bits) - ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT old smallest - ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT old smallest pos(*) - ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT old largest neg (*) - - ?line send_term_as_binary(P, 16#7fffffff), % INTEGER_EXT new largest (32 bits) - ?line send_term_as_binary(P,-16#80000000), % INTEGER_EXT new smallest (32 bis) - ?line send_term_as_binary(P, 16#80000000), % SMALL_BIG_EXT new smallest pos(*) - ?line send_term_as_binary(P,-16#80000001), % SMALL_BIG_EXT new largest neg (*) - + send_term_as_binary(P,0), % SMALL_INTEGER_EXT smallest + send_term_as_binary(P,255), % SMALL_INTEGER_EXT largest + send_term_as_binary(P,256), % INTEGER_EXT smallest pos (*) + send_term_as_binary(P,-1), % INTEGER_EXT largest neg + + send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT old largest (28 bits) + send_term_as_binary(P,-16#08000000), % INTEGER_EXT old smallest + send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT old smallest pos(*) + send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT old largest neg (*) + + send_term_as_binary(P, 16#7fffffff), % INTEGER_EXT new largest (32 bits) + send_term_as_binary(P,-16#80000000), % INTEGER_EXT new smallest (32 bis) + send_term_as_binary(P, 16#80000000), % SMALL_BIG_EXT new smallest pos(*) + send_term_as_binary(P,-16#80000001), % SMALL_BIG_EXT new largest neg (*) + case erlang:system_info({wordsize,external}) of - 4 -> - ?line send_term_as_binary(P, 16#80000000),% SMALL_BIG_EXT u32 - ?line send_term_as_binary(P, 16#ffffffff),% SMALL_BIG_EXT largest u32 - - ?line send_term_as_binary(P, 16#7fffffffffff), % largest i48 - ?line send_term_as_binary(P,-16#800000000000), % smallest i48 - ?line send_term_as_binary(P, 16#ffffffffffff), % largest u48 - ?line send_term_as_binary(P, 16#7fffffffffffffff), % largest i64 - ?line send_term_as_binary(P,-16#8000000000000000), % smallest i64 - ?line send_term_as_binary(P, 16#ffffffffffffffff); % largest u64 - 8 -> - ?line send_term_as_binary(P, 16#8000000000000000),% SMALL_BIG_EXT u64 - % SMALL_BIG_EXT largest u64 - ?line send_term_as_binary(P, 16#ffffffffffffffff), - % largest i96 - ?line send_term_as_binary(P, 16#7fffffffffffffffffffffff), - % smallest i96 - ?line send_term_as_binary(P,-16#800000000000000000000000), - % largest u96 - ?line send_term_as_binary(P, 16#ffffffffffffffffffffffff), - % largest i128 - ?line send_term_as_binary(P, 16#7fffffffffffffffffffffffffffffff), - % smallest i128 - ?line send_term_as_binary(P,-16#80000000000000000000000000000000), - % largest u128 - ?line send_term_as_binary(P, 16#ffffffffffffffffffffffffffffffff) + 4 -> + send_term_as_binary(P, 16#80000000),% SMALL_BIG_EXT u32 + send_term_as_binary(P, 16#ffffffff),% SMALL_BIG_EXT largest u32 + + send_term_as_binary(P, 16#7fffffffffff), % largest i48 + send_term_as_binary(P,-16#800000000000), % smallest i48 + send_term_as_binary(P, 16#ffffffffffff), % largest u48 + send_term_as_binary(P, 16#7fffffffffffffff), % largest i64 + send_term_as_binary(P,-16#8000000000000000), % smallest i64 + send_term_as_binary(P, 16#ffffffffffffffff); % largest u64 + 8 -> + send_term_as_binary(P, 16#8000000000000000),% SMALL_BIG_EXT u64 + % SMALL_BIG_EXT largest u64 + send_term_as_binary(P, 16#ffffffffffffffff), + % largest i96 + send_term_as_binary(P, 16#7fffffffffffffffffffffff), + % smallest i96 + send_term_as_binary(P,-16#800000000000000000000000), + % largest u96 + send_term_as_binary(P, 16#ffffffffffffffffffffffff), + % largest i128 + send_term_as_binary(P, 16#7fffffffffffffffffffffffffffffff), + % smallest i128 + send_term_as_binary(P,-16#80000000000000000000000000000000), + % largest u128 + send_term_as_binary(P, 16#ffffffffffffffffffffffffffffffff) end, - ?line send_term_as_binary(P, []), % illegal type + send_term_as_binary(P, []), % illegal type ok. send_integers2(P) -> - ?line send_term_as_binary(P,0), % SMALL_INTEGER_EXT smallest - ?line send_term_as_binary(P,255), % SMALL_INTEGER_EXT largest - ?line send_term_as_binary(P,256), % INTEGER_EXT smallest pos (*) - ?line send_term_as_binary(P,-1), % INTEGER_EXT largest neg - - ?line send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT old largest (28 bits) - ?line send_term_as_binary(P,-16#08000000), % INTEGER_EXT old smallest - ?line send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT old smallest pos(*) - ?line send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT old largest neg (*) - - ?line send_term_as_binary(P, 16#7fffffff), % INTEGER_EXT new largest (32 bits) - ?line send_term_as_binary(P,-16#80000000), % INTEGER_EXT new smallest - ?line send_term_as_binary(P, 16#80000000), % SMALL_BIG_EXT new smallest pos(*) - ?line send_term_as_binary(P,-16#80000001), % SMALL_BIG_EXT new largest neg (*) - - ?line send_term_as_binary(P, 16#ffffffff),% SMALL_BIG_EXT largest u32 - - ?line send_term_as_binary(P, 16#7fffffffffff), % largest i48 - ?line send_term_as_binary(P,-16#800000000000), % smallest i48 - ?line send_term_as_binary(P, 16#ffffffffffff), % largest u48 - ?line send_term_as_binary(P, 16#7fffffffffffffff), % largest i64 - ?line send_term_as_binary(P,-16#8000000000000000), % smallest i64 - ?line send_term_as_binary(P, 16#ffffffffffffffff), % largest u64 - ?line send_term_as_binary(P, []), % illegal type + send_term_as_binary(P,0), % SMALL_INTEGER_EXT smallest + send_term_as_binary(P,255), % SMALL_INTEGER_EXT largest + send_term_as_binary(P,256), % INTEGER_EXT smallest pos (*) + send_term_as_binary(P,-1), % INTEGER_EXT largest neg + + send_term_as_binary(P, 16#07ffffff), % INTEGER_EXT old largest (28 bits) + send_term_as_binary(P,-16#08000000), % INTEGER_EXT old smallest + send_term_as_binary(P, 16#08000000), % SMALL_BIG_EXT old smallest pos(*) + send_term_as_binary(P,-16#08000001), % SMALL_BIG_EXT old largest neg (*) + + send_term_as_binary(P, 16#7fffffff), % INTEGER_EXT new largest (32 bits) + send_term_as_binary(P,-16#80000000), % INTEGER_EXT new smallest + send_term_as_binary(P, 16#80000000), % SMALL_BIG_EXT new smallest pos(*) + send_term_as_binary(P,-16#80000001), % SMALL_BIG_EXT new largest neg (*) + + send_term_as_binary(P, 16#ffffffff), % SMALL_BIG_EXT largest u32 + + send_term_as_binary(P, 16#7fffffffffff), % largest i48 + send_term_as_binary(P,-16#800000000000), % smallest i48 + send_term_as_binary(P, 16#ffffffffffff), % largest u48 + send_term_as_binary(P, 16#7fffffffffffffff), % largest i64 + send_term_as_binary(P,-16#8000000000000000), % smallest i64 + send_term_as_binary(P, 16#ffffffffffffffff), % largest u64 + send_term_as_binary(P, []), % illegal type ok. uc_atup(ATxt) -> @@ -344,35 +311,32 @@ string_to_atom(String) -> Utf8List = string_to_utf8_list(String), Len = length(Utf8List), TagLen = case Len < 256 of - true -> [119, Len]; - false -> [118, Len bsr 8, Len band 16#ff] - end, + true -> [119, Len]; + false -> [118, Len bsr 8, Len band 16#ff] + end, binary_to_term(list_to_binary([131, TagLen, Utf8List])). string_to_utf8_list([]) -> []; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 0 =< CP, - CP =< 16#7F -> + 0 =< CP, + CP =< 16#7F -> [CP | string_to_utf8_list(CPs)]; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 16#80 =< CP, - CP =< 16#7FF -> + 16#80 =< CP, + CP =< 16#7FF -> [16#C0 bor (CP bsr 6), - 16#80 bor (16#3F band CP) - | string_to_utf8_list(CPs)]; + 16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)]; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 16#800 =< CP, - CP =< 16#FFFF -> + 16#800 =< CP, + CP =< 16#FFFF -> [16#E0 bor (CP bsr 12), 16#80 bor (16#3F band (CP bsr 6)), - 16#80 bor (16#3F band CP) - | string_to_utf8_list(CPs)]; + 16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)]; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 16#10000 =< CP, - CP =< 16#10FFFF -> + 16#10000 =< CP, + CP =< 16#10FFFF -> [16#F0 bor (CP bsr 18), 16#80 bor (16#3F band (CP bsr 12)), 16#80 bor (16#3F band (CP bsr 6)), - 16#80 bor (16#3F band CP) - | string_to_utf8_list(CPs)]. + 16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)]. diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl index 57d5031d9d..570a91e2da 100644 --- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl +++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl @@ -24,34 +24,15 @@ -include_lib("common_test/include/ct.hrl"). -include("ei_decode_encode_SUITE_data/ei_decode_encode_test_cases.hrl"). --export( - [ - all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - test_ei_decode_encode/1 - ]). +-export([all/0, suite/0, + test_ei_decode_encode/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [test_ei_decode_encode]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %% --------------------------------------------------------------------------- % NOTE: these types have no meaning on the C side so we pass them @@ -60,20 +41,19 @@ end_per_group(_GroupName, Config) -> %% ######################################################################## %% -test_ei_decode_encode(suite) -> []; test_ei_decode_encode(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_decode_encode), + P = runner:start(?test_ei_decode_encode), Fun = fun (X) -> {X,true} end, Pid = self(), Port = case os:type() of - {win32,_} -> - open_port({spawn,"sort"},[]); - {unix, darwin} -> - open_port({spawn,"/usr/bin/true"},[]); - _ -> - open_port({spawn,"/bin/true"},[]) - end, + {win32,_} -> + open_port({spawn,"sort"},[]); + {unix, darwin} -> + open_port({spawn,"/usr/bin/true"},[]); + _ -> + open_port({spawn,"/bin/true"},[]) + end, Ref = make_ref(), Trace = {1,2,3,self(),4}, % FIXME how to construct?! @@ -86,47 +66,46 @@ test_ei_decode_encode(Config) when is_list(Config) -> BigLargeB = 1 bsl 11112 + BigSmallB, BigLargeC = BigSmallA * BigSmallB * BigSmallC * BigSmallA, - ?line send_rec(P, Fun), - ?line send_rec(P, Pid), - ?line send_rec(P, Port), - ?line send_rec(P, Ref), - ?line send_rec(P, Trace), + send_rec(P, Fun), + send_rec(P, Pid), + send_rec(P, Port), + send_rec(P, Ref), + send_rec(P, Trace), % bigs - ?line send_rec(P, BigSmallA), - ?line send_rec(P, BigSmallB), - ?line send_rec(P, BigSmallC), - - ?line send_rec(P, BigLargeA), - ?line send_rec(P, BigLargeB), - ?line send_rec(P, BigLargeC), + send_rec(P, BigSmallA), + send_rec(P, BigSmallB), + send_rec(P, BigSmallC), + + send_rec(P, BigLargeA), + send_rec(P, BigLargeB), + send_rec(P, BigLargeC), %% Test large node containers... - ?line ThisNode = {node(), erlang:system_info(creation)}, - ?line TXPid = mk_pid(ThisNode, 32767, 8191), - ?line TXPort = mk_port(ThisNode, 268435455), - ?line TXRef = mk_ref(ThisNode, [262143, 4294967295, 4294967295]), + ThisNode = {node(), erlang:system_info(creation)}, + TXPid = mk_pid(ThisNode, 32767, 8191), + TXPort = mk_port(ThisNode, 268435455), + TXRef = mk_ref(ThisNode, [262143, 4294967295, 4294967295]), - ?line OtherNode = {gurka@sallad, 2}, - ?line OXPid = mk_pid(OtherNode, 32767, 8191), - ?line OXPort = mk_port(OtherNode, 268435455), - ?line OXRef = mk_ref(OtherNode, [262143, 4294967295, 4294967295]), + send_rec(P, TXPid), + send_rec(P, TXPort), + send_rec(P, TXRef), - ?line send_rec(P, TXPid), - ?line send_rec(P, TXPort), - ?line send_rec(P, TXRef), - ?line send_rec(P, OXPid), - ?line send_rec(P, OXPort), - ?line send_rec(P, OXRef), + [begin OtherNode = {gurka@sallad, Creation}, + send_rec(P, mk_pid(OtherNode, 32767, 8191)), + send_rec(P, mk_port(OtherNode, 268435455)), + send_rec(P, mk_ref(OtherNode, [262143, 4294967295, 4294967295])), + void + end || Creation <- [1, 2, 3, 4, 16#adec0ded]], %% Unicode atoms [begin send_rec(P, Atom), - send_rec(P, mk_pid({Atom,1}, 23434, 3434)), - send_rec(P, mk_port({Atom,1}, 2343434)), - send_rec(P, mk_ref({Atom,1}, [262143, 8723648, 24097245])), - void + send_rec(P, mk_pid({Atom,1}, 23434, 3434)), + send_rec(P, mk_port({Atom,1}, 2343434)), + send_rec(P, mk_ref({Atom,1}, [262143, 8723648, 24097245])), + void end || Atom <- unicode_atom_data()], send_rec(P, {}), @@ -137,7 +116,7 @@ test_ei_decode_encode(Config) when is_list(Config) -> send_rec(P, #{key => value}), send_rec(P, maps:put(Port, Ref, #{key => value, key2 => Pid})), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. @@ -146,29 +125,27 @@ test_ei_decode_encode(Config) when is_list(Config) -> % We read two packets for each test, the ei_decode_encode and ei_x_decode_encode version.... send_rec(P, Term) when is_port(P) -> - %%?t:format("Testing: ~p~n", [Term]), P ! {self(), {command, term_to_binary(Term)}}, {_B,Term} = get_buf_and_term(P). - get_buf_and_term(P) -> B = get_binaries(P), case B of - <<131>> -> - io:format("(got single magic, no content)\n",[]), - {B,'$$magic$$'}; - <<131,_>> -> - T = binary_to_term(B), - io:format("~w\n~w\n(got magic)\n",[B,T]), - {B,T}; - _ -> - B1 = list_to_binary([131,B]), % No magic, add - T = binary_to_term(B1), - %io:format("~w\n~w\n(got no magic)\n",[B,T]), - {B,T} + <<131>> -> + io:format("(got single magic, no content)\n",[]), + {B,'$$magic$$'}; + <<131,_>> -> + T = binary_to_term(B), + io:format("~w\n~w\n(got magic)\n",[B,T]), + {B,T}; + _ -> + B1 = list_to_binary([131,B]), % No magic, add + T = binary_to_term(B1), + %io:format("~w\n~w\n(got no magic)\n",[B,T]), + {B,T} end. - + get_binaries(P) -> B1 = get_binary(P), @@ -177,40 +154,17 @@ get_binaries(P) -> get_binary(P) -> case runner:get_term(P) of - {bytes,L} -> - B = list_to_binary(L), - %%io:format("~w\n",[L]), -% For strange reasons <<131>> show up as <>.... -% io:format("~w\n",[B]), - B; - Other -> - Other + {bytes,L} -> + B = list_to_binary(L), + %%io:format("~w\n",[L]), + % For strange reasons <<131>> show up as <>.... + % io:format("~w\n",[B]), + B; + Other -> + Other end. %% - -% We use our own get_term() - -get_term(P) -> - case runner:get_term(P) of - {bytes,[131]} -> - io:format("(got single magic, no content)\n",[]), - '$$magic$$'; - {bytes,[131,L]} -> - B = list_to_binary(L), - T = binary_to_term(B), - io:format("~w\n~w\n(got magic)\n",[L,T]), - T; - {bytes,L} -> - B = list_to_binary([131,L]), - T = binary_to_term(B), - io:format("~w\n~w\n(got no magic)\n",[L,T]), - T; - Other -> - Other - end. - -%% %% Node container constructor functions %% @@ -221,6 +175,9 @@ get_term(P) -> -define(PORT_EXT, 102). -define(PID_EXT, 103). -define(NEW_REFERENCE_EXT, 114). +-define(NEW_PID_EXT, $X). +-define(NEW_PORT_EXT, $Y). +-define(NEWER_REFERENCE_EXT, $Z). uint32_be(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 32 -> [(Uint bsr 24) band 16#ff, @@ -242,18 +199,22 @@ uint8(Uint) when is_integer(Uint), 0 =< Uint, Uint < 1 bsl 8 -> uint8(Uint) -> exit({badarg, uint8, [Uint]}). +pid_tag(Creation) when Creation =< 3 -> ?PID_EXT; +pid_tag(_Creation) -> ?NEW_PID_EXT. +enc_creation(Creation) when Creation =< 3 -> uint8(Creation); +enc_creation(Creation) -> uint32_be(Creation). mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), mk_pid({NodeNameExt, Creation}, Number, Serial); mk_pid({NodeNameExt, Creation}, Number, Serial) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PID_EXT, - NodeNameExt, - uint32_be(Number), - uint32_be(Serial), - uint8(Creation)])) of + pid_tag(Creation), + NodeNameExt, + uint32_be(Number), + uint32_be(Serial), + enc_creation(Creation)])) of Pid when is_pid(Pid) -> Pid; {'EXIT', {badarg, _}} -> @@ -262,15 +223,18 @@ mk_pid({NodeNameExt, Creation}, Number, Serial) -> exit({unexpected_binary_to_term_result, Other}) end. +port_tag(Creation) when Creation =< 3 -> ?PORT_EXT; +port_tag(_Creation) -> ?NEW_PORT_EXT. + mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), mk_port({NodeNameExt, Creation}, Number); mk_port({NodeNameExt, Creation}, Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PORT_EXT, + port_tag(Creation), NodeNameExt, uint32_be(Number), - uint8(Creation)])) of + enc_creation(Creation)])) of Port when is_port(Port) -> Port; {'EXIT', {badarg, _}} -> @@ -279,34 +243,38 @@ mk_port({NodeNameExt, Creation}, Number) -> exit({unexpected_binary_to_term_result, Other}) end. +ref_tag(Creation) when Creation =< 3 -> ?NEW_REFERENCE_EXT; +ref_tag(_Creation) -> ?NEWER_REFERENCE_EXT. + mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), - is_integer(Creation), - is_list(Numbers) -> + is_integer(Creation), + is_list(Numbers) -> <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), mk_ref({NodeNameExt, Creation}, Numbers); mk_ref({NodeNameExt, Creation}, [Number]) when is_binary(NodeNameExt), is_integer(Creation), + Creation =< 3, is_integer(Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?REFERENCE_EXT, - NodeNameExt, - uint32_be(Number), - uint8(Creation)])) of - Ref when is_reference(Ref) -> - Ref; - {'EXIT', {badarg, _}} -> - exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]}); - Other -> - exit({unexpected_binary_to_term_result, Other}) + ?REFERENCE_EXT, + NodeNameExt, + uint32_be(Number), + uint8(Creation)])) of + Ref when is_reference(Ref) -> + Ref; + {'EXIT', {badarg, _}} -> + exit({badarg, mk_ref, [{NodeNameExt, Creation}, [Number]]}); + Other -> + exit({unexpected_binary_to_term_result, Other}) end; mk_ref({NodeNameExt, Creation}, Numbers) when is_binary(NodeNameExt), - is_integer(Creation), - is_list(Numbers) -> + is_integer(Creation), + is_list(Numbers) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?NEW_REFERENCE_EXT, + ref_tag(Creation), uint16_be(length(Numbers)), NodeNameExt, - uint8(Creation), + enc_creation(Creation), lists:map(fun (N) -> uint32_be(N) end, @@ -333,10 +301,10 @@ unicode_atom_data() -> uc_atup(lists:seq(65500, 65754)), uc_atup(lists:seq(65500, 65563)) | lists:map(fun (N) -> - Pow2 = (1 bsl N), - uc_atup(lists:seq(Pow2 - 127, Pow2 + 127)) - end, - lists:seq(7, 20)) + Pow2 = (1 bsl N), + uc_atup(lists:seq(Pow2 - 127, Pow2 + 127)) + end, + lists:seq(7, 20)) ]. uc_atup(ATxt) -> @@ -346,33 +314,33 @@ string_to_atom(String) -> Utf8List = string_to_utf8_list(String), Len = length(Utf8List), TagLen = case Len < 256 of - true -> [119, Len]; - false -> [118, Len bsr 8, Len band 16#ff] - end, + true -> [119, Len]; + false -> [118, Len bsr 8, Len band 16#ff] + end, binary_to_term(list_to_binary([131, TagLen, Utf8List])). string_to_utf8_list([]) -> []; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 0 =< CP, - CP =< 16#7F -> + 0 =< CP, + CP =< 16#7F -> [CP | string_to_utf8_list(CPs)]; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 16#80 =< CP, - CP =< 16#7FF -> + 16#80 =< CP, + CP =< 16#7FF -> [16#C0 bor (CP bsr 6), 16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)]; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 16#800 =< CP, - CP =< 16#FFFF -> + 16#800 =< CP, + CP =< 16#FFFF -> [16#E0 bor (CP bsr 12), 16#80 bor (16#3F band (CP bsr 6)), 16#80 bor (16#3F band CP) | string_to_utf8_list(CPs)]; string_to_utf8_list([CP|CPs]) when is_integer(CP), - 16#10000 =< CP, - CP =< 16#10FFFF -> + 16#10000 =< CP, + CP =< 16#10FFFF -> [16#F0 bor (CP bsr 18), 16#80 bor (16#3F band (CP bsr 12)), 16#80 bor (16#3F band (CP bsr 6)), diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c index 22ecacafe3..467f789fdb 100644 --- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c +++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c @@ -240,7 +240,7 @@ void decode_encode(struct Type** tv, int nobj) if (err != -1) { fail("decode returned non zero but not -1"); } else { - fail("decode returned non zero"); + fail1("decode '%s' returned non zero", t->name); } return; } @@ -491,12 +491,11 @@ TESTCASE(test_ei_decode_encode) decode_encode_big(&big_type); /* Test large node containers... */ - decode_encode_one(&pid_type); - decode_encode_one(&port_type); - decode_encode_one(&ref_type); - decode_encode_one(&pid_type); - decode_encode_one(&port_type); - decode_encode_one(&ref_type); + for (i=0; i<6; i++) { + decode_encode_one(&pid_type); + decode_encode_one(&port_type); + decode_encode_one(&ref_type); + } /* Unicode atoms */ for (i=0; i<24; i++) { diff --git a/lib/erl_interface/test/ei_encode_SUITE.erl b/lib/erl_interface/test/ei_encode_SUITE.erl index f1c8bfe967..ac6ec9cf4e 100644 --- a/lib/erl_interface/test/ei_encode_SUITE.erl +++ b/lib/erl_interface/test/ei_encode_SUITE.erl @@ -24,22 +24,19 @@ -include_lib("common_test/include/ct.hrl"). -include("ei_encode_SUITE_data/ei_encode_test_cases.hrl"). --export( - [ - all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - test_ei_encode_long/1, - test_ei_encode_ulong/1, - test_ei_encode_longlong/1, - test_ei_encode_ulonglong/1, - test_ei_encode_char/1, - test_ei_encode_misc/1, - test_ei_encode_fails/1, - test_ei_encode_utf8_atom/1, - test_ei_encode_utf8_atom_len/1 - ]). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +-export([all/0, suite/0, + test_ei_encode_long/1, + test_ei_encode_ulong/1, + test_ei_encode_longlong/1, + test_ei_encode_ulonglong/1, + test_ei_encode_char/1, + test_ei_encode_misc/1, + test_ei_encode_fails/1, + test_ei_encode_utf8_atom/1, + test_ei_encode_utf8_atom_len/1]). + +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [test_ei_encode_long, test_ei_encode_ulong, @@ -48,21 +45,6 @@ all() -> test_ei_encode_fails, test_ei_encode_utf8_atom, test_ei_encode_utf8_atom_len]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %% --------------------------------------------------------------------------- @@ -72,105 +54,101 @@ end_per_group(_GroupName, Config) -> %% ######################################################################## %% -test_ei_encode_long(suite) -> []; test_ei_encode_long(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_encode_long), + P = runner:start(?test_ei_encode_long), - ?line {<<97,0>> ,0} = get_buf_and_term(P), - ?line {<<97,255>> ,255} = get_buf_and_term(P), - ?line {<<98,256:32/big-signed-integer>>,256} = get_buf_and_term(P), - ?line {<<98,-1:32/big-signed-integer>> ,-1} = get_buf_and_term(P), + {<<97,0>> ,0} = get_buf_and_term(P), + {<<97,255>> ,255} = get_buf_and_term(P), + {<<98,256:32/big-signed-integer>>,256} = get_buf_and_term(P), + {<<98,-1:32/big-signed-integer>> ,-1} = get_buf_and_term(P), - ?line {<<98, 16#07ffffff:32/big-signed-integer>>, 16#07ffffff} = get_buf_and_term(P), - ?line {<<98,-16#08000000:32/big-signed-integer>>,-16#08000000} = get_buf_and_term(P), - ?line {<<110,4,0, 0,0,0,8>> , 16#08000000} = get_buf_and_term(P), - ?line {<<110,4,1, 1,0,0,8>> ,-16#08000001} = get_buf_and_term(P), + {<<98, 16#07ffffff:32/big-signed-integer>>, 16#07ffffff} = get_buf_and_term(P), + {<<98,-16#08000000:32/big-signed-integer>>,-16#08000000} = get_buf_and_term(P), + {<<110,4,0, 0,0,0,8>> , 16#08000000} = get_buf_and_term(P), + {<<110,4,1, 1,0,0,8>> ,-16#08000001} = get_buf_and_term(P), - ?line {<<110,4,0, 255,255,255,127>> , 16#7fffffff} = get_buf_and_term(P), - ?line {<<110,4,1, 0,0,0,128>> ,-16#80000000} = get_buf_and_term(P), + {<<110,4,0, 255,255,255,127>> , 16#7fffffff} = get_buf_and_term(P), + {<<110,4,1, 0,0,0,128>> ,-16#80000000} = get_buf_and_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% ######################################################################## %% -test_ei_encode_ulong(suite) -> []; test_ei_encode_ulong(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_encode_ulong), + P = runner:start(?test_ei_encode_ulong), - ?line {<<97,0>> ,0} = get_buf_and_term(P), - ?line {<<97,255>> ,255} = get_buf_and_term(P), - ?line {<<98,256:32/big-unsigned-integer>>,256} = get_buf_and_term(P), + {<<97,0>> ,0} = get_buf_and_term(P), + {<<97,255>> ,255} = get_buf_and_term(P), + {<<98,256:32/big-unsigned-integer>>,256} = get_buf_and_term(P), - ?line {<<98, 16#07ffffff:32/big-signed-integer>>,16#07ffffff} = get_buf_and_term(P), - ?line {<<110,4,0, 0,0,0,8>> ,16#08000000} = get_buf_and_term(P), + {<<98, 16#07ffffff:32/big-signed-integer>>,16#07ffffff} = get_buf_and_term(P), + {<<110,4,0, 0,0,0,8>> ,16#08000000} = get_buf_and_term(P), - ?line {<<110,4,0, 255,255,255,127>> ,16#7fffffff} = get_buf_and_term(P), - ?line {<<110,4,0, 0,0,0,128>> ,16#80000000} = get_buf_and_term(P), - ?line {<<110,4,0, 255,255,255,255>> ,16#ffffffff} = get_buf_and_term(P), + {<<110,4,0, 255,255,255,127>> ,16#7fffffff} = get_buf_and_term(P), + {<<110,4,0, 0,0,0,128>> ,16#80000000} = get_buf_and_term(P), + {<<110,4,0, 255,255,255,255>> ,16#ffffffff} = get_buf_and_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% ######################################################################## %% -test_ei_encode_longlong(suite) -> []; test_ei_encode_longlong(Config) when is_list(Config) -> case os:type() of - vxworks -> - {skip,"Skipped on VxWorks"}; - _ -> - ?line P = runner:start(?test_ei_encode_longlong), - - ?line {<<97,0>> ,0} = get_buf_and_term(P), - ?line {<<97,255>> ,255} = get_buf_and_term(P), - ?line {<<98,256:32/big-signed-integer>>,256} = get_buf_and_term(P), - ?line {<<98,-1:32/big-signed-integer>> ,-1} = get_buf_and_term(P), - - ?line {<<98, 16#07ffffff:32/big-signed-integer>>, 16#07ffffff} = get_buf_and_term(P), - ?line {<<98,-16#08000000:32/big-signed-integer>>,-16#08000000} = get_buf_and_term(P), - ?line {<<110,4,0, 0,0,0,8>> , 16#08000000} = get_buf_and_term(P), - ?line {<<110,4,1, 1,0,0,8>> ,-16#08000001} = get_buf_and_term(P), - - ?line {<<110,4,0, 255,255,255,127>> , 16#7fffffff} = get_buf_and_term(P), - ?line {<<110,4,1, 0,0,0,128>> ,-16#80000000} = get_buf_and_term(P), - ?line {<<110,6,0, 255,255,255,255,255,127>> , 16#7fffffffffff} = get_buf_and_term(P), - ?line {<<110,6,1, 0,0,0,0,0,128>> ,-16#800000000000} = get_buf_and_term(P), - ?line {<<110,8,0, 255,255,255,255,255,255,255,127>>,16#7fffffffffffffff} = get_buf_and_term(P), - ?line {<<110,8,1, 0,0,0,0,0,0,0,128>> ,-16#8000000000000000} = get_buf_and_term(P), - - ?line runner:recv_eot(P), - ok + vxworks -> + {skip,"Skipped on VxWorks"}; + _ -> + P = runner:start(?test_ei_encode_longlong), + + {<<97,0>> ,0} = get_buf_and_term(P), + {<<97,255>> ,255} = get_buf_and_term(P), + {<<98,256:32/big-signed-integer>>,256} = get_buf_and_term(P), + {<<98,-1:32/big-signed-integer>> ,-1} = get_buf_and_term(P), + + {<<98, 16#07ffffff:32/big-signed-integer>>, 16#07ffffff} = get_buf_and_term(P), + {<<98,-16#08000000:32/big-signed-integer>>,-16#08000000} = get_buf_and_term(P), + {<<110,4,0, 0,0,0,8>> , 16#08000000} = get_buf_and_term(P), + {<<110,4,1, 1,0,0,8>> ,-16#08000001} = get_buf_and_term(P), + + {<<110,4,0, 255,255,255,127>> , 16#7fffffff} = get_buf_and_term(P), + {<<110,4,1, 0,0,0,128>> ,-16#80000000} = get_buf_and_term(P), + {<<110,6,0, 255,255,255,255,255,127>> , 16#7fffffffffff} = get_buf_and_term(P), + {<<110,6,1, 0,0,0,0,0,128>> ,-16#800000000000} = get_buf_and_term(P), + {<<110,8,0, 255,255,255,255,255,255,255,127>>,16#7fffffffffffffff} = get_buf_and_term(P), + {<<110,8,1, 0,0,0,0,0,0,0,128>> ,-16#8000000000000000} = get_buf_and_term(P), + + runner:recv_eot(P), + ok end. %% ######################################################################## %% -test_ei_encode_ulonglong(suite) -> []; test_ei_encode_ulonglong(Config) when is_list(Config) -> case os:type() of - vxworks -> - {skip,"Skipped on VxWorks"}; - _ -> - ?line P = runner:start(?test_ei_encode_ulonglong), - - ?line {<<97,0>> ,0} = get_buf_and_term(P), - ?line {<<97,255>> ,255} = get_buf_and_term(P), - ?line {<<98,256:32/big-unsigned-integer>>,256} = get_buf_and_term(P), - - ?line {<<98, 16#07ffffff:32/big-signed-integer>>,16#07ffffff} = get_buf_and_term(P), - ?line {<<110,4,0, 0,0,0,8>> ,16#08000000} = get_buf_and_term(P), - - ?line {<<110,4,0, 255,255,255,127>> ,16#7fffffff} = get_buf_and_term(P), - ?line {<<110,4,0, 0,0,0,128>> ,16#80000000} = get_buf_and_term(P), - ?line {<<110,4,0, 255,255,255,255>> ,16#ffffffff} = get_buf_and_term(P), - ?line {<<110,6,0, 255,255,255,255,255,255>>,16#ffffffffffff} = get_buf_and_term(P), - ?line {<<110,8,0, 255,255,255,255,255,255,255,255>>,16#ffffffffffffffff} = get_buf_and_term(P), - - ?line runner:recv_eot(P), - ok + vxworks -> + {skip,"Skipped on VxWorks"}; + _ -> + P = runner:start(?test_ei_encode_ulonglong), + + {<<97,0>> ,0} = get_buf_and_term(P), + {<<97,255>> ,255} = get_buf_and_term(P), + {<<98,256:32/big-unsigned-integer>>,256} = get_buf_and_term(P), + + {<<98, 16#07ffffff:32/big-signed-integer>>,16#07ffffff} = get_buf_and_term(P), + {<<110,4,0, 0,0,0,8>> ,16#08000000} = get_buf_and_term(P), + + {<<110,4,0, 255,255,255,127>> ,16#7fffffff} = get_buf_and_term(P), + {<<110,4,0, 0,0,0,128>> ,16#80000000} = get_buf_and_term(P), + {<<110,4,0, 255,255,255,255>> ,16#ffffffff} = get_buf_and_term(P), + {<<110,6,0, 255,255,255,255,255,255>>,16#ffffffffffff} = get_buf_and_term(P), + {<<110,8,0, 255,255,255,255,255,255,255,255>>,16#ffffffffffffffff} = get_buf_and_term(P), + + runner:recv_eot(P), + ok end. @@ -179,115 +157,112 @@ test_ei_encode_ulonglong(Config) when is_list(Config) -> %% it is unsigned. %% FIXME maybe the API should change to use "unsigned char" to be clear?! -test_ei_encode_char(suite) -> []; test_ei_encode_char(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_encode_char), + P = runner:start(?test_ei_encode_char), - ?line {<<97, 0>>,0} = get_buf_and_term(P), - ?line {<<97,127>>,16#7f} = get_buf_and_term(P), - ?line {<<97,255>>,16#ff} = get_buf_and_term(P), + {<<97, 0>>,0} = get_buf_and_term(P), + {<<97,127>>,16#7f} = get_buf_and_term(P), + {<<97,255>>,16#ff} = get_buf_and_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% ######################################################################## %% -test_ei_encode_misc(suite) -> []; test_ei_encode_misc(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_encode_misc), + P = runner:start(?test_ei_encode_misc), - ?line <<131>> = get_binaries(P), + <<131>> = get_binaries(P), - ?line {<<70,_:8/binary>>,F0} = get_buf_and_term(P), - ?line true = match_float(F0, 0.0), + {<<70,_:8/binary>>,F0} = get_buf_and_term(P), + true = match_float(F0, 0.0), - ?line {<<70,_:8/binary>>,Fn1} = get_buf_and_term(P), - ?line true = match_float(Fn1, -1.0), + {<<70,_:8/binary>>,Fn1} = get_buf_and_term(P), + true = match_float(Fn1, -1.0), - ?line {<<70,_:8/binary>>,Fp1} = get_buf_and_term(P), - ?line true = match_float(Fp1, 1.0), + {<<70,_:8/binary>>,Fp1} = get_buf_and_term(P), + true = match_float(Fp1, 1.0), - ?line {<<100,0,5,"false">>,false} = get_buf_and_term(P), - ?line {<<100,0,4,"true">> ,true} = get_buf_and_term(P), - ?line {<<100,0,4,"true">> ,true} = get_buf_and_term(P), - ?line {<<100,0,4,"true">> ,true} = get_buf_and_term(P), + {<<100,0,5,"false">>,false} = get_buf_and_term(P), + {<<100,0,4,"true">> ,true} = get_buf_and_term(P), + {<<100,0,4,"true">> ,true} = get_buf_and_term(P), + {<<100,0,4,"true">> ,true} = get_buf_and_term(P), - ?line {<<100,0,3,"foo">>,foo} = get_buf_and_term(P), - ?line {<<100,0,3,"foo">>,foo} = get_buf_and_term(P), - ?line {<<100,0,0,"">>,''} = get_buf_and_term(P), - ?line {<<100,0,0,"">>,''} = get_buf_and_term(P), - ?line {<<100,0,6,"ÅÄÖåäö">>,'ÅÄÖåäö'} = get_buf_and_term(P), - ?line {<<100,0,6,"ÅÄÖåäö">>,'ÅÄÖåäö'} = get_buf_and_term(P), + {<<100,0,3,"foo">>,foo} = get_buf_and_term(P), + {<<100,0,3,"foo">>,foo} = get_buf_and_term(P), + {<<100,0,0,"">>,''} = get_buf_and_term(P), + {<<100,0,0,"">>,''} = get_buf_and_term(P), + {<<100,0,6,"ÅÄÖåäö">>,'ÅÄÖåäö'} = get_buf_and_term(P), + {<<100,0,6,"ÅÄÖåäö">>,'ÅÄÖåäö'} = get_buf_and_term(P), - ?line {<<107,0,3,"foo">>,"foo"} = get_buf_and_term(P), - ?line {<<107,0,3,"foo">>,"foo"} = get_buf_and_term(P), - ?line {<<106>>,""} = get_buf_and_term(P), - ?line {<<106>>,""} = get_buf_and_term(P), - ?line {<<107,0,6,"ÅÄÖåäö">>,"ÅÄÖåäö"} = get_buf_and_term(P), - ?line {<<107,0,6,"ÅÄÖåäö">>,"ÅÄÖåäö"} = get_buf_and_term(P), + {<<107,0,3,"foo">>,"foo"} = get_buf_and_term(P), + {<<107,0,3,"foo">>,"foo"} = get_buf_and_term(P), + {<<106>>,""} = get_buf_and_term(P), + {<<106>>,""} = get_buf_and_term(P), + {<<107,0,6,"ÅÄÖåäö">>,"ÅÄÖåäö"} = get_buf_and_term(P), + {<<107,0,6,"ÅÄÖåäö">>,"ÅÄÖåäö"} = get_buf_and_term(P), - ?line {<<109,0,0,0,3,"foo">>,<<"foo">>} = get_buf_and_term(P), - ?line {<<109,0,0,0,0,"">>,<<>>} = get_buf_and_term(P), - ?line {<<109,0,0,0,6,"ÅÄÖåäö">>,<<"ÅÄÖåäö">>} = get_buf_and_term(P), + {<<109,0,0,0,3,"foo">>,<<"foo">>} = get_buf_and_term(P), + {<<109,0,0,0,0,"">>,<<>>} = get_buf_and_term(P), + {<<109,0,0,0,6,"ÅÄÖåäö">>,<<"ÅÄÖåäö">>} = get_buf_and_term(P), - ?line {<<104,0>>,{}} = get_buf_and_term(P), % Tuple header for {} - ?line {<<106>>,[]} = get_buf_and_term(P), % Empty list [] + {<<104,0>>,{}} = get_buf_and_term(P), % Tuple header for {} + {<<106>>,[]} = get_buf_and_term(P), % Empty list [] - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% ######################################################################## %% -test_ei_encode_fails(suite) -> []; test_ei_encode_fails(Config) when is_list(Config) -> - ?line P = runner:start(?test_ei_encode_fails), + P = runner:start(?test_ei_encode_fails), - ?line XAtom = list_to_atom(lists:duplicate(255, $x)), - ?line YAtom = list_to_atom(lists:duplicate(255, $y)), + XAtom = list_to_atom(lists:duplicate(255, $x)), + YAtom = list_to_atom(lists:duplicate(255, $y)), - ?line XAtom = get_term(P), - ?line XAtom = get_term(P), - ?line YAtom = get_term(P), - ?line YAtom = get_term(P), + XAtom = get_term(P), + XAtom = get_term(P), + YAtom = get_term(P), + YAtom = get_term(P), - ?line {{{{}}}} = get_term(P), + {{{{}}}} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% ######################################################################## %% test_ei_encode_utf8_atom(Config) -> - ?line P = runner:start(?test_ei_encode_utf8_atom), - - ?line {<<119,2,195,133>>,'Ã…'} = get_buf_and_term(P), - ?line {<<100,0,1,197>>,'Ã…'} = get_buf_and_term(P), - ?line {<<100,0,1,197>>,'Ã…'} = get_buf_and_term(P), - ?line {<<119,2,195,133>>,'Ã…'} = get_buf_and_term(P), + P = runner:start(?test_ei_encode_utf8_atom), - ?line {<<119,1,$A>>,'A'} = get_buf_and_term(P), - ?line {<<100,0,1,$A>>,'A'} = get_buf_and_term(P), + {<<119,2,195,133>>,'Ã…'} = get_buf_and_term(P), + {<<100,0,1,197>>,'Ã…'} = get_buf_and_term(P), + {<<100,0,1,197>>,'Ã…'} = get_buf_and_term(P), + {<<119,2,195,133>>,'Ã…'} = get_buf_and_term(P), - ?line runner:recv_eot(P), + {<<119,1,$A>>,'A'} = get_buf_and_term(P), + {<<100,0,1,$A>>,'A'} = get_buf_and_term(P), + + runner:recv_eot(P), ok. %% ######################################################################## %% test_ei_encode_utf8_atom_len(Config) -> - ?line P = runner:start(?test_ei_encode_utf8_atom_len), - - ?line {<<119,2,195,133>>,'Ã…'} = get_buf_and_term(P), - ?line {<<100,0,2,197,196>>,'ÅÄ'} = get_buf_and_term(P), - ?line {<<100,0,1,197>>,'Ã…'} = get_buf_and_term(P), - ?line {<<119,4,195,133,195,132>>,'ÅÄ'} = get_buf_and_term(P), - - ?line {<<119,1,$A>>,'A'} = get_buf_and_term(P), - ?line {<<100,0,2,$A,$B>>,'AB'} = get_buf_and_term(P), - ?line {<<100,0,255,_:(255*8)>>,_} = get_buf_and_term(P), - - ?line runner:recv_eot(P), + P = runner:start(?test_ei_encode_utf8_atom_len), + + {<<119,2,195,133>>,'Ã…'} = get_buf_and_term(P), + {<<100,0,2,197,196>>,'ÅÄ'} = get_buf_and_term(P), + {<<100,0,1,197>>,'Ã…'} = get_buf_and_term(P), + {<<119,4,195,133,195,132>>,'ÅÄ'} = get_buf_and_term(P), + + {<<119,1,$A>>,'A'} = get_buf_and_term(P), + {<<100,0,2,$A,$B>>,'AB'} = get_buf_and_term(P), + {<<100,0,255,_:(255*8)>>,_} = get_buf_and_term(P), + + runner:recv_eot(P), ok. %% ######################################################################## %% @@ -297,20 +272,20 @@ test_ei_encode_utf8_atom_len(Config) -> get_buf_and_term(P) -> B = get_binaries(P), case B of - <<131>> -> - io:format("(got single magic, no content)\n",[]), - {B,'$$magic$$'}; - <<131,_>> -> - T = binary_to_term(B), - io:format("~w\n~w\n(got magic)\n",[B,T]), - {B,T}; - _ -> - B1 = list_to_binary([131,B]), % No magic, add - T = binary_to_term(B1), - io:format("~w\n~w\n(got no magic)\n",[B,T]), - {B,T} + <<131>> -> + io:format("(got single magic, no content)\n",[]), + {B,'$$magic$$'}; + <<131,_>> -> + T = binary_to_term(B), + io:format("~w\n~w\n(got magic)\n",[B,T]), + {B,T}; + _ -> + B1 = list_to_binary([131,B]), % No magic, add + T = binary_to_term(B1), + io:format("~w\n~w\n(got no magic)\n",[B,T]), + {B,T} end. - + get_binaries(P) -> B1 = get_binary(P), @@ -319,14 +294,14 @@ get_binaries(P) -> get_binary(P) -> case runner:get_term(P) of - {bytes,L} -> - B = list_to_binary(L), - io:format("~w\n",[L]), -% For strange reasons <<131>> show up as <>.... -% io:format("~w\n",[B]), - B; - Other -> - Other + {bytes,L} -> + B = list_to_binary(L), + io:format("~w\n",[L]), + % For strange reasons <<131>> show up as <>.... + % io:format("~w\n",[B]), + B; + Other -> + Other end. %% @@ -335,27 +310,26 @@ get_binary(P) -> get_term(P) -> case runner:get_term(P) of - {bytes,[131]} -> - io:format("(got single magic, no content)\n",[]), - '$$magic$$'; - {bytes,[131,L]} -> - B = list_to_binary(L), - T = binary_to_term(B), - io:format("~w\n~w\n(got magic)\n",[L,T]), - T; - {bytes,L} -> - B = list_to_binary([131,L]), - T = binary_to_term(B), - io:format("~w\n~w\n(got no magic)\n",[L,T]), - T; - Other -> - Other + {bytes,[131]} -> + io:format("(got single magic, no content)\n",[]), + '$$magic$$'; + {bytes,[131,L]} -> + B = list_to_binary(L), + T = binary_to_term(B), + io:format("~w\n~w\n(got magic)\n",[L,T]), + T; + {bytes,L} -> + B = list_to_binary([131,L]), + T = binary_to_term(B), + io:format("~w\n~w\n(got no magic)\n",[L,T]), + T; + Other -> + Other end. - + %% match_float(F, Match) when is_float(F), is_float(Match), F == Match -> true; match_float(F, Match) when is_float(F), F > Match*0.99, F < Match*1.01 -> true. - diff --git a/lib/erl_interface/test/ei_format_SUITE.erl b/lib/erl_interface/test/ei_format_SUITE.erl index 382310231e..07ee479b1f 100644 --- a/lib/erl_interface/test/ei_format_SUITE.erl +++ b/lib/erl_interface/test/ei_format_SUITE.erl @@ -24,155 +24,131 @@ -include_lib("common_test/include/ct.hrl"). -include("ei_format_SUITE_data/ei_format_test_cases.hrl"). --export([ - format_wo_ver/1, - all/0, suite/0,groups/0, - init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - atoms/1, - tuples/1, - lists/1 - ]). +-export([format_wo_ver/1, + all/0, suite/0, + atoms/1, + tuples/1, + lists/1]). -import(runner, [get_term/1]). %% This test suite test the erl_format() function. %% It uses the port program "ei_format_test". -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [format_wo_ver, atoms, tuples, lists]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %% Tests formatting various atoms. -atoms(suite) -> []; atoms(Config) when is_list(Config) -> - ?line P = runner:start(?atoms), - - ?line {term, ''} = get_term(P), - ?line {term, 'a'} = get_term(P), - ?line {term, 'A'} = get_term(P), - ?line {term, 'abc'} = get_term(P), - ?line {term, 'Abc'} = get_term(P), - ?line {term, 'ab@c'} = get_term(P), - ?line {term, 'The rain in Spain stays mainly in the plains'} = - get_term(P), - - ?line {term, a} = get_term(P), - ?line {term, ab} = get_term(P), - ?line {term, abc} = get_term(P), - ?line {term, ab@c} = get_term(P), - ?line {term, abcdefghijklmnopq} = get_term(P), - - ?line {term, ''} = get_term(P), - ?line {term, 'a'} = get_term(P), - ?line {term, 'A'} = get_term(P), - ?line {term, 'abc'} = get_term(P), - ?line {term, 'Abc'} = get_term(P), - ?line {term, 'ab@c'} = get_term(P), - ?line {term, 'The rain in Spain stays mainly in the plains'} = - get_term(P), - - ?line {term, a} = get_term(P), - ?line {term, ab} = get_term(P), - ?line {term, abc} = get_term(P), - ?line {term, ab@c} = get_term(P), - ?line {term, ' abcdefghijklmnopq '} = get_term(P), - - ?line runner:recv_eot(P), + P = runner:start(?atoms), + + {term, ''} = get_term(P), + {term, 'a'} = get_term(P), + {term, 'A'} = get_term(P), + {term, 'abc'} = get_term(P), + {term, 'Abc'} = get_term(P), + {term, 'ab@c'} = get_term(P), + {term, 'The rain in Spain stays mainly in the plains'} = + get_term(P), + + {term, a} = get_term(P), + {term, ab} = get_term(P), + {term, abc} = get_term(P), + {term, ab@c} = get_term(P), + {term, abcdefghijklmnopq} = get_term(P), + + {term, ''} = get_term(P), + {term, 'a'} = get_term(P), + {term, 'A'} = get_term(P), + {term, 'abc'} = get_term(P), + {term, 'Abc'} = get_term(P), + {term, 'ab@c'} = get_term(P), + {term, 'The rain in Spain stays mainly in the plains'} = + get_term(P), + + {term, a} = get_term(P), + {term, ab} = get_term(P), + {term, abc} = get_term(P), + {term, ab@c} = get_term(P), + {term, ' abcdefghijklmnopq '} = get_term(P), + + runner:recv_eot(P), ok. %% Tests formatting various tuples -tuples(suite) -> []; tuples(Config) when is_list(Config) -> - ?line P = runner:start(?tuples), - - ?line {term, {}} = get_term(P), - ?line {term, {a}} = get_term(P), - ?line {term, {a, b}} = get_term(P), - ?line {term, {a, b, c}} = get_term(P), - ?line {term, {1}} = get_term(P), - ?line {term, {[]}} = get_term(P), - ?line {term, {[], []}} = get_term(P), - ?line {term, {[], a, b, c}} = get_term(P), - ?line {term, {[], a, [], b, c}} = get_term(P), - ?line {term, {[], a, '', b, c}} = get_term(P), - - ?line runner:recv_eot(P), + P = runner:start(?tuples), + + {term, {}} = get_term(P), + {term, {a}} = get_term(P), + {term, {a, b}} = get_term(P), + {term, {a, b, c}} = get_term(P), + {term, {1}} = get_term(P), + {term, {[]}} = get_term(P), + {term, {[], []}} = get_term(P), + {term, {[], a, b, c}} = get_term(P), + {term, {[], a, [], b, c}} = get_term(P), + {term, {[], a, '', b, c}} = get_term(P), + + runner:recv_eot(P), ok. %% Tests formatting various lists -lists(suite) -> []; lists(Config) when is_list(Config) -> - ?line P = runner:start(?lists), - - ?line {term, []} = get_term(P), - ?line {term, [a]} = get_term(P), - ?line {term, [a, b]} = get_term(P), - ?line {term, [a, b, c]} = get_term(P), - ?line {term, [1]} = get_term(P), - ?line {term, [[]]} = get_term(P), - ?line {term, [[], []]} = get_term(P), - ?line {term, [[], a, b, c]} = get_term(P), - ?line {term, [[], a, [], b, c]} = get_term(P), - ?line {term, [[], a, '', b, c]} = get_term(P), - ?line {term, [[x, 2], [y, 3], [z, 4]]}= get_term(P), - ?line {term, [{a,b},{c,d}]}= get_term(P), -%% ?line {term, [{name, 'Madonna'}, {age, 21}, {data, [{addr, "E-street", 42}]}]} = -%% get_term(P), - - ?line {term, [{pi, F1}, {'cos(70)', F2}]} = get_term(P), + P = runner:start(?lists), + + {term, []} = get_term(P), + {term, [a]} = get_term(P), + {term, [a, b]} = get_term(P), + {term, [a, b, c]} = get_term(P), + {term, [1]} = get_term(P), + {term, [[]]} = get_term(P), + {term, [[], []]} = get_term(P), + {term, [[], a, b, c]} = get_term(P), + {term, [[], a, [], b, c]} = get_term(P), + {term, [[], a, '', b, c]} = get_term(P), + {term, [[x, 2], [y, 3], [z, 4]]}= get_term(P), + {term, [{a,b},{c,d}]} = get_term(P), + %% {term, [{name, 'Madonna'}, {age, 21}, {data, [{addr, "E-street", 42}]}]} = get_term(P), + + {term, [{pi, F1}, {'cos(70)', F2}]} = get_term(P), %% don't match floats directly true= abs(3.1415-F1) < 0.01, true= abs(0.34202-F2) < 0.01, - ?line {term, [[pi, F3], ['cos(70)', F4]]} = get_term(P), + {term, [[pi, F3], ['cos(70)', F4]]} = get_term(P), true= abs(3.1415-F3) < 0.01, true= abs(0.34202-F4) < 0.01, -%% ?line {term, [[pi, 3.1415], [], ["cos(70)", 0.34202]]} = get_term(P), - ?line {term, [-1]} = get_term(P), - ?line {term, "hejsan"} = get_term(P), + %% {term, [[pi, 3.1415], [], ["cos(70)", 0.34202]]} = get_term(P), + {term, [-1]} = get_term(P), + {term, "hejsan"} = get_term(P), - ?line Str1 = lists:duplicate(65535,$A), - ?line Str2 = lists:duplicate(65536,$A), - ?line {term,Str1} = get_term(P), - ?line {term,Str2} = get_term(P), + Str1 = lists:duplicate(65535,$A), + Str2 = lists:duplicate(65536,$A), + {term,Str1} = get_term(P), + {term,Str2} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. -format_wo_ver(suite) -> []; format_wo_ver(Config) when is_list(Config) -> - ?line P = runner:start(?format_wo_ver), + P = runner:start(?format_wo_ver), - ?line {term, [-1, 2, $c, {a, "b"}, {c, 10}]} = get_term(P), + {term, [-1, 2, $c, {a, "b"}, {c, 10}]} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. diff --git a/lib/erl_interface/test/ei_print_SUITE.erl b/lib/erl_interface/test/ei_print_SUITE.erl index 6cb670f578..6d5c341eae 100644 --- a/lib/erl_interface/test/ei_print_SUITE.erl +++ b/lib/erl_interface/test/ei_print_SUITE.erl @@ -24,141 +24,117 @@ -include_lib("common_test/include/ct.hrl"). -include("ei_print_SUITE_data/ei_print_test_cases.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - atoms/1, tuples/1, lists/1, strings/1]). +-export([all/0, suite/0, + atoms/1, tuples/1, lists/1, strings/1]). -import(runner, [get_term/1]). %% This test suite test the ei_print() function. %% It uses the port program "ei_format_test". -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [atoms, tuples, lists, strings]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %% Tests formatting various atoms. -atoms(suite) -> []; atoms(Config) when is_list(Config) -> - ?line P = runner:start(?atoms), - - ?line {term, "''"} = get_term(P), - ?line {term, "a"} = get_term(P), - ?line {term, "'A'"} = get_term(P), - ?line {term, "abc"} = get_term(P), - ?line {term, "'Abc'"} = get_term(P), - ?line {term, "ab@c"} = get_term(P), - ?line {term, "'The rain in Spain stays mainly in the plains'"} = - get_term(P), - - ?line {term, "a"} = get_term(P), - ?line {term, "ab"} = get_term(P), - ?line {term, "abc"} = get_term(P), - ?line {term, "ab@c"} = get_term(P), - ?line {term, "abcdefghijklmnopq"} = get_term(P), - - ?line {term, "''"} = get_term(P), - ?line {term, "a"} = get_term(P), - ?line {term, "'A'"} = get_term(P), - ?line {term, "abc"} = get_term(P), - ?line {term, "'Abc'"} = get_term(P), - ?line {term, "ab@c"} = get_term(P), - ?line {term, "'The rain in Spain stays mainly in the plains'"} = - get_term(P), - - ?line {term, "a"} = get_term(P), - ?line {term, "ab"} = get_term(P), - ?line {term, "abc"} = get_term(P), - ?line {term, "ab@c"} = get_term(P), - ?line {term, "' abcdefghijklmnopq '"} = get_term(P), - - ?line runner:recv_eot(P), + P = runner:start(?atoms), + + {term, "''"} = get_term(P), + {term, "a"} = get_term(P), + {term, "'A'"} = get_term(P), + {term, "abc"} = get_term(P), + {term, "'Abc'"} = get_term(P), + {term, "ab@c"} = get_term(P), + {term, "'The rain in Spain stays mainly in the plains'"} = get_term(P), + + {term, "a"} = get_term(P), + {term, "ab"} = get_term(P), + {term, "abc"} = get_term(P), + {term, "ab@c"} = get_term(P), + {term, "abcdefghijklmnopq"} = get_term(P), + + {term, "''"} = get_term(P), + {term, "a"} = get_term(P), + {term, "'A'"} = get_term(P), + {term, "abc"} = get_term(P), + {term, "'Abc'"} = get_term(P), + {term, "ab@c"} = get_term(P), + {term, "'The rain in Spain stays mainly in the plains'"} = get_term(P), + + {term, "a"} = get_term(P), + {term, "ab"} = get_term(P), + {term, "abc"} = get_term(P), + {term, "ab@c"} = get_term(P), + {term, "' abcdefghijklmnopq '"} = get_term(P), + + runner:recv_eot(P), ok. %% Tests formatting various tuples -tuples(suite) -> []; tuples(Config) when is_list(Config) -> - ?line P = runner:start(?tuples), - - ?line {term, "{}"} = get_term(P), - ?line {term, "{a}"} = get_term(P), - ?line {term, "{a, b}"} = get_term(P), - ?line {term, "{a, b, c}"} = get_term(P), - ?line {term, "{1}"} = get_term(P), - ?line {term, "{[]}"} = get_term(P), - ?line {term, "{[], []}"} = get_term(P), - ?line {term, "{[], a, b, c}"} = get_term(P), - ?line {term, "{[], a, [], b, c}"} = get_term(P), - ?line {term, "{[], a, '', b, c}"} = get_term(P), - - ?line runner:recv_eot(P), + P = runner:start(?tuples), + + {term, "{}"} = get_term(P), + {term, "{a}"} = get_term(P), + {term, "{a, b}"} = get_term(P), + {term, "{a, b, c}"} = get_term(P), + {term, "{1}"} = get_term(P), + {term, "{[]}"} = get_term(P), + {term, "{[], []}"} = get_term(P), + {term, "{[], a, b, c}"} = get_term(P), + {term, "{[], a, [], b, c}"} = get_term(P), + {term, "{[], a, '', b, c}"} = get_term(P), + + runner:recv_eot(P), ok. %% Tests formatting various lists -lists(suite) -> []; lists(Config) when is_list(Config) -> - ?line P = runner:start(?lists), - - ?line {term, "[]"} = get_term(P), - ?line {term, "[a]"} = get_term(P), - ?line {term, "[a, b]"} = get_term(P), - ?line {term, "[a, b, c]"} = get_term(P), - ?line {term, "[1]"} = get_term(P), - ?line {term, "[[]]"} = get_term(P), - ?line {term, "[[], []]"} = get_term(P), - ?line {term, "[[], a, b, c]"} = get_term(P), - ?line {term, "[[], a, [], b, c]"} = get_term(P), - ?line {term, "[[], a, '', b, c]"} = get_term(P), - ?line {term, "[[x, 2], [y, 3], [z, 4]]"}= get_term(P), - -%% ?line {term, "[{name, 'Madonna'}, {age, 21}, {data, [{addr, "E-street", 42}]}]"} = -%% get_term(P), + P = runner:start(?lists), + + {term, "[]"} = get_term(P), + {term, "[a]"} = get_term(P), + {term, "[a, b]"} = get_term(P), + {term, "[a, b, c]"} = get_term(P), + {term, "[1]"} = get_term(P), + {term, "[[]]"} = get_term(P), + {term, "[[], []]"} = get_term(P), + {term, "[[], a, b, c]"} = get_term(P), + {term, "[[], a, [], b, c]"} = get_term(P), + {term, "[[], a, '', b, c]"} = get_term(P), + {term, "[[x, 2], [y, 3], [z, 4]]"}= get_term(P), + + %% {term, "[{name, 'Madonna'}, {age, 21}, {data, [{addr, "E-street", 42}]}]"} = get_term(P), %% maybe regexp instead? - ?line {term, "[{pi, 3.141500}, {'cos(70)', 0.342020}]"} = get_term(P), - ?line {term, "[[pi, 3.141500], ['cos(70)', 0.342020]]"} = get_term(P), + {term, "[{pi, 3.141500}, {'cos(70)', 0.342020}]"} = get_term(P), + {term, "[[pi, 3.141500], ['cos(70)', 0.342020]]"} = get_term(P), - ?line {term, "[-1]"} = get_term(P), + {term, "[-1]"} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. -strings(suite) -> []; strings(Config) when is_list(Config) -> - ?line P = runner:start(?strings), - - ?line {term, "\"\\n\""} = get_term(P), - ?line {term, "\"\\r\\n\""} = get_term(P), - ?line {term, "\"a\""} = get_term(P), - ?line {term, "\"A\""} = get_term(P), - ?line {term, "\"0\""} = get_term(P), - ?line {term, "\"9\""} = get_term(P), - ?line {term, "\"The rain in Spain stays mainly in the plains\""} = get_term(P), - ?line {term, "\" abcdefghijklmnopq \""} = get_term(P), - - ?line runner:recv_eot(P), + P = runner:start(?strings), + + {term, "\"\\n\""} = get_term(P), + {term, "\"\\r\\n\""} = get_term(P), + {term, "\"a\""} = get_term(P), + {term, "\"A\""} = get_term(P), + {term, "\"0\""} = get_term(P), + {term, "\"9\""} = get_term(P), + {term, "\"The rain in Spain stays mainly in the plains\""} = get_term(P), + {term, "\" abcdefghijklmnopq \""} = get_term(P), + + runner:recv_eot(P), ok. - diff --git a/lib/erl_interface/test/ei_tmo_SUITE.erl b/lib/erl_interface/test/ei_tmo_SUITE.erl index e170b81b16..003fe20594 100644 --- a/lib/erl_interface/test/ei_tmo_SUITE.erl +++ b/lib/erl_interface/test/ei_tmo_SUITE.erl @@ -25,333 +25,300 @@ -include_lib("kernel/include/inet.hrl"). -include("ei_tmo_SUITE_data/ei_tmo_test_cases.hrl"). --define(dummy_host,test01). +-export([all/0, suite/0, + init_per_testcase/2, end_per_testcase/2, + framework_check/1, ei_accept_tmo/1, ei_connect_tmo/1, ei_send_tmo/1, + ei_connect_tmo/0, + ei_recv_tmo/1]). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2, end_per_testcase/2, - framework_check/1, ei_accept_tmo/1, ei_connect_tmo/1, ei_send_tmo/1, - ei_recv_tmo/1]). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> [framework_check, ei_accept_tmo, ei_connect_tmo, ei_send_tmo, ei_recv_tmo]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?t:minutes(1)), % test if platform is vxworks_simso - ?line {_,Host} = split(node()), + {_,Host} = split(node()), Bool = case atom_to_list(Host) of - [$v,$x,$s,$i,$m | _] -> true; - _ -> false - end, - [{vxsim,Bool},{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), + [$v,$x,$s,$i,$m | _] -> true; + _ -> false + end, + [{vxsim,Bool}|Config]. + +end_per_testcase(_Case, _Config) -> ok. -framework_check(doc) -> - ["Check the framework."]; -framework_check(suite) -> - []; +%% Check the framework. framework_check(Config) when is_list(Config) -> %%dbg:tracer(), %%dbg:p(self()), - ?line P = runner:start(?framework_check), - ?line runner:send_term(P,{hello,world}), - ?line {term, {hello,world}} = runner:get_term(P), - ?line runner:recv_eot(P), + P = runner:start(?framework_check), + runner:send_term(P,{hello,world}), + {term, {hello,world}} = runner:get_term(P), + runner:recv_eot(P), ok. -ei_recv_tmo(doc) -> - ["Check recv with timeouts."]; -ei_recv_tmo(suite) -> - []; +%% Check recv with timeouts. ei_recv_tmo(Config) when is_list(Config) -> - ?line do_one_recv(Config,c_node_recv_tmo_1), - ?line do_one_recv_failure(Config,c_node_recv_tmo_2), + do_one_recv(Config,c_node_recv_tmo_1), + do_one_recv_failure(Config,c_node_recv_tmo_2), ok. do_one_recv(Config,CNode) -> - ?line {_,Host} = split(node()), - ?line P1 = runner:start(?recv_tmo), - ?line runner:send_term(P1,{CNode, - erlang:get_cookie(), - node()}), - ?line {term, X} = runner:get_term(P1, 10000), - ?line true = is_integer(X), - ?line CNode1 = join(CNode,Host), - ?line Term1 = {hej,[hopp,{i,[lingon,"skogen"]}]}, - ?line {test,CNode1} ! Term1, - ?line {term, Term1} = runner:get_term(P1, 10000), - ?line runner:recv_eot(P1). - + {_,Host} = split(node()), + P1 = runner:start(?recv_tmo), + runner:send_term(P1,{CNode, + erlang:get_cookie(), + node()}), + {term, X} = runner:get_term(P1, 10000), + true = is_integer(X), + CNode1 = join(CNode,Host), + Term1 = {hej,[hopp,{i,[lingon,"skogen"]}]}, + {test,CNode1} ! Term1, + {term, Term1} = runner:get_term(P1, 10000), + runner:recv_eot(P1). + do_one_recv_failure(Config,CNode) -> - ?line P1 = runner:start(?recv_tmo), - ?line runner:send_term(P1,{CNode, - erlang:get_cookie(), - node()}), - ?line {term, X} = runner:get_term(P1, 10000), - ?line true = is_integer(X), - ?line {term, {Ret,ETimedout,ETimedout}} = runner:get_term(P1, 10000), - ?line true = (Ret < 0), - ?line runner:recv_eot(P1). - - -ei_send_tmo(doc) -> - ["Check send with timeouts."]; -ei_send_tmo(suite) -> - []; + P1 = runner:start(?recv_tmo), + runner:send_term(P1,{CNode, + erlang:get_cookie(), + node()}), + {term, X} = runner:get_term(P1, 10000), + true = is_integer(X), + {term, {Ret,ETimedout,ETimedout}} = runner:get_term(P1, 10000), + true = (Ret < 0), + runner:recv_eot(P1). + + +%% Check send with timeouts. ei_send_tmo(Config) when is_list(Config) -> %dbg:tracer(), %dbg:p(self()), - VxSim = ?config(vxsim, Config), - ?line register(ei_send_tmo_1,self()), - ?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), + VxSim = proplists:get_value(vxsim, Config), + register(ei_send_tmo_1,self()), + do_one_send(Config,self(),c_node_send_tmo_1), + do_one_send(Config,ei_send_tmo_1,c_node_send_tmo_2), + do_one_send_failure(Config,self(),cccc1,c_nod_send_tmo_3,VxSim), + do_one_send_failure(Config,ei_send_tmo_1,cccc2,c_nod_send_tmo_4,VxSim), ok. - + do_one_send(Config,From,CNode) -> - ?line {_,Host} = split(node()), - ?line P1 = runner:start(?send_tmo), - ?line runner:send_term(P1,{CNode, - erlang:get_cookie(), - node()}), - ?line {term, X} = runner:get_term(P1, 10000), - ?line true = is_integer(X), - ?line CNode1 = join(CNode,Host), - ?line Term1 = {hej,[hopp,{i,[lingon,"skogen"]}]}, - ?line {test,CNode1} ! {From,1,Term1}, - ?line ok = receive - Term1 -> - ok - after 2000 -> - error - end, - ?line {term, 0} = runner:get_term(P1, 10000), - ?line runner:recv_eot(P1). + {_,Host} = split(node()), + P1 = runner:start(?send_tmo), + runner:send_term(P1,{CNode, + erlang:get_cookie(), + node()}), + {term, X} = runner:get_term(P1, 10000), + true = is_integer(X), + CNode1 = join(CNode,Host), + Term1 = {hej,[hopp,{i,[lingon,"skogen"]}]}, + {test,CNode1} ! {From,1,Term1}, + ok = receive + Term1 -> + ok + after 2000 -> + error + end, + {term, 0} = runner:get_term(P1, 10000), + runner:recv_eot(P1). do_one_send_failure(Config,From,FakeName,CName,VxSim) -> - ?line {_,Host} = split(node()), - ?line OurName = join(FakeName,Host), - ?line Node = join(CName,Host), - ?line LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of - {ok, Socket} -> - ?line Socket; - Else -> - ?line exit(Else) - end, - ?line EpmdSocket = register(OurName, LSocket, 1, 5), - ?line P3 = runner:start(?send_tmo), - ?line Cookie = kaksmula_som_ingen_bryr_sig_om, - ?line runner:send_term(P3,{CName, - Cookie, - OurName}), - ?line SocketB = case gen_tcp:accept(LSocket) of - {ok, Socket1} -> - ?line Socket1; - Else2 -> - ?line exit(Else2) - end, - ?line {hidden,Node,5} = recv_name(SocketB), % See 1) - ?line send_status(SocketB, ok), - ?line MyChallengeB = gen_challenge(), - ?line send_challenge(SocketB, OurName, MyChallengeB, 5), - ?line HisChallengeB = recv_challenge_reply( - SocketB, - MyChallengeB, - Cookie), - ?line DigestB = gen_digest(HisChallengeB,Cookie), - ?line send_challenge_ack(SocketB, DigestB), - ?line inet:setopts(SocketB, [{active, false}, - {packet, 4}]), - ?line {term, X} = runner:get_term(P3, 10000), - ?line true = is_integer(X), - ?line Message = [112,term_to_binary({6,self(),'',test}), - term_to_binary({From,10000, - {app,["lapp",{sa,["att",du,{slapp, - sitta}]}]}})], - ?line gen_tcp:send(SocketB,Message), - - %% At this point the test program starts sending messages (max 10000). Since + {_,Host} = split(node()), + OurName = join(FakeName,Host), + Node = join(CName,Host), + LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of + {ok, Socket} -> + Socket; + Else -> + exit(Else) + end, + EpmdSocket = register(OurName, LSocket, 1, 5), + P3 = runner:start(?send_tmo), + Cookie = kaksmula_som_ingen_bryr_sig_om, + runner:send_term(P3,{CName, + Cookie, + OurName}), + SocketB = case gen_tcp:accept(LSocket) of + {ok, Socket1} -> + Socket1; + Else2 -> + exit(Else2) + end, + {hidden,Node,5} = recv_name(SocketB), % See 1) + send_status(SocketB, ok), + MyChallengeB = gen_challenge(), + send_challenge(SocketB, OurName, MyChallengeB, 5), + HisChallengeB = recv_challenge_reply(SocketB, + MyChallengeB, + Cookie), + DigestB = gen_digest(HisChallengeB,Cookie), + send_challenge_ack(SocketB, DigestB), + inet:setopts(SocketB, [{active, false}, + {packet, 4}]), + {term, X} = runner:get_term(P3, 10000), + true = is_integer(X), + Message = [112,term_to_binary({6,self(),'',test}), + term_to_binary({From,50000, + {app,["lapp",{sa,["att",du,{slapp, + sitta}]}]}})], + gen_tcp:send(SocketB,Message), + + %% At this point the test program starts sending messages (max 50000). Since %% we're not receiving, eventually the send buffer fills up. Then no more %% sending is possible and select() times out. The number of messages sent %% before this happens is returned in Iters. The timeout value for get_term/2 %% must be large enough so there's time for the select() to time out and %% the test program to return the error tuple (below). - Res0 = - if VxSim == false -> - ?line {term,{Res,ETO,Iters,ETO}} = runner:get_term(P3, 20000), - Res; - true -> % relax the test for vxsim - ?line case runner:get_term(P3, 20000) of - {term,{Res,ETO,Iters,ETO}} -> - Res; - {term,{Res,_,Iters,ETO}} -> % EIO? - Res - end - end, - ?line runner:recv_eot(P3), - ?line true = ((Res0 < 0) and (Iters > 0)), - ?line gen_tcp:close(SocketB), - ?line gen_tcp:close(EpmdSocket), + + Res0 = if VxSim == false -> + {term,{Res,ETO,Iters,ETO}} = runner:get_term(P3, 20000), + Res; + true -> % relax the test for vxsim + case runner:get_term(P3, 20000) of + {term,{Res,ETO,Iters,ETO}} -> + Res; + {term,{Res,_,Iters,_ETO}} -> % EIO? + Res + end + end, + runner:recv_eot(P3), + true = ((Res0 < 0) and (Iters > 0)), + gen_tcp:close(SocketB), + gen_tcp:close(EpmdSocket), ok. - -ei_connect_tmo(doc) -> - ["Check accept with timeouts."]; -ei_connect_tmo(suite) -> - []; + +%% Check accept with timeouts. +ei_connect_tmo() -> [{require, test_host_not_reachable}]. + ei_connect_tmo(Config) when is_list(Config) -> %dbg:tracer(), %dbg:p(self()), - VxSim = ?config(vxsim, Config), + VxSim = proplists:get_value(vxsim, Config), DummyNode = make_and_check_dummy(), - ?line P = runner:start(?connect_tmo), - ?line runner:send_term(P,{c_nod_connect_tmo_1, - kaksmula_som_ingen_bryr_sig_om, - DummyNode}), + P = runner:start(?connect_tmo), + runner:send_term(P,{c_nod_connect_tmo_1, + kaksmula_som_ingen_bryr_sig_om, + DummyNode}), ETimedout = - if VxSim == false -> - ?line {term,{-3,ETO,ETO}} = runner:get_term(P, 10000), - ?line ETO; - true -> % relax the test for vxsim - ?line case runner:get_term(P, 10000) of - {term,{-3,ETO,ETO}} -> - ?line ETO; - {term,{-1,_,ETO}} -> % EHOSTUNREACH = ok - ?line ETO - end - end, - ?line runner:recv_eot(P), - ?line P2 = runner:start(?connect_tmo), - ?line runner:send_term(P2,{c_nod_connect_tmo_2, - erlang:get_cookie(), - node()}), - ?line {term, X} = runner:get_term(P2, 10000), - ?line runner:recv_eot(P2), - ?line true = is_integer(X), + if VxSim == false -> + {term,{-3,ETO,ETO}} = runner:get_term(P, 10000), + ETO; + true -> % relax the test for vxsim + case runner:get_term(P, 10000) of + {term,{-3,ETO,ETO}} -> + ETO; + {term,{-1,_,ETO}} -> % EHOSTUNREACH = ok + ETO + end + end, + runner:recv_eot(P), + P2 = runner:start(?connect_tmo), + runner:send_term(P2,{c_nod_connect_tmo_2, + erlang:get_cookie(), + node()}), + {term, X} = runner:get_term(P2, 10000), + runner:recv_eot(P2), + true = is_integer(X), %% Aborted handshake test... - ?line {_,Host} = split(node()), - ?line OurName = join(cccc,Host), - ?line Node = join(c_nod_connect_tmo_3,Host), - ?line LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of - {ok, Socket} -> - ?line Socket; - Else -> - ?line exit(Else) - end, - ?line EpmdSocket = register(OurName, LSocket, 1, 5), - ?line P3 = runner:start(?connect_tmo), - ?line Cookie = kaksmula_som_ingen_bryr_sig_om, - ?line runner:send_term(P3,{c_nod_connect_tmo_3, - Cookie, - OurName}), - ?line SocketB = case gen_tcp:accept(LSocket) of - {ok, Socket1} -> - ?line Socket1; - Else2 -> - ?line exit(Else2) - end, - ?line {hidden,Node,5} = recv_name(SocketB), % See 1) - ?line send_status(SocketB, ok), - ?line MyChallengeB = gen_challenge(), - ?line send_challenge(SocketB, OurName, MyChallengeB, 5), - ?line HisChallengeB = recv_challenge_reply( - SocketB, - MyChallengeB, - Cookie), - ?line {term,{-1,ETimedout,ETimedout}} = runner:get_term(P3, 10000), - ?line runner:recv_eot(P3), - ?line gen_tcp:close(SocketB), - ?line gen_tcp:close(EpmdSocket), + {_,Host} = split(node()), + OurName = join(cccc,Host), + Node = join(c_nod_connect_tmo_3,Host), + LSocket = case gen_tcp:listen(0, [{active, false}, {packet,2}]) of + {ok, Socket} -> + Socket; + Else -> + exit(Else) + end, + EpmdSocket = register(OurName, LSocket, 1, 5), + P3 = runner:start(?connect_tmo), + Cookie = kaksmula_som_ingen_bryr_sig_om, + runner:send_term(P3,{c_nod_connect_tmo_3, + Cookie, + OurName}), + SocketB = case gen_tcp:accept(LSocket) of + {ok, Socket1} -> + Socket1; + Else2 -> + exit(Else2) + end, + {hidden,Node,5} = recv_name(SocketB), % See 1) + send_status(SocketB, ok), + MyChallengeB = gen_challenge(), + send_challenge(SocketB, OurName, MyChallengeB, 5), + _HisChallengeB = recv_challenge_reply(SocketB, + MyChallengeB, + Cookie), + {term,{-1,ETimedout,ETimedout}} = runner:get_term(P3, 10000), + runner:recv_eot(P3), + gen_tcp:close(SocketB), + gen_tcp:close(EpmdSocket), ok. - -ei_accept_tmo(doc) -> - ["Check accept with timeouts."]; -ei_accept_tmo(suite) -> - []; + +%% Check accept with timeouts. ei_accept_tmo(Config) when is_list(Config) -> %%dbg:tracer(), %%dbg:p(self()), - ?line P = runner:start(?accept_tmo), - ?line runner:send_term(P,{c_nod_som_ingen_kontaktar_1, - kaksmula_som_ingen_bryr_sig_om}), - ?line {term,{-1,ETimedout,ETimedout}} = runner:get_term(P, 10000), - ?line runner:recv_eot(P), - ?line P2 = runner:start(?accept_tmo), - ?line runner:send_term(P2,{c_nod_som_vi_kontaktar_1, - erlang:get_cookie()}), - ?line receive after 1000 -> ok end, - ?line CNode1 = make_node(c_nod_som_vi_kontaktar_1), - ?line {ignored,CNode1} ! tjenare, - ?line {term, X} = runner:get_term(P2, 10000), - ?line runner:recv_eot(P2), - ?line true = is_integer(X), - ?line P3 = runner:start(?accept_tmo), - ?line runner:send_term(P3,{c_nod_som_vi_kontaktar_2, - erlang:get_cookie()}), - ?line receive after 1000 -> ok end, - ?line CNode2 = make_node(c_nod_som_vi_kontaktar_2), - ?line {NA,NB} = split(CNode2), - ?line {_,Host} = split(node()), - ?line OurName = join(ccc,Host), - ?line {port,PortNo,_} = erl_epmd:port_please(NA,NB), - ?line {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, - [{active,false}, - {packet,2}]), - ?line send_name(SocketA,OurName,5), - ?line ok = recv_status(SocketA), - ?line {hidden,Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) - ?line OurChallengeA = gen_challenge(), - ?line OurDigestA = gen_digest(HisChallengeA, erlang:get_cookie()), + P = runner:start(?accept_tmo), + runner:send_term(P,{c_nod_som_ingen_kontaktar_1, + kaksmula_som_ingen_bryr_sig_om}), + {term,{-1,ETimedout,ETimedout}} = runner:get_term(P, 10000), + runner:recv_eot(P), + P2 = runner:start(?accept_tmo), + runner:send_term(P2,{c_nod_som_vi_kontaktar_1, + erlang:get_cookie()}), + receive after 1000 -> ok end, + CNode1 = make_node(c_nod_som_vi_kontaktar_1), + {ignored,CNode1} ! tjenare, + {term, X} = runner:get_term(P2, 10000), + runner:recv_eot(P2), + true = is_integer(X), + P3 = runner:start(?accept_tmo), + runner:send_term(P3,{c_nod_som_vi_kontaktar_2, + erlang:get_cookie()}), + receive after 1000 -> ok end, + CNode2 = make_node(c_nod_som_vi_kontaktar_2), + {NA,NB} = split(CNode2), + {_,Host} = split(node()), + OurName = join(ccc,Host), + {port,PortNo,_} = erl_epmd:port_please(NA,NB), + {ok, SocketA} = gen_tcp:connect(atom_to_list(NB),PortNo, + [{active,false}, + {packet,2}]), + send_name(SocketA,OurName,5), + ok = recv_status(SocketA), + {hidden,_Node,5,HisChallengeA} = recv_challenge(SocketA), % See 1) + _OurChallengeA = gen_challenge(), + _OurDigestA = gen_digest(HisChallengeA, erlang:get_cookie()), %% Dont do the last two steps of the connection setup... %% send_challenge_reply(SocketA, OurChallengeA, OurDigestA), %% ok = recv_challenge_ack(SocketA, OurChallengeA, erlang:get_cookie()), - ?line {term, {-1,ETimedout,ETimedout}} = runner:get_term(P3, 10000), - ?line runner:recv_eot(P3), - ?line gen_tcp:close(SocketA), + {term, {-1,ETimedout,ETimedout}} = runner:get_term(P3, 10000), + runner:recv_eot(P3), + gen_tcp:close(SocketA), ok. make_node(X) -> list_to_atom(atom_to_list(X) ++ "@" ++ - hd(tl(string:tokens(atom_to_list(node()),"@")))). + hd(tl(string:tokens(atom_to_list(node()),"@")))). make_and_check_dummy() -> % First check that the host has an ip and is *not* reachable - ?line case gen_tcp:connect(?dummy_host,23,[{active,false}],5000) of - {error,timeout} -> ok; - {error,ehostunreach} -> ok - end, + HostNotReachable = ct:get_config(test_host_not_reachable), + case gen_tcp:connect(HostNotReachable, 23, [{active,false}],5000) of + {error,timeout} -> ok; + {error,ehostunreach} -> ok + end, - list_to_atom("dummy@"++atom_to_list(?dummy_host)). + list_to_atom("dummy@"++HostNotReachable). %% %% Stolen from the erl_distribution_wb_test in kernel @@ -359,12 +326,12 @@ make_and_check_dummy() -> %% -define(to_port(Socket, Data), - case inet_tcp:send(Socket, Data) of - {error, closed} -> - self() ! {tcp_closed, Socket}, - {error, closed}; - R -> - R + case inet_tcp:send(Socket, Data) of + {error, closed} -> + self() ! {tcp_closed, Socket}, + {error, closed}; + R -> + R end). -define(DFLAG_PUBLISHED,1). @@ -382,8 +349,8 @@ make_and_check_dummy() -> -define(int16(X), [((X) bsr 8) band 16#ff, (X) band 16#ff]). -define(int32(X), - [((X) bsr 24) band 16#ff, ((X) bsr 16) band 16#ff, - ((X) bsr 8) band 16#ff, (X) band 16#ff]). + [((X) bsr 24) band 16#ff, ((X) bsr 16) band 16#ff, + ((X) bsr 8) band 16#ff, (X) band 16#ff]). -define(i16(X1,X0), (?u16(X1,X0) - @@ -406,9 +373,9 @@ make_and_check_dummy() -> %% This is no proper random number, but that is not really important in %% this test gen_challenge() -> - {_,_,N} = erlang:now(), + {_,_,N} = os:timestamp(), N. - + %% Generate a message digest from Challenge number and Cookie gen_digest(Challenge, Cookie) when is_integer(Challenge), is_atom(Cookie) -> C0 = erlang:md5_init(), @@ -423,95 +390,93 @@ gen_digest(Challenge, Cookie) when is_integer(Challenge), is_atom(Cookie) -> send_status(Socket, Stat) -> case gen_tcp:send(Socket, [$s | atom_to_list(Stat)]) of - {error, _} -> - ?shutdown(could_not_send_status); - _ -> - true + {error, _} -> ?shutdown(could_not_send_status); + _ -> true end. recv_status(Socket) -> case gen_tcp:recv(Socket, 0) of - {ok, [$s|StrStat]} -> - list_to_atom(StrStat); - Bad -> - exit(Bad) + {ok, [$s|StrStat]} -> + list_to_atom(StrStat); + Bad -> + exit(Bad) end. send_challenge(Socket, Node, Challenge, Version) -> send_challenge(Socket, Node, Challenge, Version, ?COMPULSORY_DFLAGS). send_challenge(Socket, Node, Challenge, Version, Flags) -> - {ok, {{Ip1,Ip2,Ip3,Ip4}, _}} = inet:sockname(Socket), + {ok, {{_Ip1,_Ip2,_Ip3,_Ip4}, _}} = inet:sockname(Socket), ?to_port(Socket, [$n,?int16(Version),?int32(Flags), - ?int32(Challenge), atom_to_list(Node)]). + ?int32(Challenge), atom_to_list(Node)]). recv_challenge(Socket) -> case gen_tcp:recv(Socket, 0) of - {ok,[$n,V1,V0,Fl1,Fl2,Fl3,Fl4,CA3,CA2,CA1,CA0 | Ns]} -> - Flags = ?u32(Fl1,Fl2,Fl3,Fl4), - Type = case Flags band ?DFLAG_PUBLISHED of - 0 -> - hidden; - _ -> - normal - end, - Node =list_to_atom(Ns), - Version = ?u16(V1,V0), - Challenge = ?u32(CA3,CA2,CA1,CA0), - {Type,Node,Version,Challenge}; - _ -> - ?shutdown(no_node) + {ok,[$n,V1,V0,Fl1,Fl2,Fl3,Fl4,CA3,CA2,CA1,CA0 | Ns]} -> + Flags = ?u32(Fl1,Fl2,Fl3,Fl4), + Type = case Flags band ?DFLAG_PUBLISHED of + 0 -> + hidden; + _ -> + normal + end, + Node =list_to_atom(Ns), + Version = ?u16(V1,V0), + Challenge = ?u32(CA3,CA2,CA1,CA0), + {Type,Node,Version,Challenge}; + _ -> + ?shutdown(no_node) end. -send_challenge_reply(Socket, Challenge, Digest) -> - ?to_port(Socket, [$r,?int32(Challenge),Digest]). +%send_challenge_reply(Socket, Challenge, Digest) -> +% ?to_port(Socket, [$r,?int32(Challenge),Digest]). recv_challenge_reply(Socket, ChallengeA, Cookie) -> case gen_tcp:recv(Socket, 0) of - {ok,[$r,CB3,CB2,CB1,CB0 | SumB]} when length(SumB) == 16 -> - SumA = gen_digest(ChallengeA, Cookie), - ChallengeB = ?u32(CB3,CB2,CB1,CB0), - if SumB == SumA -> - ChallengeB; - true -> - ?shutdown(bad_challenge_reply) - end; - _ -> - ?shutdown(no_node) + {ok,[$r,CB3,CB2,CB1,CB0 | SumB]} when length(SumB) == 16 -> + SumA = gen_digest(ChallengeA, Cookie), + ChallengeB = ?u32(CB3,CB2,CB1,CB0), + if SumB == SumA -> + ChallengeB; + true -> + ?shutdown(bad_challenge_reply) + end; + _ -> + ?shutdown(no_node) end. send_challenge_ack(Socket, Digest) -> ?to_port(Socket, [$a,Digest]). -recv_challenge_ack(Socket, ChallengeB, CookieA) -> - case gen_tcp:recv(Socket, 0) of - {ok,[$a | SumB]} when length(SumB) == 16 -> - SumA = gen_digest(ChallengeB, CookieA), - if SumB == SumA -> - ok; - true -> - ?shutdown(bad_challenge_ack) - end; - _ -> - ?shutdown(bad_challenge_ack) - end. +%recv_challenge_ack(Socket, ChallengeB, CookieA) -> +% case gen_tcp:recv(Socket, 0) of +% {ok,[$a | SumB]} when length(SumB) == 16 -> +% SumA = gen_digest(ChallengeB, CookieA), +% if SumB == SumA -> +% ok; +% true -> +% ?shutdown(bad_challenge_ack) +% end; +% _ -> +% ?shutdown(bad_challenge_ack) +% end. send_name(Socket, MyNode0, Version) -> send_name(Socket, MyNode0, Version, ?COMPULSORY_DFLAGS). send_name(Socket, MyNode0, Version, Flags) -> MyNode = atom_to_list(MyNode0), ?to_port(Socket, [$n,?int16(Version),?int32(Flags)] ++ - MyNode). + MyNode). %% %% recv_name is common for both old and new handshake. %% recv_name(Socket) -> case gen_tcp:recv(Socket, 0) of - {ok,Data} -> - get_name(Data); - Res -> - ?shutdown({no_node,Res}) + {ok,Data} -> + get_name(Data); + Res -> + ?shutdown({no_node,Res}) end. get_name([$m,VersionA,VersionB,_Ip1,_Ip2,_Ip3,_Ip4|OtherNode]) -> @@ -520,11 +485,9 @@ get_name([$h,VersionA,VersionB,_Ip1,_Ip2,_Ip3,_Ip4|OtherNode]) -> {hidden, list_to_atom(OtherNode), ?u16(VersionA,VersionB)}; get_name([$n,VersionA, VersionB, Flag1, Flag2, Flag3, Flag4 | OtherNode]) -> Type = case ?u32(Flag1, Flag2, Flag3, Flag4) band ?DFLAG_PUBLISHED of - 0 -> - hidden; - _ -> - normal - end, + 0 -> hidden; + _ -> normal + end, {Type, list_to_atom(OtherNode), ?u16(VersionA,VersionB)}; get_name(Data) -> @@ -533,74 +496,73 @@ get_name(Data) -> %% %% tell_name is for old handshake %% -tell_name(Socket, MyNode0, Version) -> - MyNode = atom_to_list(MyNode0), - {ok, {{Ip1,Ip2,Ip3,Ip4}, _}} = inet:sockname(Socket), - ?to_port(Socket, [$h,?int16(Version),Ip1,Ip2,Ip3,Ip4] ++ - MyNode). +%tell_name(Socket, MyNode0, Version) -> +% MyNode = atom_to_list(MyNode0), +% {ok, {{Ip1,Ip2,Ip3,Ip4}, _}} = inet:sockname(Socket), +% ?to_port(Socket, [$h,?int16(Version),Ip1,Ip2,Ip3,Ip4] ++ MyNode). %% %% The communication with EPMD follows %% do_register_node(NodeName, TcpPort, VLow, VHigh) -> case gen_tcp:connect({127,0,0,1}, get_epmd_port(), []) of - {ok, Socket} -> - {N0,_} = split(NodeName), - Name = atom_to_list(N0), - Extra = "", - Elen = length(Extra), - Len = 1+2+1+1+2+2+2+length(Name)+2+Elen, - gen_tcp:send(Socket, [?int16(Len), $x, - ?int16(TcpPort), - $M, - 0, - ?int16(VHigh), - ?int16(VLow), - ?int16(length(Name)), - Name, - ?int16(Elen), - Extra]), - case wait_for_reg_reply(Socket, []) of - {error, epmd_close} -> - exit(epmd_broken); - Other -> - Other - end; - Error -> - Error + {ok, Socket} -> + {N0,_} = split(NodeName), + Name = atom_to_list(N0), + Extra = "", + Elen = length(Extra), + Len = 1+2+1+1+2+2+2+length(Name)+2+Elen, + gen_tcp:send(Socket, [?int16(Len), $x, + ?int16(TcpPort), + $M, + 0, + ?int16(VHigh), + ?int16(VLow), + ?int16(length(Name)), + Name, + ?int16(Elen), + Extra]), + case wait_for_reg_reply(Socket, []) of + {error, epmd_close} -> + exit(epmd_broken); + Other -> + Other + end; + Error -> + Error end. wait_for_reg_reply(Socket, SoFar) -> receive - {tcp, Socket, Data0} -> - case SoFar ++ Data0 of - [$y, Result, A, B] -> - case Result of - 0 -> - {alive, Socket, ?u16(A, B)}; - _ -> - {error, duplicate_name} - end; - Data when length(Data) < 4 -> - wait_for_reg_reply(Socket, Data); - Garbage -> - {error, {garbage_from_epmd, Garbage}} - end; - {tcp_closed, Socket} -> - {error, epmd_close} + {tcp, Socket, Data0} -> + case SoFar ++ Data0 of + [$y, Result, A, B] -> + case Result of + 0 -> + {alive, Socket, ?u16(A, B)}; + _ -> + {error, duplicate_name} + end; + Data when length(Data) < 4 -> + wait_for_reg_reply(Socket, Data); + Garbage -> + {error, {garbage_from_epmd, Garbage}} + end; + {tcp_closed, Socket} -> + {error, epmd_close} after 10000 -> - gen_tcp:close(Socket), - {error, no_reg_reply_from_epmd} + gen_tcp:close(Socket), + {error, no_reg_reply_from_epmd} end. register(NodeName, ListenSocket, VLow, VHigh) -> {ok,{_,TcpPort}} = inet:sockname(ListenSocket), case do_register_node(NodeName, TcpPort, VLow, VHigh) of - {alive, Socket, Creation} -> - Socket; - Other -> - exit(Other) + {alive, Socket, _Creation} -> + Socket; + Other -> + exit(Other) end. @@ -618,69 +580,10 @@ split(Atom) -> {A,B} = split(atom_to_list(Atom),[]), {list_to_atom(A),list_to_atom(B)}. -%% Build a simple distribution message -build_message(Cookie) -> - [$?,term_to_binary({6,self(),Cookie,rex}),term_to_binary(plupp)]. - -%% Build a distribution message that will make rex answer -build_rex_message(Cookie,OurName) -> - [$?,term_to_binary({6,self(),Cookie,rex}), - term_to_binary({'$gen_cast', - {cast, - rpc, - cast, - [OurName, hello, world, []], - self()} })]. - -%% Receive a distribution message -recv_message(Socket) -> - case gen_tcp:recv(Socket, 0) of - {ok,Data} -> - B0 = list_to_binary(Data), - {_,B1} = erlang:split_binary(B0,1), - Header = erlang:binary_to_term(B1), - Siz = size(term_to_binary(Header)), - {_,B2} = erlang:split_binary(B1,Siz), - Message = case (catch erlang:binary_to_term(B2)) of - {'EXIT', _} -> - could_not_digest_message; - Other -> - Other - end, - {Header, Message}; - Res -> - exit({no_message,Res}) - end. - %% Build a nodename join(Name,Host) -> list_to_atom(atom_to_list(Name) ++ "@" ++ atom_to_list(Host)). -%% start/stop slave. -start_node(Name, Param) -> - ?t:start_node(Name, slave, [{args, Param}]). - -stop_node(Node) -> - ?t:stop_node(Node). - - -get_nodenames(N, T) -> - get_nodenames(N, T, []). - -get_nodenames(0, _, Acc) -> - Acc; -get_nodenames(N, T, Acc) -> - {A, B, C} = now(), - get_nodenames(N-1, T, [list_to_atom(atom_to_list(?MODULE) - ++ "-" - ++ atom_to_list(T) - ++ "-" - ++ integer_to_list(A) - ++ "-" - ++ integer_to_list(B) - ++ "-" - ++ integer_to_list(C)) | Acc]). - get_epmd_port() -> case init:get_argument(epmd_port) of {ok, [[PortStr|_]|_]} when is_list(PortStr) -> diff --git a/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c b/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c index 8410969b11..0079ef8c86 100644 --- a/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c +++ b/lib/erl_interface/test/ei_tmo_SUITE_data/ei_tmo_test.c @@ -512,18 +512,21 @@ TESTCASE(send_tmo) for (i=0;i < iterations; ++i) { res = ei_send_tmo(com_sock, &pid, send_buffer.buff, send_buffer.index, 5000); - DEBUGF(("Sent bindata (%d):\n",res)); + if (res < 0) { + DEBUGF(("Sent bindata failed (%d) after %d iterations:\n", res, i)); + break; + } #ifdef DEBUG + if (i < 10 || (i % 100 == 0)) /* don't flood the log */ { int ndx = 0; int v; + DEBUGF(("%d: Sent bindata (%d): ", i, res)); ei_decode_version(send_buffer.buff,&ndx,&v); ei_print_term(debugfile, send_buffer.buff, &ndx); + DEBUGF(("\n")); } #endif - DEBUGF(("\n")); - if (res < 0) - break; } if (res < 0) { DEBUGF(("ei_send_tmo failure at line %d\n",__LINE__)); diff --git a/lib/erl_interface/test/erl_connect_SUITE.erl b/lib/erl_interface/test/erl_connect_SUITE.erl index e1ae0cfe91..cd73f07b8f 100644 --- a/lib/erl_interface/test/erl_connect_SUITE.erl +++ b/lib/erl_interface/test/erl_connect_SUITE.erl @@ -24,87 +24,64 @@ -include_lib("common_test/include/ct.hrl"). -include("erl_connect_SUITE_data/erl_connect_test_cases.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - init_per_testcase/2,end_per_testcase/2, - erl_send/1,erl_reg_send/1, erl_send_cookie_file/1]). +-export([all/0, suite/0, + erl_send/1, erl_reg_send/1, + erl_send_cookie_file/1]). -import(runner, [get_term/1,send_term/2]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. all() -> [erl_send, erl_reg_send, erl_send_cookie_file]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?t:minutes(0.25)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. erl_send(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line 1 = erl_connect_init(P, 42, erlang:get_cookie(), 0), - ?line {ok,Fd} = erl_connect(P, node()), + P = runner:start(?interpret), + 1 = erl_connect_init(P, 42, erlang:get_cookie(), 0), + {ok,Fd} = erl_connect(P, node()), - ?line ok = erl_send(P, Fd, self(), AMsg={a,message}), - ?line receive AMsg -> ok end, + ok = erl_send(P, Fd, self(), AMsg={a,message}), + receive AMsg -> ok end, - ?line 0 = erl_close_connection(P,Fd), - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + 0 = erl_close_connection(P,Fd), + runner:send_eot(P), + runner:recv_eot(P), ok. erl_send_cookie_file(Config) when is_list(Config) -> case os:type() of - vxworks -> - {skip,"Skipped on VxWorks"}; - _ -> - ?line P = runner:start(?interpret), - ?line 1 = erl_connect_init(P, 42, '', 0), - ?line {ok,Fd} = erl_connect(P, node()), - - ?line ok = erl_send(P, Fd, self(), AMsg={a,message}), - ?line receive AMsg -> ok end, - - ?line 0 = erl_close_connection(P,Fd), - ?line runner:send_eot(P), - ?line runner:recv_eot(P), - ok + vxworks -> + {skip,"Skipped on VxWorks"}; + _ -> + P = runner:start(?interpret), + 1 = erl_connect_init(P, 42, '', 0), + {ok,Fd} = erl_connect(P, node()), + + ok = erl_send(P, Fd, self(), AMsg={a,message}), + receive AMsg -> ok end, + + 0 = erl_close_connection(P,Fd), + runner:send_eot(P), + runner:recv_eot(P), + ok end. erl_reg_send(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line 1 = erl_connect_init(P, 42, erlang:get_cookie(), 0), - ?line {ok,Fd} = erl_connect(P, node()), + P = runner:start(?interpret), + 1 = erl_connect_init(P, 42, erlang:get_cookie(), 0), + {ok,Fd} = erl_connect(P, node()), ARegName = a_strange_registred_name, - ?line register(ARegName, self()), - ?line ok = erl_reg_send(P, Fd, ARegName, AMsg={another,[strange],message}), - ?line receive AMsg -> ok end, + register(ARegName, self()), + ok = erl_reg_send(P, Fd, ARegName, AMsg={another,[strange],message}), + receive AMsg -> ok end, - ?line 0 = erl_close_connection(P,Fd), - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + 0 = erl_close_connection(P,Fd), + runner:send_eot(P), + runner:recv_eot(P), ok. @@ -113,20 +90,20 @@ erl_reg_send(Config) when is_list(Config) -> erl_connect_init(P, Num, Cookie, Creation) -> send_command(P, erl_connect_init, [Num,Cookie,Creation]), case get_term(P) of - {term,Int} when is_integer(Int) -> Int + {term,Int} when is_integer(Int) -> Int end. erl_connect(P, Node) -> send_command(P, erl_connect, [Node]), case get_term(P) of - {term,{Fd,_}} when Fd >= 0 -> {ok,Fd}; - {term,{-1,Errno}} -> {error,Errno} + {term,{Fd,_}} when Fd >= 0 -> {ok,Fd}; + {term,{-1,Errno}} -> {error,Errno} end. erl_close_connection(P, FD) -> send_command(P, erl_close_connection, [FD]), case get_term(P) of - {term,Int} when is_integer(Int) -> Int + {term,Int} when is_integer(Int) -> Int end. erl_send(P, Fd, To, Msg) -> @@ -139,17 +116,12 @@ erl_reg_send(P, Fd, To, Msg) -> get_send_result(P) -> case get_term(P) of - {term,{1,_}} -> ok; - {term,{-1,Errno}} -> {error,Errno}; - {term,{Res,Errno}}-> - io:format("Return value: ~p\nerl_errno: ~p", [Res,Errno]), - ?t:fail(bad_return_value) + {term,{1,_}} -> ok; + {term,{-1,Errno}} -> {error,Errno}; + {term,{Res,Errno}}-> + io:format("Return value: ~p\nerl_errno: ~p", [Res,Errno]), + ct:fail(bad_return_value) end. send_command(P, Name, Args) -> runner:send_term(P, {Name,list_to_tuple(Args)}). - - - - - diff --git a/lib/erl_interface/test/erl_eterm_SUITE.erl b/lib/erl_interface/test/erl_eterm_SUITE.erl index e62b873c3f..0e51a50c19 100644 --- a/lib/erl_interface/test/erl_eterm_SUITE.erl +++ b/lib/erl_interface/test/erl_eterm_SUITE.erl @@ -34,40 +34,39 @@ %%% 5. Miscellanous functions. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - build_terms/1, round_trip_conversion/1, - decode_terms/1, decode_float/1, - t_erl_mk_int/1, t_erl_mk_list/1, - basic_copy/1, - t_erl_cons/1, - t_erl_mk_atom/1, - t_erl_mk_binary/1, - t_erl_mk_empty_list/1, - t_erl_mk_float/1, - t_erl_mk_pid/1, - t_erl_mk_xpid/1, - t_erl_mk_port/1, - t_erl_mk_xport/1, - t_erl_mk_ref/1, - t_erl_mk_long_ref/1, - t_erl_mk_string/1, - t_erl_mk_estring/1, - t_erl_mk_tuple/1, - t_erl_mk_uint/1, - t_erl_mk_var/1, - t_erl_size/1, - t_erl_var_content/1, - t_erl_element/1, - t_erl_length/1, t_erl_hd/1, t_erl_tl/1, - type_checks/1, extractor_macros/1, - t_erl_iolist_length/1, t_erl_iolist_to_binary/1, - t_erl_iolist_to_string/1, - erl_print_term/1, print_string/1, - t_erl_free_compound/1, - high_chaparal/1, - broken_data/1, - cnode_1/1]). +-export([all/0, suite/0, + build_terms/1, round_trip_conversion/1, + decode_terms/1, decode_float/1, + t_erl_mk_int/1, t_erl_mk_list/1, + basic_copy/1, + t_erl_cons/1, + t_erl_mk_atom/1, + t_erl_mk_binary/1, + t_erl_mk_empty_list/1, + t_erl_mk_float/1, + t_erl_mk_pid/1, + t_erl_mk_xpid/1, + t_erl_mk_port/1, + t_erl_mk_xport/1, + t_erl_mk_ref/1, + t_erl_mk_long_ref/1, + t_erl_mk_string/1, + t_erl_mk_estring/1, + t_erl_mk_tuple/1, + t_erl_mk_uint/1, + t_erl_mk_var/1, + t_erl_size/1, + t_erl_var_content/1, + t_erl_element/1, + t_erl_length/1, t_erl_hd/1, t_erl_tl/1, + type_checks/1, extractor_macros/1, + t_erl_iolist_length/1, t_erl_iolist_to_binary/1, + t_erl_iolist_to_string/1, + erl_print_term/1, print_string/1, + t_erl_free_compound/1, + high_chaparal/1, + broken_data/1, + cnode_1/1]). -export([start_cnode/1]). @@ -76,7 +75,8 @@ %% This test suite controls the running of the C language functions %% in eterm_test.c and print_term.c. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [build_terms, round_trip_conversion, decode_terms, @@ -93,22 +93,6 @@ all() -> erl_print_term, print_string, t_erl_free_compound, high_chaparal, broken_data, cnode_1]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %%% @@ -119,82 +103,77 @@ end_per_group(_GroupName, Config) -> %% This test asks the C function to construct all data types in %% a list and verifies that the result is as expected. -build_terms(suite) -> []; build_terms(Config) when is_list(Config) -> - ?line P = runner:start(?build_terms), - ?line {term, Term} = get_term(P), - ?line io:format("Received: ~p", [Term]), - ?line [ARefLN, ARef, APortLN, APort, APidLN, APid, - {element1, 42, 767}, "A string", - 1, -1, 0, 3.0, ABin, 'I am an atom'] = Term, - ?line "A binary" = binary_to_list(ABin), - ?line case ARef of - R when is_reference(R), node(R) == kalle@localhost -> ok - end, - ?line case ARefLN of - R1 when is_reference(R1), node(R1) == abcdefghijabcdefghij@localhost -> ok - end, - ?line case APort of - Port when is_port(Port), node(Port) == kalle@localhost -> ok - end, - ?line case APortLN of - Port1 when is_port(Port1), node(Port1) == abcdefghijabcdefghij@localhost -> ok - end, - ?line case APid of - Pid when is_pid(Pid), node(Pid) == kalle@localhost -> ok - end, - ?line case APidLN of - Pid1 when is_pid(Pid1), node(Pid1) == abcdefghijabcdefghij@localhost -> ok - end, - - ?line runner:recv_eot(P), + P = runner:start(?build_terms), + {term, Term} = get_term(P), + io:format("Received: ~p", [Term]), + [ARefLN, ARef, APortLN, APort, APidLN, APid, + {element1, 42, 767}, "A string", + 1, -1, 0, 3.0, ABin, 'I am an atom'] = Term, + "A binary" = binary_to_list(ABin), + case ARef of + R when is_reference(R), node(R) == kalle@localhost -> ok + end, + case ARefLN of + R1 when is_reference(R1), node(R1) == abcdefghijabcdefghij@localhost -> ok + end, + case APort of + Port when is_port(Port), node(Port) == kalle@localhost -> ok + end, + case APortLN of + Port1 when is_port(Port1), node(Port1) == abcdefghijabcdefghij@localhost -> ok + end, + case APid of + Pid when is_pid(Pid), node(Pid) == kalle@localhost -> ok + end, + case APidLN of + Pid1 when is_pid(Pid1), node(Pid1) == abcdefghijabcdefghij@localhost -> ok + end, + + runner:recv_eot(P), ok. %% This test is run entirely in C code. -round_trip_conversion(suite) -> []; round_trip_conversion(Config) when is_list(Config) -> - ?line runner:test(?round_trip_conversion), + runner:test(?round_trip_conversion), ok. %% This test sends a list of all data types to the C code function, %% which decodes it and verifies it. -decode_terms(suite) -> []; decode_terms(Config) when is_list(Config) -> - ?line Dummy1 = list_to_atom(filename:join(?config(priv_dir, Config), - dummy_file1)), - ?line Dummy2 = list_to_atom(filename:join(?config(priv_dir, Config), - dummy_file2)), - ?line Port1 = open_port(Dummy1, [out]), - ?line Port2 = open_port(Dummy2, [out]), - ?line ABinary = list_to_binary("A binary"), - ?line Terms = [make_ref(), make_ref(), - Port1, Port2, - self(), self(), - {element1, 42, 767}, "A string", - 1, -1, 0, 3.0, ABinary, 'I am an atom'], - - ?line P = runner:start(?decode_terms), - ?line runner:send_term(P, Terms), - ?line runner:recv_eot(P), + Dummy1 = list_to_atom(filename:join(proplists:get_value(priv_dir, Config), + dummy_file1)), + Dummy2 = list_to_atom(filename:join(proplists:get_value(priv_dir, Config), + dummy_file2)), + Port1 = open_port(Dummy1, [out]), + Port2 = open_port(Dummy2, [out]), + ABinary = list_to_binary("A binary"), + Terms = [make_ref(), make_ref(), + Port1, Port2, + self(), self(), + {element1, 42, 767}, "A string", + 1, -1, 0, 3.0, ABinary, 'I am an atom'], + + P = runner:start(?decode_terms), + runner:send_term(P, Terms), + runner:recv_eot(P), ok. %% Decodes the floating point number 3.1415. -decode_float(suite) -> []; decode_float(Config) when is_list(Config) -> - ?line P = runner:start(?decode_float), - ?line runner:send_term(P, 3.1415), - ?line runner:recv_eot(P), + P = runner:start(?decode_float), + runner:send_term(P, 3.1415), + runner:recv_eot(P), ok. %% Tests the erl_free_compound() function. -t_erl_free_compound(suite) -> []; t_erl_free_compound(Config) when is_list(Config) -> - ?line runner:test(?t_erl_free_compound), + runner:test(?t_erl_free_compound), ok. @@ -206,317 +185,296 @@ t_erl_free_compound(Config) when is_list(Config) -> %% This tests the erl_mk_list() function. -t_erl_mk_list(suite) -> []; t_erl_mk_list(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_list), + P = runner:start(?t_erl_mk_list), - ?line {term, []} = get_term(P), - ?line {term, [abc]} = get_term(P), - ?line {term, [abcdef, 42]} = get_term(P), - ?line {term, [0.0, 23, [], 3.1415]} = get_term(P), + {term, []} = get_term(P), + {term, [abc]} = get_term(P), + {term, [abcdef, 42]} = get_term(P), + {term, [0.0, 23, [], 3.1415]} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_mk_int() function. -t_erl_mk_int(suite) -> []; t_erl_mk_int(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_int), - - ?line {term, 0} = get_term(P), - ?line {term, 127} = get_term(P), - ?line {term, 128} = get_term(P), - ?line {term, 255} = get_term(P), - ?line {term, 256} = get_term(P), - - ?line {term, 16#FFFF} = get_term(P), - ?line {term, 16#10000} = get_term(P), - - ?line {term, 16#07FFFFFF} = get_term(P), - ?line {term, 16#0FFFFFFF} = get_term(P), - ?line {term, 16#1FFFFFFF} = get_term(P), - ?line {term, 16#3FFFFFFF} = get_term(P), - ?line {term, 16#7FFFFFFF} = get_term(P), - - ?line {term, 16#08000000} = get_term(P), - ?line {term, 16#10000000} = get_term(P), - ?line {term, 16#20000000} = get_term(P), - ?line {term, 16#40000000} = get_term(P), - - - ?line {term, -16#07FFFFFF} = get_term(P), - ?line {term, -16#0FFFFFFF} = get_term(P), - ?line {term, -16#1FFFFFFF} = get_term(P), - ?line {term, -16#3FFFFFFF} = get_term(P), - ?line {term, -16#7FFFFFFF} = get_term(P), - - ?line {term, -16#08000000} = get_term(P), - ?line {term, -16#10000000} = get_term(P), - ?line {term, -16#20000000} = get_term(P), - ?line {term, -16#40000000} = get_term(P), - - ?line {term, -16#08000001} = get_term(P), - ?line {term, -16#10000001} = get_term(P), - ?line {term, -16#20000001} = get_term(P), - ?line {term, -16#40000001} = get_term(P), - - ?line {term, -16#08000002} = get_term(P), - ?line {term, -16#10000002} = get_term(P), - ?line {term, -16#20000002} = get_term(P), - ?line {term, -16#40000002} = get_term(P), - - ?line {term, -1999999999} = get_term(P), - ?line {term, -2000000000} = get_term(P), - ?line {term, -2000000001} = get_term(P), - - ?line runner:recv_eot(P), + P = runner:start(?t_erl_mk_int), + + {term, 0} = get_term(P), + {term, 127} = get_term(P), + {term, 128} = get_term(P), + {term, 255} = get_term(P), + {term, 256} = get_term(P), + + {term, 16#FFFF} = get_term(P), + {term, 16#10000} = get_term(P), + + {term, 16#07FFFFFF} = get_term(P), + {term, 16#0FFFFFFF} = get_term(P), + {term, 16#1FFFFFFF} = get_term(P), + {term, 16#3FFFFFFF} = get_term(P), + {term, 16#7FFFFFFF} = get_term(P), + + {term, 16#08000000} = get_term(P), + {term, 16#10000000} = get_term(P), + {term, 16#20000000} = get_term(P), + {term, 16#40000000} = get_term(P), + + + {term, -16#07FFFFFF} = get_term(P), + {term, -16#0FFFFFFF} = get_term(P), + {term, -16#1FFFFFFF} = get_term(P), + {term, -16#3FFFFFFF} = get_term(P), + {term, -16#7FFFFFFF} = get_term(P), + + {term, -16#08000000} = get_term(P), + {term, -16#10000000} = get_term(P), + {term, -16#20000000} = get_term(P), + {term, -16#40000000} = get_term(P), + + {term, -16#08000001} = get_term(P), + {term, -16#10000001} = get_term(P), + {term, -16#20000001} = get_term(P), + {term, -16#40000001} = get_term(P), + + {term, -16#08000002} = get_term(P), + {term, -16#10000002} = get_term(P), + {term, -16#20000002} = get_term(P), + {term, -16#40000002} = get_term(P), + + {term, -1999999999} = get_term(P), + {term, -2000000000} = get_term(P), + {term, -2000000001} = get_term(P), + + runner:recv_eot(P), ok. %% Basic test of erl_copy_term(). -basic_copy(suite) -> []; basic_copy(Config) when is_list(Config) -> - ?line runner:test(?basic_copy), + runner:test(?basic_copy), ok. %% This tests the erl_mk_tuple() function. -t_erl_mk_tuple(suite) -> []; t_erl_mk_tuple(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_tuple), + P = runner:start(?t_erl_mk_tuple), - ?line {term, {madonna, 21, 'mad donna', 12}} = get_term(P), - ?line {term, {'Madonna',21,{children,{"Isabella",2}}, - {'home page',"http://www.madonna.com/"}}} = get_term(P), + {term, {madonna, 21, 'mad donna', 12}} = get_term(P), + {term, {'Madonna',21,{children,{"Isabella",2}}, + {'home page',"http://www.madonna.com/"}}} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_mk_atom() function. -t_erl_mk_atom(suite) -> []; t_erl_mk_atom(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_atom), - - ?line {term, madonna} = (get_term(P)), - ?line {term, 'Madonna'} = (get_term(P)), - ?line {term, 'mad donna'} = (get_term(P)), - ?line {term, '_madonna_'} = (get_term(P)), - ?line {term, '/home/madonna/tour_plan'} = (get_term(P)), - ?line {term, 'http://www.madonna.com/tour_plan'} = (get_term(P)), - ?line {term, '\'madonna\''} = (get_term(P)), - ?line {term, '\"madonna\"'} = (get_term(P)), - ?line {term, '\\madonna\\'} = (get_term(P)), - ?line {term, '{madonna,21,\'mad donna\',12}'} = (get_term(P)), - - ?line runner:recv_eot(P), + P = runner:start(?t_erl_mk_atom), + + {term, madonna} = (get_term(P)), + {term, 'Madonna'} = (get_term(P)), + {term, 'mad donna'} = (get_term(P)), + {term, '_madonna_'} = (get_term(P)), + {term, '/home/madonna/tour_plan'} = (get_term(P)), + {term, 'http://www.madonna.com/tour_plan'} = (get_term(P)), + {term, '\'madonna\''} = (get_term(P)), + {term, '\"madonna\"'} = (get_term(P)), + {term, '\\madonna\\'} = (get_term(P)), + {term, '{madonna,21,\'mad donna\',12}'} = (get_term(P)), + + runner:recv_eot(P), ok. %% This tests the erl_mk_binary() function. -t_erl_mk_binary(suite) -> []; t_erl_mk_binary(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_binary), + P = runner:start(?t_erl_mk_binary), - ?line {term, Bin} = (get_term(P)), - ?line "{madonna,21,'mad donna',1234.567.890, !#$%&/()=?+-@, \" \\}" = - binary_to_list(Bin), + {term, Bin} = (get_term(P)), + "{madonna,21,'mad donna',1234.567.890, !#$%&/()=?+-@, \" \\}" = binary_to_list(Bin), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_mk_empty_list() function. -t_erl_mk_empty_list(suite) -> []; t_erl_mk_empty_list(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_empty_list), + P = runner:start(?t_erl_mk_empty_list), - ?line {term, []} = get_term(P), + {term, []} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_mk_float() function. -t_erl_mk_float(suite) -> []; t_erl_mk_float(Config) when is_list(Config) -> case os:type() of - vxworks -> - {skipped, "Floating point numbers never compare equal on PPC"}; - _ -> - ?line P = runner:start(?t_erl_mk_float), - ?line {term, {3.1415, 1.999999, 2.000000, 2.000001, - 2.000002, 12345.67890}} = - get_term(P), - ?line runner:recv_eot(P), - ok + vxworks -> + {skipped, "Floating point numbers never compare equal on PPC"}; + _ -> + P = runner:start(?t_erl_mk_float), + {term, {3.1415, 1.999999, 2.000000, 2.000001, + 2.000002, 12345.67890}} = get_term(P), + runner:recv_eot(P), + ok end. %% This tests the erl_mk_pid() function. -t_erl_mk_pid(suite) -> []; t_erl_mk_pid(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_pid), + P = runner:start(?t_erl_mk_pid), - ?line {term, A_pid} = (get_term(P)), - ?line {pid, kalle@localhost, 3, 2} = nc2vinfo(A_pid), + {term, A_pid} = (get_term(P)), + {pid, kalle@localhost, 3, 2} = nc2vinfo(A_pid), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. -t_erl_mk_xpid(suite) -> []; t_erl_mk_xpid(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_xpid), + P = runner:start(?t_erl_mk_xpid), - ?line {term, A_pid} = (get_term(P)), - ?line {pid, kalle@localhost, 32767, 8191} = nc2vinfo(A_pid), + {term, A_pid} = (get_term(P)), + {pid, kalle@localhost, 32767, 8191} = nc2vinfo(A_pid), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_mk_port() function. -t_erl_mk_port(suite) -> []; t_erl_mk_port(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_port), + P = runner:start(?t_erl_mk_port), - ?line {term, A_port} = (get_term(P)), - ?line {port, kalle@localhost, 4} = nc2vinfo(A_port), + {term, A_port} = (get_term(P)), + {port, kalle@localhost, 4} = nc2vinfo(A_port), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. -t_erl_mk_xport(suite) -> []; t_erl_mk_xport(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_xport), + P = runner:start(?t_erl_mk_xport), - ?line {term, A_port} = (get_term(P)), - ?line {port, kalle@localhost, 268435455} = nc2vinfo(A_port), + {term, A_port} = (get_term(P)), + {port, kalle@localhost, 268435455} = nc2vinfo(A_port), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_mk_ref() function. -t_erl_mk_ref(suite) -> []; t_erl_mk_ref(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_ref), + P = runner:start(?t_erl_mk_ref), - ?line {term, A_ref} = (get_term(P)), - ?line {ref, kalle@localhost, _Length, [6]} = nc2vinfo(A_ref), + {term, A_ref} = (get_term(P)), + {ref, kalle@localhost, _Length, [6]} = nc2vinfo(A_ref), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. -t_erl_mk_long_ref(suite) -> []; t_erl_mk_long_ref(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_long_ref), + P = runner:start(?t_erl_mk_long_ref), - ?line {term, A_ref} = (get_term(P)), - ?line {ref, kalle@localhost, _Length, [4294967295,4294967295,262143]} - = nc2vinfo(A_ref), + {term, A_ref} = (get_term(P)), + {ref, kalle@localhost, _Length, [4294967295,4294967295,262143]} + = nc2vinfo(A_ref), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_mk_string() function. -t_erl_mk_string(suite) -> []; t_erl_mk_string(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_string), - - ?line {term, "madonna"} = (get_term(P)), - ?line {term, "Madonna"} = (get_term(P)), - ?line {term, "mad donna"} = (get_term(P)), - ?line {term, "_madonna_"} = (get_term(P)), - ?line {term, "/home/madonna/tour_plan"} = (get_term(P)), - ?line {term, "http://www.madonna.com/tour_plan"} = (get_term(P)), - ?line {term, "\'madonna\'"} = (get_term(P)), - ?line {term, "\"madonna\""} = (get_term(P)), - ?line {term, "\\madonna\\"} = (get_term(P)), - ?line {term, "{madonna,21,'mad donna',12}"} = (get_term(P)), - - ?line runner:recv_eot(P), + P = runner:start(?t_erl_mk_string), + + {term, "madonna"} = (get_term(P)), + {term, "Madonna"} = (get_term(P)), + {term, "mad donna"} = (get_term(P)), + {term, "_madonna_"} = (get_term(P)), + {term, "/home/madonna/tour_plan"} = (get_term(P)), + {term, "http://www.madonna.com/tour_plan"} = (get_term(P)), + {term, "\'madonna\'"} = (get_term(P)), + {term, "\"madonna\""} = (get_term(P)), + {term, "\\madonna\\"} = (get_term(P)), + {term, "{madonna,21,'mad donna',12}"} = (get_term(P)), + + runner:recv_eot(P), ok. %% This tests the erl_mk_estring() function. -t_erl_mk_estring(suite) -> []; t_erl_mk_estring(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_estring), - - ?line {term, "madonna"} = (get_term(P)), - ?line {term, "Madonna"} = (get_term(P)), - ?line {term, "mad donna"} = (get_term(P)), - ?line {term, "_madonna_"} = (get_term(P)), - ?line {term, "/home/madonna/tour_plan"} = (get_term(P)), - ?line {term, "http://www.madonna.com/tour_plan"} = (get_term(P)), - ?line {term, "\'madonna\'"} = (get_term(P)), - ?line {term, "\"madonna\""} = (get_term(P)), - ?line {term, "\\madonna\\"} = (get_term(P)), - ?line {term, "{madonna,21,'mad donna',12}"} = (get_term(P)), - - ?line runner:recv_eot(P), + P = runner:start(?t_erl_mk_estring), + + {term, "madonna"} = (get_term(P)), + {term, "Madonna"} = (get_term(P)), + {term, "mad donna"} = (get_term(P)), + {term, "_madonna_"} = (get_term(P)), + {term, "/home/madonna/tour_plan"} = (get_term(P)), + {term, "http://www.madonna.com/tour_plan"} = (get_term(P)), + {term, "\'madonna\'"} = (get_term(P)), + {term, "\"madonna\""} = (get_term(P)), + {term, "\\madonna\\"} = (get_term(P)), + {term, "{madonna,21,'mad donna',12}"} = (get_term(P)), + + runner:recv_eot(P), ok. %% This tests the erl_mk_uint() function. -t_erl_mk_uint(suite) -> []; t_erl_mk_uint(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_uint), + P = runner:start(?t_erl_mk_uint), - ?line {term, 54321} = (get_term(P)), - ?line {term, 2147483647} = (get_term(P)), - ?line {term, 2147483648} = (get_term(P)), - ?line {term, 2147483649} = (get_term(P)), - ?line {term, 2147483650} = (get_term(P)), - ?line {term, 4294967295} = (get_term(P)), + {term, 54321} = (get_term(P)), + {term, 2147483647} = (get_term(P)), + {term, 2147483648} = (get_term(P)), + {term, 2147483649} = (get_term(P)), + {term, 2147483650} = (get_term(P)), + {term, 4294967295} = (get_term(P)), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_mk_var() function. -t_erl_mk_var(suite) -> []; t_erl_mk_var(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_mk_var), + P = runner:start(?t_erl_mk_var), - ?line {term, 1} = (get_term(P)), - ?line {term, 0} = (get_term(P)), - ?line {term, 1} = (get_term(P)), - ?line {term, 0} = (get_term(P)), - ?line {term, 1} = (get_term(P)), - ?line {term, 0} = (get_term(P)), - ?line {term, 1} = (get_term(P)), + {term, 1} = (get_term(P)), + {term, 0} = (get_term(P)), + {term, 1} = (get_term(P)), + {term, 0} = (get_term(P)), + {term, 1} = (get_term(P)), + {term, 0} = (get_term(P)), + {term, 1} = (get_term(P)), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_cons() function. -t_erl_cons(suite) -> []; t_erl_cons(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_cons), + P = runner:start(?t_erl_cons), - ?line {term, [madonna, 21]} = get_term(P), + {term, [madonna, 21]} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. @@ -531,21 +489,20 @@ t_erl_cons(Config) when is_list(Config) -> %% Tests the erl_length() function. -t_erl_length(suite) -> []; t_erl_length(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_length), + P = runner:start(?t_erl_length), - ?line 0 = erl_length(P, []), - ?line 1 = erl_length(P, [a]), - ?line 2 = erl_length(P, [a, b]), - ?line 3 = erl_length(P, [a, b, c]), + 0 = erl_length(P, []), + 1 = erl_length(P, [a]), + 2 = erl_length(P, [a, b]), + 3 = erl_length(P, [a, b, c]), - ?line 4 = erl_length(P, [a, [x, y], c, []]), + 4 = erl_length(P, [a, [x, y], c, []]), - ?line -1 = erl_length(P, [a|b]), - ?line -1 = erl_length(P, a), + -1 = erl_length(P, [a|b]), + -1 = erl_length(P, a), - ?line runner:finish(P), + runner:finish(P), ok. %% Invokes the erl_length() function. @@ -555,22 +512,21 @@ erl_length(Port, List) -> %% Tests the erl_hd() function. -t_erl_hd(suite) -> []; t_erl_hd(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_hd), - - ?line 'NULL' = erl_hd(P, 42), - ?line 'NULL' = erl_hd(P, abc), - ?line 'NULL' = erl_hd(P, []), - - ?line [] = erl_hd(P, [[], a]), - ?line a = erl_hd(P, [a]), - ?line a = erl_hd(P, [a, b]), - ?line a = erl_hd(P, [a, b, c]), - ?line a = erl_hd(P, [a|b]), - - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + P = runner:start(?t_erl_hd), + + 'NULL' = erl_hd(P, 42), + 'NULL' = erl_hd(P, abc), + 'NULL' = erl_hd(P, []), + + [] = erl_hd(P, [[], a]), + a = erl_hd(P, [a]), + a = erl_hd(P, [a, b]), + a = erl_hd(P, [a, b, c]), + a = erl_hd(P, [a|b]), + + runner:send_eot(P), + runner:recv_eot(P), ok. %% Invokes the erl_hd() function. @@ -580,22 +536,21 @@ erl_hd(Port, List) -> %% Tests the erl_tail() function. -t_erl_tl(suite) -> []; t_erl_tl(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_tl), + P = runner:start(?t_erl_tl), - ?line 'NULL' = erl_tl(P, 42), - ?line 'NULL' = erl_tl(P, abc), - ?line 'NULL' = erl_tl(P, []), + 'NULL' = erl_tl(P, 42), + 'NULL' = erl_tl(P, abc), + 'NULL' = erl_tl(P, []), - ?line [] = erl_tl(P, [a]), - ?line [b] = erl_tl(P, [a, b]), - ?line [b, c] = erl_tl(P, [a, b, c]), + [] = erl_tl(P, [a]), + [b] = erl_tl(P, [a, b]), + [b, c] = erl_tl(P, [a, b, c]), - ?line b = erl_tl(P, [a|b]), + b = erl_tl(P, [a|b]), - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + runner:send_eot(P), + runner:recv_eot(P), ok. %% Invokes the erl_tail() function in erl_interface. @@ -605,68 +560,63 @@ erl_tl(Port, List) -> %% Tests the type checking macros (done in the C program). -type_checks(suite) -> []; type_checks(Config) when is_list(Config) -> - ?line runner:test(?type_checks), + runner:test(?type_checks), ok. %% Tests the extractor macros (done in the C program). -extractor_macros(suite) -> []; extractor_macros(Config) when is_list(Config) -> - ?line runner:test(?extractor_macros), + runner:test(?extractor_macros), ok. %% This tests the erl_size() function. -t_erl_size(suite) -> []; t_erl_size(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_size), + P = runner:start(?t_erl_size), - ?line {term, 0} = (get_term(P)), - ?line {term, 4} = (get_term(P)), + {term, 0} = (get_term(P)), + {term, 4} = (get_term(P)), - ?line {term, 0} = (get_term(P)), - ?line {term, 27} = (get_term(P)), + {term, 0} = (get_term(P)), + {term, 27} = (get_term(P)), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_var_content() function. -t_erl_var_content(suite) -> []; t_erl_var_content(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_var_content), + P = runner:start(?t_erl_var_content), - ?line {term, 17} = (get_term(P)), - ?line {term, "http://www.madonna.com"} = (get_term(P)), - ?line {term, 2} = (get_term(P)), - ?line {term, "http://www.madonna.com"} = (get_term(P)), - ?line {term, 2} = (get_term(P)), + {term, 17} = (get_term(P)), + {term, "http://www.madonna.com"} = (get_term(P)), + {term, 2} = (get_term(P)), + {term, "http://www.madonna.com"} = (get_term(P)), + {term, 2} = (get_term(P)), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. %% This tests the erl_element() function. -t_erl_element(suite) -> []; t_erl_element(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_element), + P = runner:start(?t_erl_element), - ?line {term, madonna} = get_term(P), - ?line {term, 21} = get_term(P), - ?line {term, 'mad donna'} = get_term(P), - ?line {term, 12} = get_term(P), + {term, madonna} = get_term(P), + {term, 21} = get_term(P), + {term, 'mad donna'} = get_term(P), + {term, 12} = get_term(P), - ?line {term, 'Madonna'} = get_term(P), - ?line {term, 21} = get_term(P), - ?line {term, {children,{"Isabella",2}}} = get_term(P), - ?line {term, {'home page',"http://www.madonna.com/"}} = get_term(P), + {term, 'Madonna'} = get_term(P), + {term, 21} = get_term(P), + {term, {children,{"Isabella",2}}} = get_term(P), + {term, {'home page',"http://www.madonna.com/"}} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. @@ -679,65 +629,64 @@ t_erl_element(Config) when is_list(Config) -> %% Tests the erl_iolist_length() function. -t_erl_iolist_length(suite) -> []; t_erl_iolist_length(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_iolist_length), + P = runner:start(?t_erl_iolist_length), %% Flat lists. - ?line 0 = erl_iolist_length(P, []), - ?line 1 = erl_iolist_length(P, [10]), - ?line 2 = erl_iolist_length(P, [10, 20]), - ?line 3 = erl_iolist_length(P, [10, 20, 30]), - ?line 256 = erl_iolist_length(P, lists:seq(0, 255)), + 0 = erl_iolist_length(P, []), + 1 = erl_iolist_length(P, [10]), + 2 = erl_iolist_length(P, [10, 20]), + 3 = erl_iolist_length(P, [10, 20, 30]), + 256 = erl_iolist_length(P, lists:seq(0, 255)), %% Deep lists. - ?line 0 = erl_iolist_length(P, [[]]), - ?line 1 = erl_iolist_length(P, [[], 42]), - ?line 1 = erl_iolist_length(P, [42, []]), - ?line 2 = erl_iolist_length(P, [42, [], 45]), + 0 = erl_iolist_length(P, [[]]), + 1 = erl_iolist_length(P, [[], 42]), + 1 = erl_iolist_length(P, [42, []]), + 2 = erl_iolist_length(P, [42, [], 45]), - ?line 3 = erl_iolist_length(P, [42, [90], 45]), - ?line 3 = erl_iolist_length(P, [[42, [90]], 45]), - ?line 3 = erl_iolist_length(P, [[42, [90]], 45]), + 3 = erl_iolist_length(P, [42, [90], 45]), + 3 = erl_iolist_length(P, [[42, [90]], 45]), + 3 = erl_iolist_length(P, [[42, [90]], 45]), %% List with binaries. - ?line 0 = erl_iolist_length(P, [list_to_binary([])]), - ?line 0 = erl_iolist_length(P, [[], list_to_binary([])]), - ?line 1 = erl_iolist_length(P, [[1], list_to_binary([])]), - ?line 1 = erl_iolist_length(P, [[], list_to_binary([2])]), - ?line 2 = erl_iolist_length(P, [[42], list_to_binary([2])]), - ?line 4 = erl_iolist_length(P, [[42], list_to_binary([2, 3, 4])]), + 0 = erl_iolist_length(P, [list_to_binary([])]), + 0 = erl_iolist_length(P, [[], list_to_binary([])]), + 1 = erl_iolist_length(P, [[1], list_to_binary([])]), + 1 = erl_iolist_length(P, [[], list_to_binary([2])]), + 2 = erl_iolist_length(P, [[42], list_to_binary([2])]), + 4 = erl_iolist_length(P, [[42], list_to_binary([2, 3, 4])]), %% Binaries as tail. - ?line 0 = erl_iolist_length(P, [[]| list_to_binary([])]), - ?line 1 = erl_iolist_length(P, [[1]| list_to_binary([])]), - ?line 1 = erl_iolist_length(P, [[]| list_to_binary([2])]), - ?line 2 = erl_iolist_length(P, [[42]| list_to_binary([2])]), + 0 = erl_iolist_length(P, [[]| list_to_binary([])]), + 1 = erl_iolist_length(P, [[1]| list_to_binary([])]), + 1 = erl_iolist_length(P, [[]| list_to_binary([2])]), + 2 = erl_iolist_length(P, [[42]| list_to_binary([2])]), %% Binaries only. - ?line 0 = erl_iolist_length(P, list_to_binary("")), - ?line 1 = erl_iolist_length(P, list_to_binary([1])), - ?line 2 = erl_iolist_length(P, list_to_binary([1, 2])), + 0 = erl_iolist_length(P, list_to_binary("")), + 1 = erl_iolist_length(P, list_to_binary([1])), + 2 = erl_iolist_length(P, list_to_binary([1, 2])), %% Illegal cases. - ?line -1 = erl_iolist_length(P, [42|43]), - ?line -1 = erl_iolist_length(P, a), + -1 = erl_iolist_length(P, [42|43]), + -1 = erl_iolist_length(P, a), - ?line -1 = erl_iolist_length(P, [a]), - ?line -1 = erl_iolist_length(P, [256]), - ?line -1 = erl_iolist_length(P, [257]), - ?line -1 = erl_iolist_length(P, [-1]), - ?line -1 = erl_iolist_length(P, [-2]), - ?line -1 = erl_iolist_length(P, [-127]), - ?line -1 = erl_iolist_length(P, [-128]), + -1 = erl_iolist_length(P, [a]), + -1 = erl_iolist_length(P, [256]), + -1 = erl_iolist_length(P, [257]), + -1 = erl_iolist_length(P, [-1]), + -1 = erl_iolist_length(P, [-2]), + -1 = erl_iolist_length(P, [-127]), + -1 = erl_iolist_length(P, [-128]), - ?line runner:finish(P), + runner:finish(P), ok. %% Invokes the erl_iolist_length() function. @@ -747,143 +696,141 @@ erl_iolist_length(Port, List) -> %% Tests the erl_iolist_to_binary() function. -t_erl_iolist_to_binary(suite) -> []; t_erl_iolist_to_binary(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_iolist_to_binary), + P = runner:start(?t_erl_iolist_to_binary), %% Flat lists. - ?line [] = iolist_to_list(P, []), - ?line [10] = iolist_to_list(P, [10]), - ?line [10, 20] = iolist_to_list(P, [10, 20]), - ?line [10, 20, 30] = iolist_to_list(P, [10, 20, 30]), - ?line AllBytes = lists:seq(0, 255), - ?line AllBytes = iolist_to_list(P, AllBytes), + [] = iolist_to_list(P, []), + [10] = iolist_to_list(P, [10]), + [10, 20] = iolist_to_list(P, [10, 20]), + [10, 20, 30] = iolist_to_list(P, [10, 20, 30]), + AllBytes = lists:seq(0, 255), + AllBytes = iolist_to_list(P, AllBytes), %% Deep lists. - ?line [] = iolist_to_list(P, [[]]), - ?line [42] = iolist_to_list(P, [[], 42]), - ?line [42] = iolist_to_list(P, [42, []]), - ?line [42, 45] = iolist_to_list(P, [42, [], 45]), + [] = iolist_to_list(P, [[]]), + [42] = iolist_to_list(P, [[], 42]), + [42] = iolist_to_list(P, [42, []]), + [42, 45] = iolist_to_list(P, [42, [], 45]), - ?line [42, 90, 45] = iolist_to_list(P, [42, [90], 45]), - ?line [42, 90, 45] = iolist_to_list(P, [[42, [90]], 45]), - ?line [42, 90, 45] = iolist_to_list(P, [[42, [90]], 45]), + [42, 90, 45] = iolist_to_list(P, [42, [90], 45]), + [42, 90, 45] = iolist_to_list(P, [[42, [90]], 45]), + [42, 90, 45] = iolist_to_list(P, [[42, [90]], 45]), %% List with binaries. - ?line [] = iolist_to_list(P, [list_to_binary([])]), - ?line [] = iolist_to_list(P, [[], list_to_binary([])]), - ?line [1] = iolist_to_list(P, [[1], list_to_binary([])]), - ?line [2] = iolist_to_list(P, [[], list_to_binary([2])]), - ?line [42, 2] = iolist_to_list(P, [[42], list_to_binary([2])]), - ?line [42, 2, 3, 4] = iolist_to_list(P, [[42], list_to_binary([2, 3, 4])]), + [] = iolist_to_list(P, [list_to_binary([])]), + [] = iolist_to_list(P, [[], list_to_binary([])]), + [1] = iolist_to_list(P, [[1], list_to_binary([])]), + [2] = iolist_to_list(P, [[], list_to_binary([2])]), + [42, 2] = iolist_to_list(P, [[42], list_to_binary([2])]), + [42, 2, 3, 4] = iolist_to_list(P, [[42], list_to_binary([2, 3, 4])]), %% Binaries as tail. - ?line [] = iolist_to_list(P, [[]| list_to_binary([])]), - ?line [1] = iolist_to_list(P, [[1]| list_to_binary([])]), - ?line [2] = iolist_to_list(P, [[]| list_to_binary([2])]), - ?line [42, 2] = iolist_to_list(P, [[42]| list_to_binary([2])]), + [] = iolist_to_list(P, [[]| list_to_binary([])]), + [1] = iolist_to_list(P, [[1]| list_to_binary([])]), + [2] = iolist_to_list(P, [[]| list_to_binary([2])]), + [42, 2] = iolist_to_list(P, [[42]| list_to_binary([2])]), %% Binaries only. - ?line [] = iolist_to_list(P, list_to_binary("")), - ?line [1] = iolist_to_list(P, list_to_binary([1])), - ?line [1, 2] = iolist_to_list(P, list_to_binary([1, 2])), + [] = iolist_to_list(P, list_to_binary("")), + [1] = iolist_to_list(P, list_to_binary([1])), + [1, 2] = iolist_to_list(P, list_to_binary([1, 2])), %% Illegal cases. - ?line 'NULL' = iolist_to_list(P, [42|43]), - ?line 'NULL' = iolist_to_list(P, a), + 'NULL' = iolist_to_list(P, [42|43]), + 'NULL' = iolist_to_list(P, a), - ?line 'NULL' = iolist_to_list(P, [a]), - ?line 'NULL' = iolist_to_list(P, [256]), - ?line 'NULL' = iolist_to_list(P, [257]), - ?line 'NULL' = iolist_to_list(P, [-1]), - ?line 'NULL' = iolist_to_list(P, [-2]), - ?line 'NULL' = iolist_to_list(P, [-127]), - ?line 'NULL' = iolist_to_list(P, [-128]), + 'NULL' = iolist_to_list(P, [a]), + 'NULL' = iolist_to_list(P, [256]), + 'NULL' = iolist_to_list(P, [257]), + 'NULL' = iolist_to_list(P, [-1]), + 'NULL' = iolist_to_list(P, [-2]), + 'NULL' = iolist_to_list(P, [-127]), + 'NULL' = iolist_to_list(P, [-128]), - ?line runner:finish(P), + runner:finish(P), ok. iolist_to_list(Port, Term) -> case call_erl_function(Port, Term) of - 'NULL' -> - 'NULL'; - Bin when is_binary(Bin) -> - binary_to_list(Bin) + 'NULL' -> + 'NULL'; + Bin when is_binary(Bin) -> + binary_to_list(Bin) end. %% Tests the erl_iolist_to_string() function. -t_erl_iolist_to_string(suite) -> []; t_erl_iolist_to_string(Config) when is_list(Config) -> - ?line P = runner:start(?t_erl_iolist_to_string), + P = runner:start(?t_erl_iolist_to_string), %% Flat lists. - ?line [0] = iolist_to_string(P, []), - ?line [10, 0] = iolist_to_string(P, [10]), - ?line [10, 20, 0] = iolist_to_string(P, [10, 20]), - ?line [10, 20, 30, 0] = iolist_to_string(P, [10, 20, 30]), - ?line AllBytes = lists:seq(1, 255)++[0], - ?line AllBytes = iolist_to_string(P, lists:seq(1, 255)), + [0] = iolist_to_string(P, []), + [10, 0] = iolist_to_string(P, [10]), + [10, 20, 0] = iolist_to_string(P, [10, 20]), + [10, 20, 30, 0] = iolist_to_string(P, [10, 20, 30]), + AllBytes = lists:seq(1, 255)++[0], + AllBytes = iolist_to_string(P, lists:seq(1, 255)), %% Deep lists. - ?line [0] = iolist_to_string(P, [[]]), - ?line [42, 0] = iolist_to_string(P, [[], 42]), - ?line [42, 0] = iolist_to_string(P, [42, []]), - ?line [42, 45, 0] = iolist_to_string(P, [42, [], 45]), + [0] = iolist_to_string(P, [[]]), + [42, 0] = iolist_to_string(P, [[], 42]), + [42, 0] = iolist_to_string(P, [42, []]), + [42, 45, 0] = iolist_to_string(P, [42, [], 45]), - ?line [42, 90, 45, 0] = iolist_to_string(P, [42, [90], 45]), - ?line [42, 90, 45, 0] = iolist_to_string(P, [[42, [90]], 45]), - ?line [42, 90, 45, 0] = iolist_to_string(P, [[42, [90]], 45]), + [42, 90, 45, 0] = iolist_to_string(P, [42, [90], 45]), + [42, 90, 45, 0] = iolist_to_string(P, [[42, [90]], 45]), + [42, 90, 45, 0] = iolist_to_string(P, [[42, [90]], 45]), %% List with binaries. - ?line [0] = iolist_to_string(P, [list_to_binary([])]), - ?line [0] = iolist_to_string(P, [[], list_to_binary([])]), - ?line [1, 0] = iolist_to_string(P, [[1], list_to_binary([])]), - ?line [2, 0] = iolist_to_string(P, [[], list_to_binary([2])]), - ?line [42, 2, 0] = iolist_to_string(P, [[42], list_to_binary([2])]), - ?line [42, 2, 3, 4, 0] = iolist_to_string(P, [[42], - list_to_binary([2, 3, 4])]), + [0] = iolist_to_string(P, [list_to_binary([])]), + [0] = iolist_to_string(P, [[], list_to_binary([])]), + [1, 0] = iolist_to_string(P, [[1], list_to_binary([])]), + [2, 0] = iolist_to_string(P, [[], list_to_binary([2])]), + [42, 2, 0] = iolist_to_string(P, [[42], list_to_binary([2])]), + [42, 2, 3, 4, 0] = iolist_to_string(P, [[42], + list_to_binary([2, 3, 4])]), %% Binaries as tail. - ?line [0] = iolist_to_string(P, [[]| list_to_binary([])]), - ?line [1, 0] = iolist_to_string(P, [[1]| list_to_binary([])]), - ?line [2, 0] = iolist_to_string(P, [[]| list_to_binary([2])]), - ?line [42, 2, 0] = iolist_to_string(P, [[42]| list_to_binary([2])]), + [0] = iolist_to_string(P, [[]| list_to_binary([])]), + [1, 0] = iolist_to_string(P, [[1]| list_to_binary([])]), + [2, 0] = iolist_to_string(P, [[]| list_to_binary([2])]), + [42, 2, 0] = iolist_to_string(P, [[42]| list_to_binary([2])]), %% Binaries only. - ?line [0] = iolist_to_string(P, list_to_binary("")), - ?line [1, 0] = iolist_to_string(P, list_to_binary([1])), - ?line [1, 2, 0] = iolist_to_string(P, list_to_binary([1, 2])), + [0] = iolist_to_string(P, list_to_binary("")), + [1, 0] = iolist_to_string(P, list_to_binary([1])), + [1, 2, 0] = iolist_to_string(P, list_to_binary([1, 2])), %% Illegal cases. - ?line 'NULL' = iolist_to_string(P, [0]), - ?line 'NULL' = iolist_to_string(P, [65, 0, 66]), - ?line 'NULL' = iolist_to_string(P, [65, 66, 67, 0]), + 'NULL' = iolist_to_string(P, [0]), + 'NULL' = iolist_to_string(P, [65, 0, 66]), + 'NULL' = iolist_to_string(P, [65, 66, 67, 0]), - ?line 'NULL' = iolist_to_string(P, [42|43]), - ?line 'NULL' = iolist_to_string(P, a), + 'NULL' = iolist_to_string(P, [42|43]), + 'NULL' = iolist_to_string(P, a), - ?line 'NULL' = iolist_to_string(P, [a]), - ?line 'NULL' = iolist_to_string(P, [256]), - ?line 'NULL' = iolist_to_string(P, [257]), - ?line 'NULL' = iolist_to_string(P, [-1]), - ?line 'NULL' = iolist_to_string(P, [-2]), - ?line 'NULL' = iolist_to_string(P, [-127]), - ?line 'NULL' = iolist_to_string(P, [-128]), + 'NULL' = iolist_to_string(P, [a]), + 'NULL' = iolist_to_string(P, [256]), + 'NULL' = iolist_to_string(P, [257]), + 'NULL' = iolist_to_string(P, [-1]), + 'NULL' = iolist_to_string(P, [-2]), + 'NULL' = iolist_to_string(P, [-127]), + 'NULL' = iolist_to_string(P, [-128]), - ?line runner:finish(P), + runner:finish(P), ok. %% Invokes the erl_iolist_to_string() function. @@ -891,8 +838,8 @@ t_erl_iolist_to_string(Config) when is_list(Config) -> iolist_to_string(Port, Term) -> runner:send_term(Port, Term), case get_term(Port) of - {bytes, Result} -> Result; - 'NULL' -> 'NULL' + {bytes, Result} -> Result; + 'NULL' -> 'NULL' end. @@ -902,38 +849,37 @@ iolist_to_string(Port, Term) -> %%% %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -erl_print_term(suite) -> []; -erl_print_term(doc) -> "Tests the erl_print_term() function"; +%% Tests the erl_print_term() function erl_print_term(Config) when is_list(Config) -> - ?line PrintTerm = print_term(Config), - ?line P = open_port({spawn, PrintTerm}, [stream]), + PrintTerm = print_term(Config), + P = open_port({spawn, PrintTerm}, [stream]), %% Lists. - ?line print(P, "[]", []), - ?line print(P, "[a]", [a]), - ?line print(P, "[[a]]", [[a]]), - ?line print(P, "[[]]", [[]]), - ?line print(P, "[a,b,c]", [a,b,c]), - ?line print(P, "[a,b|c]", [a,b|c]), - ?line print(P, "[a,[],c]", [a,[],c]), - ?line print(P, "[a,[1000,1],c]", [a,[1000,1],c]), + print(P, "[]", []), + print(P, "[a]", [a]), + print(P, "[[a]]", [[a]]), + print(P, "[[]]", [[]]), + print(P, "[a,b,c]", [a,b,c]), + print(P, "[a,b|c]", [a,b|c]), + print(P, "[a,[],c]", [a,[],c]), + print(P, "[a,[1000,1],c]", [a,[1000,1],c]), %% Tuples. - ?line print(P, "{}", {}), - ?line print(P, "{ok}", {ok}), - ?line print(P, "{1,2,3}", {1, 2, 3}), + print(P, "{}", {}), + print(P, "{ok}", {ok}), + print(P, "{1,2,3}", {1, 2, 3}), %% Pids. - ?line {_X, Y, Z} = split_pid(self()), - ?line PidString = lists:flatten(io_lib:format("<~s.~w.~w>", - [node(), Y, Z])), - ?line print(P, PidString, self()), + {_X, Y, Z} = split_pid(self()), + PidString = lists:flatten(io_lib:format("<~s.~w.~w>", + [node(), Y, Z])), + print(P, PidString, self()), - ?line unlink(P), - ?line exit(P, die), + unlink(P), + exit(P, die), ok. split_pid(Pid) when is_pid(Pid) -> @@ -948,23 +894,22 @@ split_pid([$.|Rest], Cur, Result) -> split_pid([$>], Cur, Result) -> list_to_tuple(Result++[Cur]). -print_string(suite) -> []; -print_string(doc) -> "Test printing a string with erl_print_term()"; +%% Test printing a string with erl_print_term() print_string(Config) when is_list(Config) -> - ?line PrintTerm = print_term(Config), - ?line P = open_port({spawn, PrintTerm}, [stream]), + PrintTerm = print_term(Config), + P = open_port({spawn, PrintTerm}, [stream]), %% Strings. - ?line print(P, "\"ABC\"", "ABC"), - ?line {11, "\"\\tABC\\r\\n\""} = print(P, "\tABC\r\n"), + print(P, "\"ABC\"", "ABC"), + {11, "\"\\tABC\\r\\n\""} = print(P, "\tABC\r\n"), %% Not strings. - ?line print(P, "[65,66,67,0]", "ABC\000"), + print(P, "[65,66,67,0]", "ABC\000"), - ?line unlink(P), - ?line exit(P, die), + unlink(P), + exit(P, die), ok. print(Port, TermString, Term) -> @@ -983,15 +928,15 @@ print(Port, Term) -> collect_line(Port, Result) -> receive - {Port, {data, Data}} -> - case lists:reverse(Data) of - [$\n|Rest] -> - collect_line1(Rest++Result, []); - Chars -> - collect_line(Port, Chars++Result) - end - after test_server:seconds(5) -> - test_server:fail("No response from C program") + {Port, {data, Data}} -> + case lists:reverse(Data) of + [$\n|Rest] -> + collect_line1(Rest++Result, []); + Chars -> + collect_line(Port, Chars++Result) + end + after 5000 -> + ct:fail("No response from C program") end. collect_line1([$\r|Rest], Result) -> @@ -1001,18 +946,16 @@ collect_line1([C|Rest], Result) -> %% Test case submitted by Per Lundgren, ERV. -high_chaparal(suite) -> []; high_chaparal(Config) when is_list(Config) -> - ?line P = runner:start(?high_chaparal), - ?line {term, [hello, world]} = get_term(P), - ?line runner:recv_eot(P), + P = runner:start(?high_chaparal), + {term, [hello, world]} = get_term(P), + runner:recv_eot(P), ok. %% OTP-7448 -broken_data(suite) -> []; broken_data(Config) when is_list(Config) -> - ?line P = runner:start(?broken_data), - ?line runner:recv_eot(P), + P = runner:start(?broken_data), + runner:recv_eot(P), ok. %% This calls a C function with one parameter and returns the result. @@ -1020,12 +963,12 @@ broken_data(Config) when is_list(Config) -> call_erl_function(Port, Term) -> runner:send_term(Port, Term), case get_term(Port) of - {term, Result} -> Result; - 'NULL' -> 'NULL' + {term, Result} -> Result; + 'NULL' -> 'NULL' end. print_term(Config) when is_list(Config) -> - filename:join(?config(data_dir, Config), "print_term"). + filename:join(proplists:get_value(data_dir, Config), "print_term"). @@ -1034,57 +977,57 @@ print_term(Config) when is_list(Config) -> %%% back, without having been mutated into short form. We must take %%% care then to check the actual returned ref, and not the original %%% one, which is equal to it. -cnode_1(suite) -> []; -cnode_1(doc) -> "Tests involving cnode: sends a long ref from a cnode to us"; + +%% Tests involving cnode: sends a long ref from a cnode to us cnode_1(Config) when is_list(Config) -> - ?line Cnode = filename:join(?config(data_dir, Config), "cnode"), - ?line register(mip, self()), - ?line spawn_link(?MODULE, start_cnode, [Cnode]), - ?line Ref1 = get_ref(), + Cnode = filename:join(proplists:get_value(data_dir, Config), "cnode"), + register(mip, self()), + spawn_link(?MODULE, start_cnode, [Cnode]), + Ref1 = get_ref(), io:format("Ref1 ~p~n", [Ref1]), - ?line check_ref(Ref1), - ?line Ref2 = make_ref(), - ?line receive - Pid -> Pid - end, - ?line Fun1 = fun(X) -> {Pid, X} end, % sneak in a fun test here - %?line Fun1 = {wait_with_funs, new_dist_format}, - ?line Term = {Ref2, Fun1, {1,2,3,4,5,6,7,8,9,10}}, + check_ref(Ref1), + Ref2 = make_ref(), + Pid = receive + Msg -> Msg %% pid + end, + Fun1 = fun(X) -> {Pid, X} end, % sneak in a fun test here + %Fun1 = {wait_with_funs, new_dist_format}, + Term = {Ref2, Fun1, {1,2,3,4,5,6,7,8,9,10}}, %% A term which will overflow the original buffer used in 'cnode'. - ?line Pid ! Term, - ?line receive - Term2 -> - io:format("received ~p~n", [Term2]), - case Term2 of - Term -> - {Ref22,_,_} = Term2, - ?line check_ref(Ref22); - X -> - test_server:fail({receive1,X}) - end - after 5000 -> - test_server:fail(receive1) - end, - ?line receive - Pid -> - ok; - Y -> - test_server:fail({receive1,Y}) - after 5000 -> - test_server:fail(receive2) - end, - ?line io:format("ref = ~p~n", [Ref1]), - ?line check_ref(Ref1), + Pid ! Term, + receive + Term2 -> + io:format("received ~p~n", [Term2]), + case Term2 of + Term -> + {Ref22,_,_} = Term2, + check_ref(Ref22); + X -> + ct:fail({receive1,X}) + end + after 5000 -> + ct:fail(receive1) + end, + receive + Pid -> + ok; + Y -> + ct:fail({receive1,Y}) + after 5000 -> + ct:fail(receive2) + end, + io:format("ref = ~p~n", [Ref1]), + check_ref(Ref1), ok. check_ref(Ref) -> case bin_ext_type(Ref) of - 101 -> - test_server:fail(oldref); - 114 -> - ok; - Type -> - test_server:fail({type, Type}) + 101 -> + ct:fail(oldref); + 114 -> + ok; + Type -> + ct:fail({type, Type}) end. bin_ext_type(T) -> @@ -1093,10 +1036,10 @@ bin_ext_type(T) -> get_ref() -> receive - X when is_reference(X) -> - X + X when is_reference(X) -> + X after 5000 -> - test_server:fail({cnode, timeout}) + ct:fail({cnode, timeout}) end. start_cnode(Cnode) -> @@ -1105,35 +1048,33 @@ start_cnode(Cnode) -> rec_cnode() -> receive - X -> - io:format("from cnode: ~p~n", [X]), - rec_cnode() + X -> + io:format("from cnode: ~p~n", [X]), + rec_cnode() end. nc2vinfo(Pid) when is_pid(Pid) -> - ?line [_NodeStr, NumberStr, SerialStr] - = string:tokens(pid_to_list(Pid), "<.>"), - ?line Number = list_to_integer(NumberStr), - ?line Serial = list_to_integer(SerialStr), - ?line {pid, node(Pid), Number, Serial}; + [_NodeStr, NumberStr, SerialStr] + = string:tokens(pid_to_list(Pid), "<.>"), + Number = list_to_integer(NumberStr), + Serial = list_to_integer(SerialStr), + {pid, node(Pid), Number, Serial}; nc2vinfo(Port) when is_port(Port) -> - ?line ["#Port", _NodeStr, NumberStr] - = string:tokens(erlang:port_to_list(Port), "<.>"), - ?line Number = list_to_integer(NumberStr), - ?line {port, node(Port), Number}; + ["#Port", _NodeStr, NumberStr] + = string:tokens(erlang:port_to_list(Port), "<.>"), + Number = list_to_integer(NumberStr), + {port, node(Port), Number}; nc2vinfo(Ref) when is_reference(Ref) -> - ?line ["#Ref", _NodeStr | NumStrList] - = string:tokens(erlang:ref_to_list(Ref), "<.>"), - ?line {Len, RevNumList} = lists:foldl(fun ("0", {N, []}) -> - {N+1, []}; - (IStr, {N, Is}) -> - {N+1, - [list_to_integer(IStr)|Is]} - end, - {0, []}, - NumStrList), - ?line {ref, node(Ref), Len, lists:reverse(RevNumList)}; + ["#Ref", _NodeStr | NumStrList] + = string:tokens(erlang:ref_to_list(Ref), "<.>"), + {Len, RevNumList} = lists:foldl(fun ("0", {N, []}) -> + {N+1, []}; + (IStr, {N, Is}) -> + {N+1, + [list_to_integer(IStr)|Is]} + end, + {0, []}, + NumStrList), + {ref, node(Ref), Len, lists:reverse(RevNumList)}; nc2vinfo(Other) -> - ?line {badarg, Other}. - - + {badarg, Other}. diff --git a/lib/erl_interface/test/erl_ext_SUITE.erl b/lib/erl_interface/test/erl_ext_SUITE.erl index e1c184c14d..afaba1fd93 100644 --- a/lib/erl_interface/test/erl_ext_SUITE.erl +++ b/lib/erl_interface/test/erl_ext_SUITE.erl @@ -24,74 +24,44 @@ -include_lib("common_test/include/ct.hrl"). -include("erl_ext_SUITE_data/ext_test_cases.hrl"). --export([ - all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - compare_tuple/1, - compare_list/1, - compare_string/1, - compare_list_string/1, - compare_nc_ext/1 - ]). +-export([all/0, suite/0, + compare_tuple/1, + compare_list/1, + compare_string/1, + compare_list_string/1, + compare_nc_ext/1]). -import(runner, [get_term/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [compare_tuple, compare_list, compare_string, compare_list_string, compare_nc_ext]. -groups() -> - []. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -compare_tuple(suite) -> []; -compare_tuple(doc) -> []; compare_tuple(Config) when is_list(Config) -> - ?line P = runner:start(?compare_tuple), - ?line runner:recv_eot(P), + P = runner:start(?compare_tuple), + runner:recv_eot(P), ok. -compare_list(suite) -> []; -compare_list(doc) -> []; compare_list(Config) when is_list(Config) -> - ?line P = runner:start(?compare_list), - ?line runner:recv_eot(P), + P = runner:start(?compare_list), + runner:recv_eot(P), ok. -compare_string(suite) -> []; -compare_string(doc) -> []; compare_string(Config) when is_list(Config) -> - ?line P = runner:start(?compare_string), - ?line runner:recv_eot(P), + P = runner:start(?compare_string), + runner:recv_eot(P), ok. -compare_list_string(suite) -> []; -compare_list_string(doc) -> []; compare_list_string(Config) when is_list(Config) -> - ?line P = runner:start(?compare_list_string), - ?line runner:recv_eot(P), + P = runner:start(?compare_list_string), + runner:recv_eot(P), ok. -compare_nc_ext(suite) -> []; -compare_nc_ext(doc) -> []; compare_nc_ext(Config) when is_list(Config) -> - ?line P = runner:start(?compare_nc_ext), - ?line runner:recv_eot(P), + P = runner:start(?compare_nc_ext), + runner:recv_eot(P), ok. - - - diff --git a/lib/erl_interface/test/erl_format_SUITE.erl b/lib/erl_interface/test/erl_format_SUITE.erl index f5f2d9fbf7..c1a7d8377e 100644 --- a/lib/erl_interface/test/erl_format_SUITE.erl +++ b/lib/erl_interface/test/erl_format_SUITE.erl @@ -24,134 +24,108 @@ -include_lib("common_test/include/ct.hrl"). -include("erl_format_SUITE_data/format_test_cases.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, atoms/1, tuples/1, lists/1]). +-export([all/0, suite/0, + atoms/1, tuples/1, lists/1]). -import(runner, [get_term/1]). %% This test suite test the erl_format() function. %% It uses the port program "format_test". -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [atoms, tuples, lists]. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - %% Tests formatting various atoms. -atoms(suite) -> []; atoms(Config) when is_list(Config) -> - ?line P = runner:start(?atoms), - - ?line {term, ''} = get_term(P), - ?line {term, 'a'} = get_term(P), - ?line {term, 'A'} = get_term(P), - ?line {term, 'abc'} = get_term(P), - ?line {term, 'Abc'} = get_term(P), - ?line {term, 'ab@c'} = get_term(P), - ?line {term, 'The rain in Spain stays mainly in the plains'} = - get_term(P), - - ?line {term, a} = get_term(P), - ?line {term, ab} = get_term(P), - ?line {term, abc} = get_term(P), - ?line {term, ab@c} = get_term(P), - ?line {term, abcdefghijklmnopq} = get_term(P), - - ?line {term, ''} = get_term(P), - ?line {term, 'a'} = get_term(P), - ?line {term, 'A'} = get_term(P), - ?line {term, 'abc'} = get_term(P), - ?line {term, 'Abc'} = get_term(P), - ?line {term, 'ab@c'} = get_term(P), - ?line {term, 'The rain in Spain stays mainly in the plains'} = - get_term(P), - - ?line {term, a} = get_term(P), - ?line {term, ab} = get_term(P), - ?line {term, abc} = get_term(P), - ?line {term, ab@c} = get_term(P), - ?line {term, ' abcdefghijklmnopq '} = get_term(P), - - ?line runner:recv_eot(P), + P = runner:start(?atoms), + + {term, ''} = get_term(P), + {term, 'a'} = get_term(P), + {term, 'A'} = get_term(P), + {term, 'abc'} = get_term(P), + {term, 'Abc'} = get_term(P), + {term, 'ab@c'} = get_term(P), + {term, 'The rain in Spain stays mainly in the plains'} = get_term(P), + + {term, a} = get_term(P), + {term, ab} = get_term(P), + {term, abc} = get_term(P), + {term, ab@c} = get_term(P), + {term, abcdefghijklmnopq} = get_term(P), + + {term, ''} = get_term(P), + {term, 'a'} = get_term(P), + {term, 'A'} = get_term(P), + {term, 'abc'} = get_term(P), + {term, 'Abc'} = get_term(P), + {term, 'ab@c'} = get_term(P), + {term, 'The rain in Spain stays mainly in the plains'} = get_term(P), + + {term, a} = get_term(P), + {term, ab} = get_term(P), + {term, abc} = get_term(P), + {term, ab@c} = get_term(P), + {term, ' abcdefghijklmnopq '} = get_term(P), + + runner:recv_eot(P), ok. %% Tests formatting various tuples -tuples(suite) -> []; tuples(Config) when is_list(Config) -> - ?line P = runner:start(?tuples), - - ?line {term, {}} = get_term(P), - ?line {term, {a}} = get_term(P), - ?line {term, {a, b}} = get_term(P), - ?line {term, {a, b, c}} = get_term(P), - ?line {term, {1}} = get_term(P), - ?line {term, {[]}} = get_term(P), - ?line {term, {[], []}} = get_term(P), - ?line {term, {[], a, b, c}} = get_term(P), - ?line {term, {[], a, [], b, c}} = get_term(P), - ?line {term, {[], a, '', b, c}} = get_term(P), - - ?line runner:recv_eot(P), + P = runner:start(?tuples), + + {term, {}} = get_term(P), + {term, {a}} = get_term(P), + {term, {a, b}} = get_term(P), + {term, {a, b, c}} = get_term(P), + {term, {1}} = get_term(P), + {term, {[]}} = get_term(P), + {term, {[], []}} = get_term(P), + {term, {[], a, b, c}} = get_term(P), + {term, {[], a, [], b, c}} = get_term(P), + {term, {[], a, '', b, c}} = get_term(P), + + runner:recv_eot(P), ok. %% Tests formatting various lists -lists(suite) -> []; lists(Config) when is_list(Config) -> - ?line P = runner:start(?lists), - - ?line {term, []} = get_term(P), - ?line {term, [a]} = get_term(P), - ?line {term, [a, b]} = get_term(P), - ?line {term, [a, b, c]} = get_term(P), - ?line {term, [1]} = get_term(P), - ?line {term, [[]]} = get_term(P), - ?line {term, [[], []]} = get_term(P), - ?line {term, [[], a, b, c]} = get_term(P), - ?line {term, [[], a, [], b, c]} = get_term(P), - ?line {term, [[], a, '', b, c]} = get_term(P), - - ?line {term, [{name, 'Madonna'}, {age, 21}, {data, [{addr, "E-street", 42}]}]} = - get_term(P), + P = runner:start(?lists), + + {term, []} = get_term(P), + {term, [a]} = get_term(P), + {term, [a, b]} = get_term(P), + {term, [a, b, c]} = get_term(P), + {term, [1]} = get_term(P), + {term, [[]]} = get_term(P), + {term, [[], []]} = get_term(P), + {term, [[], a, b, c]} = get_term(P), + {term, [[], a, [], b, c]} = get_term(P), + {term, [[], a, '', b, c]} = get_term(P), + + {term, [{name, 'Madonna'}, {age, 21}, {data, [{addr, "E-street", 42}]}]} = get_term(P), case os:type() of - vxworks -> - ?line {term, [{pi, _}, {'cos(70)', _}]} = get_term(P), - ?line {term, [[pi, _], ['cos(70)', _]]} = get_term(P), - ?line {term, [[pi, _], [], ["cos(70)", _]]} = - get_term(P); - _ -> - ?line {term, [{pi, 3.1415}, {'cos(70)', 0.34202}]} = get_term(P), - ?line {term, [[pi, 3.1415], ['cos(70)', 0.34202]]} = get_term(P), - ?line {term, [[pi, 3.1415], [], ["cos(70)", 0.34202]]} = - get_term(P) + vxworks -> + {term, [{pi, _}, {'cos(70)', _}]} = get_term(P), + {term, [[pi, _], ['cos(70)', _]]} = get_term(P), + {term, [[pi, _], [], ["cos(70)", _]]} = get_term(P); + _ -> + {term, [{pi, 3.1415}, {'cos(70)', 0.34202}]} = get_term(P), + {term, [[pi, 3.1415], ['cos(70)', 0.34202]]} = get_term(P), + {term, [[pi, 3.1415], [], ["cos(70)", 0.34202]]} = get_term(P) end, - ?line {term, [-1]} = get_term(P), + {term, [-1]} = get_term(P), - ?line runner:recv_eot(P), + runner:recv_eot(P), ok. - - - diff --git a/lib/erl_interface/test/erl_global_SUITE.erl b/lib/erl_interface/test/erl_global_SUITE.erl index a24108f410..ecc6753c7f 100644 --- a/lib/erl_interface/test/erl_global_SUITE.erl +++ b/lib/erl_interface/test/erl_global_SUITE.erl @@ -24,9 +24,9 @@ -include_lib("common_test/include/ct.hrl"). -include("erl_global_SUITE_data/erl_global_test_cases.hrl"). --export([all/0,suite/0,init_per_suite/1,end_per_suite/1, - init_per_testcase/2,end_per_testcase/2, - erl_global_registration/1, erl_global_whereis/1, erl_global_names/1]). +-export([all/0,suite/0, + erl_global_registration/1, + erl_global_whereis/1, erl_global_names/1]). -import(runner, [get_term/1,send_term/2]). @@ -35,62 +35,50 @@ all() -> [erl_global_registration, erl_global_whereis, erl_global_names]. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 30}}]. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?t:minutes(0.25)), - [{watchdog, Dog}|Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - ok. erl_global_registration(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), + P = runner:start(?interpret), + {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), - ?line ok = erl_global_register(P, Fd, ?GLOBAL_NAME), - ?line ok = erl_global_unregister(P, Fd, ?GLOBAL_NAME), + ok = erl_global_register(P, Fd, ?GLOBAL_NAME), + ok = erl_global_unregister(P, Fd, ?GLOBAL_NAME), - ?line 0 = erl_close_connection(P,Fd), - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + 0 = erl_close_connection(P,Fd), + runner:send_eot(P), + runner:recv_eot(P), ok. erl_global_whereis(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), - - ?line Self = self(), - ?line yes = global:register_name(?GLOBAL_NAME, Self), - ?line Self = erl_global_whereis(P, Fd, ?GLOBAL_NAME), - ?line global:unregister_name(?GLOBAL_NAME), - ?line 0 = erl_close_connection(P, Fd), - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + P = runner:start(?interpret), + {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), + + Self = self(), + yes = global:register_name(?GLOBAL_NAME, Self), + Self = erl_global_whereis(P, Fd, ?GLOBAL_NAME), + global:unregister_name(?GLOBAL_NAME), + 0 = erl_close_connection(P, Fd), + runner:send_eot(P), + runner:recv_eot(P), ok. erl_global_names(Config) when is_list(Config) -> - ?line P = runner:start(?interpret), - ?line {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), - - ?line Self = self(), - ?line global:register_name(?GLOBAL_NAME, Self), - ?line {Names1, _N1} = erl_global_names(P, Fd), - ?line true = lists:member(atom_to_list(?GLOBAL_NAME), Names1), - ?line global:unregister_name(?GLOBAL_NAME), - ?line {Names2, _N2} = erl_global_names(P, Fd), - ?line false = lists:member(atom_to_list(?GLOBAL_NAME), Names2), - ?line 0 = erl_close_connection(P, Fd), - ?line runner:send_eot(P), - ?line runner:recv_eot(P), + P = runner:start(?interpret), + {ok, Fd} = erl_connect(P, node(), 42, erlang:get_cookie(), 0), + + Self = self(), + global:register_name(?GLOBAL_NAME, Self), + {Names1, _N1} = erl_global_names(P, Fd), + true = lists:member(atom_to_list(?GLOBAL_NAME), Names1), + global:unregister_name(?GLOBAL_NAME), + {Names2, _N2} = erl_global_names(P, Fd), + false = lists:member(atom_to_list(?GLOBAL_NAME), Names2), + 0 = erl_close_connection(P, Fd), + runner:send_eot(P), + runner:recv_eot(P), ok. %%% Interface functions for erl_interface functions. @@ -98,14 +86,14 @@ erl_global_names(Config) when is_list(Config) -> erl_connect(P, Node, Num, Cookie, Creation) -> send_command(P, erl_connect, [Num, Node, Cookie, Creation]), case get_term(P) of - {term,{Fd,_}} when Fd >= 0 -> {ok,Fd}; - {term,{-1,Errno}} -> {error,Errno} + {term,{Fd,_}} when Fd >= 0 -> {ok,Fd}; + {term,{-1,Errno}} -> {error,Errno} end. erl_close_connection(P, FD) -> send_command(P, erl_close_connection, [FD]), case get_term(P) of - {term,Int} when is_integer(Int) -> Int + {term,Int} when is_integer(Int) -> Int end. erl_global_register(P, Fd, Name) -> @@ -115,15 +103,15 @@ erl_global_register(P, Fd, Name) -> erl_global_whereis(P, Fd, Name) -> send_command(P, erl_global_whereis, [Fd,Name]), case get_term(P) of - {term, What} -> - What + {term, What} -> + What end. erl_global_names(P, Fd) -> send_command(P, erl_global_names, [Fd]), case get_term(P) of - {term, What} -> - What + {term, What} -> + What end. erl_global_unregister(P, Fd, Name) -> @@ -132,11 +120,11 @@ erl_global_unregister(P, Fd, Name) -> get_send_result(P) -> case get_term(P) of - {term,{1,_}} -> ok; - {term,{0, 0}} -> ok; - {term,{-1, Errno}} -> {error,Errno}; - {term,{_,_}}-> - ?t:fail(bad_return_value) + {term,{1,_}} -> ok; + {term,{0, 0}} -> ok; + {term,{-1, Errno}} -> {error,Errno}; + {term,{_,_}}-> + ct:fail(bad_return_value) end. send_command(P, Name, Args) -> diff --git a/lib/erl_interface/test/erl_match_SUITE.erl b/lib/erl_interface/test/erl_match_SUITE.erl index 76952a6f60..5566714092 100644 --- a/lib/erl_interface/test/erl_match_SUITE.erl +++ b/lib/erl_interface/test/erl_match_SUITE.erl @@ -24,246 +24,218 @@ -include_lib("common_test/include/ct.hrl"). -include("erl_match_SUITE_data/match_test_cases.hrl"). --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, - init_per_group/2,end_per_group/2, - atoms/1, lists/1, tuples/1, references/1, pids/1, ports/1, - bind/1, integers/1, floats/1, binaries/1, strings/1]). +-export([all/0, suite/0, + atoms/1, lists/1, tuples/1, references/1, pids/1, ports/1, + bind/1, integers/1, floats/1, binaries/1, strings/1]). %% For interactive running of matcher. -export([start_matcher/1, erl_match/3]). %% This test suite tests the erl_match() function. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}]. all() -> [atoms, lists, tuples, references, pids, ports, bind, integers, floats, binaries, strings]. -groups() -> - []. -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -atoms(suite) -> []; atoms(Config) when is_list(Config) -> - ?line P = start_matcher(Config), + P = start_matcher(Config), - ?line eq(P, '', ''), - ?line eq(P, a, a), - ?line ne(P, a, b), - ?line ne(P, a, aa), - ?line eq(P, kalle, kalle), - ?line ne(P, kalle, arne), + eq(P, '', ''), + eq(P, a, a), + ne(P, a, b), + ne(P, a, aa), + eq(P, kalle, kalle), + ne(P, kalle, arne), - ?line ne(P, kalle, 42), - ?line ne(P, 42, kalle), + ne(P, kalle, 42), + ne(P, 42, kalle), - ?line runner:finish(P), + runner:finish(P), ok. -lists(suite) -> []; lists(Config) when is_list(Config) -> - ?line P = start_matcher(Config), - ?line eq(P, [], []), + P = start_matcher(Config), + eq(P, [], []), - ?line ne(P, [], [a]), - ?line ne(P, [a], []), + ne(P, [], [a]), + ne(P, [a], []), - ?line eq(P, [a], [a]), - ?line ne(P, [a], [b]), + eq(P, [a], [a]), + ne(P, [a], [b]), - ?line eq(P, [a|b], [a|b]), - ?line ne(P, [a|b], [a|x]), + eq(P, [a|b], [a|b]), + ne(P, [a|b], [a|x]), - ?line eq(P, [a, b], [a, b]), - ?line ne(P, [a, b], [a, x]), + eq(P, [a, b], [a, b]), + ne(P, [a, b], [a, x]), - ?line eq(P, [a, b, c], [a, b, c]), - ?line ne(P, [a, b|c], [a, b|x]), - ?line ne(P, [a, b, c], [a, b, x]), - ?line ne(P, [a, b|c], [a, b|x]), - ?line ne(P, [a, x|c], [a, b|c]), - ?line ne(P, [a, b, c], [a, x, c]), + eq(P, [a, b, c], [a, b, c]), + ne(P, [a, b|c], [a, b|x]), + ne(P, [a, b, c], [a, b, x]), + ne(P, [a, b|c], [a, b|x]), + ne(P, [a, x|c], [a, b|c]), + ne(P, [a, b, c], [a, x, c]), - ?line runner:finish(P), + runner:finish(P), ok. -tuples(suite) -> []; tuples(Config) when is_list(Config) -> - ?line P = start_matcher(Config), + P = start_matcher(Config), - ?line ne(P, {}, {a, b}), - ?line ne(P, {a, b}, {}), - ?line ne(P, {a}, {a, b}), - ?line ne(P, {a, b}, {a}), + ne(P, {}, {a, b}), + ne(P, {a, b}, {}), + ne(P, {a}, {a, b}), + ne(P, {a, b}, {a}), - ?line eq(P, {}, {}), + eq(P, {}, {}), - ?line eq(P, {a}, {a}), - ?line ne(P, {a}, {b}), + eq(P, {a}, {a}), + ne(P, {a}, {b}), - ?line eq(P, {1}, {1}), - ?line ne(P, {1}, {2}), + eq(P, {1}, {1}), + ne(P, {1}, {2}), - ?line eq(P, {a, b}, {a, b}), - ?line ne(P, {x, b}, {a, b}), + eq(P, {a, b}, {a, b}), + ne(P, {x, b}, {a, b}), - ?line ne(P, {error, x}, {error, y}), - ?line ne(P, {error, {undefined, {subscriber, last}}}, - {error, {undefined, {subscriber, name}}}), + ne(P, {error, x}, {error, y}), + ne(P, {error, {undefined, {subscriber, last}}}, + {error, {undefined, {subscriber, name}}}), - ?line runner:finish(P), + runner:finish(P), ok. -references(suite) -> []; references(Config) when is_list(Config) -> - ?line P = start_matcher(Config), - ?line Ref1 = make_ref(), - ?line Ref2 = make_ref(), - - ?line eq(P, Ref1, Ref1), - ?line eq(P, Ref2, Ref2), - ?line ne(P, Ref1, Ref2), - ?line ne(P, Ref2, Ref1), - - ?line runner:finish(P), + P = start_matcher(Config), + Ref1 = make_ref(), + Ref2 = make_ref(), + + eq(P, Ref1, Ref1), + eq(P, Ref2, Ref2), + ne(P, Ref1, Ref2), + ne(P, Ref2, Ref1), + + runner:finish(P), ok. -pids(suite) -> []; pids(Config) when is_list(Config) -> - ?line P = start_matcher(Config), - ?line Pid1 = c:pid(0,1,2), - ?line Pid2 = c:pid(0,1,3), - - ?line eq(P, self(), self()), - ?line eq(P, Pid1, Pid1), - ?line ne(P, Pid1, self()), - ?line ne(P, Pid2, Pid1), - - ?line runner:finish(P), + P = start_matcher(Config), + Pid1 = c:pid(0,1,2), + Pid2 = c:pid(0,1,3), + + eq(P, self(), self()), + eq(P, Pid1, Pid1), + ne(P, Pid1, self()), + ne(P, Pid2, Pid1), + + runner:finish(P), ok. -ports(suite) -> []; ports(Config) when is_list(Config) -> case os:type() of - vxworks -> - {skipped,"not on vxworks, pucko"}; - _ -> - ?line P = start_matcher(Config), - ?line P2 = start_matcher(Config), - - ?line eq(P, P, P), - ?line ne(P, P, P2), - - ?line runner:finish(P), - ?line runner:finish(P2), - ok + vxworks -> + {skipped,"not on vxworks, pucko"}; + _ -> + P = start_matcher(Config), + P2 = start_matcher(Config), + + eq(P, P, P), + ne(P, P, P2), + + runner:finish(P), + runner:finish(P2), + ok end. -integers(suite) -> []; integers(Config) when is_list(Config) -> - ?line P = start_matcher(Config), - ?line I1 = 123, - ?line I2 = 12345, - ?line I3 = -123, - ?line I4 = 2234, - - ?line eq(P, I1, I1), - ?line eq(P, I2, I2), - ?line ne(P, I1, I2), - ?line ne(P, I1, I3), - ?line eq(P, I4, I4), - - ?line runner:finish(P), + P = start_matcher(Config), + I1 = 123, + I2 = 12345, + I3 = -123, + I4 = 2234, + + eq(P, I1, I1), + eq(P, I2, I2), + ne(P, I1, I2), + ne(P, I1, I3), + eq(P, I4, I4), + + runner:finish(P), ok. -floats(suite) -> []; floats(Config) when is_list(Config) -> - ?line P = start_matcher(Config), - ?line F1 = 3.1414, - ?line F2 = 3.1415, - ?line F3 = 3.1416, - - ?line S1 = "string", - ?line S2 = "string2", - - ?line eq(P, F1, F1), - ?line eq(P, F2, F2), - ?line ne(P, F1, F2), - ?line ne(P, F3, F2), - - ?line eq(P, S2, S2), - ?line ne(P, S1, S2), - - ?line runner:finish(P), + P = start_matcher(Config), + F1 = 3.1414, + F2 = 3.1415, + F3 = 3.1416, + + S1 = "string", + S2 = "string2", + + eq(P, F1, F1), + eq(P, F2, F2), + ne(P, F1, F2), + ne(P, F3, F2), + + eq(P, S2, S2), + ne(P, S1, S2), + + runner:finish(P), ok. -binaries(suite) -> []; binaries(Config) when is_list(Config) -> - ?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"), - - ?line eq(P, Bin1, Bin1), - ?line eq(P, Bin2, Bin2), - ?line eq(P, Bin3, Bin3), - ?line ne(P, Bin1, Bin2), - ?line ne(P, Bin1, Bin3), - ?line ne(P, Bin2, Bin3), - - ?line runner:finish(P), + P = start_matcher(Config), + Bin1 = term_to_binary({kalle, 146015, {kungsgatan, 23}}), + Bin2 = term_to_binary(sune), + Bin3 = list_to_binary("sune"), + + eq(P, Bin1, Bin1), + eq(P, Bin2, Bin2), + eq(P, Bin3, Bin3), + ne(P, Bin1, Bin2), + ne(P, Bin1, Bin3), + ne(P, Bin2, Bin3), + + runner:finish(P), ok. - -strings(suite) -> []; strings(Config) when is_list(Config) -> - ?line P = start_matcher(Config), + P = start_matcher(Config), - ?line S1 = "string", - ?line S2 = "streng", - ?line S3 = "String", - - ?line eq(P, S1, S1), - ?line ne(P, S1, S2), - ?line ne(P, S1, S3), + S1 = "string", + S2 = "streng", + S3 = "String", - ?line runner:finish(P), - ok. + eq(P, S1, S1), + ne(P, S1, S2), + ne(P, S1, S3), + runner:finish(P), + ok. -bind(suite) -> []; bind(Config) when is_list(Config) -> - ?line P = start_bind(Config), - ?line S = "[X,Y,Z]", - ?line L1 = [301,302,302], - ?line L2 = [65,66,67], - - ?line bind_ok(P, S, L1), - ?line bind_ok(P, S, L2), - - ?line runner:finish(P), + P = start_bind(Config), + S = "[X,Y,Z]", + L1 = [301,302,302], + L2 = [65,66,67], + + bind_ok(P, S, L1), + bind_ok(P, S, L2), + + runner:finish(P), ok. start_bind(Config) -> @@ -279,15 +251,12 @@ erl_bind(Port, Pattern, Term) -> Port ! {self(), {command, [$b, Pattern, 0]}}, runner:send_term(Port, Term), case runner:get_term(Port) of - {term, 0} -> false; - {term, 1} -> true + {term, 0} -> false; + {term, 1} -> true end. - - - start_matcher(Config) -> runner:start(?erl_match_server). @@ -303,8 +272,6 @@ erl_match(Port, Pattern, Term) -> runner:send_term(Port, Pattern), runner:send_term(Port, Term), case runner:get_term(Port) of - {term, 0} -> false; - {term, 1} -> true + {term, 0} -> false; + {term, 1} -> true end. - - diff --git a/lib/erl_interface/test/port_call_SUITE.erl b/lib/erl_interface/test/port_call_SUITE.erl index 04b80de34a..fb10bd895f 100644 --- a/lib/erl_interface/test/port_call_SUITE.erl +++ b/lib/erl_interface/test/port_call_SUITE.erl @@ -32,96 +32,78 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% --export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, basic/1]). +-export([all/0, suite/0, basic/1]). + % Private exports -include_lib("common_test/include/ct.hrl"). - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {seconds, 10}}]. all() -> -[basic]. - -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. + [basic]. -basic(suite) -> []; basic(Config) when is_list(Config) -> case os:type() of - {unix, linux} -> - do_basic(Config); - {unix, sunos} -> - do_basic(Config); - {win32,_} -> - do_basic(Config); - _ -> - {skipped, "Dynamic linking and erl_interface not fully examined" - " on this platform..."} + {unix, linux} -> + do_basic(Config); + {unix, sunos} -> + do_basic(Config); + {win32,_} -> + do_basic(Config); + _ -> + {skipped, "Dynamic linking and erl_interface not fully examined" + " on this platform..."} end. do_basic(Config) -> - ?line Dog = test_server:timetrap(test_server:seconds(10)), - ?line Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), - ?line erl_ddll:start(), + erl_ddll:start(), %% Load the echo driver and verify that it was loaded. {ok,L1,L2}=load_port_call_driver(Path), %% Verify that the driver works. - ?line Port = open_port({spawn, port_call_drv}, [eof]), - ?line {hej, "hopp",4711,123445567436543653} = - erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}), - ?line {hej, "hopp",4711,123445567436543653} = - erlang:port_call(Port,0,{hej, "hopp",4711,123445567436543653}), - ?line {[], a, [], b, c} = - erlang:port_call(Port,1,{hej, "hopp",4711,123445567436543653}), - ?line {return, {[], a, [], b, c}} = - erlang:port_call(Port,2,{[], a, [], b, c}), - ?line List = lists:duplicate(200,5), - ?line {return, List} = erlang:port_call(Port,2,List), - ?line {'EXIT',{badarg,_}} = (catch erlang:port_call(Port,4711,[])), - ?line {'EXIT',{badarg,_}} = (catch erlang:port_call(sune,2,[])), - ?line register(gunnar,Port), - ?line {return, List} = erlang:port_call(gunnar,2,List), - ?line {return, a} = erlang:port_call(gunnar,2,a), - ?line erlang:port_close(Port), + Port = open_port({spawn, port_call_drv}, [eof]), + {hej, "hopp",4711,123445567436543653} = + erlang:port_call(Port,{hej, "hopp",4711,123445567436543653}), + {hej, "hopp",4711,123445567436543653} = + erlang:port_call(Port,0,{hej, "hopp",4711,123445567436543653}), + {[], a, [], b, c} = + erlang:port_call(Port,1,{hej, "hopp",4711,123445567436543653}), + {return, {[], a, [], b, c}} = + erlang:port_call(Port,2,{[], a, [], b, c}), + List = lists:duplicate(200,5), + {return, List} = erlang:port_call(Port,2,List), + {'EXIT',{badarg,_}} = (catch erlang:port_call(Port,4711,[])), + {'EXIT',{badarg,_}} = (catch erlang:port_call(sune,2,[])), + register(gunnar,Port), + {return, List} = erlang:port_call(gunnar,2,List), + {return, a} = erlang:port_call(gunnar,2,a), + erlang:port_close(Port), %% Unload the driver and verify that it was unloaded. ok=unload_port_call_driver(L1,L2), - ?line {error, {already_started, _}} = erl_ddll:start(), - ?line ok = erl_ddll:stop(), - - ?line test_server:timetrap_cancel(Dog), + {error, {already_started, _}} = erl_ddll:start(), + ok = erl_ddll:stop(), ok. load_port_call_driver(Path) -> - ?line {ok, L1} = erl_ddll:loaded_drivers(), - ?line ok = erl_ddll:load_driver(Path, port_call_drv), - ?line {ok, L2} = erl_ddll:loaded_drivers(), - ?line ["port_call_drv"] = ordsets:to_list(ordsets:subtract(ordsets:from_list(L2), - ordsets:from_list(L1))), + {ok, L1} = erl_ddll:loaded_drivers(), + ok = erl_ddll:load_driver(Path, port_call_drv), + {ok, L2} = erl_ddll:loaded_drivers(), + ["port_call_drv"] = ordsets:to_list(ordsets:subtract(ordsets:from_list(L2), + ordsets:from_list(L1))), {ok,L1,L2}. unload_port_call_driver(L1,L2) -> - ?line {ok, L2} = erl_ddll:loaded_drivers(), - ?line ok = erl_ddll:unload_driver(port_call_drv), - ?line {ok, L3} = erl_ddll:loaded_drivers(), - ?line [] = ordsets:to_list(ordsets:subtract(ordsets:from_list(L3), - ordsets:from_list(L1))), + {ok, L2} = erl_ddll:loaded_drivers(), + ok = erl_ddll:unload_driver(port_call_drv), + {ok, L3} = erl_ddll:loaded_drivers(), + [] = ordsets:to_list(ordsets:subtract(ordsets:from_list(L3), + ordsets:from_list(L1))), ok. - diff --git a/lib/erl_interface/test/runner.erl b/lib/erl_interface/test/runner.erl index 85439140cc..9a27eda038 100644 --- a/lib/erl_interface/test/runner.erl +++ b/lib/erl_interface/test/runner.erl @@ -25,7 +25,7 @@ start/1, send_term/2, finish/1, send_eot/1, recv_eot/1, get_term/1, get_term/2]). --define(default_timeout, test_server:seconds(5)). +-define(default_timeout, 5000). %% Executes a test case in a C program. %% @@ -45,7 +45,7 @@ test(Tc, Timeout) -> io:format("In this test case, a success/failure result was"), io:format("expected from the C program.\n"), io:format("Received: ~p", [Other]), - test_server:fail() + ct:fail(badresult) end. %% Executes a test case in a C program. Returns the port. @@ -80,7 +80,7 @@ send_eot(Port) when is_port(Port) -> Port ! {self(), {command, [$e]}}. %% Waits for an 'eot' indication from the C program. -%% Either returns 'ok' or invokes test_server:fail(). +%% Either returns 'ok' or invokes ct:fail(badresult). recv_eot(Port) when is_port(Port) -> case get_term(Port) of @@ -90,12 +90,12 @@ recv_eot(Port) when is_port(Port) -> io:format("Error finishing test case. Expected eof from"), io:format("C program, but got:"), io:format("~p", [Other]), - test_server:fail() + ct:fail(badresult) end. %% Reads a term from the C program. %% -%% Returns: {term, Term}|eot|'NULL' or calls test_server:fail/1,2. +%% Returns: {term, Term}|eot|'NULL' or calls ct:fail/1,2. get_term(Port) -> get_term(Port, ?default_timeout). @@ -105,9 +105,9 @@ get_term(Port, Timeout) -> [$b|Bytes] -> {bytes, Bytes}; [$f] -> - test_server:fail(); + ct:fail(failure); [$f|Reason] -> - test_server:fail(Reason); + ct:fail(Reason); [$t|Term] -> {term, binary_to_term(list_to_binary(Term))}; [$N] -> @@ -119,7 +119,7 @@ get_term(Port, Timeout) -> get_term(Port, Timeout); Other -> io:format("Garbage received from C program: ~p", [Other]), - test_server:fail("Illegal response from C program") + ct:fail("Illegal response from C program") end. get_reply(Port, Timeout) when is_port(Port) -> @@ -127,5 +127,5 @@ get_reply(Port, Timeout) when is_port(Port) -> {Port, {data, Reply}} -> Reply after Timeout -> - test_server:fail("No response from C program") + ct:fail("No response from C program") end. diff --git a/lib/hipe/icode/hipe_icode_fp.erl b/lib/hipe/icode/hipe_icode_fp.erl index ea62aeadff..4a5877074c 100644 --- a/lib/hipe/icode/hipe_icode_fp.erl +++ b/lib/hipe/icode/hipe_icode_fp.erl @@ -34,13 +34,37 @@ -include("hipe_icode.hrl"). -include("../flow/cfg.hrl"). --record(state, {edge_map = gb_trees:empty() :: gb_trees:tree(), - fp_ebb_map = gb_trees:empty() :: gb_trees:tree(), - cfg :: #cfg{}}). +-type mapped_fvar() :: icode_fvar() | {assigned, icode_fvar()} . +-type incoming_fvars() :: [{icode_lbl(), mapped_fvar()}]. +-type initial_var_map() :: #{icode_var() => incoming_fvars()}. +-type bb_phi_list() :: [{icode_fvar(), [{icode_lbl(), icode_fvar()}]}]. +-type var_map_phi() :: #{phi => bb_phi_list(), + icode_var() => mapped_fvar()}. +-type var_map() :: #{icode_var() => mapped_fvar()}. + +-type edge() :: {icode_lbl(), icode_lbl()}. +-type edge_map() :: #{edge() => var_map()}. + +-type worklist(Item) :: {[Item], [Item], gb_sets:set(Item)}. +-type worklist() :: worklist(icode_lbl()). + +-type fail_lbl() :: [] | icode_lbl(). +-type in_block() :: {true, fail_lbl()} | false. +-type fp_ebb_map() :: #{{inblock_in | inblock_out, icode_lbl()} | edge() + => in_block()}. + +-record(state, {edge_map = #{} :: edge_map(), + fp_ebb_map = #{} :: fp_ebb_map(), + cfg :: cfg()}). +-type state() :: #state{}. + +-type icode_phi() :: #icode_phi{}. +-type icode_variable() :: #icode_variable{}. +-type icode_const() :: #icode_const{}. %%-------------------------------------------------------------------- --spec cfg(#cfg{}) -> #cfg{}. +-spec cfg(cfg()) -> cfg(). cfg(Cfg) -> %%hipe_icode_cfg:pp(Cfg), @@ -59,10 +83,14 @@ cfg(Cfg) -> %% corresponding fcheckerror. %%-------------------------------------------------------------------- +-spec annotate_fclearerror(cfg()) -> cfg(). + annotate_fclearerror(Cfg) -> Labels = hipe_icode_cfg:reverse_postorder(Cfg), annotate_fclearerror(Labels, Cfg). +-spec annotate_fclearerror([icode_lbl()], cfg()) -> cfg(). + annotate_fclearerror([Label|Left], Cfg) -> BB = hipe_icode_cfg:bb(Cfg, Label), Code = hipe_bb:code(BB), @@ -73,6 +101,9 @@ annotate_fclearerror([Label|Left], Cfg) -> annotate_fclearerror([], Cfg) -> Cfg. +-spec annotate_fclearerror1(icode_instrs(), icode_lbl(), cfg(), icode_instrs()) + -> icode_instrs(). + annotate_fclearerror1([I|Left], Label, Cfg, Acc) -> case I of #icode_call{} -> @@ -90,6 +121,9 @@ annotate_fclearerror1([I|Left], Label, Cfg, Acc) -> annotate_fclearerror1([], _Label, _Cfg, Acc) -> lists:reverse(Acc). +-spec lookahead_for_fcheckerror(icode_instrs(), icode_lbl(), cfg()) -> + fail_lbl(). + lookahead_for_fcheckerror([I|Left], Label, Cfg) -> case I of #icode_call{} -> @@ -111,10 +145,14 @@ lookahead_for_fcheckerror([], Label, Cfg) -> lookahead_for_fcheckerror(Code, Succ, Cfg) end. +-spec unannotate_fclearerror(cfg()) -> cfg(). + unannotate_fclearerror(Cfg) -> Labels = hipe_icode_cfg:reverse_postorder(Cfg), unannotate_fclearerror(Labels, Cfg). +-spec unannotate_fclearerror([icode_lbl()], cfg()) -> cfg(). + unannotate_fclearerror([Label|Left], Cfg) -> BB = hipe_icode_cfg:bb(Cfg, Label), Code = hipe_bb:code(BB), @@ -125,6 +163,9 @@ unannotate_fclearerror([Label|Left], Cfg) -> unannotate_fclearerror([], Cfg) -> Cfg. +-spec unannotate_fclearerror1(icode_instrs(), icode_instrs()) -> + icode_instrs(). + unannotate_fclearerror1([I|Left], Acc) -> case I of #icode_call{} -> @@ -145,10 +186,14 @@ unannotate_fclearerror1([], Acc) -> %% Make float EBBs %%-------------------------------------------------------------------- +-spec place_fp_blocks(state()) -> state(). + place_fp_blocks(State) -> WorkList = new_worklist(State), transform_block(WorkList, State). +-spec transform_block(worklist(), state()) -> state(). + transform_block(WorkList, State) -> case get_work(WorkList) of none -> @@ -182,6 +227,10 @@ transform_block(WorkList, State) -> end end. +-spec update_maps(state(), icode_lbl(), ordsets:ordset(icode_lbl()), + var_map(), ordsets:ordset(icode_lbl()), var_map()) + -> fixpoint | {state(), [icode_lbl()]}. + update_maps(State, Label, SuccSet, SuccMap, FailSet, FailMap) -> {NewState, Add1} = update_maps(State, Label, SuccSet, SuccMap, []), case update_maps(NewState, Label, FailSet, FailMap, Add1) of @@ -189,6 +238,10 @@ update_maps(State, Label, SuccSet, SuccMap, FailSet, FailMap) -> {_NewState1, _Add} = Ret -> Ret end. +-spec update_maps(state(), icode_lbl(), ordsets:ordset(icode_lbl()), + var_map(), [icode_lbl()]) + -> {state(), [icode_lbl()]}. + update_maps(State, From, [To|Left], Map, Acc) -> case state__map_update(State, From, To, Map) of fixpoint -> @@ -199,10 +252,13 @@ update_maps(State, From, [To|Left], Map, Acc) -> update_maps(State, _From, [], _Map, Acc) -> {State, Acc}. +-spec transform_instrs(icode_instrs(), edge_map(), var_map(), icode_instrs()) + -> {var_map(), icode_instrs()}. + transform_instrs([I|Left], PhiMap, Map, Acc) -> Defines = hipe_icode:defines(I), - NewMap = delete_all(Defines, Map), - NewPhiMap = delete_all(Defines, PhiMap), + NewMap = maps:without(Defines, Map), + NewPhiMap = maps:without(Defines, PhiMap), case I of #icode_phi{} -> Uses = hipe_icode:uses(I), @@ -214,7 +270,7 @@ transform_instrs([I|Left], PhiMap, Map, Acc) -> %% All arguments are untagged. Let's untag the destination. Dst = hipe_icode:phi_dst(I), NewDst = hipe_icode:mk_new_fvar(), - NewMap1 = gb_trees:enter(Dst, NewDst, NewMap), + NewMap1 = NewMap#{Dst => NewDst}, NewI = subst_phi_uncond(I, NewDst, PhiMap), transform_instrs(Left, NewPhiMap, NewMap1, [NewI|Acc]); _ -> @@ -233,7 +289,7 @@ transform_instrs([I|Left], PhiMap, Map, Acc) -> [Src] -> case lookup(Src, Map) of none -> - NewMap1 = gb_trees:enter(Src, {assigned, Dst}, NewMap), + NewMap1 = NewMap#{Src => {assigned, Dst}}, transform_instrs(Left, NewPhiMap, NewMap1, [I|Acc]); Dst -> %% This is the instruction that untagged the variable. @@ -256,7 +312,7 @@ transform_instrs([I|Left], PhiMap, Map, Acc) -> unsafe_tag_float -> [Dst] = hipe_icode:defines(I), [Src] = hipe_icode:uses(I), - NewMap1 = gb_trees:enter(Dst, {assigned, Src}, NewMap), + NewMap1 = NewMap#{Dst => {assigned, Src}}, transform_instrs(Left, NewPhiMap, NewMap1,[I|Acc]); _ -> {NewMap1, NewAcc} = check_for_fop_candidates(I, NewMap, Acc), @@ -269,6 +325,9 @@ transform_instrs([I|Left], PhiMap, Map, Acc) -> transform_instrs([], _PhiMap, Map, Acc) -> {Map, lists:reverse(Acc)}. +-spec check_for_fop_candidates(icode_instr(), var_map(), icode_instrs()) + -> {var_map(), icode_instrs()}. + check_for_fop_candidates(I, Map, Acc) -> case is_fop_cand(I) of false -> @@ -311,6 +370,8 @@ check_for_fop_candidates(I, Map, Acc) -> end. +-spec handle_untagged_arguments(icode_instr(), var_map()) -> icode_instrs(). + %% If this is an instruction that needs to operate on tagged values, %% which currently are untagged, we must tag the values and perhaps %% end the fp ebb. @@ -322,23 +383,24 @@ handle_untagged_arguments(I, Map) -> Tag -> TagIntrs = [hipe_icode:mk_primop([Dst], unsafe_tag_float, - [gb_trees:get(Dst, Map)]) || Dst <- Tag], + [maps:get(Dst, Map)]) || Dst <- Tag], [I|TagIntrs] end. +-spec do_prelude(var_map_phi()) -> {[icode_phi()], var_map()}. + %% Add phi nodes for untagged fp values. -do_prelude(Map) -> - case gb_trees:lookup(phi, Map) of - none -> - {[], Map}; - {value, List} -> - %%io:format("Adding phi: ~w\n", [List]), - Fun = fun ({FVar, Bindings}, Acc) -> - [hipe_icode:mk_phi(FVar, Bindings)|Acc] - end, - {lists:foldl(Fun, [], List), gb_trees:delete(phi, Map)} - end. +do_prelude(Map = #{phi := List}) -> + %%io:format("Adding phi: ~w\n", [List]), + Fun = fun ({FVar, Bindings}, Acc) -> + [hipe_icode:mk_phi(FVar, Bindings)|Acc] + end, + {lists:foldl(Fun, [], List), maps:remove(phi, Map)}; +do_prelude(Map) -> {[], Map}. + +-spec split_code([I]) -> {[I], I} when + I :: icode_instr(). split_code(Code) -> split_code(Code, []). @@ -349,6 +411,8 @@ split_code([I|Left], Acc) -> split_code(Left, [I|Acc]). +-spec finalize(state()) -> state(). + %% When all code is mapped to fp instructions we must make sure that %% the fp ebb information going into each block is the same as the %% information coming out of each predecessor. Otherwise, we must add @@ -360,17 +424,25 @@ finalize(State) -> Edges = needs_fcheckerror(NewState), finalize(Edges, NewState). +-spec finalize([edge()], state()) -> state(). + finalize([{From, To}|Left], State) -> NewState = add_fp_ebb_fixup(From, To, State), finalize(Left, NewState); finalize([], State) -> State. +-spec needs_fcheckerror(state()) -> [{none | icode_lbl(), icode_lbl()}]. + needs_fcheckerror(State) -> Cfg = state__cfg(State), Labels = hipe_icode_cfg:labels(Cfg), needs_fcheckerror(Labels, State, []). +-spec needs_fcheckerror([icode_lbl()], state(), + [{none | icode_lbl(), icode_lbl()}]) + -> [{none | icode_lbl(), icode_lbl()}]. + needs_fcheckerror([Label|Left], State, Acc) -> case state__get_in_block_in(State, Label) of {true, _} -> @@ -395,6 +467,8 @@ needs_fcheckerror([Label|Left], State, Acc) -> needs_fcheckerror([], _State, Acc) -> Acc. +-spec add_fp_ebb_fixup(none | icode_lbl(), icode_lbl(), state()) -> state(). + add_fp_ebb_fixup('none', To, State) -> %% Add the fcheckerror to the start of the block. BB = state__bb(State, To), @@ -416,9 +490,15 @@ add_fp_ebb_fixup(From, To, State) -> NewToBB = hipe_bb:code_update(ToBB, NewToCode), state__bb_add(NewState1, To, NewToBB). +-spec redirect_phis(icode_instrs(), icode_lbl(), icode_lbl()) + -> icode_instrs(). + redirect_phis(Code, OldFrom, NewFrom) -> redirect_phis(Code, OldFrom, NewFrom, []). +-spec redirect_phis(icode_instrs(), icode_lbl(), icode_lbl(), icode_instrs()) + -> icode_instrs(). + redirect_phis([I|Is] = Code, OldFrom, NewFrom, Acc) -> case I of #icode_phi{} -> @@ -430,13 +510,20 @@ redirect_phis([I|Is] = Code, OldFrom, NewFrom, Acc) -> redirect_phis([], _OldFrom, _NewFrom, Acc) -> lists:reverse(Acc). +-spec subst_phi(icode_phi(), icode_variable(), edge_map()) + -> icode_phi(). + subst_phi(I, Dst, Map) -> ArgList = subst_phi_uses0(hipe_icode:phi_arglist(I), Map, []), hipe_icode:mk_phi(Dst, ArgList). +-spec subst_phi_uses0([{icode_lbl(), icode_variable()}], edge_map(), + [{icode_lbl(), icode_variable()}]) + -> [{icode_lbl(), icode_variable()}]. + subst_phi_uses0([{Pred, Var}|Left], Map, Acc) -> - case gb_trees:lookup(Var, Map) of - {value, List} -> + case Map of + #{Var := List} -> case lists:keyfind(Pred, 1, List) of {Pred, {assigned, _NewVar}} -> %% The variable is untagged, but it has been assigned. Keep it! @@ -448,20 +535,27 @@ subst_phi_uses0([{Pred, Var}|Left], Map, Acc) -> %% The variable is not untagged. subst_phi_uses0(Left, Map, [{Pred, Var} | Acc]) end; - none -> + #{} -> %% The variable is not untagged. subst_phi_uses0(Left, Map, [{Pred, Var} | Acc]) end; subst_phi_uses0([], _Map, Acc) -> Acc. +-spec subst_phi_uncond(icode_phi(), icode_variable(), edge_map()) + -> icode_phi(). + subst_phi_uncond(I, Dst, Map) -> ArgList = subst_phi_uses_uncond0(hipe_icode:phi_arglist(I), Map, []), hipe_icode:mk_phi(Dst, ArgList). +-spec subst_phi_uses_uncond0([{icode_lbl(), icode_variable()}], edge_map(), + [{icode_lbl(), icode_variable()}]) + -> [{icode_lbl(), icode_variable()}]. + subst_phi_uses_uncond0([{Pred, Var}|Left], Map, Acc) -> - case gb_trees:lookup(Var, Map) of - {value, List} -> + case Map of + #{Var := List} -> case lists:keyfind(Pred, 1, List) of {Pred, {assigned, NewVar}} -> %% The variable is untagged! @@ -473,13 +567,15 @@ subst_phi_uses_uncond0([{Pred, Var}|Left], Map, Acc) -> %% The variable is not untagged. subst_phi_uses_uncond0(Left, Map, [{Pred, Var} | Acc]) end; - none -> + #{} -> %% The variable is not untagged. subst_phi_uses_uncond0(Left, Map, [{Pred, Var} | Acc]) end; subst_phi_uses_uncond0([], _Map, Acc) -> Acc. +-spec place_error_handling(worklist(), state()) -> state(). + place_error_handling(WorkList, State) -> case get_work(WorkList) of none -> @@ -502,6 +598,9 @@ place_error_handling(WorkList, State) -> end end. +-spec place_error(icode_instrs(), in_block(), icode_instrs()) + -> {icode_instrs(), in_block()}. + place_error([I|Left], InBlock, Acc) -> case I of #icode_call{} -> @@ -638,12 +737,10 @@ instr_allowed_in_fp_ebb(Instr) -> %%============================================================= %% ------------------------------------------------------------ -%% Handling the gb_tree +%% Handling the variable map -delete_all([Key|Left], Tree) -> - delete_all(Left, gb_trees:delete_any(Key, Tree)); -delete_all([], Tree) -> - Tree. +-spec lookup_list([icode_var() | icode_const()], var_map()) + -> [none | icode_fvar()]. lookup_list(List, Info) -> lookup_list(List, fun lookup/2, Info, []). @@ -653,33 +750,43 @@ lookup_list([H|T], Fun, Info, Acc) -> lookup_list([], _, _, Acc) -> lists:reverse(Acc). +-spec lookup(icode_var() | icode_const(), var_map()) -> none | icode_fvar(). + lookup(Key, Tree) -> case hipe_icode:is_const(Key) of %% This can be true if the same constant has been %% untagged more than once true -> none; false -> - case gb_trees:lookup(Key, Tree) of - none -> none; - {value, {assigned, Val}} -> Val; - {value, Val} -> Val + case Tree of + #{Key := {assigned, Val}} -> Val; + #{Key := Val} -> Val; + #{} -> none end end. +-spec lookup_list_keep_consts([icode_var() | icode_const()], var_map()) + -> [none | icode_fvar() | icode_const()]. + lookup_list_keep_consts(List, Info) -> lookup_list(List, fun lookup_keep_consts/2, Info, []). +-spec lookup_keep_consts(icode_var() | icode_const(), var_map()) + -> none | icode_fvar() | icode_const(). + lookup_keep_consts(Key, Tree) -> case hipe_icode:is_const(Key) of true -> Key; false -> - case gb_trees:lookup(Key, Tree) of - none -> none; - {value, {assigned, Val}} -> Val; - {value, Val} -> Val + case Tree of + #{Key := {assigned, Val}} -> Val; + #{Key := Val} -> Val; + #{} -> none end end. +-spec get_type(icode_argument()) -> erl_types:erl_type(). + get_type(Var) -> case hipe_icode:is_const(Var) of true -> erl_types:t_from_term(hipe_icode:const_value(Var)); @@ -695,98 +802,108 @@ get_type(Var) -> %% ------------------------------------------------------------ %% Handling the map from variables to fp-variables +-spec join_maps([edge()], edge_map()) -> initial_var_map(). + join_maps(Edges, EdgeMap) -> - join_maps(Edges, EdgeMap, gb_trees:empty()). + join_maps(Edges, EdgeMap, #{}). join_maps([Edge = {Pred, _}|Left], EdgeMap, Map) -> - case gb_trees:lookup(Edge, EdgeMap) of - none -> + case EdgeMap of + #{Edge := OldMap} -> + NewMap = join_maps0(maps:to_list(OldMap), Pred, Map), + join_maps(Left, EdgeMap, NewMap); + #{} -> %% All predecessors have not been handled. Use empty map. - gb_trees:empty(); - {value, OldMap} -> - NewMap = join_maps0(gb_trees:to_list(OldMap), Pred, Map), - join_maps(Left, EdgeMap, NewMap) + #{} end; join_maps([], _, Map) -> Map. -join_maps0([{phi, _}|Tail], Pred, Map) -> - join_maps0(Tail, Pred, Map); -join_maps0([{Var, FVar}|Tail], Pred, Map) -> - case gb_trees:lookup(Var, Map) of - none -> - join_maps0(Tail, Pred, gb_trees:enter(Var, [{Pred, FVar}], Map)); - {value, List} -> +-spec join_maps0(list(), icode_lbl(), initial_var_map()) -> initial_var_map(). + +join_maps0([{Var=#icode_variable{kind=var}, FVar}|Tail], Pred, Map) -> + case Map of + #{Var := List} -> case lists:keyfind(Pred, 1, List) of false -> - join_maps0(Tail, Pred, gb_trees:update(Var, [{Pred, FVar}|List], Map)); + join_maps0(Tail, Pred, Map#{Var := [{Pred, FVar}|List]}); {Pred, FVar} -> %% No problem. join_maps0(Tail, Pred, Map); _ -> exit('New binding to same variable') - end + end; + #{} -> + join_maps0(Tail, Pred, Map#{Var => [{Pred, FVar}]}) end; join_maps0([], _, Map) -> Map. +-spec filter_map(initial_var_map(), pos_integer()) -> var_map_phi(). + filter_map(Map, NofPreds) -> - filter_map(gb_trees:to_list(Map), NofPreds, Map). + filter_map(maps:to_list(Map), NofPreds, Map). + +-spec filter_map([{icode_var(), incoming_fvars()}], pos_integer(), + var_map_phi()) -> var_map_phi(). filter_map([{Var, Bindings}|Left], NofPreds, Map) -> case length(Bindings) =:= NofPreds of true -> + BindingsAllAssigned = lists:all(fun({_, {assigned, _}}) -> true; + ({_, _}) -> false + end, Bindings), case all_args_equal(Bindings) of true -> - {_, FVar} = hd(Bindings), - filter_map(Left, NofPreds, gb_trees:update(Var, FVar, Map)); + NewBinding = + case hd(Bindings) of + {Pred, {assigned, FVar0}} when is_integer(Pred) -> + case BindingsAllAssigned of + true -> {assigned, FVar0}; + false -> FVar0 + end; + {Pred, FVar0} when is_integer(Pred) -> FVar0 + end, + filter_map(Left, NofPreds, Map#{Var := NewBinding}); false -> PhiDst = hipe_icode:mk_new_fvar(), PhiArgs = strip_of_assigned(Bindings), NewMap = - case gb_trees:lookup(phi, Map) of - none -> - gb_trees:insert(phi, [{PhiDst, PhiArgs}], Map); - {value, Val} -> - gb_trees:update(phi, [{PhiDst, PhiArgs}|Val], Map) + case Map of + #{phi := Val} -> + Map#{phi := [{PhiDst, PhiArgs}|Val]}; + #{} -> + Map#{phi => [{PhiDst, PhiArgs}]} end, NewBinding = - case bindings_are_assigned(Bindings) of + case BindingsAllAssigned of true -> {assigned, PhiDst}; false -> PhiDst end, - filter_map(Left, NofPreds, gb_trees:update(Var, NewBinding, NewMap)) + filter_map(Left, NofPreds, NewMap#{Var := NewBinding}) end; false -> - filter_map(Left, NofPreds, gb_trees:delete(Var, Map)) + filter_map(Left, NofPreds, maps:remove(Var, Map)) end; filter_map([], _NofPreds, Map) -> Map. -bindings_are_assigned([{_, {assigned, _}}|Left]) -> - assert_assigned(Left), - true; -bindings_are_assigned(Bindings) -> - assert_not_assigned(Bindings), - false. - -assert_assigned([{_, {assigned, _}}|Left]) -> - assert_assigned(Left); -assert_assigned([]) -> - ok. - -assert_not_assigned([{_, FVar}|Left]) -> - true = hipe_icode:is_fvar(FVar), - assert_not_assigned(Left); -assert_not_assigned([]) -> - ok. +-spec all_args_equal(incoming_fvars()) -> boolean(). %% all_args_equal returns true if the mapping for a variable is the %% same from all predecessors, i.e., we do not need a phi-node. +%% During the fixpoint loop, a mapping might become assigned, without that +%% information having propagated into all predecessors. We take care to answer +%% true even if FVar is only assigned in some predecessors. + +all_args_equal([{_, {assigned, FVar}}|Left]) -> + all_args_equal(Left, FVar); all_args_equal([{_, FVar}|Left]) -> all_args_equal(Left, FVar). +all_args_equal([{_, {assigned, FVar1}}|Left], FVar1) -> + all_args_equal(Left, FVar1); all_args_equal([{_, FVar1}|Left], FVar1) -> all_args_equal(Left, FVar1); all_args_equal([], _) -> @@ -795,20 +912,24 @@ all_args_equal(_, _) -> false. +-spec add_new_bindings_unassigned([icode_var()], var_map()) -> var_map(). + %% We differentiate between values that have been assigned as %% tagged variables and those that got a 'virtual' binding. add_new_bindings_unassigned([Var|Left], Map) -> FVar = hipe_icode:mk_new_fvar(), - add_new_bindings_unassigned(Left, gb_trees:insert(Var, FVar, Map)); + add_new_bindings_unassigned(Left, Map#{Var => FVar}); add_new_bindings_unassigned([], Map) -> Map. +-spec add_new_bindings_assigned([icode_var()], var_map()) -> var_map(). + add_new_bindings_assigned([Var|Left], Map) -> case lookup(Var, Map) of none -> FVar = hipe_icode:mk_new_fvar(), - NewMap = gb_trees:insert(Var, {assigned, FVar}, Map), + NewMap = Map#{Var => {assigned, FVar}}, add_new_bindings_assigned(Left, NewMap); _ -> add_new_bindings_assigned(Left, Map) @@ -816,6 +937,8 @@ add_new_bindings_assigned([Var|Left], Map) -> add_new_bindings_assigned([], Map) -> Map. +-spec strip_of_assigned(incoming_fvars()) -> [{icode_lbl(), icode_fvar()}]. + strip_of_assigned(List) -> strip_of_assigned(List, []). @@ -830,6 +953,8 @@ strip_of_assigned([], Acc) -> %% Help functions for the transformation from ordinary instruction to %% fp-instruction +-spec is_fop_cand(icode_instr()) -> boolean(). + is_fop_cand(I) -> case hipe_icode:call_fun(I) of '/' -> true; @@ -840,6 +965,8 @@ is_fop_cand(I) -> end end. +-spec any_is_float([icode_argument()]) -> boolean(). + any_is_float(Vars) -> lists:any(fun (V) -> erl_types:t_is_float(get_type(V)) end, Vars). @@ -866,25 +993,32 @@ fun_to_fop(Fun) -> end. +-spec must_be_tagged(icode_var(), var_map()) -> boolean(). + %% If there is a tagged version of this variable available we don't %% have to tag the untagged version. must_be_tagged(Var, Map) -> - case gb_trees:lookup(Var, Map) of - none -> false; - {value, {assigned, _}} -> false; - {value, Val} -> hipe_icode:is_fvar(Val) + case Map of + #{Var := {assigned, _}} -> false; + #{Var := Val} -> hipe_icode:is_fvar(Val); + #{} -> false end. +-spec get_conv_instrs([icode_argument()], var_map()) -> icode_instrs(). + %% Converting to floating point variables get_conv_instrs(Vars, Map) -> get_conv_instrs(Vars, Map, []). +-spec get_conv_instrs([icode_argument()], var_map(), icode_instrs()) + -> icode_instrs(). + get_conv_instrs([Var|Left], Map, Acc) -> - {_, Dst} = gb_trees:get(Var, Map), - NewI = + #{Var := {_, Dst}} = Map, + NewI = case erl_types:t_is_float(get_type(Var)) of true -> [hipe_icode:mk_primop([Dst], unsafe_untag_float, [Var])]; @@ -896,6 +1030,8 @@ get_conv_instrs([], _, Acc) -> Acc. +-spec conv_consts([icode_const()], icode_instr()) -> icode_instr(). + conv_consts(ConstArgs, I) -> conv_consts(ConstArgs, I, []). @@ -934,63 +1070,79 @@ state__bb_add(S = #state{cfg = Cfg}, Label, BB) -> NewCfg = hipe_icode_cfg:bb_add(Cfg, Label, BB), S#state{cfg = NewCfg}. +-spec state__map(state(), icode_lbl()) -> initial_var_map(). + state__map(S = #state{edge_map = EM}, To) -> join_maps([{From, To} || From <- state__pred(S, To)], EM). +-spec state__map_update(state(), icode_lbl(), icode_lbl(), var_map()) -> + fixpoint | state(). + state__map_update(S = #state{edge_map = EM}, From, To, Map) -> FromTo = {From, To}, MapChanged = - case gb_trees:lookup(FromTo, EM) of - {value, Map1} -> not match(Map1, Map); - none -> true + case EM of + #{FromTo := Map1} -> not match(Map1, Map); + #{} -> true end, case MapChanged of true -> - NewEM = gb_trees:enter(FromTo, Map, EM), + NewEM = EM#{FromTo => Map}, S#state{edge_map = NewEM}; false -> fixpoint end. +-spec state__join_in_block(state(), icode_lbl()) + -> fixpoint | {state(), in_block()}. + state__join_in_block(S = #state{fp_ebb_map = Map}, Label) -> Pred = state__pred(S, Label), Edges = [{X, Label} || X <- Pred], - NewInBlock = join_in_block([gb_trees:lookup(X, Map) || X <- Edges]), + NewInBlock = join_in_block([maps:find(X, Map) || X <- Edges]), InBlockLabel = {inblock_in, Label}, - case gb_trees:lookup(InBlockLabel, Map) of - none -> - NewMap = gb_trees:insert(InBlockLabel, NewInBlock, Map), - {S#state{fp_ebb_map = NewMap}, NewInBlock}; - {value, NewInBlock} -> + case Map of + #{InBlockLabel := NewInBlock} -> fixpoint; - _Other -> - NewMap = gb_trees:update(InBlockLabel, NewInBlock, Map), + _ -> + NewMap = Map#{InBlockLabel => NewInBlock}, {S#state{fp_ebb_map = NewMap}, NewInBlock} end. +-spec state__in_block_out_update(state(), icode_lbl(), in_block()) + -> state(). + state__in_block_out_update(S = #state{fp_ebb_map = Map}, Label, NewInBlock) -> Succ = state__succ(S, Label), Edges = [{Label, X} || X <- Succ], NewMap = update_edges(Edges, NewInBlock, Map), - NewMap1 = gb_trees:enter({inblock_out, Label}, NewInBlock, NewMap), + NewMap1 = NewMap#{{inblock_out, Label} => NewInBlock}, S#state{fp_ebb_map = NewMap1}. +-spec update_edges([edge()], in_block(), fp_ebb_map()) -> fp_ebb_map(). + update_edges([Edge|Left], NewInBlock, Map) -> - NewMap = gb_trees:enter(Edge, NewInBlock, Map), + NewMap = Map#{Edge => NewInBlock}, update_edges(Left, NewInBlock, NewMap); update_edges([], _NewInBlock, NewMap) -> NewMap. +-spec join_in_block([error | {ok, in_block()}]) -> in_block(). + join_in_block([]) -> false; -join_in_block([none|_]) -> +join_in_block([error|_]) -> false; -join_in_block([{value, InBlock}|Left]) -> +join_in_block([{ok, InBlock}|Left]) -> join_in_block(Left, InBlock). -join_in_block([none|_], _Current) -> +-spec join_in_block([error | {ok, in_block()}], Current) + -> false | Current when + Current :: in_block(). + +join_in_block([error|_], _Current) -> false; -join_in_block([{value, InBlock}|Left], Current) -> +join_in_block([{ok, InBlock}|Left], Current) -> if Current =:= InBlock -> join_in_block(Left, Current); Current =:= false -> false; InBlock =:= false -> false; @@ -998,19 +1150,25 @@ join_in_block([{value, InBlock}|Left], Current) -> end; join_in_block([], Current) -> Current. - + + +-spec state__get_in_block_in(state(), icode_lbl()) -> in_block(). state__get_in_block_in(#state{fp_ebb_map = Map}, Label) -> - gb_trees:get({inblock_in, Label}, Map). + maps:get({inblock_in, Label}, Map). state__get_in_block_out(#state{fp_ebb_map = Map}, Label) -> - gb_trees:get({inblock_out, Label}, Map). + maps:get({inblock_out, Label}, Map). + +-spec new_worklist(state()) -> worklist(). new_worklist(#state{cfg = Cfg}) -> Start = hipe_icode_cfg:start_label(Cfg), {[Start], [], gb_sets:insert(Start, gb_sets:empty())}. +-spec get_work(worklist()) -> none | {icode_lbl(), worklist()}. + get_work({[Label|Left], List, Set}) -> {Label, {Left, List, gb_sets:delete(Label, Set)}}; get_work({[], [], _Set}) -> @@ -1018,6 +1176,8 @@ get_work({[], [], _Set}) -> get_work({[], List, Set}) -> get_work({lists:reverse(List), [], Set}). +-spec add_work(worklist(), [icode_lbl()]) -> worklist(). + add_work({List1, List2, Set} = Work, [Label|Left]) -> case gb_sets:is_member(Label, Set) of true -> @@ -1030,15 +1190,7 @@ add_work({List1, List2, Set} = Work, [Label|Left]) -> add_work(WorkList, []) -> WorkList. -match(Tree1, Tree2) -> - match_1(gb_trees:to_list(Tree1), Tree2) andalso - match_1(gb_trees:to_list(Tree2), Tree1). +-spec match(var_map(), var_map()) -> boolean(). -match_1([{Key, Val}|Left], Tree2) -> - case gb_trees:lookup(Key, Tree2) of - {value, Val} -> - match_1(Left, Tree2); - _ -> false - end; -match_1([], _) -> - true. +match(Tree1, Tree2) when is_map(Tree1), is_map(Tree2) -> + Tree1 =:= Tree2. diff --git a/lib/hipe/test/basic_SUITE_data/basic_floats.erl b/lib/hipe/test/basic_SUITE_data/basic_floats.erl index eec175075a..ce28f6e156 100644 --- a/lib/hipe/test/basic_SUITE_data/basic_floats.erl +++ b/lib/hipe/test/basic_SUITE_data/basic_floats.erl @@ -18,6 +18,8 @@ test() -> ok = test_catch_fp_conv(), ok = test_fp_with_fp_exceptions(), %% ok = test_fmt_double_fpe_leak(), % this requires printing + ok = test_icode_type_crash(), + ok = test_icode_type_crash_2(), ok. %%-------------------------------------------------------------------- @@ -178,3 +180,71 @@ test_fmt_double_fpe_leak(X, Y) -> math:log10(Y). int_two() -> 2. + +%%-------------------------------------------------------------------- +%% Contains code which confuses the icode_type analysis and results +%% in a compiler crash. Stipped down from code sent by Paul Guyot. +%% Compiles alright with the option 'no_icode_type' but that defeats +%% the purpose of the test. + +test_icode_type_crash() -> + Fun = f(1, 2, 3), + 42.0 = Fun(), + ok. + +f(A, B, C) -> + fun () -> + X = case A of + 0 -> 1 / B; + _ -> A / C + end, + Y = case B of + a -> 1.0; + b -> 2.0; + _ -> 6.0 + end, + Z = case C of + c -> 0.1 * X; + _ -> 7.0 + end, + Y * Z + end. + +%%-------------------------------------------------------------------- +%% Contains another case that crashed hipe_icode_fp. This sample was +%% sent by Mattias Jansson (25 Nov 2015). It compiled alright with the +%% option 'no_icode_type' but that defeats the purpose of the test. +%% Unfortunately, the execution of this code goes into an infinite +%% loop, even if the map in the second argument of eat_what/2 gets the +%% appropriate key-value pairs. Still, it is retained as a test +%% because it exposed a different crash than test_icode_type_crash/0. + +test_icode_type_crash_2() -> + {'EXIT', {function_clause, _}} = (catch eat()), + ok. + +eat() -> + eat_what(1.0, #{}). + +eat_what(Factor, #{rat_type := LT} = Rat) -> + #{cheese := Cheese} = Rat, + UnitCheese = Cheese / 2, + RetA = case eat() of + {full, RetA1} -> + CheeseB2 = min(RetA1, UnitCheese) * Factor, + case eat() of + full -> {win, RetA1}; + hungry -> {partial, RetA1 - CheeseB2} + end; + AOther -> AOther + end, + RetB = case eat() of + {full, RetB1} -> + CheeseA2 = min(RetB1, UnitCheese) * Factor, + rat:init(single, LT, CheeseA2), + case eat() of + full -> {full, RetB1}; + hungry -> {hungry, RetB1 - CheeseA2} + end + end, + {RetA, RetB}. diff --git a/lib/inets/doc/src/notes.xml b/lib/inets/doc/src/notes.xml index 2aa14b6559..b3d2221930 100644 --- a/lib/inets/doc/src/notes.xml +++ b/lib/inets/doc/src/notes.xml @@ -33,7 +33,22 @@ <file>notes.xml</file> </header> - <section><title>Inets 6.2</title> + <section><title>Inets 6.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Mend ipv6_host_with_brackets option in httpc</p> + <p> + Own Id: OTP-13417</p> + </item> + </list> + </section> + +</section> + +<section><title>Inets 6.2</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/inets/examples/server_root/conf/8080.conf b/lib/inets/examples/server_root/conf/8080.conf index 48e66f0114..7b1b4a15b2 100644 --- a/lib/inets/examples/server_root/conf/8080.conf +++ b/lib/inets/examples/server_root/conf/8080.conf @@ -1,7 +1,7 @@ Port 8080 #ServerName your.server.net SocketType ip_comm -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log ServerAdmin [email protected] ServerRoot /var/tmp/server_root ErrorLog logs/error_log_8080 diff --git a/lib/inets/examples/server_root/conf/8888.conf b/lib/inets/examples/server_root/conf/8888.conf index 79bb7fcca4..042779fcd0 100644 --- a/lib/inets/examples/server_root/conf/8888.conf +++ b/lib/inets/examples/server_root/conf/8888.conf @@ -1,7 +1,7 @@ Port 8888 #ServerName your.server.net SocketType ip_comm -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log ServerAdmin [email protected] ServerRoot /var/tmp/server_root ErrorLog logs/error_log_8888 diff --git a/lib/inets/examples/server_root/conf/httpd.conf b/lib/inets/examples/server_root/conf/httpd.conf index 1794711e2a..3f9fde03b5 100644 --- a/lib/inets/examples/server_root/conf/httpd.conf +++ b/lib/inets/examples/server_root/conf/httpd.conf @@ -64,7 +64,7 @@ SocketType ip_comm # WARNING! Do not tamper with this directive unless you are familiar with # EWSAPI. -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_include mod_dir mod_get mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_dir mod_get mod_log mod_disk_log # ServerAdmin: Your address, where problems with the server should be # e-mailed. diff --git a/lib/inets/examples/server_root/conf/ssl.conf b/lib/inets/examples/server_root/conf/ssl.conf index 8b8c57a98b..de49ceafd0 100644 --- a/lib/inets/examples/server_root/conf/ssl.conf +++ b/lib/inets/examples/server_root/conf/ssl.conf @@ -1,7 +1,7 @@ Port 8088 #ServerName your.server.net SocketType ssl -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log ServerAdmin [email protected] ServerRoot /var/tmp/server_root ErrorLog logs/error_log_8088 diff --git a/lib/inets/src/http_client/httpc.erl b/lib/inets/src/http_client/httpc.erl index c6b81462b7..91d87289a2 100644 --- a/lib/inets/src/http_client/httpc.erl +++ b/lib/inets/src/http_client/httpc.erl @@ -556,7 +556,7 @@ handle_request(Method, Url, Request = #request{from = Receiver, scheme = Scheme, - address = {Host, Port}, + address = {host_address(Host, BracketedHost), Port}, path = MaybeEscPath, pquery = MaybeEscQuery, method = Method, @@ -1268,3 +1268,7 @@ child_name(Pid, [_ | Children]) -> %% d(_, _, _) -> %% ok. +host_address(Host, false) -> + Host; +host_address(Host, true) -> + string:strip(string:strip(Host, right, $]), left, $[). diff --git a/lib/inets/src/http_client/httpc_request.erl b/lib/inets/src/http_client/httpc_request.erl index eabe2c6b22..e8d020c165 100644 --- a/lib/inets/src/http_client/httpc_request.erl +++ b/lib/inets/src/http_client/httpc_request.erl @@ -186,16 +186,19 @@ is_client_closing(Headers) -> %%%======================================================================== %%% Internal functions %%%======================================================================== -post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) - when (Method =:= post) orelse (Method =:= put) - orelse (Method =:= patch) -> +post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) + when (Method =:= post) + orelse (Method =:= put) + orelse (Method =:= patch) + orelse (Method =:= delete) -> + NewBody = case Headers#http_request_h.expect of - "100-continue" -> - ""; - _ -> - Body - end, - + "100-continue" -> + ""; + _ -> + Body + end, + NewHeaders = case HeadersAsIs of [] -> Headers#http_request_h{ @@ -213,7 +216,7 @@ post_data(Method, Headers, {ContentType, Body}, HeadersAsIs) _ -> HeadersAsIs end, - + {NewHeaders, NewBody}; post_data(_, Headers, _, []) -> diff --git a/lib/inets/src/inets_app/inets.appup.src b/lib/inets/src/inets_app/inets.appup.src index 3a31daeb20..4d0d656acb 100644 --- a/lib/inets/src/inets_app/inets.appup.src +++ b/lib/inets/src/inets_app/inets.appup.src @@ -18,10 +18,12 @@ %% %CopyrightEnd% {"%VSN%", [ + {<<"6.2">>, [{load_module, httpc, soft_purge, soft_purge, []}]}, {<<"6\\..*">>,[{restart_application, inets}]}, {<<"5\\..*">>,[{restart_application, inets}]} ], [ + {<<"6.2">>, [{load_module, httpc, soft_purge, soft_purge, []}]}, {<<"6\\..*">>,[{restart_application, inets}]}, {<<"5\\..*">>,[{restart_application, inets}]} ] diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 93b96e101f..f9b3aa5b59 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -67,6 +67,7 @@ real_requests()-> head, get, post, + delete, post_stream, patch, async, @@ -256,6 +257,29 @@ post(Config) when is_list(Config) -> {ok, {{_,504,_}, [_ | _], []}} = httpc:request(post, {URL, [{"expect","100-continue"}], "text/plain", "foobar"}, [], []). +%%-------------------------------------------------------------------- +delete() -> + [{"Test http delete request against local server. We do in this case " + "only care about the client side of the the delete. The server " + "script will not actually use the delete data."}]. +delete(Config) when is_list(Config) -> + CGI = case test_server:os_type() of + {win32, _} -> + "/cgi-bin/cgi_echo.exe"; + _ -> + "/cgi-bin/cgi_echo" + end, + + URL = url(group_name(Config), CGI, Config), + Body = lists:duplicate(100, "1"), + + {ok, {{_,200,_}, [_ | _], [_ | _]}} = + httpc:request(delete, {URL, [{"expect","100-continue"}], + "text/plain", Body}, [], []), + + {ok, {{_,504,_}, [_ | _], []}} = + httpc:request(delete, {URL, [{"expect","100-continue"}], + "text/plain", "foobar"}, [], []). %%-------------------------------------------------------------------- patch() -> diff --git a/lib/inets/test/httpd_1_1.erl b/lib/inets/test/httpd_1_1.erl index b65b997193..3755ed117b 100644 --- a/lib/inets/test/httpd_1_1.erl +++ b/lib/inets/test/httpd_1_1.erl @@ -233,14 +233,6 @@ trace(Type, Port, Host, Node)-> "Max-Forwards:2\r\n\r\n", [{statuscode, 200}]). head(Type, Port, Host, Node)-> - %% mod_include - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - "HEAD /fsize.shtml HTTP/1.0\r\n\r\n", - [{statuscode, 200}, - {version, "HTTP/1.0"}]), - ok = httpd_test_lib:verify_request(Type, Host, Port, Node, - "HEAD /fsize.shtml HTTP/1.1\r\nhost:" ++ - Host ++ "\r\n\r\n", [{statuscode, 200}]), %% mod_esi ok = httpd_test_lib:verify_request(Type, Host, Port, Node, "HEAD /cgi-bin/erl/httpd_example/newformat" diff --git a/lib/inets/test/httpd_test_data/server_root/conf/8080.conf b/lib/inets/test/httpd_test_data/server_root/conf/8080.conf index 48e66f0114..7b1b4a15b2 100644 --- a/lib/inets/test/httpd_test_data/server_root/conf/8080.conf +++ b/lib/inets/test/httpd_test_data/server_root/conf/8080.conf @@ -1,7 +1,7 @@ Port 8080 #ServerName your.server.net SocketType ip_comm -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log ServerAdmin [email protected] ServerRoot /var/tmp/server_root ErrorLog logs/error_log_8080 diff --git a/lib/inets/test/httpd_test_data/server_root/conf/8888.conf b/lib/inets/test/httpd_test_data/server_root/conf/8888.conf index 79bb7fcca4..042779fcd0 100644 --- a/lib/inets/test/httpd_test_data/server_root/conf/8888.conf +++ b/lib/inets/test/httpd_test_data/server_root/conf/8888.conf @@ -1,7 +1,7 @@ Port 8888 #ServerName your.server.net SocketType ip_comm -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log ServerAdmin [email protected] ServerRoot /var/tmp/server_root ErrorLog logs/error_log_8888 diff --git a/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf b/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf index 1794711e2a..3f9fde03b5 100644 --- a/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf +++ b/lib/inets/test/httpd_test_data/server_root/conf/httpd.conf @@ -64,7 +64,7 @@ SocketType ip_comm # WARNING! Do not tamper with this directive unless you are familiar with # EWSAPI. -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_include mod_dir mod_get mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_dir mod_get mod_log mod_disk_log # ServerAdmin: Your address, where problems with the server should be # e-mailed. diff --git a/lib/inets/test/httpd_test_data/server_root/conf/ssl.conf b/lib/inets/test/httpd_test_data/server_root/conf/ssl.conf index 8b8c57a98b..de49ceafd0 100644 --- a/lib/inets/test/httpd_test_data/server_root/conf/ssl.conf +++ b/lib/inets/test/httpd_test_data/server_root/conf/ssl.conf @@ -1,7 +1,7 @@ Port 8088 #ServerName your.server.net SocketType ssl -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log ServerAdmin [email protected] ServerRoot /var/tmp/server_root ErrorLog logs/error_log_8088 diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf index 48e66f0114..7b1b4a15b2 100644 --- a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8080.conf @@ -1,7 +1,7 @@ Port 8080 #ServerName your.server.net SocketType ip_comm -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log ServerAdmin [email protected] ServerRoot /var/tmp/server_root ErrorLog logs/error_log_8080 diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf index 79bb7fcca4..042779fcd0 100644 --- a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/8888.conf @@ -1,7 +1,7 @@ Port 8888 #ServerName your.server.net SocketType ip_comm -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log ServerAdmin [email protected] ServerRoot /var/tmp/server_root ErrorLog logs/error_log_8888 diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf index 1794711e2a..3f9fde03b5 100644 --- a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/httpd.conf @@ -64,7 +64,7 @@ SocketType ip_comm # WARNING! Do not tamper with this directive unless you are familiar with # EWSAPI. -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_include mod_dir mod_get mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_responsecontrol mod_trace mod_range mod_head mod_dir mod_get mod_log mod_disk_log # ServerAdmin: Your address, where problems with the server should be # e-mailed. diff --git a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf index 8b8c57a98b..de49ceafd0 100644 --- a/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf +++ b/lib/inets/test/old_httpd_SUITE_data/server_root/conf/ssl.conf @@ -1,7 +1,7 @@ Port 8088 #ServerName your.server.net SocketType ssl -Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_include mod_dir mod_get mod_head mod_log mod_disk_log +Modules mod_alias mod_auth mod_esi mod_actions mod_cgi mod_dir mod_get mod_head mod_log mod_disk_log ServerAdmin [email protected] ServerRoot /var/tmp/server_root ErrorLog logs/error_log_8088 diff --git a/lib/inets/vsn.mk b/lib/inets/vsn.mk index 250957f261..121f55c389 100644 --- a/lib/inets/vsn.mk +++ b/lib/inets/vsn.mk @@ -19,6 +19,6 @@ # %CopyrightEnd% APPLICATION = inets -INETS_VSN = 6.2 +INETS_VSN = 6.2.1 PRE_VSN = APP_VSN = "$(APPLICATION)-$(INETS_VSN)$(PRE_VSN)" diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java index b235527e82..222330654a 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/AbstractNode.java @@ -95,6 +95,7 @@ public class AbstractNode implements OtpTransportFactory { static final int dFlagUnicodeIo = 0x1000; static final int dFlagUtf8Atoms = 0x10000; static final int dFlagMapTag = 0x20000; + static final int dFlagBigCreation = 0x40000; int ntype = NTYPE_R6; int proto = 0; // tcp/ip @@ -103,7 +104,8 @@ public class AbstractNode implements OtpTransportFactory { int creation = 0; int flags = dFlagExtendedReferences | dFlagExtendedPidsPorts | dFlagBitBinaries | dFlagNewFloats | dFlagFunTags - | dflagNewFunTags | dFlagUtf8Atoms | dFlagMapTag; + | dflagNewFunTags | dFlagUtf8Atoms | dFlagMapTag + | dFlagBigCreation; /* initialize hostname and default cookie */ static { diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java index 6ed4ac1805..9cbd735751 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPid.java @@ -27,6 +27,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> // don't change this! private static final long serialVersionUID = 1664394142301803659L; + private final int tag; private final String node; private final int id; private final int serial; @@ -44,6 +45,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> public OtpErlangPid(final OtpLocalNode self) { final OtpErlangPid p = self.createPid(); + tag = p.tag; id = p.id; serial = p.serial; creation = p.creation; @@ -65,6 +67,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> throws OtpErlangDecodeException { final OtpErlangPid p = buf.read_pid(); + tag = p.tag; node = p.node(); id = p.id(); serial = p.serial(); @@ -85,15 +88,52 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> * used. * * @param creation - * yet another arbitrary number. Only the low order 2 bits will + * yet another arbitrary number. Ony the low order 2 bits will * be used. */ public OtpErlangPid(final String node, final int id, final int serial, - final int creation) { - this.node = node; - this.id = id & 0x7fff; // 15 bits - this.serial = serial & 0x1fff; // 13 bits - this.creation = creation & 0x03; // 2 bits + final int creation) { + this(OtpExternal.pidTag, node, id, serial, creation); + } + + /** + * Create an Erlang pid from its components. + * + * @param tag + * the external format to be compliant with + * OtpExternal.pidTag where only a subset of the bits are significant (see other constructor). + * OtpExternal.newPidTag where all 32 bits of id,serial and creation are significant. + * newPidTag can only be decoded by OTP-19 and newer. + * @param node + * the nodename. + * + * @param id + * an arbitrary number. + * + * @param serial + * another arbitrary number. + * + * @param creation + * yet another arbitrary number. + */ + protected OtpErlangPid(final int tag, final String node, final int id, + final int serial, final int creation) { + this.tag = tag; + this.node = node; + if (tag == OtpExternal.pidTag) { + this.id = id & 0x7fff; // 15 bits + this.serial = serial & 0x1fff; // 13 bits + this.creation = creation & 0x03; // 2 bits + } + else { // allow all 32 bits for newPidTag + this.id = id; + this.serial = serial; + this.creation = creation; + } + } + + protected int tag() { + return tag; } /** @@ -151,7 +191,7 @@ public class OtpErlangPid extends OtpErlangObject implements Comparable<Object> */ @Override public void encode(final OtpOutputStream buf) { - buf.write_pid(node, id, serial, creation); + buf.write_pid(this); } /** diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java index 2b8eaf495b..79b5d2736c 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangPort.java @@ -26,6 +26,7 @@ public class OtpErlangPort extends OtpErlangObject { // don't change this! private static final long serialVersionUID = 4037115468007644704L; + private final int tag; private final String node; private final int id; private final int creation; @@ -42,6 +43,7 @@ public class OtpErlangPort extends OtpErlangObject { private OtpErlangPort(final OtpSelf self) { final OtpErlangPort p = self.createPort(); + tag = p.tag; id = p.id; creation = p.creation; node = p.node; @@ -62,6 +64,7 @@ public class OtpErlangPort extends OtpErlangObject { throws OtpErlangDecodeException { final OtpErlangPort p = buf.read_port(); + tag = p.tag; node = p.node(); id = p.id(); creation = p.creation(); @@ -77,13 +80,45 @@ public class OtpErlangPort extends OtpErlangObject { * an arbitrary number. Only the low order 28 bits will be used. * * @param creation - * another arbitrary number. Only the low order 2 bits will be - * used. + * another arbitrary number. Only the low order 2 bits will be used. */ public OtpErlangPort(final String node, final int id, final int creation) { - this.node = node; - this.id = id & 0xfffffff; // 28 bits - this.creation = creation & 0x03; // 2 bits + this(OtpExternal.portTag, node, id, creation); + } + + /** + * Create an Erlang port from its components. + * + * @param tag + * the external format to be compliant with. + * OtpExternal.portTag where only a subset of the bits are used (see other constructor) + * OtpExternal.newPortTag where all 32 bits of id and creation are significant. + * newPortTag can only be decoded by OTP-19 and newer. + * @param node + * the nodename. + * + * @param id + * an arbitrary number. Only the low order 28 bits will be used. + * + * @param creation + * another arbitrary number. + */ + public OtpErlangPort(final int tag, final String node, final int id, + final int creation) { + this.tag = tag; + this.node = node; + if (tag == OtpExternal.portTag) { + this.id = id & 0xfffffff; // 28 bits + this.creation = creation & 0x3; // 2 bits + } + else { + this.id = id; + this.creation = creation; + } + } + + protected int tag() { + return tag; } /** @@ -132,7 +167,7 @@ public class OtpErlangPort extends OtpErlangObject { */ @Override public void encode(final OtpOutputStream buf) { - buf.write_port(node, id, creation); + buf.write_port(this); } /** diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java index 92bf48f3c7..2165397013 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpErlangRef.java @@ -28,11 +28,13 @@ public class OtpErlangRef extends OtpErlangObject { // don't change this! private static final long serialVersionUID = -7022666480768586521L; + private final int tag; private final String node; private final int creation; // old style refs have one 18-bit id // r6 "new" refs have array of ids, first one is only 18 bits however + // 19 "newer" refs have full 32-bits for creation and for ids[0] private int ids[] = null; /** @@ -47,6 +49,7 @@ public class OtpErlangRef extends OtpErlangObject { public OtpErlangRef(final OtpLocalNode self) { final OtpErlangRef r = self.createRef(); + tag = r.tag; ids = r.ids; creation = r.creation; node = r.node; @@ -67,6 +70,7 @@ public class OtpErlangRef extends OtpErlangObject { throws OtpErlangDecodeException { final OtpErlangRef r = buf.read_ref(); + tag = r.tag; node = r.node(); creation = r.creation(); @@ -83,10 +87,10 @@ public class OtpErlangRef extends OtpErlangObject { * an arbitrary number. Only the low order 18 bits will be used. * * @param creation - * another arbitrary number. Only the low order 2 bits will be - * used. + * another arbitrary number. */ public OtpErlangRef(final String node, final int id, final int creation) { + this.tag = OtpExternal.newRefTag; this.node = node; ids = new int[1]; ids[0] = id & 0x3ffff; // 18 bits @@ -110,10 +114,34 @@ public class OtpErlangRef extends OtpErlangObject { * used. */ public OtpErlangRef(final String node, final int[] ids, final int creation) { + this(OtpExternal.newRefTag, node, ids, creation); + } + + /** + * Create a new(er) style Erlang ref from its components. + * + * @param tag + * the external format to be compliant with. + * OtpExternal.newRefTag where only a subset of the bits are used (see other constructor) + * OtpExternal.newerRefTag where all bits of ids and creation are used. + * newerPortTag can only be decoded by OTP-19 and newer. + * + * @param node + * the nodename. + * + * @param ids + * an array of arbitrary numbers. At most three numbers + * will be read from the array. + * + * @param creation + * another arbitrary number. + */ + public OtpErlangRef(final int tag, final String node, final int[] ids, + final int creation) { + this.tag = tag; this.node = node; - this.creation = creation & 0x03; // 2 bits - // use at most 82 bits (18 + 32 + 32) + // use at most 3 words int len = ids.length; this.ids = new int[3]; this.ids[0] = 0; @@ -124,7 +152,17 @@ public class OtpErlangRef extends OtpErlangObject { len = 3; } System.arraycopy(ids, 0, this.ids, 0, len); - this.ids[0] &= 0x3ffff; // only 18 significant bits in first number + if (tag == OtpExternal.newRefTag) { + this.creation = creation & 0x3; + this.ids[0] &= 0x3ffff; // only 18 significant bits in first number + } + else { + this.creation = creation; + } + } + + protected int tag() { + return tag; } /** @@ -202,7 +240,7 @@ public class OtpErlangRef extends OtpErlangObject { */ @Override public void encode(final OtpOutputStream buf) { - buf.write_ref(node, ids, creation); + buf.write_ref(this); } /** diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java index 7128ce7220..da8ac3612f 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpExternal.java @@ -46,9 +46,11 @@ public class OtpExternal { /** The tag used for ports */ public static final int portTag = 102; + public static final int newPortTag = 89; /** The tag used for PIDs */ public static final int pidTag = 103; + public static final int newPidTag = 88; /** The tag used for small tuples */ public static final int smallTupleTag = 104; @@ -85,6 +87,7 @@ public class OtpExternal { /** The tag used for new style references */ public static final int newRefTag = 114; + public static final int newerRefTag = 90; /** The tag used for maps */ public static final int mapTag = 116; diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java index a6fc67868b..ded8f6e1e5 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpInputStream.java @@ -954,18 +954,23 @@ public class OtpInputStream extends ByteArrayInputStream { tag = read1skip_version(); - if (tag != OtpExternal.pidTag) { + if (tag != OtpExternal.pidTag && + tag != OtpExternal.newPidTag) { throw new OtpErlangDecodeException( "Wrong tag encountered, expected " + OtpExternal.pidTag + + " or " + OtpExternal.newPidTag + ", got " + tag); } node = read_atom(); - id = read4BE() & 0x7fff; // 15 bits - serial = read4BE() & 0x1fff; // 13 bits - creation = read1() & 0x03; // 2 bits - - return new OtpErlangPid(node, id, serial, creation); + id = read4BE(); + serial = read4BE(); + if (tag == OtpExternal.pidTag) + creation = read1(); + else + creation = read4BE(); + + return new OtpErlangPid(tag, node, id, serial, creation); } /** @@ -984,17 +989,22 @@ public class OtpInputStream extends ByteArrayInputStream { tag = read1skip_version(); - if (tag != OtpExternal.portTag) { + if (tag != OtpExternal.portTag && + tag != OtpExternal.newPortTag) { throw new OtpErlangDecodeException( "Wrong tag encountered, expected " + OtpExternal.portTag + + " or " + OtpExternal.newPortTag + ", got " + tag); } node = read_atom(); - id = read4BE() & 0xfffffff; // 28 bits - creation = read1() & 0x03; // 2 bits + id = read4BE(); + if (tag == OtpExternal.portTag) + creation = read1(); + else + creation = read4BE(); - return new OtpErlangPort(node, id, creation); + return new OtpErlangPort(tag, node, id, creation); } /** @@ -1021,16 +1031,23 @@ public class OtpInputStream extends ByteArrayInputStream { return new OtpErlangRef(node, id, creation); case OtpExternal.newRefTag: + case OtpExternal.newerRefTag: final int arity = read2BE(); + if (arity > 3) { + throw new OtpErlangDecodeException( + "Ref arity " + arity + " too large "); + } node = read_atom(); - creation = read1() & 0x03; // 2 bits + if (tag == OtpExternal.newRefTag) + creation = read1(); + else + creation = read4BE(); final int[] ids = new int[arity]; for (int i = 0; i < arity; i++) { ids[i] = read4BE(); } - ids[0] &= 0x3ffff; // first id gets truncated to 18 bits - return new OtpErlangRef(node, ids, creation); + return new OtpErlangRef(tag, node, ids, creation); default: throw new OtpErlangDecodeException( @@ -1200,15 +1217,18 @@ public class OtpInputStream extends ByteArrayInputStream { case OtpExternal.refTag: case OtpExternal.newRefTag: + case OtpExternal.newerRefTag: return new OtpErlangRef(this); case OtpExternal.mapTag: return new OtpErlangMap(this); case OtpExternal.portTag: + case OtpExternal.newPortTag: return new OtpErlangPort(this); case OtpExternal.pidTag: + case OtpExternal.newPidTag: return new OtpErlangPid(this); case OtpExternal.stringTag: diff --git a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java index 43bddd52e9..dca2eb7c51 100644 --- a/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java +++ b/lib/jinterface/java_src/com/ericsson/otp/erlang/OtpOutputStream.java @@ -728,14 +728,38 @@ public class OtpOutputStream extends ByteArrayOutputStream { */ public void write_pid(final String node, final int id, final int serial, final int creation) { - write1(OtpExternal.pidTag); - write_atom(node); - write4BE(id & 0x7fff); // 15 bits - write4BE(serial & 0x1fff); // 13 bits - write1(creation & 0x3); // 2 bits + write1(OtpExternal.pidTag); + write_atom(node); + write4BE(id & 0x7fff); // 15 bits + write4BE(serial & 0x1fff); // 13 bits + write1(creation & 0x3); // 2 bits } /** + * Write an Erlang PID to the stream. + * + * @param pid + * the pid + */ + public void write_pid(OtpErlangPid pid) { + write1(pid.tag()); + write_atom(pid.node()); + write4BE(pid.id()); + write4BE(pid.serial()); + switch (pid.tag()) { + case OtpExternal.pidTag: + write1(pid.creation()); + break; + case OtpExternal.newPidTag: + write4BE(pid.creation()); + break; + default: + throw new AssertionError("Invalid pid tag " + pid.tag()); + } + } + + + /** * Write an Erlang port to the stream. * * @param node @@ -745,15 +769,36 @@ public class OtpOutputStream extends ByteArrayOutputStream { * an arbitrary number. Only the low order 28 bits will be used. * * @param creation - * another arbitrary number. Only the low order 2 bits will be - * used. - * + * another arbitrary number. Only the low order 2 bits will + * be used. */ public void write_port(final String node, final int id, final int creation) { - write1(OtpExternal.portTag); - write_atom(node); - write4BE(id & 0xfffffff); // 28 bits - write1(creation & 0x3); // 2 bits + write1(OtpExternal.portTag); + write_atom(node); + write4BE(id & 0xfffffff); // 28 bits + write1(creation & 0x3); // 2 bits + } + + /** + * Write an Erlang port to the stream. + * + * @param port + * the port. + */ + public void write_port(OtpErlangPort port) { + write1(port.tag()); + write_atom(port.node()); + write4BE(port.id()); + switch (port.tag()) { + case OtpExternal.portTag: + write1(port.creation()); + break; + case OtpExternal.newPortTag: + write4BE(port.creation()); + break; + default: + throw new AssertionError("Invalid port tag " + port.tag()); + } } /** @@ -766,32 +811,31 @@ public class OtpOutputStream extends ByteArrayOutputStream { * an arbitrary number. Only the low order 18 bits will be used. * * @param creation - * another arbitrary number. Only the low order 2 bits will be - * used. + * another arbitrary number. * */ public void write_ref(final String node, final int id, final int creation) { - write1(OtpExternal.refTag); - write_atom(node); - write4BE(id & 0x3ffff); // 18 bits - write1(creation & 0x3); // 2 bits + /* Always encode as an extended reference; all + participating parties are now expected to be + able to decode extended references. */ + int ids[] = new int[1]; + ids[0] = id; + write_ref(node, ids, creation); } /** - * Write a new style (R6 and later) Erlang ref to the stream. + * Write an Erlang ref to the stream. * * @param node * the nodename. * * @param ids * an array of arbitrary numbers. Only the low order 18 bits of - * the first number will be used. If the array contains only one - * number, an old style ref will be written instead. At most - * three numbers will be read from the array. + * the first number will be used. At most three numbers + * will be read from the array. * * @param creation - * another arbitrary number. Only the low order 2 bits will be - * used. + * another arbitrary number. Only the low order 2 bits will be used. * */ public void write_ref(final String node, final int[] ids, final int creation) { @@ -800,29 +844,54 @@ public class OtpOutputStream extends ByteArrayOutputStream { arity = 3; // max 3 words in ref } - if (arity == 1) { - // use old method - this.write_ref(node, ids[0], creation); - } else { - // r6 ref - write1(OtpExternal.newRefTag); + write1(OtpExternal.newRefTag); - // how many id values - write2BE(arity); + // how many id values + write2BE(arity); - write_atom(node); + write_atom(node); - // note: creation BEFORE id in r6 ref - write1(creation & 0x3); // 2 bits + write1(creation & 0x3); // 2 bits - // first int gets truncated to 18 bits - write4BE(ids[0] & 0x3ffff); + // first int gets truncated to 18 bits + write4BE(ids[0] & 0x3ffff); - // remaining ones are left as is - for (int i = 1; i < arity; i++) { - write4BE(ids[i]); - } - } + // remaining ones are left as is + for (int i = 1; i < arity; i++) { + write4BE(ids[i]); + } + } + + /** + * Write an Erlang ref to the stream. + * + * @param ref + * the reference + */ + public void write_ref(OtpErlangRef ref) { + int[] ids = ref.ids(); + int arity = ids.length; + + write1(ref.tag()); + write2BE(arity); + write_atom(ref.node()); + + switch (ref.tag()) { + case OtpExternal.newRefTag: + write1(ref.creation()); + write4BE(ids[0] & 0x3ffff); // first word gets truncated to 18 bits + break; + case OtpExternal.newerRefTag: + write4BE(ref.creation()); + write4BE(ids[0]); // full first word + break; + default: + throw new AssertionError("Invalid ref tag " + ref.tag()); + } + + for (int i = 1; i < arity; i++) { + write4BE(ids[i]); + } } /** diff --git a/lib/jinterface/test/nc_SUITE.erl b/lib/jinterface/test/nc_SUITE.erl index 8bf1c2f328..81944f71d4 100644 --- a/lib/jinterface/test/nc_SUITE.erl +++ b/lib/jinterface/test/nc_SUITE.erl @@ -31,6 +31,10 @@ -define(NEW_REFERENCE_EXT, 114). -define(ATOM_UTF8_EXT, 118). -define(SMALL_ATOM_UTF8_EXT, 119). +-define(NEW_PID_EXT, $X). +-define(NEW_PORT_EXT, $Y). +-define(NEWER_REFERENCE_EXT, $Z). + -export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2, init_per_suite/1, @@ -123,12 +127,13 @@ pid_roundtrip(doc) -> []; pid_roundtrip(suite) -> []; pid_roundtrip(Config) when is_list(Config)-> ThisNode = {node(), erlang:system_info(creation)}, - RemNode = {gurka@sallad, 2}, + RemPids = [mk_pid({gurka@sallad, Cr}, Num, Ser) + || Cr <- [1,2,3,4,16#adec0ded], + {Num, Ser} <- [{4711,4711},{32767, 8191}]], do_echo([self(), mk_pid(ThisNode, 4711, 4711), - mk_pid(ThisNode, 32767, 8191), - mk_pid(RemNode, 4711, 4711), - mk_pid(RemNode, 32767, 8191)], + mk_pid(ThisNode, 32767, 8191) + | RemPids], Config). fun_roundtrip(doc) -> []; @@ -144,26 +149,29 @@ port_roundtrip(doc) -> []; port_roundtrip(suite) -> []; port_roundtrip(Config) when is_list(Config)-> ThisNode = {node(), erlang:system_info(creation)}, - RemNode = {gurka@sallad, 2}, + RemPorts = [mk_port({gurka@sallad, Cr}, Num) + || Cr <- [1,2,3,4,16#adec0ded], + Num <- [4711, 268435455]], do_echo([hd(erlang:ports()), mk_port(ThisNode, 4711), - mk_port(ThisNode, 268435455), - mk_port(RemNode, 4711), - mk_port(RemNode, 268435455)], + mk_port(ThisNode, 268435455) + | RemPorts], Config). ref_roundtrip(doc) -> []; ref_roundtrip(suite) -> []; ref_roundtrip(Config) when is_list(Config)-> ThisNode = {node(), erlang:system_info(creation)}, - RemNode = {gurka@sallad, 2}, + RemRefs = [mk_ref({gurka@sallad, Cr}, Words) + || Cr <- [1,2,3,4,16#adec0ded], + Words <- [[4711], + [4711, 4711, 4711], + [262143, 4294967295, 4294967295]]], do_echo([make_ref(), mk_ref(ThisNode, [4711]), mk_ref(ThisNode, [4711, 4711, 4711]), - mk_ref(ThisNode, [262143, 4294967295, 4294967295]), - mk_ref(RemNode, [4711]), - mk_ref(RemNode, [4711, 4711, 4711]), - mk_ref(RemNode, [262143, 4294967295, 4294967295])], + mk_ref(ThisNode, [262143, 4294967295, 4294967295]) + | RemRefs], Config). new_float(doc) -> []; @@ -759,17 +767,22 @@ uint8(Uint) -> exit({badarg, uint8, [Uint]}). +pid_tag(Creation) when Creation =< 3 -> ?PID_EXT; +pid_tag(_Creation) -> ?NEW_PID_EXT. + +enc_creation(Creation) when Creation =< 3 -> uint8(Creation); +enc_creation(Creation) -> uint32_be(Creation). mk_pid({NodeName, Creation}, Number, Serial) when is_atom(NodeName) -> <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), mk_pid({NodeNameExt, Creation}, Number, Serial); mk_pid({NodeNameExt, Creation}, Number, Serial) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PID_EXT, + pid_tag(Creation), NodeNameExt, uint32_be(Number), uint32_be(Serial), - uint8(Creation)])) of + enc_creation(Creation)])) of Pid when is_pid(Pid) -> Pid; {'EXIT', {badarg, _}} -> @@ -778,15 +791,18 @@ mk_pid({NodeNameExt, Creation}, Number, Serial) -> exit({unexpected_binary_to_term_result, Other}) end. +port_tag(Creation) when Creation =< 3 -> ?PORT_EXT; +port_tag(_Creation) -> ?NEW_PORT_EXT. + mk_port({NodeName, Creation}, Number) when is_atom(NodeName) -> <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), mk_port({NodeNameExt, Creation}, Number); mk_port({NodeNameExt, Creation}, Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?PORT_EXT, + port_tag(Creation), NodeNameExt, uint32_be(Number), - uint8(Creation)])) of + enc_creation(Creation)])) of Port when is_port(Port) -> Port; {'EXIT', {badarg, _}} -> @@ -795,12 +811,16 @@ mk_port({NodeNameExt, Creation}, Number) -> exit({unexpected_binary_to_term_result, Other}) end. +ref_tag(Creation) when Creation =< 3 -> ?NEW_REFERENCE_EXT; +ref_tag(_Creation) -> ?NEWER_REFERENCE_EXT. + mk_ref({NodeName, Creation}, [Number] = NL) when is_atom(NodeName), is_integer(Creation), is_integer(Number) -> <<?VERSION_MAGIC, NodeNameExt/binary>> = term_to_binary(NodeName), mk_ref({NodeNameExt, Creation}, NL); mk_ref({NodeNameExt, Creation}, [Number]) when is_integer(Creation), + Creation =< 3, is_integer(Number) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, ?REFERENCE_EXT, @@ -822,10 +842,10 @@ mk_ref({NodeName, Creation}, Numbers) when is_atom(NodeName), mk_ref({NodeNameExt, Creation}, Numbers) when is_integer(Creation), is_list(Numbers) -> case catch binary_to_term(list_to_binary([?VERSION_MAGIC, - ?NEW_REFERENCE_EXT, + ref_tag(Creation), uint16_be(length(Numbers)), NodeNameExt, - uint8(Creation), + enc_creation(Creation), lists:map(fun (N) -> uint32_be(N) end, diff --git a/lib/kernel/doc/src/app.xml b/lib/kernel/doc/src/app.xml index 68c042b2ae..5e0da409a3 100644 --- a/lib/kernel/doc/src/app.xml +++ b/lib/kernel/doc/src/app.xml @@ -42,12 +42,12 @@ </description> <section> - <title>FILE SYNTAX</title> - <p>The application resource file should be called - <c>Application.app</c> where <c>Application</c> is the name of - the application. The file should be located in the <c>ebin</c> - directory for the application.</p> - <p>It must contain one single Erlang term, which is called an + <title>File Syntax</title> + <p>The application resource file is to be called + <c>Application.app</c>, where <c>Application</c> is the + application name. The file is to be located in directory <c>ebin</c> + for the application.</p> + <p>The file must contain a single Erlang term, which is called an <em>application specification</em>:</p> <code type="none"> {application, Application, @@ -80,19 +80,26 @@ Env [{Par,Val}] [] Start {Module,StartArgs} [] Phases [{Phase,PhaseArgs}] undefined RTDeps [ApplicationVersion] [] - Module = Name = App = Par = Phase = atom() - Val = StartArgs = PhaseArgs = term() - ApplicationVersion = string()</code> - <p><c>Application</c> is the name of the application.</p> + +Module = Name = App = Par = Phase = atom() +Val = StartArgs = PhaseArgs = term() +ApplicationVersion = string()</code> + <taglist> + <tag><c>Application</c></tag> + <item>Application name.</item> + </taglist> <p>For the application controller, all keys are optional. The respective default values are used for any omitted keys.</p> <p>The functions in <c>systools</c> require more information. If - they are used, the following keys are mandatory: - <c>description</c>, <c>vsn</c>, <c>modules</c>, <c>registered</c> - and <c>applications</c>. The other keys are ignored by - <c>systools</c>.</p> - <warning><p>The <c>RTDeps</c> type was introduced in OTP 17.0 and - might be subject to changes during the OTP 17 release.</p></warning> + they are used, the following keys are mandatory:</p> + <list type="bulleted"> + <item><c>description</c></item> + <item><c>vsn</c></item> + <item><c>modules</c></item> + <item><c>registered</c></item> + <item><c>applications</c></item> + </list> + <p>The other keys are ignored by <c>systools</c>.</p> <taglist> <tag><c>description</c></tag> <item> @@ -104,7 +111,7 @@ RTDeps [ApplicationVersion] [] </item> <tag><c>vsn</c></tag> <item> - <p>The version of the application.</p> + <p>Version of the application.</p> </item> <tag><c>modules</c></tag> <item> @@ -114,15 +121,14 @@ RTDeps [ApplicationVersion] [] </item> <tag><c>maxP</c></tag> <item> - <p><em>Deprecated - will be ignored</em> <br></br> - - The maximum number of processes allowed in the application.</p> + <p><em>Deprecated - is ignored</em></p> + <p>Maximum number of processes allowed in the application.</p> </item> <tag><c>maxT</c></tag> <item> - <p>The maximum time in milliseconds that the application is - allowed to run. After the specified time the application will - automatically terminate.</p> + <p>Maximum time, in milliseconds, that the application is + allowed to run. After the specified time, the application + terminates automatically.</p> </item> <tag><c>registered</c></tag> <item> @@ -132,20 +138,20 @@ RTDeps [ApplicationVersion] [] </item> <tag><c>included_applications</c></tag> <item> - <p>All applications which are included by this application. - When this application is started, all included application - will automatically be loaded, but not started, by - the application controller. It is assumed that the topmost + <p>All applications included by this application. + When this application is started, all included applications + are loaded automatically, but not started, by + the application controller. It is assumed that the top-most supervisor of the included application is started by a supervisor of this application.</p> </item> <tag><c>applications</c></tag> <item> - <p>All applications which must be started before this + <p>All applications that must be started before this application is allowed to be started. <c>systools</c> uses this list to generate correct start scripts. Defaults to - the empty list, but note that all applications have - dependencies to (at least) <c>kernel</c> and <c>stdlib</c>.</p> + the empty list, but notice that all applications have + dependencies to (at least) <c>Kernel</c> and <c>STDLIB</c>.</p> </item> <tag><c>env</c></tag> <item> @@ -153,78 +159,84 @@ RTDeps [ApplicationVersion] [] of a configuration parameter is retrieved by calling <c>application:get_env/1,2</c>. The values in the application resource file can be overridden by values in a configuration - file (see <c>config(4)</c>) or by command line flags (see - <c>erl(1)</c>).</p> + file (see <seealso marker="config"><c>config(4)</c></seealso>) + or by command-line flags (see + <seealso marker="erts:erl"><c>erts:erl(1)</c></seealso>).</p> </item> <tag><c>mod</c></tag> <item> - <p>Specifies the application callback module and a start - argument, see <c>application(3)</c>.</p> - <p>The <c>mod</c> key is necessary for an application - implemented as a supervision tree, or the application - controller will not know how to start it. The <c>mod</c> key + <p>Specifies the application callback module and a start argument, see + <seealso marker="application"><c>application(3)</c></seealso>.</p> + <p>Key <c>mod</c> is necessary for an application + implemented as a supervision tree, otherwise the application + controller does not know how to start it. <c>mod</c> can be omitted for applications without processes, typically - code libraries such as the application STDLIB.</p> + code libraries, for example, <c>STDLIB</c>.</p> </item> <tag><c>start_phases</c></tag> <item> <p>A list of start phases and corresponding start arguments for the application. If this key is present, the application - master will - in addition to the usual call to - <c>Module:start/2</c> - also call + master, in addition to the usual call to + <c>Module:start/2</c>, also calls <c>Module:start_phase(Phase,Type,PhaseArgs)</c> for each - start phase defined by the <c>start_phases</c> key, and only - after this extended start procedure will - <c>application:start(Application)</c> return.</p> - <p>Start phases may be used to synchronize startup of an + start phase defined by key <c>start_phases</c>. Only + after this extended start procedure, + <c>application:start(Application)</c> returns.</p> + <p>Start phases can be used to synchronize startup of an application and its included applications. In this case, - the <c>mod</c> key must be specified as:</p> + key <c>mod</c> must be specified as follows:</p> <code type="none"> {mod, {application_starter,[Module,StartArgs]}}</code> - <p>The application master will then call <c>Module:start/2</c> + <p>The application master then calls <c>Module:start/2</c> for the primary application, followed by calls to <c>Module:start_phase/3</c> for each start phase (as defined - for the primary application) both for the primary application - and for each of its included application, for which the start + for the primary application), both for the primary application + and for each of its included applications, for which the start phase is defined.</p> <p>This implies that for an included application, the set of start phases must be a subset of the set of phases defined - for the primary application. Refer to <em>OTP Design Principles</em> for more information.</p> + for the primary application. For more information, see + <seealso marker="doc/design_principles:applications">OTP Design Principles</seealso>. + </p> </item> - <tag><marker id="runtime_dependencies"></marker><c>runtime_dependencies</c></tag> - <item><p>A list of application versions that the application - depends on. An example of such an application version is - <c>"kernel-3.0"</c>. Application versions specified as runtime - dependencies are minimum requirements. That is, a larger - application version than the one specified in the - dependency satisfies the requirement. For information on - how to compare application versions see - <seealso marker="doc/system_principles:versions">the - documentation of versions in the system principles - guide</seealso>. Note that the application version - specifies a source code version. An additional indirect - requirement is that installed binary application of - the specified version has been built so that it is - compatible with the rest of the system.</p> - <p>Some dependencies might only be required in specific runtime - scenarios. In the case such optional dependencies exist, these are - specified and documented in the corresponding "App" documentation - of the specific application.</p> - <warning><p>The <c>runtime_dependencies</c> key was introduced in - OTP 17.0. The type of its value might be subject to changes during - the OTP 17 release.</p></warning> - <warning><p>All runtime dependencies specified in OTP applications - during the OTP 17 release may not be completely correct. This - is actively being worked on. Declared runtime dependencies in OTP - applications are expected to be correct in OTP 18.</p></warning> + <tag> + <marker id="runtime_dependencies"></marker> + <c>runtime_dependencies</c></tag> + <item> + <p>A list of application versions that the application + depends on. An example of such an application version is + <c>"kernel-3.0"</c>. Application versions specified as runtime + dependencies are minimum requirements. That is, a larger + application version than the one specified in the + dependency satisfies the requirement. For information about + how to compare application versions, see section + <seealso marker="doc/system_principles:versions">Versions</seealso> + in the System Principles User's Guide.</p> + <p>Notice that the application version + specifies a source code version. One more, indirect, + requirement is that the installed binary application of + the specified version is built so that it is + compatible with the rest of the system.</p> + <p>Some dependencies can only be required in specific runtime + scenarios. When such optional dependencies exist, these are + specified and documented in the corresponding "App" documentation + of the specific application.</p> + <warning><p>The <c>runtime_dependencies</c> key was introduced in + OTP 17.0. The type of its value might be subject to changes during + the OTP 17 release.</p></warning> + <warning><p>All runtime dependencies specified in OTP applications + during the OTP 17 release may not be completely correct. This + is actively being worked on. Declared runtime dependencies in OTP + applications are expected to be correct in OTP 18.</p></warning> </item> </taglist> </section> <section> - <title>SEE ALSO</title> - <p><seealso marker="application">application(3)</seealso>, - systools(3)</p> + <title>See Also</title> + <p><seealso marker="application"><c>application(3)</c></seealso>, + <seealso marker="sasl:systools"><c>sasl:systools(3)</c></seealso></p> </section> </fileref> diff --git a/lib/kernel/doc/src/application.xml b/lib/kernel/doc/src/application.xml index 3e3e6cdfa9..3928078932 100644 --- a/lib/kernel/doc/src/application.xml +++ b/lib/kernel/doc/src/application.xml @@ -33,23 +33,25 @@ <description> <p>In OTP, <em>application</em> denotes a component implementing some specific functionality, that can be started and stopped as a - unit, and which can be re-used in other systems as well. This - module interfaces the <em>application controller</em>, a process - started at every Erlang runtime system, and contains functions - for controlling applications (for example starting and stopping + unit, and that can be reused in other systems. This + module interacts with <em>application controller</em>, a process + started at every Erlang runtime system. This module contains functions + for controlling applications (for example, starting and stopping applications), and functions to access information about - applications (for example configuration parameters).</p> - <p>An application is defined by an <em>application specification</em>. The specification is normally located in an - <em>application resource file</em> called <c>Application.app</c>, - where <c>Application</c> is the name of the application. Refer to - <seealso marker="app">app(4)</seealso> for more information about - the application specification.</p> + applications (for example, configuration parameters).</p> + <p>An application is defined by an <em>application specification</em>. + The specification is normally located in an + <em>application resource file</em> named <c>Application.app</c>, + where <c>Application</c> is the application name. For details + about the application specification, see + <seealso marker="app"><c>app(4)</c></seealso>.</p> <p>This module can also be viewed as a behaviour for an application implemented according to the OTP design principles as a supervision tree. The definition of how to start and stop - the tree should be located in an <em>application callback module</em> exporting a pre-defined set of functions.</p> - <p>Refer to <seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso> for more information about - applications and behaviours.</p> + the tree is to be located in an <em>application callback module</em>, + exporting a predefined set of functions.</p> + <p>For details about applications and behaviours, see + <seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso>.</p> </description> <datatypes> <datatype> @@ -59,7 +61,6 @@ <name name="restart_type"/> </datatype> <datatype> - <!-- Parameterized opaque types are NYI: --> <name>tuple_of(T)</name> <desc><p><marker id="type-tuple_of"/> A tuple where the elements are of type <c>T</c>.</p></desc> @@ -67,9 +68,37 @@ </datatypes> <funcs> <func> + <name name="ensure_all_started" arity="1"/> + <name name="ensure_all_started" arity="2"/> + <fsummary>Load and start an application and its dependencies, recursively.</fsummary> + <desc> + <p>Equivalent to calling + <seealso marker="#start/1"><c>start/1,2</c></seealso> + repeatedly on all dependencies that are not yet started for an application.</p> + <p>Returns <c>{ok, AppNames}</c> for a successful start or for an already started + application (which is, however, omitted from the <c>AppNames</c> list).</p> + <p>The function reports <c>{error, {AppName,Reason}}</c> for errors, where + <c>Reason</c> is any possible reason returned by + <seealso marker="#start/1"><c>start/1,2</c></seealso> + when starting a specific dependency.</p> + <p>If an error occurs, the applications started by the function are stopped + to bring the set of running applications back to its initial state.</p> + </desc> + </func> + <func> + <name name="ensure_started" arity="1"/> + <name name="ensure_started" arity="2"/> + <fsummary>Load and start an application.</fsummary> + <desc> + <p>Equivalent to + <seealso marker="#start/1"><c>start/1,2</c></seealso> + except it returns <c>ok</c> for already started applications.</p> + </desc> + </func> + <func> <name name="get_all_env" arity="0"/> <name name="get_all_env" arity="1"/> - <fsummary>Get the configuration parameters for an application</fsummary> + <fsummary>Get the configuration parameters for an application.</fsummary> <desc> <p>Returns the configuration parameters and their values for <c><anno>Application</anno></c>. If the argument is omitted, it defaults to @@ -82,7 +111,7 @@ <func> <name name="get_all_key" arity="0"/> <name name="get_all_key" arity="1"/> - <fsummary>Get the application specification keys</fsummary> + <fsummary>Get the application specification keys.</fsummary> <desc> <p>Returns the application specification keys and their values for <c><anno>Application</anno></c>. If the argument is omitted, it @@ -96,7 +125,7 @@ <func> <name name="get_application" arity="0"/> <name name="get_application" arity="1"/> - <fsummary>Get the name of an application containing a certain process or module</fsummary> + <fsummary>Get the name of an application containing a certain process or module.</fsummary> <desc> <p>Returns the name of the application to which the process <c><anno>Pid</anno></c> or the module <c><anno>Module</anno></c> belongs. Providing no @@ -110,222 +139,212 @@ <func> <name name="get_env" arity="1"/> <name name="get_env" arity="2"/> - <fsummary>Get the value of a configuration parameter</fsummary> + <fsummary>Get the value of a configuration parameter.</fsummary> <desc> - <p>Returns the value of the configuration parameter <c><anno>Par</anno></c> + <p>Returns the value of configuration parameter <c><anno>Par</anno></c> for <c><anno>Application</anno></c>. If the application argument is omitted, it defaults to the application of the calling process.</p> - <p>If the specified application is not loaded, or - the configuration parameter does not exist, or if the process - executing the call does not belong to any application, - the function returns <c>undefined</c>.</p> + <p>Returns <c>undefined</c> if any of the following applies:</p> + <list type="bulleted"> + <item>The specified application is not loaded.</item> + <item>The configuration parameter does not exist.</item> + <item>The process executing the call does not belong to any application.</item> + </list> </desc> </func> <func> <name name="get_env" arity="3"/> - <fsummary>Get the value of a configuration parameter using a default</fsummary> + <fsummary>Get the value of a configuration parameter using a default.</fsummary> <desc> - <p>Works like <seealso marker="#get_env/2">get_env/2</seealso> but returns - <c><anno>Def</anno></c> value when configuration parameter + <p>Works like <seealso marker="#get_env/2"><c>get_env/2</c></seealso> but returns + value <c><anno>Def</anno></c> when configuration parameter <c><anno>Par</anno></c> does not exist.</p> </desc> </func> <func> <name name="get_key" arity="1"/> <name name="get_key" arity="2"/> - <fsummary>Get the value of an application specification key</fsummary> + <fsummary>Get the value of an application specification key.</fsummary> <desc> <p>Returns the value of the application specification key <c><anno>Key</anno></c> for <c><anno>Application</anno></c>. If the application argument is omitted, it defaults to the application of the calling process.</p> - <p>If the specified application is not loaded, or - the specification key does not exist, or if the process - executing the call does not belong to any application, - the function returns <c>undefined</c>.</p> + <p>Returns <c>undefined</c> if any of the following applies:</p> + <list type="bulleted"> + <item>The specified application is not loaded.</item> + <item>The specification key does not exist.</item> + <item>The process executing the call does not belong to any application.</item> + </list> + </desc> </func> <func> <name name="load" arity="1"/> <name name="load" arity="2"/> - <fsummary>Load an application</fsummary> + <fsummary>Load an application.</fsummary> <type name="application_spec"/> <type name="application_opt"/> <desc> <p>Loads the application specification for an application into - the application controller. It will also load the application - specifications for any included applications. Note that - the function does not load the actual Erlang object code.</p> - <p>The application can be given by its name <c><anno>Application</anno></c>. - In this case the application controller will search the code + the application controller. It also loads the application + specifications for any included applications. Notice that + the function does not load the Erlang object code.</p> + <p>The application can be specified by its name <c><anno>Application</anno></c>. + In this case, the application controller searches the code path for the application resource file <c><anno>Application</anno>.app</c> - and load the specification it contains.</p> - <p>The application specification can also be given directly as a - tuple <c><anno>AppSpec</anno></c>. This tuple should have the format and - contents as described in <c>app(4)</c>.</p> + and loads the specification it contains.</p> + <p>The application specification can also be specified directly as a + tuple <c><anno>AppSpec</anno></c>, having the format and + contents as described in + <seealso marker="app"><c>app(4)</c></seealso>.</p> <p>If <c><anno>Distributed</anno> == {<anno>Application</anno>,[<anno>Time</anno>,]<anno>Nodes</anno>}</c>, - the application will be distributed. The argument overrides - the value for the application in the Kernel configuration + the application becomes distributed. The argument overrides + the value for the application in the <c>Kernel</c> configuration parameter <c>distributed</c>. <c><anno>Application</anno></c> must be - the name of the application (same as in the first argument). - If a node crashes and <c><anno>Time</anno></c> has been specified, then - the application controller will wait for <c><anno>Time</anno></c> + the application name (same as in the first argument). + If a node crashes and <c><anno>Time</anno></c> is specified, + the application controller waits for <c><anno>Time</anno></c> milliseconds before attempting to restart the application on - another node. If <c><anno>Time</anno></c> is not specified, it will - default to 0 and the application will be restarted + another node. If <c><anno>Time</anno></c> is not specified, it + defaults to <c>0</c> and the application is restarted immediately.</p> <p><c><anno>Nodes</anno></c> is a list of node names where the application - may run, in priority from left to right. Node names can be + can run, in priority from left to right. Node names can be grouped using tuples to indicate that they have the same - priority. Example:</p> + priority.</p> + <p><em>Example:</em></p> <code type="none"> Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> - <p>This means that the application should preferably be started + <p>This means that the application is preferably to be started at <c>cp1@cave</c>. If <c>cp1@cave</c> is down, - the application should be started at either <c>cp2@cave</c> + the application is to be started at <c>cp2@cave</c> or <c>cp3@cave</c>.</p> <p>If <c>Distributed == default</c>, the value for - the application in the Kernel configuration parameter - <c>distributed</c> will be used.</p> + the application in the <c>Kernel</c> configuration parameter + <c>distributed</c> is used.</p> </desc> </func> <func> <name name="loaded_applications" arity="0"/> - <fsummary>Get the currently loaded applications</fsummary> + <fsummary>Get the currently loaded applications.</fsummary> <desc> - <p>Returns a list with information about the applications which - have been loaded using <c>load/1,2</c>, also included - applications. <c><anno>Application</anno></c> is the application name. - <c><anno>Description</anno></c> and <c><anno>Vsn</anno></c> are the values of its - <c>description</c> and <c>vsn</c> application specification + <p>Returns a list with information about the applications, and included + applications, which are loaded using <c>load/1,2</c>. + <c><anno>Application</anno></c> is the application name. + <c><anno>Description</anno></c> and <c><anno>Vsn</anno></c> are the values + of their <c>description</c> and <c>vsn</c> application specification keys, respectively.</p> </desc> </func> <func> <name name="permit" arity="2"/> - <fsummary>Change an application's permission to run on a node.</fsummary> + <fsummary>Change the permission for an application to run at a node.</fsummary> <desc> <p>Changes the permission for <c><anno>Application</anno></c> to run at - the current node. The application must have been loaded using + the current node. The application must be loaded using <c>load/1,2</c> for the function to have effect.</p> <p>If the permission of a loaded, but not started, application - is set to <c>false</c>, <c>start</c> will return <c>ok</c> but - the application will not be started until the permission is + is set to <c>false</c>, <c>start</c> returns <c>ok</c> but + the application is not started until the permission is set to <c>true</c>.</p> <p>If the permission of a running application is set to - <c>false</c>, the application will be stopped. If - the permission later is set to <c>true</c>, it will be + <c>false</c>, the application is stopped. If + the permission later is set to <c>true</c>, it is restarted.</p> <p>If the application is distributed, setting the permission to <c>false</c> means that the application will be started at, or moved to, another node according to how its distribution is - configured (see <c>load/2</c> above).</p> + configured + (see <seealso marker="#load/2"><c>load/2</c></seealso>).</p> <p>The function does not return until the application is - started, stopped or successfully moved to another node. - However, in some cases where permission is set to <c>true</c> - the function may return <c>ok</c> even though the application - itself has not started. This is true when an application - cannot start because it has dependencies to other - applications which have not yet been started. When they have - been started, <c>Application</c> will be started as well.</p> + started, stopped, or successfully moved to another node. + However, in some cases where permission is set to <c>true</c>, + the function returns <c>ok</c> even though the application + is not started. This is true when an application + cannot start because of dependencies to other + applications that are not yet started. When they are + started, <c>Application</c> is started as well.</p> <p>By default, all applications are loaded with permission - <c>true</c> on all nodes. The permission is configurable by - using the Kernel configuration parameter <c>permissions</c>.</p> + <c>true</c> on all nodes. The permission can be configured + using the <c>Kernel</c> configuration parameter <c>permissions</c>.</p> </desc> </func> <func> <name name="set_env" arity="3"/> <name name="set_env" arity="4"/> - <fsummary>Set the value of a configuration parameter</fsummary> + <fsummary>Set the value of a configuration parameter.</fsummary> <desc> - <p>Sets the value of the configuration parameter <c><anno>Par</anno></c> for + <p>Sets the value of configuration parameter <c><anno>Par</anno></c> for <c><anno>Application</anno></c>.</p> - <p><c>set_env/4</c> uses the standard <c>gen_server</c> timeout - value (5000 ms). The <c>timeout</c> option can be provided - if another timeout value is useful, for example, in situations + <p><c>set_env/4</c> uses the standard <c>gen_server</c> time-out + value (5000 ms). Option <c>timeout</c> can be specified + if another time-out value is useful, for example, in situations where the application controller is heavily loaded.</p> <p>If <c>set_env/4</c> is called before the application is loaded, - the application environment values specified in the <c>Application.app</c> - file will override the ones previously set. This is also true for application + the application environment values specified in file <c>Application.app</c> + override the ones previously set. This is also true for application reloads.</p> - <p>The <c>persistent</c> option can be set to <c>true</c> - when there is a need to guarantee parameters set with <c>set_env/4</c> - will not be overridden by the ones defined in the application resource - file on load. This means persistent values will stick after the application + <p>Option <c>persistent</c> can be set to <c>true</c> + to guarantee that parameters set with <c>set_env/4</c> + are not overridden by those defined in the application resource + file on load. This means that persistent values will stick after the application is loaded and also on application reload.</p> <warning> - <p>Use this function only if you know what you are doing, - that is, on your own applications. It is very application - and configuration parameter dependent when and how often - the value is read by the application, and careless use - of this function may put the application in a - weird, inconsistent, and malfunctioning state. </p> + <p>Use this function only if you know what you are doing, + that is, on your own applications. It is very + application-dependent and + configuration parameter-dependent when and how often + the value is read by the application. Careless use + of this function can put the application in a + weird, inconsistent, and malfunctioning state.</p> </warning> </desc> </func> <func> - <name name="ensure_started" arity="1"/> - <name name="ensure_started" arity="2"/> - <fsummary>Load and start an application</fsummary> - <desc> - <p>Equivalent to <seealso marker="#start/2"><c>application:start/1,2</c></seealso> except - it returns <c>ok</c> for already started applications.</p> - </desc> - </func> - <func> - <name name="ensure_all_started" arity="1"/> - <name name="ensure_all_started" arity="2"/> - <fsummary>Load and start an application and its dependencies, recursively</fsummary> - <desc> - <p>Equivalent to calling <seealso marker="#start/2"><c>application:start/1,2</c></seealso> - repeatedly on all dependencies that have not yet been started for an application. - The function returns <c>{ok, AppNames}</c> for a successful start or for an already started - application (which are however omitted from the <c>AppNames</c> list), and reports - <c>{error, {AppName,Reason}}</c> for errors, where <c>Reason</c> is any possible reason - returned by <seealso marker="#start/2"><c>application:start/1,2</c></seealso> when starting a - specific dependency. In case of an error, the applications that were started by the - function are stopped to bring the set of running applications back to its initial state.</p> - </desc> - </func> - <func> <name name="start" arity="1"/> <name name="start" arity="2"/> - <fsummary>Load and start an application</fsummary> - <desc> + <fsummary>Load and start an application.</fsummary> + <desc> <p>Starts <c><anno>Application</anno></c>. If it is not loaded, - the application controller will first load it using - <c>load/1</c>. It will make sure any included applications - are loaded, but will not start them. That is assumed to be + the application controller first loads it using + <c>load/1</c>. It ensures that any included applications + are loaded, but does not start them. That is assumed to be taken care of in the code for <c><anno>Application</anno></c>.</p> <p>The application controller checks the value of the application specification key <c>applications</c>, to - ensure that all applications that should be started before - this application are running. If not, + ensure that all applications needed to be started before + this application are running. Otherwise, <c>{error,{not_started,App}}</c> is returned, where <c>App</c> is the name of the missing application.</p> - <p>The application controller then creates an <em>application master</em> for the application. The application master is + <p>The application controller then creates an <em>application master</em> + for the application. The application master is the group leader of all the processes in the application. The application master starts the application by calling the application callback function <c>Module:start/2</c> as defined by the application specification key <c>mod</c>.</p> - <p>The <c><anno>Type</anno></c> argument specifies the type of + <p>Argument <c><anno>Type</anno></c> specifies the type of the application. If omitted, it defaults to <c>temporary</c>.</p> <list type="bulleted"> <item>If a permanent application terminates, all other applications and the entire Erlang node are also terminated.</item> - <item>If a transient application terminates with <c>Reason == normal</c>, this is reported but no other applications are - terminated. If a transient application terminates - abnormally, all other applications and the entire Erlang - node are also terminated.</item> + <item> + <list type="bulleted"> + <item>If a transient application terminates with <c>Reason == normal</c>, + this is reported but no other applications are terminated.</item> + <item>If a transient application terminates abnormally, all other + applications and the entire Erlang node are also terminated.</item> + </list> + </item> <item>If a temporary application terminates, this is reported but no other applications are terminated.</item> </list> - <p>Note that it is always possible to stop an application + <p>Notice that an application can always be stopped explicitly by calling <c>stop/1</c>. Regardless of the type of - the application, no other applications will be affected.</p> - <p>Note also that the transient type is of little practical use, - since when a supervision tree terminates, the reason is set to + the application, no other applications are affected.</p> + <p>Notice also that the transient type is of little practical use, + because when a supervision tree terminates, the reason is set to <c>shutdown</c>, not <c>normal</c>.</p> </desc> </func> @@ -334,13 +353,13 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> <fsummary>Get the start type of an ongoing application startup.</fsummary> <desc> <p>This function is intended to be called by a process belonging - to an application, when the application is being started, to - determine the start type which is either <c><anno>StartType</anno></c> or + to an application, when the application is started, to + determine the start type, which is <c><anno>StartType</anno></c> or <c>local</c>.</p> - <p>See <seealso marker="#start_type"><c>Module:start/2</c></seealso> for a description of - <c><anno>StartType</anno></c>.</p> - <p><c>local</c> is returned if only parts of the application is - being restarted (by a supervisor), or if the function is + <p>For a description of <c><anno>StartType</anno></c>, see + <seealso marker="#start_type"><c>Module:start/2</c></seealso>.</p> + <p><c>local</c> is returned if only parts of the application are + restarted (by a supervisor), or if the function is called outside a startup.</p> <p>If the process executing the call does not belong to any application, the function returns <c>undefined</c>.</p> @@ -348,103 +367,106 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </func> <func> <name name="stop" arity="1"/> - <fsummary>Stop an application</fsummary> + <fsummary>Stop an application.</fsummary> <desc> <p>Stops <c><anno>Application</anno></c>. The application master calls <c>Module:prep_stop/1</c>, if such a function is defined, and - then tells the top supervisor of the application to shutdown - (see <c>supervisor(3)</c>). This means that the entire + then tells the top supervisor of the application to shut down + (see <seealso marker="stdlib:supervisor"><c>supervisor(3)</c></seealso>). + This means that the entire supervision tree, including included applications, is terminated in reversed start order. After the shutdown, the application master calls <c>Module:stop/1</c>. <c>Module</c> is the callback module as defined by the application specification key <c>mod</c>.</p> - <p>Last, the application master itself terminates. Note that all - processes with the application master as group leader, i.e. + <p>Last, the application master terminates. Notice that all + processes with the application master as group leader, that is, processes spawned from a process belonging to the application, - thus are terminated as well.</p> + are also terminated.</p> <p>When stopped, the application is still loaded.</p> - <p>In order to stop a distributed application, <c>stop/1</c> - has to be called on all nodes where it can execute (that is, + <p>To stop a distributed application, <c>stop/1</c> + must be called on all nodes where it can execute (that is, on all nodes where it has been started). The call to <c>stop/1</c> on the node where the application currently - executes will stop its execution. The application will not be - moved between nodes due to <c>stop/1</c> being called on + executes stops its execution. The application is not + moved between nodes, as <c>stop/1</c> is called on the node where the application currently executes before <c>stop/1</c> is called on the other nodes.</p> </desc> </func> <func> <name name="takeover" arity="2"/> - <fsummary>Take over a distributed application</fsummary> + <fsummary>Take over a distributed application.</fsummary> <desc> - <p>Performs a takeover of the distributed application + <p>Takes over the distributed application <c><anno>Application</anno></c>, which executes at another node <c>Node</c>. At the current node, the application is restarted by calling <c>Module:start({takeover,Node},StartArgs)</c>. <c>Module</c> and <c>StartArgs</c> are retrieved from the loaded application specification. The application at the other node is not - stopped until the startup is completed, i.e. when + stopped until the startup is completed, that is, when <c>Module:start/2</c> and any calls to <c>Module:start_phase/3</c> have returned.</p> - <p>Thus two instances of the application will run simultaneously - during the takeover, which makes it possible to transfer data - from the old to the new instance. If this is not acceptable - behavior, parts of the old instance may be shut down when - the new instance is started. Note that the application may - not be stopped entirely however, at least the top supervisor + <p>Thus, two instances of the application run simultaneously + during the takeover, so that data can be transferred + from the old to the new instance. If this is not an acceptable + behavior, parts of the old instance can be shut down when + the new instance is started. However, the application cannot + be stopped entirely, at least the top supervisor must remain alive.</p> - <p>See <c>start/1,2</c> for a description of <c>Type</c>.</p> + <p>For a description of <c>Type</c>, see + <seealso marker="#start/1"><c>start/1,2</c></seealso>.</p> </desc> </func> <func> <name name="unload" arity="1"/> - <fsummary>Unload an application</fsummary> + <fsummary>Unload an application.</fsummary> <desc> <p>Unloads the application specification for <c><anno>Application</anno></c> - from the application controller. It will also unload + from the application controller. It also unloads the application specifications for any included applications. - Note that the function does not purge the actual Erlang + Notice that the function does not purge the Erlang object code.</p> </desc> </func> <func> <name name="unset_env" arity="2"/> <name name="unset_env" arity="3"/> - <fsummary>Unset the value of a configuration parameter</fsummary> + <fsummary>Unset the value of a configuration parameter.</fsummary> <desc> <p>Removes the configuration parameter <c><anno>Par</anno></c> and its value for <c><anno>Application</anno></c>.</p> <p><c>unset_env/2</c> uses the standard <c>gen_server</c> - timeout value (5000 ms). The <c>timeout</c> option can be - provided if another timeout value is useful, for example, in + time-out value (5000 ms). Option <c>timeout</c> can be + specified if another time-out value is useful, for example, in situations where the application controller is heavily loaded.</p> <p><c>unset_env/3</c> also allows the persistent option to be passed - (see <c>set_env/4</c> above).</p> - <warning> - <p>Use this function only if you know what you are doing, - that is, on your own applications. It is very application - and configuration parameter dependent when and how often - the value is read by the application, and careless use - of this function may put the application in a - weird, inconsistent, and malfunctioning state. </p> + (see <seealso marker="#set_env/4"><c>set_env/4</c></seealso>).</p> + <warning> + <p>Use this function only if you know what you are doing, + that is, on your own applications. It is very + application-dependent and configuration + parameter-dependent when and how often + the value is read by the application. Careless use + of this function can put the application in a + weird, inconsistent, and malfunctioning state.</p> </warning> </desc> </func> <func> <name name="which_applications" arity="0"/> <name name="which_applications" arity="1"/> - <fsummary>Get the currently running applications</fsummary> + <fsummary>Get the currently running applications.</fsummary> <desc> - <p>Returns a list with information about the applications which + <p>Returns a list with information about the applications that are currently running. <c><anno>Application</anno></c> is the application - name. <c><anno>Description</anno></c> and <c><anno>Vsn</anno></c> are the values of its - <c>description</c> and <c>vsn</c> application specification + name. <c><anno>Description</anno></c> and <c><anno>Vsn</anno></c> are the + values of their <c>description</c> and <c>vsn</c> application specification keys, respectively.</p> <p><c>which_applications/0</c> uses the standard - <c>gen_server</c> timeout value (5000 ms). A <c><anno>Timeout</anno></c> - argument can be provided if another timeout value is useful, + <c>gen_server</c> time-out value (5000 ms). A <c><anno>Timeout</anno></c> + argument can be specified if another time-out value is useful, for example, in situations where the application controller is heavily loaded.</p> </desc> @@ -452,81 +474,81 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </funcs> <section> - <title>CALLBACK MODULE</title> - <p>The following functions should be exported from an + <title>Callback Module</title> + <p>The following functions are to be exported from an <c>application</c> callback module.</p> </section> <funcs> <func> <name>Module:start(StartType, StartArgs) -> {ok, Pid} | {ok, Pid, State} | {error, Reason}</name> - <fsummary>Start an application</fsummary> + <fsummary>Start an application.</fsummary> <type> - <v>StartType = <seealso marker="#type-start_type">start_type()</seealso></v> + <v>StartType = <seealso marker="#type-start_type"><c>start_type()</c></seealso></v> <v>StartArgs = term()</v> <v>Pid = pid()</v> <v>State = term()</v> </type> <desc> <p>This function is called whenever an application is started - using <c>application:start/1,2</c>, and should start + using <c>start/1,2</c>, and is to start the processes of the application. If the application is structured according to the OTP design principles as a supervision tree, this means starting the top supervisor of the tree.</p> <p><marker id="start_type"/><c>StartType</c> defines the type of start:</p> <list type="bulleted"> - <item><c>normal</c> if it's a normal startup.</item> + <item><c>normal</c> if it is a normal startup.</item> <item><c>normal</c> also if the application is distributed and - started at the current node due to a failover from another + started at the current node because of a failover from another node, and the application specification key <c>start_phases == undefined</c>.</item> <item><c>{takeover,Node}</c> if the application is - distributed and started at the current node due to a + distributed and started at the current node because of a takeover from <c>Node</c>, either because - <c>application:takeover/2</c> has been called or because + <c>takeover/2</c> has been called or because the current node has higher priority than <c>Node</c>.</item> <item><c>{failover,Node}</c> if the application is - distributed and started at the current node due to a + distributed and started at the current node because of a failover from <c>Node</c>, and the application specification key <c>start_phases /= undefined</c>.</item> </list> <p><c>StartArgs</c> is the <c>StartArgs</c> argument defined by the application specification key <c>mod</c>.</p> - <p>The function should return <c>{ok,Pid}</c> or - <c>{ok,Pid,State}</c> where <c>Pid</c> is the pid of the top + <p>The function is to return <c>{ok,Pid}</c> or + <c>{ok,Pid,State}</c>, where <c>Pid</c> is the pid of the top supervisor and <c>State</c> is any term. If omitted, - <c>State</c> defaults to <c>[]</c>. If later the application - is stopped, <c>State</c> is passed to + <c>State</c> defaults to <c>[]</c>. If the application + is stopped later, <c>State</c> is passed to <c>Module:prep_stop/1</c>.</p> </desc> </func> <func> <name>Module:start_phase(Phase, StartType, PhaseArgs) -> ok | {error, Reason}</name> - <fsummary>Extended start of an application</fsummary> + <fsummary>Extended start of an application.</fsummary> <type> <v>Phase = atom()</v> - <v>StartType = <seealso marker="#type-start_type">start_type()</seealso></v> + <v>StartType = <seealso marker="#type-start_type"><c>start_type()</c></seealso></v> <v>PhaseArgs = term()</v> <v>Pid = pid()</v> <v>State = state()</v> </type> <desc> - <p>This function is used to start an application with included - applications, when there is a need for synchronization between + <p>Starts an application with included + applications, when synchronization is needed between processes in the different applications during startup.</p> - <p>The start phases is defined by the application specification + <p>The start phases are defined by the application specification key <c>start_phases == [{Phase,PhaseArgs}]</c>. For included applications, the set of phases must be a subset of the set of phases defined for the including application.</p> <p>The function is called for each start phase (as defined for the primary application) for the primary application and all included applications, for which the start phase is defined.</p> - <p>See <c>Module:start/2</c> for a description of - <c>StartType</c>.</p> + <p>For a description of <c>StartType</c>, see + <seealso marker="Module:start/2"><c>Module:start/2</c></seealso>.</p> </desc> </func> <func> <name>Module:prep_stop(State) -> NewState</name> - <fsummary>Prepare an application for termination</fsummary> + <fsummary>Prepare an application for termination.</fsummary> <type> <v>State = NewState = term()</v> </type> @@ -536,28 +558,26 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> the application.</p> <p><c>State</c> is the state returned from <c>Module:start/2</c>, or <c>[]</c> if no state was returned. - <c>NewState</c> is any term and will be passed to + <c>NewState</c> is any term and is passed to <c>Module:stop/1</c>.</p> <p>The function is optional. If it is not defined, the processes - will be terminated and then <c>Module:stop(State)</c> is - called.</p> + are terminated and then <c>Module:stop(State)</c> is called.</p> </desc> </func> <func> <name>Module:stop(State)</name> - <fsummary>Clean up after termination of an application</fsummary> + <fsummary>Clean up after termination of an application.</fsummary> <type> <v>State = term()</v> </type> <desc> <p>This function is called whenever an application has stopped. It is intended to be the opposite of <c>Module:start/2</c> - and should do any necessary cleaning up. The return value is + and is to do any necessary cleaning up. The return value is ignored.</p> - <p><c>State</c> is the return value of - <c>Module:prep_stop/1</c>, if such a function exists. - Otherwise <c>State</c> is taken from the return value of - <c>Module:start/2</c>.</p> + <p><c>State</c> is the return value of <c>Module:prep_stop/1</c>, + if such a function exists. Otherwise <c>State</c> is taken from + the return value of <c>Module:start/2</c>.</p> </desc> </func> <func> @@ -572,19 +592,18 @@ Nodes = [cp1@cave, {cp2@cave, cp3@cave}]</code> </type> <desc> <p>This function is called by an application after a code - replacement, if there are any changes to the configuration - parameters.</p> - <p><c>Changed</c> is a list of parameter-value tuples with all - configuration parameters with changed values, <c>New</c> is - a list of parameter-value tuples with all configuration - parameters that have been added, and <c>Removed</c> is a list - of all parameters that have been removed.</p> + replacement, if the configuration parameters have changed.</p> + <p><c>Changed</c> is a list of parameter-value tuples including all + configuration parameters with changed values.</p> + <p><c>New</c> is a list of parameter-value tuples including all + added configuration parameters.</p> + <p><c>Removed</c> is a list of all removed parameters.</p> </desc> </func> </funcs> <section> - <title>SEE ALSO</title> + <title>See Also</title> <p><seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso>, <seealso marker="kernel_app">kernel(6)</seealso>, <seealso marker="app">app(4)</seealso></p> diff --git a/lib/kernel/doc/src/auth.xml b/lib/kernel/doc/src/auth.xml index 3ff7f16439..03f983b96d 100644 --- a/lib/kernel/doc/src/auth.xml +++ b/lib/kernel/doc/src/auth.xml @@ -29,7 +29,7 @@ <rev></rev> </header> <module>auth</module> - <modulesummary>Erlang Network Authentication Server</modulesummary> + <modulesummary>Erlang network authentication server.</modulesummary> <description> <p>This module is deprecated. For a description of the Magic Cookie system, refer to @@ -42,60 +42,60 @@ </datatypes> <funcs> <func> - <name name="is_auth" arity="1"/> - <fsummary>Status of communication authorization (deprecated)</fsummary> - <desc> - <p>Returns <c>yes</c> if communication with <c><anno>Node</anno></c> is - authorized. Note that a connection to <c><anno>Node</anno></c> will - be established in this case. Returns <c>no</c> if <c><anno>Node</anno></c> - does not exist or communication is not authorized (it has - another cookie than <c>auth</c> thinks it has).</p> - <p>Use <seealso marker="net_adm#ping/1">net_adm:ping(<c><anno>Node</anno></c>)</seealso> - instead.</p> - </desc> - </func> - <func> <name name="cookie" arity="0"/> - <fsummary>Magic cookie for local node (deprecated)</fsummary> + <fsummary>Magic cookie for local node (deprecated).</fsummary> <desc> <p>Use - <seealso marker="erts:erlang#erlang:get_cookie/0">erlang:get_cookie()</seealso> - instead.</p> + <seealso marker="erts:erlang#erlang:get_cookie/0"><c>erlang:get_cookie()</c></seealso> + in <c>ERTS</c> instead.</p> </desc> </func> <func> <name name="cookie" arity="1"/> - <fsummary>Set the magic for the local node (deprecated)</fsummary> + <fsummary>Set the magic for the local node (deprecated).</fsummary> <type_desc variable="TheCookie"> - The cookie may also be given as a list with a single atom element. + The cookie can also be specified as a list with a single atom element. </type_desc> <desc> <p>Use - <seealso marker="erts:erlang#erlang:set_cookie/2">erlang:set_cookie(node(), <c><anno>Cookie</anno></c>)</seealso> + <seealso marker="erts:erlang#erlang:set_cookie/2"><c>erlang:set_cookie(node(), <anno>Cookie</anno>)</c> + in <c>ERTS</c></seealso> instead.</p> + </desc> + </func> + <func> + <name name="is_auth" arity="1"/> + <fsummary>Status of communication authorization (deprecated).</fsummary> + <desc> + <p>Returns <c>yes</c> if communication with <c><anno>Node</anno></c> is + authorized. Notice that a connection to <c><anno>Node</anno></c> + is established in this case. Returns <c>no</c> if <c><anno>Node</anno></c> + does not exist or communication is not authorized (it has + another cookie than <c>auth</c> thinks it has).</p> + <p>Use <seealso marker="net_adm#ping/1"><c>net_adm:ping(<anno>Node</anno>)</c></seealso> instead.</p> </desc> </func> <func> <name>node_cookie([Node, Cookie]) -> yes | no</name> - <fsummary>Set the magic cookie for a node and verify authorization (deprecated)</fsummary> + <fsummary>Set the magic cookie for a node and verify authorization (deprecated).</fsummary> <type> <v>Node = node()</v> - <v>Cookie = <seealso marker="#type-cookie">cookie()</seealso></v> + <v>Cookie = <seealso marker="#type-cookie"><c>cookie()</c></seealso></v> </type> <desc> <p>Equivalent to - <seealso marker="#node_cookie/2">node_cookie(Node, Cookie)</seealso>.</p> + <seealso marker="#node_cookie/2"><c>node_cookie(Node, Cookie)</c></seealso>.</p> </desc> </func> <func> <name name="node_cookie" arity="2"/> - <fsummary>Set the magic cookie for a node and verify authorization (deprecated)</fsummary> + <fsummary>Set the magic cookie for a node and verify authorization (deprecated).</fsummary> <desc> - <p>Sets the magic cookie of <c><anno>Node</anno></c> to <c><anno>Cookie</anno></c>, and - verifies the status of the authorization. + <p>Sets the magic cookie of <c><anno>Node</anno></c> to + <c><anno>Cookie</anno></c> and verifies the status of the authorization. Equivalent to calling - <seealso marker="erts:erlang#erlang:set_cookie/2">erlang:set_cookie(<c><anno>Node</anno></c>, <c><anno>Cookie</anno>)</c></seealso>, followed by - <seealso marker="#is_auth/1">auth:is_auth(<c><anno>Node</anno></c>)</seealso>.</p> + <seealso marker="erts:erlang#erlang:set_cookie/2"><c>erlang:set_cookie(<anno>Node</anno>, <anno>Cookie</anno>)</c></seealso>, followed by + <seealso marker="#is_auth/1"><c>auth:is_auth(<anno>Node</anno>)</c></seealso>.</p> </desc> </func> </funcs> diff --git a/lib/kernel/doc/src/code.xml b/lib/kernel/doc/src/code.xml index 3a63d7edd2..d3611d6a03 100644 --- a/lib/kernel/doc/src/code.xml +++ b/lib/kernel/doc/src/code.xml @@ -29,238 +29,240 @@ <rev></rev> </header> <module>code</module> - <modulesummary>Erlang Code Server</modulesummary> + <modulesummary>Erlang code server.</modulesummary> <description> <p>This module contains the interface to the Erlang <em>code server</em>, which deals with the loading of compiled code into a running Erlang runtime system.</p> - <p>The runtime system can be started in either <em>embedded</em> or - <em>interactive</em> mode. Which one is decided by the command - line flag <c>-mode</c>.</p> + <p>The runtime system can be started in <em>embedded</em> or + <em>interactive</em> mode. Which one is decided by command-line + flag <c>-mode</c>:</p> <pre> % <input>erl -mode interactive</input></pre> - <p>Default mode is <c>interactive</c>.</p> + <p>The modes are as follows:</p> <list type="bulleted"> <item> - <p>In embedded mode, all code is loaded during system start-up + <p>In embedded mode, all code is loaded during system startup according to the boot script. (Code can also be loaded later by explicitly ordering the code server to do so).</p> </item> <item> - <p>In interactive mode, only some code is loaded during system - startup-up, basically the modules needed by the runtime - system itself. Other code is dynamically loaded when first + <p>In interactive mode, which is default, only some code is loaded + during system startup, basically the modules needed by the runtime + system. Other code is dynamically loaded when first referenced. When a call to a function in a certain module is made, and the module is not loaded, the code server searches for and tries to load the module.</p> </item> </list> - <p>To prevent accidentally reloading modules affecting the Erlang - runtime system itself, the <c>kernel</c>, <c>stdlib</c> and - <c>compiler</c> directories are considered <em>sticky</em>. This + <p>To prevent accidentally reloading of modules affecting the Erlang + runtime system, directories <c>kernel</c>, <c>stdlib</c>, + and <c>compiler</c> are considered <em>sticky</em>. This means that the system issues a warning and rejects the request if a user tries to reload a module residing in any of them. - The feature can be disabled by using the command line flag + The feature can be disabled by using command-line flag <c>-nostick</c>.</p> </description> <section> <title>Code Path</title> - <p>In interactive mode, the code server maintains a search path -- - usually called the <em>code path</em> -- consisting of a list of + <p>In interactive mode, the code server maintains a search path, + usually called the <em>code path</em>, consisting of a list of directories, which it searches sequentially when trying to load a module.</p> <p>Initially, the code path consists of the current working - directory and all Erlang object code directories under the library + directory and all Erlang object code directories under library directory <c>$OTPROOT/lib</c>, where <c>$OTPROOT</c> is the installation directory of Erlang/OTP, <c>code:root_dir()</c>. Directories can be named <c>Name[-Vsn]</c> and the code server, by default, chooses the directory with the highest version number - among those which have the same <c>Name</c>. The <c>-Vsn</c> - suffix is optional. If an <c>ebin</c> directory exists under - <c>Name[-Vsn]</c>, it is this directory which is added to - the code path.</p> - <p>The environment variable <c>ERL_LIBS</c> (defined in the operating - system) can be used to define additional library directories that - will be handled in the same way as the standard OTP library - directory described above, except that directories that do not - have an <c>ebin</c> directory will be ignored.</p> + among those having the same <c>Name</c>. Suffix <c>-Vsn</c> + is optional. If an <c>ebin</c> directory exists under + <c>Name[-Vsn]</c>, this directory is added to the code path.</p> + <p>Environment variable <c>ERL_LIBS</c> (defined in the operating + system) can be used to define more library directories to + be handled in the same way as the standard OTP library + directory described above, except that directories without + an <c>ebin</c> directory are ignored.</p> <p>All application directories found in the additional directories - will appear before the standard OTP applications, except for the - Kernel and STDLIB applications, which will be placed before any - additional applications. In other words, modules found in any - of the additional library directories will override modules with - the same name in OTP, except for modules in Kernel and - STDLIB.</p> - <p>The environment variable <c>ERL_LIBS</c> (if defined) should contain + appears before the standard OTP applications, except for the + <c>Kernel</c> and <c>STDLIB</c> applications, which are placed before + any additional applications. In other words, modules found in any + of the additional library directories override modules with + the same name in OTP, except for modules in <c>Kernel</c> and + <c>STDLIB</c>.</p> + <p>Environment variable <c>ERL_LIBS</c> (if defined) is to contain a colon-separated (for Unix-like systems) or semicolon-separated (for Windows) list of additional libraries.</p> - <p>Example: On an Unix-like system, <c>ERL_LIBS</c> could be set to - <c>/usr/local/jungerl:/home/some_user/my_erlang_lib</c>. (On Windows, - use semi-colon as separator.)</p> + <p><em>Example:</em></p> + <p>On a Unix-like system, <c>ERL_LIBS</c> can be set to the following</p> + <code> +/usr/local/jungerl:/home/some_user/my_erlang_lib</code> + <p>On Windows, use semi-colon as separator.</p> </section> <section> <title>Loading of Code From Archive Files</title> - <warning><p>The support for loading of code from archive files is - experimental. The sole purpose of releasing it before it is ready + <warning><p>The support for loading code from archive files is + experimental. The purpose of releasing it before it is ready is to obtain early feedback. The file format, semantics, - interfaces etc. may be changed in a future release. The function - <c>lib_dir/2</c> and the flag <c>-code_path_choice</c> are also + interfaces, and so on, can be changed in a future release. The function + <seealso marker="#lib_dir/2"><c>lib_dir/2</c></seealso> + and flag <c>-code_path_choice</c> are also experimental.</p></warning> - <p>In the current implementation, Erlang archives are <c>ZIP</c> - files with <c>.ez</c> extension. Erlang archives may also be + <p>The Erlang archives are <c>ZIP</c> + files with extension <c>.ez</c>. Erlang archives can also be enclosed in <c>escript</c> files whose file extension is arbitrary.</p> - <p>Erlang archive files may contain entire Erlang applications or + <p>Erlang archive files can contain entire Erlang applications or parts of applications. The structure in an archive file is the - same as the directory structure for an application. If you for - example would create an archive of <c>mnesia-4.4.7</c>, the + same as the directory structure for an application. If you, for + example, create an archive of <c>mnesia-4.4.7</c>, the archive file must be named <c>mnesia-4.4.7.ez</c> and it must - contain a top directory with the name <c>mnesia-4.4.7</c>. If the + contain a top directory named <c>mnesia-4.4.7</c>. If the version part of the name is omitted, it must also be omitted in the archive. That is, a <c>mnesia.ez</c> archive must contain a <c>mnesia</c> top directory.</p> - <p>An archive file for an application may for example be + <p>An archive file for an application can, for example, be created like this:</p> <pre> - zip:create("mnesia-4.4.7.ez", - ["mnesia-4.4.7"], - [{cwd, code:lib_dir()}, - {compress, all}, - {uncompress,[".beam",".app"]}]).</pre> - - <p>Any file in the archive may be compressed, but in order to - speed up the access of frequently read files, it may be a good +zip:create("mnesia-4.4.7.ez", + ["mnesia-4.4.7"], + [{cwd, code:lib_dir()}, + {compress, all}, + {uncompress,[".beam",".app"]}]).</pre> + + <p>Any file in the archive can be compressed, but to + speed up the access of frequently read files, it can be a good idea to store <c>beam</c> and <c>app</c> files uncompressed in the archive.</p> - <p>Normally the top directory of an application is located either - in the library directory <c>$OTPROOT/lib</c> or in a directory - referred to by the environment variable <c>ERL_LIBS</c>. At - startup when the initial code path is computed, the code server - will also look for archive files in these directories and - possibly add <c>ebin</c> directories in archives to the code path. The - code path will then contain paths to directories that looks like + <p>Normally the top directory of an application is located + in library directory <c>$OTPROOT/lib</c> or in a directory + referred to by environment variable <c>ERL_LIBS</c>. At + startup, when the initial code path is computed, the code server + also looks for archive files in these directories and + possibly adds <c>ebin</c> directories in archives to the code path. The + code path then contains paths to directories that look like <c>$OTPROOT/lib/mnesia.ez/mnesia/ebin</c> or <c>$OTPROOT/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>.</p> - <p>The code server uses the module <c>erl_prim_loader</c> - (possibly via the <c>erl_boot_server</c>) to read code files from - archives. But the functions in <c>erl_prim_loader</c> may also be + <p>The code server uses module <c>erl_prim_loader</c> in <c>ERTS</c> + (possibly through <c>erl_boot_server</c>) to read code files from + archives. However, the functions in <c>erl_prim_loader</c> can also be used by other applications to read files from archives. For example, the call <c>erl_prim_loader:list_dir( "/otp/root/lib/mnesia-4.4.7.ez/mnesia-4.4.7/examples/bench)"</c> would list the contents of a directory inside an archive. - See <seealso marker="erts:erl_prim_loader">erl_prim_loader(3)</seealso>.</p> + See <seealso marker="erts:erl_prim_loader"><c>erl_prim_loader(3)</c></seealso>.</p> <p>An application archive file and a regular application directory - may coexist. This may be useful when there is a need of having + can coexist. This can be useful when it is needed to have parts of the application as regular files. A typical case is the - <c>priv</c> directory which must reside as a regular directory in - order to be able to dynamically link in drivers and start port - programs. For other applications that do not have this need, the - <c>priv</c> directory may reside in the archive and the files - under the <c>priv</c> directory may be read via the + <c>priv</c> directory, which must reside as a regular directory + to link in drivers dynamically and start port programs. + For other applications that do not need this, directory + <c>priv</c> can reside in the archive and the files + under the directory <c>priv</c> can be read through <c>erl_prim_loader</c>.</p> - <p>At the time point when a directory is added to the code path as - well as when the entire code path is (re)set, the code server - will decide which subdirectories in an application that shall be - read from the archive and which that shall be read as regular + <p>When a directory is added to the code path and + when the entire code path is (re)set, the code server + decides which subdirectories in an application that are to be + read from the archive and which that are to be read as regular files. If directories are added or removed afterwards, the file - access may fail if the code path is not updated (possibly to the - same path as before in order to trigger the directory resolution - update). For each directory on the second level (ebin, priv, src - etc.) in the application archive, the code server will firstly - choose the regular directory if it exists and secondly from the - archive. The function - <c>code:lib_dir/2</c> returns the path to the subdirectory. For - example <c>code:lib_dir(megaco,ebin)</c> may return + access can fail if the code path is not updated (possibly to the + same path as before, to trigger the directory resolution + update).</p> + + <p>For each directory on the second level in the application archive + (<c>ebin</c>, <c>priv</c>, <c>src</c>, and so on), the code server first + chooses the regular directory if it exists and second from the + archive. Function <c>code:lib_dir/2</c> returns the path to the + subdirectory. For example, <c>code:lib_dir(megaco,ebin)</c> can return <c>/otp/root/lib/megaco-3.9.1.1.ez/megaco-3.9.1.1/ebin</c> while - <c>code:lib_dir(megaco,priv)</c> may return + <c>code:lib_dir(megaco,priv)</c> can return <c>/otp/root/lib/megaco-3.9.1.1/priv</c>.</p> <p>When an <c>escript</c> file contains an archive, there are - neither restrictions on the name of the <c>escript</c> nor on how - many applications that may be stored in the embedded - archive. Single <c>beam</c> files may also reside on the top - level in the archive. At startup, both the top directory in the - embedded archive as well as all (second level) <c>ebin</c> + no restrictions on the name of the <c>escript</c> and no restrictions + on how many applications that can be stored in the embedded + archive. Single Beam files can also reside on the top + level in the archive. At startup, the top directory in the + embedded archive and all (second level) <c>ebin</c> directories in the embedded archive are added to the code path. - See <seealso marker="erts:escript">escript(1)</seealso></p> + See <seealso marker="erts:escript"><c>erts:escript(1)</c></seealso>.</p> <p>When the choice of directories in the code path is - <c>strict</c>, the directory that ends up in the code path will - be exactly the stated one. This means that if for example the + <c>strict</c>, the directory that ends up in the code path is + exactly the stated one. This means that if, for example, the directory <c>$OTPROOT/lib/mnesia-4.4.7/ebin</c> is explicitly - added to the code path, the code server will not load files from - <c>$OTPROOT/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c> and vice - versa. </p> + added to the code path, the code server does not load files from + <c>$OTPROOT/lib/mnesia-4.4.7.ez/mnesia-4.4.7/ebin</c>.</p> - <p>This behavior can be controlled via the command line flag + <p>This behavior can be controlled through command-line flag <c>-code_path_choice Choice</c>. If the flag is set to <c>relaxed</c>, - the code server will instead choose a suitable directory - depending on the actual file structure. If there exists a regular - application ebin directory, it will be chosen. But if it does - not exist, the ebin directory in the archive is chosen if it - exists. If neither of them exists the original directory will be + the code server instead chooses a suitable directory + depending on the actual file structure. If a regular + application <c>ebin</c> directory exists, it is chosen. Otherwise, + the directory <c>ebin</c> in the archive is chosen if it + exists. If neither of them exists, the original directory is chosen.</p> - <p>The command line flag <c>-code_path_choice Choice</c> does also - affect how <c>init</c> interprets the <c>boot script</c>. The - interpretation of the explicit code paths in the <c>boot - script</c> may be <c>strict</c> or <c>relaxed</c>. It is - particular useful to set the flag to <c>relaxed</c> when you want - to elaborate with code loading from archives without editing the + <p>Command-line flag <c>-code_path_choice Choice</c> also + affects how module <c>init</c> interprets the <c>boot script</c>. + The interpretation of the explicit code paths in the <c>boot + script</c> can be <c>strict</c> or <c>relaxed</c>. It is + particularly useful to set the flag to <c>relaxed</c> when + elaborating with code loading from archives without editing the <c>boot script</c>. The default is <c>relaxed</c>. See <seealso - marker="erts:init">init(3)</seealso></p></section> + marker="erts:init"><c>erts:init(3)</c></seealso>.</p></section> <section> <title>Current and Old Code</title> - <p>The code of a module can exists in two variants in a system: + <p>The code for a module can exist in two variants in a system: <em>current code</em> and <em>old code</em>. When a module is - loaded into the system for the first time, the code of the module + loaded into the system for the first time, the module code becomes 'current' and the global <em>export table</em> is updated with references to all functions exported from the module.</p> - <p>If then a new instance of the module is loaded (perhaps because - of the correction of an error), then the code of the previous + <p>If then a new instance of the module is loaded (for example, because of + error correction), the code of the previous instance becomes 'old', and all export entries referring to - the previous instance are removed. After that the new instance is - loaded as if it was loaded for the first time, as described above, - and becomes 'current'.</p> - <p>Both old and current code for a module are valid, and may even be + the previous instance are removed. After that, the new instance is + loaded as for the first time, and becomes 'current'.</p> + <p>Both old and current code for a module are valid, and can even be evaluated concurrently. The difference is that exported functions - in old code are unavailable. Hence there is no way to make a - global call to an exported function in old code, but old code may + in old code are unavailable. Hence, a global call cannot be made + to an exported function in old code, but old code can still be evaluated because of processes lingering in it.</p> - <p>If a third instance of the module is loaded, the code server will - remove (purge) the old code and any processes lingering in it will - be terminated. Then the third instance becomes 'current' and + <p>If a third instance of the module is loaded, the code server + removes (purges) the old code and any processes lingering in it + are terminated. Then the third instance becomes 'current' and the previously current code becomes 'old'.</p> <p>For more information about old and current code, and how to - make a process switch from old to current code, refer to + make a process switch from old to current code, see section + Compilation and Code Loading in the <seealso marker="doc/reference_manual:code_loading">Erlang Reference Manual</seealso>.</p> </section> <section> <title>Argument Types and Invalid Arguments</title> - <p>Generally, module and application names are atoms, while file and directory + <p>Module and application names are atoms, while file and directory names are strings. For backward compatibility reasons, some functions accept both strings and atoms, but a future release will probably only allow the arguments that are documented.</p> - <p>From the R12B release, functions in this module will generally fail with an - exception if they are passed an incorrect type (for instance, an integer or a tuple - where an atom was expected). An error tuple will be returned if the type of the argument - was correct, but there was some other error (for instance, a non-existing directory - was given to <c>set_path/1</c>).</p> + <p>As from Erlang/OTP R12B, functions in this module generally fail with an + exception if they are passed an incorrect type (for example, an integer or a tuple + where an atom is expected). An error tuple is returned if the argument type + is correct, but there are some other errors (for example, a non-existing directory + is specified to <c>set_path/1</c>).</p> </section> <section> @@ -312,33 +314,36 @@ </datatype> <datatype> <name name="prepared_code"/> - <desc>An opaque term holding prepared code.</desc> + <desc><p>An opaque term holding prepared code.</p></desc> </datatype> </datatypes> <funcs> <func> <name name="set_path" arity="1"/> - <fsummary>Set the code server search path</fsummary> + <fsummary>Set the code server search path.</fsummary> <desc> <p>Sets the code path to the list of directories <c><anno>Path</anno></c>.</p> - <p>Returns <c>true</c> if successful, or - <c>{error, bad_directory}</c> if any <c><anno>Dir</anno></c> is not - the name of a directory, or <c>{error, bad_path}</c> if - the argument is invalid.</p> + <p>Returns:</p> + <taglist> + <tag><c>true</c></tag> + <item><p>If successful</p></item> + <tag><c>{error, bad_directory}</c></tag> + <item><p>If any <c><anno>Dir</anno></c> is not a directory name</p></item> + </taglist> </desc> </func> <func> <name name="get_path" arity="0"/> - <fsummary>Return the code server search path</fsummary> + <fsummary>Return the code server search path.</fsummary> <desc> - <p>Returns the code path</p> + <p>Returns the code path.</p> </desc> </func> <func> <name name="add_path" arity="1"/> <name name="add_pathz" arity="1"/> - <fsummary>Add a directory to the end of the code path</fsummary> + <fsummary>Add a directory to the end of the code path.</fsummary> <type name="add_path_ret"/> <desc> <p>Adds <c><anno>Dir</anno></c> to the code path. The directory is added as @@ -351,11 +356,11 @@ </func> <func> <name name="add_patha" arity="1"/> - <fsummary>Add a directory to the beginning of the code path</fsummary> + <fsummary>Add a directory to the beginning of the code path.</fsummary> <type name="add_path_ret"/> <desc> <p>Adds <c><anno>Dir</anno></c> to the beginning of the code path. If - <c><anno>Dir</anno></c> already exists, it is removed from the old + <c><anno>Dir</anno></c> exists, it is removed from the old position in the code path.</p> <p>Returns <c>true</c> if successful, or <c>{error, bad_directory}</c> if <c><anno>Dir</anno></c> is not the name @@ -365,69 +370,81 @@ <func> <name name="add_paths" arity="1"/> <name name="add_pathsz" arity="1"/> - <fsummary>Add directories to the end of the code path</fsummary> + <fsummary>Add directories to the end of the code path.</fsummary> <desc> <p>Adds the directories in <c><anno>Dirs</anno></c> to the end of the code - path. If a <c><anno>Dir</anno></c> already exists, it is not added. This - function always returns <c>ok</c>, regardless of the validity + path. If a <c><anno>Dir</anno></c> exists, it is not added.</p> + <p>Always returns <c>ok</c>, regardless of the validity of each individual <c><anno>Dir</anno></c>.</p> </desc> </func> <func> <name name="add_pathsa" arity="1"/> - <fsummary>Add directories to the beginning of the code path</fsummary> + <fsummary>Add directories to the beginning of the code path.</fsummary> <desc> <p>Adds the directories in <c><anno>Dirs</anno></c> to the beginning of - the code path. If a <c><anno>Dir</anno></c> already exists, it is removed - from the old position in the code path. This function always - returns <c>ok</c>, regardless of the validity of each + the code path. If a <c><anno>Dir</anno></c> exists, it is removed + from the old position in the code path.</p> + <p>Always returns <c>ok</c>, regardless of the validity of each individual <c><anno>Dir</anno></c>.</p> </desc> </func> <func> <name name="del_path" arity="1"/> - <fsummary>Delete a directory from the code path</fsummary> + <fsummary>Delete a directory from the code path.</fsummary> <desc> <p>Deletes a directory from the code path. The argument can be an atom <c><anno>Name</anno></c>, in which case the directory with the name <c>.../<anno>Name</anno>[-Vsn][/ebin]</c> is deleted from the code - path. It is also possible to give the complete directory name - <c><anno>Dir</anno></c> as argument.</p> - <p>Returns <c>true</c> if successful, or <c>false</c> if - the directory is not found, or <c>{error, bad_name}</c> if - the argument is invalid.</p> + path. Also, the complete directory name <c><anno>Dir</anno></c> can be + specified as argument.</p> + <p>Returns:</p> + <taglist> + <tag><c>true</c></tag> + <item><p>If successful</p></item> + <tag><c>false</c></tag> + <item><p>If the directory is not found</p></item> + <tag><c>{error, bad_name}</c></tag> + <item><p>If the argument is invalid</p></item> + </taglist> </desc> </func> <func> <name name="replace_path" arity="2"/> - <fsummary>Replace a directory with another in the code path</fsummary> - <desc> - <p>This function replaces an old occurrence of a directory - named <c>.../<anno>Name</anno>[-Vsn][/ebin]</c>, in the code path, with - <c><anno>Dir</anno></c>. If <c><anno>Name</anno></c> does not exist, it adds the new - directory <c><anno>Dir</anno></c> last in the code path. The new directory - must also be named <c>.../<anno>Name</anno>[-Vsn][/ebin]</c>. This function - should be used if a new version of the directory (library) is + <fsummary>Replace a directory with another in the code path.</fsummary> + <desc> + <p>Replaces an old occurrence of a directory + named <c>.../<anno>Name</anno>[-Vsn][/ebin]</c> in the code path, with + <c><anno>Dir</anno></c>. If <c><anno>Name</anno></c> does not exist, it adds + the new directory <c><anno>Dir</anno></c> last in the code path. The new + directory must also be named <c>.../<anno>Name</anno>[-Vsn][/ebin]</c>. + This function is to be used if a new version of the directory (library) is added to a running system.</p> - <p>Returns <c>true</c> if successful, or - <c>{error, bad_name}</c> if <c><anno>Name</anno></c> is not found, or - <c>{error, bad_directory}</c> if <c><anno>Dir</anno></c> does not exist, or - <c>{error, {badarg, [<anno>Name</anno>, <anno>Dir</anno>]}}</c> if <c><anno>Name</anno></c> or - <c><anno>Dir</anno></c> is invalid.</p> + <p>Returns:</p> + <taglist> + <tag><c>true</c></tag> + <item><p>If successful</p></item> + <tag><c>{error, bad_name}</c></tag> + <item><p>If <c><anno>Name</anno></c> is not found</p></item> + <tag><c>{error, bad_directory}</c></tag> + <item><p>If <c><anno>Dir</anno></c> does not exist</p></item> + <tag><c>{error, {badarg, [<anno>Name</anno>, <anno>Dir</anno>]}}</c></tag> + <item><p>If <c><anno>Name</anno></c> or <c><anno>Dir</anno></c> is invalid</p></item> + </taglist> </desc> </func> <func> <name name="load_file" arity="1"/> - <fsummary>Load a module</fsummary> + <fsummary>Load a module.</fsummary> <type name="load_ret"/> <desc> <p>Tries to load the Erlang module <c><anno>Module</anno></c>, using the code path. It looks for the object code file with an - extension that corresponds to the Erlang machine used, for - example <c><anno>Module</anno>.beam</c>. The loading fails if the module + extension corresponding to the Erlang machine used, for + example, <c><anno>Module</anno>.beam</c>. The loading fails if the module name found in the object code differs from the name <c><anno>Module</anno></c>. - <seealso marker="#load_binary/3">load_binary/3</seealso> must + <seealso marker="#load_binary/3"><c>load_binary/3</c></seealso> must be used to load object code with a module name that is different from the file name.</p> <p>Returns <c>{module, <anno>Module</anno>}</c> if successful, or @@ -437,45 +454,45 @@ </func> <func> <name name="load_abs" arity="1"/> - <fsummary>Load a module, residing in a given file</fsummary> + <fsummary>Load a module, residing in a specified file.</fsummary> <type name="load_ret"/> <type name="loaded_filename"/> <type name="loaded_ret_atoms"/> <desc> - <p>Does the same as <c>load_file(<anno>Module</anno>)</c>, but - <c><anno>Filename</anno></c> is either an absolute file name, or a - relative file name. The code path is not searched. It returns + <p>Same as <c>load_file(<anno>Module</anno>)</c>, but + <c><anno>Filename</anno></c> is an absolute or + relative filename. The code path is not searched. It returns a value in the same way as - <seealso marker="#load_file/1">load_file/1</seealso>. Note - that <c><anno>Filename</anno></c> should not contain the extension (for - example <c>".beam"</c>); <c>load_abs/1</c> adds the correct - extension itself.</p> + <seealso marker="#load_file/1"><c>load_file/1</c></seealso>. Notice + that <c><anno>Filename</anno></c> must not contain the extension (for + example, <c>.beam</c>) because <c>load_abs/1</c> adds the correct + extension.</p> </desc> </func> <func> <name name="ensure_loaded" arity="1"/> - <fsummary>Ensure that a module is loaded</fsummary> + <fsummary>Ensure that a module is loaded.</fsummary> <desc> - <p>Tries to to load a module in the same way as - <seealso marker="#load_file/1">load_file/1</seealso>, + <p>Tries to load a module in the same way as + <seealso marker="#load_file/1"><c>load_file/1</c></seealso>, unless the module is already loaded. - In embedded mode, however, it does not load a module which is not + However, in embedded mode it does not load a module that is not already loaded, but returns <c>{error, embedded}</c> instead. See <seealso marker="#error_reasons">Error Reasons for Code-Loading Functions</seealso> for a description of other possible error reasons.</p> </desc> </func> <func> <name name="load_binary" arity="3"/> - <fsummary>Load object code for a module</fsummary> + <fsummary>Load object code for a module.</fsummary> <type name="loaded_filename"/> <type name="loaded_ret_atoms"/> <desc> <p>This function can be used to load object code on remote - Erlang nodes. The argument <c><anno>Binary</anno></c> must contain + Erlang nodes. Argument <c><anno>Binary</anno></c> must contain object code for <c><anno>Module</anno></c>. <c><anno>Filename</anno></c> is only used by the code server to keep a record of from which file the object code for <c><anno>Module</anno></c> - comes. Accordingly, <c><anno>Filename</anno></c> is not opened and read by + comes. Thus, <c><anno>Filename</anno></c> is not opened and read by the code server.</p> <p>Returns <c>{module, <anno>Module</anno>}</c> if successful, or <c>{error, Reason}</c> if loading fails. @@ -616,92 +633,94 @@ ok = code:finish_loading(Prepared), </func> <func> <name name="delete" arity="1"/> - <fsummary>Removes current code for a module</fsummary> + <fsummary>Remove current code for a module.</fsummary> <desc> <p>Removes the current code for <c><anno>Module</anno></c>, that is, the current code for <c><anno>Module</anno></c> is made old. This means that processes can continue to execute the code in the module, - but that no external function calls can be made to it.</p> + but no external function calls can be made to it.</p> <p>Returns <c>true</c> if successful, or <c>false</c> if there - is old code for <c><anno>Module</anno></c> which must be purged first, or + is old code for <c><anno>Module</anno></c> that must be purged first, or if <c><anno>Module</anno></c> is not a (loaded) module.</p> </desc> </func> <func> <name name="purge" arity="1"/> - <fsummary>Removes old code for a module</fsummary> + <fsummary>Remove old code for a module.</fsummary> <desc> <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old. If some processes still linger in the old code, these processes are killed before the code is removed.</p> - <p>Returns <c>true</c> if successful and any process needed to + <p>Returns <c>true</c> if successful and any process is needed to be killed, otherwise <c>false</c>.</p> </desc> </func> <func> <name name="soft_purge" arity="1"/> - <fsummary>Removes old code for a module, unless no process uses it</fsummary> + <fsummary>Remove old code for a module, unless no process uses it.</fsummary> <desc> <p>Purges the code for <c><anno>Module</anno></c>, that is, removes code marked as old, but only if no processes linger in it.</p> - <p>Returns <c>false</c> if the module could not be purged due - to processes lingering in old code, otherwise <c>true</c>.</p> + <p>Returns <c>false</c> if the module cannot be purged because + of processes lingering in old code, otherwise <c>true</c>.</p> </desc> </func> <func> <name name="is_loaded" arity="1"/> - <fsummary>Check if a module is loaded</fsummary> + <fsummary>Check if a module is loaded.</fsummary> <type name="loaded_filename"/> <type name="loaded_ret_atoms"/> - <type_desc name="loaded_filename"><c><anno>Filename</anno></c> is an absolute filename</type_desc> + <type_desc name="loaded_filename"><c><anno>Filename</anno></c> is an absolute + filename.</type_desc> <desc> <p>Checks if <c><anno>Module</anno></c> is loaded. If it is, <c>{file, <anno>Loaded</anno>}</c> is returned, otherwise <c>false</c>.</p> - <p>Normally, <c><anno>Loaded</anno></c> is the absolute file name - <c>Filename</c> from which the code was obtained. If the module + <p>Normally, <c><anno>Loaded</anno></c> is the absolute filename + <c>Filename</c> from which the code is obtained. If the module is preloaded (see - <seealso marker="sasl:script">script(4)</seealso>), - <c>Loaded==preloaded</c>. If the module is Cover compiled (see - <seealso marker="tools:cover">cover(3)</seealso>), + <seealso marker="sasl:script"><c>sasl:script(4)</c></seealso>), + <c>Loaded==preloaded</c>. If the module is Cover-compiled (see + <seealso marker="tools:cover"><c>tools:cover(3)</c></seealso>), <c>Loaded==cover_compiled</c>.</p> </desc> </func> <func> <name name="all_loaded" arity="0"/> - <fsummary>Get all loaded modules</fsummary> + <fsummary>Get all loaded modules.</fsummary> <type name="loaded_filename"/> <type name="loaded_ret_atoms"/> - <type_desc name="loaded_filename"><c><anno>Filename</anno></c> is an absolute filename</type_desc> + <type_desc name="loaded_filename"><c><anno>Filename</anno></c> is an absolute + filename.</type_desc> <desc> <p>Returns a list of tuples <c>{<anno>Module</anno>, <anno>Loaded</anno>}</c> for all - loaded modules. <c><anno>Loaded</anno></c> is normally the absolute file - name, as described for - <seealso marker="#is_loaded/1">is_loaded/1</seealso>.</p> + loaded modules. <c><anno>Loaded</anno></c> is normally the absolute filename, + as described for + <seealso marker="#is_loaded/1"><c>is_loaded/1</c></seealso>.</p> </desc> </func> <func> <name name="which" arity="1"/> - <fsummary>The object code file of a module</fsummary> + <fsummary>The object code file of a module.</fsummary> <type name="loaded_ret_atoms"/> <desc> <p>If the module is not loaded, this function searches the code - path for the first file which contains object code for - <c><anno>Module</anno></c> and returns the absolute file name. If - the module is loaded, it returns the name of the file which - contained the loaded object code. If the module is pre-loaded, - <c>preloaded</c> is returned. If the module is Cover compiled, - <c>cover_compiled</c> is returned. <c>non_existing</c> is - returned if the module cannot be found.</p> + path for the first file containing object code for + <c><anno>Module</anno></c> and returns the absolute filename.</p> + <p>If the module is loaded, it returns the name of the file + containing the loaded object code.</p> + <p>If the module is preloaded, <c>preloaded</c> is returned.</p> + <p>If the module is Cover-compiled, <c>cover_compiled</c> is returned.</p> + <p>If the module cannot be found, <c>non_existing</c> is returned.</p> </desc> </func> <func> <name name="get_object_code" arity="1"/> - <fsummary>Get the object code for a module</fsummary> + <fsummary>Gets the object code for a module.</fsummary> <desc> - <p>Searches the code path for the object code of the module - <c><anno>Module</anno></c>. It returns <c>{<anno>Module</anno>, <anno>Binary</anno>, <anno>Filename</anno>}</c> - if successful, and <c>error</c> if not. <c><anno>Binary</anno></c> is a - binary data object which contains the object code for + <p>Searches the code path for the object code of module + <c><anno>Module</anno></c>. Returns <c>{<anno>Module</anno>, <anno>Binary</anno>, <anno>Filename</anno>}</c> + if successful, otherwise <c>error</c>. <c><anno>Binary</anno></c> is a + binary data object, which contains the object code for the module. This can be useful if code is to be loaded on a remote node in a distributed system. For example, loading module <c><anno>Module</anno></c> on a node <c>Node</c> is done as @@ -715,10 +734,11 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </func> <func> <name name="root_dir" arity="0"/> - <fsummary>Root directory of Erlang/OTP</fsummary> + <fsummary>Root directory of Erlang/OTP.</fsummary> <desc> <p>Returns the root directory of Erlang/OTP, which is the directory where it is installed.</p> + <p><em>Example:</em></p> <pre> > <input>code:root_dir().</input> "/usr/local/otp"</pre> @@ -726,10 +746,11 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </func> <func> <name name="lib_dir" arity="0"/> - <fsummary>Library directory of Erlang/OTP</fsummary> + <fsummary>Library directory of Erlang/OTP.</fsummary> <desc> <p>Returns the library directory, <c>$OTPROOT/lib</c>, where <c>$OTPROOT</c> is the root directory of Erlang/OTP.</p> + <p><em>Example:</em></p> <pre> > <input>code:lib_dir().</input> "/usr/local/otp/lib"</pre> @@ -737,50 +758,49 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </func> <func> <name name="lib_dir" arity="1"/> - <fsummary>Library directory for an application</fsummary> + <fsummary>Library directory for an application.</fsummary> <desc> - <p>This function is mainly intended for finding out the path + <p>Returns the path for the "library directory", the top directory, for an application <c><anno>Name</anno></c> located under <c>$OTPROOT/lib</c> or - on a directory referred to via the <c>ERL_LIBS</c> - environment variable.</p> - <p>If there is a regular directory called <c><anno>Name</anno></c> or - <c><anno>Name</anno>-Vsn</c> in the code path with an <c>ebin</c> + on a directory referred to with environment variable <c>ERL_LIBS</c>.</p> + <p>If a regular directory called <c><anno>Name</anno></c> or + <c><anno>Name</anno>-Vsn</c> exists in the code path with an <c>ebin</c> subdirectory, the path to this directory is returned (not - the <c>ebin</c> directory). If the directory refers to a - directory in an archive, the archive name is stripped away - before the path is returned. For example, if the directory + the <c>ebin</c> directory).</p> + <p>If the directory refers to a directory in an archive, the + archive name is stripped away before the path is returned. + For example, if directory <c>/usr/local/otp/lib/mnesia-4.2.2.ez/mnesia-4.2.2/ebin</c> is in the path, <c>/usr/local/otp/lib/mnesia-4.2.2/ebin</c> - will be returned. This means that the library directory for - an application is the same, regardless of whether the + is returned. This means that the library directory for + an application is the same, regardless if the application resides in an archive or not.</p> - + <p><em>Example:</em></p> <pre> > <input>code:lib_dir(mnesia).</input> "/usr/local/otp/lib/mnesia-4.2.2"</pre> <p>Returns <c>{error, bad_name}</c> if <c><anno>Name</anno></c> is not the name of an application under <c>$OTPROOT/lib</c> or - on a directory referred to via the <c>ERL_LIBS</c> - environment variable. Fails with an exception if <c>Name</c> - has the wrong type.</p> + on a directory referred to through environment variable <c>ERL_LIBS</c>. + Fails with an exception if <c>Name</c> has the wrong type.</p> - <warning><p>For backward compatibility, <c><anno>Name</anno></c> is also allowed to - be a string. That will probably change in a future release.</p></warning> + <warning><p>For backward compatibility, <c><anno>Name</anno></c> is also + allowed to be a string. That will probably change in a future release.</p></warning> </desc> </func> <func> <name name="lib_dir" arity="2"/> - <fsummary>subdirectory for an application</fsummary> + <fsummary>Subdirectory for an application.</fsummary> <desc> <p>Returns the path to a subdirectory directly under the top directory of an application. Normally the subdirectories - resides under the top directory for the application, but when - applications at least partly resides in an archive the - situation is different. Some of the subdirectories may reside - as regular directories while other resides in an archive - file. It is not checked if this directory really exists.</p> - + reside under the top directory for the application, but when + applications at least partly resides in an archive, the + situation is different. Some of the subdirectories can reside + as regular directories while other reside in an archive + file. It is not checked whether this directory exists.</p> + <p><em>Example:</em></p> <pre> > <input>code:lib_dir(megaco, priv).</input> "/usr/local/otp/lib/megaco-3.9.1.1/priv"</pre> @@ -791,7 +811,7 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </func> <func> <name name="compiler_dir" arity="0"/> - <fsummary>Library directory for the compiler</fsummary> + <fsummary>Library directory for the compiler.</fsummary> <desc> <p>Returns the compiler library directory. Equivalent to <c>code:lib_dir(compiler)</c>.</p> @@ -799,10 +819,10 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </func> <func> <name name="priv_dir" arity="1"/> - <fsummary>Priv directory for an application</fsummary> + <fsummary>Priv directory for an application.</fsummary> <desc> <p>Returns the path to the <c>priv</c> directory in an - application. Equivalent to <c>code:lib_dir(<anno>Name</anno>, priv).</c>.</p> + application. Equivalent to <c>code:lib_dir(<anno>Name</anno>, priv)</c>.</p> <warning><p>For backward compatibility, <c><anno>Name</anno></c> is also allowed to be a string. That will probably change in a future release.</p></warning> @@ -810,43 +830,43 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), </func> <func> <name name="objfile_extension" arity="0"/> - <fsummary>Object code file extension</fsummary> + <fsummary>Object code file extension.</fsummary> <desc> - <p>Returns the object code file extension that corresponds to - the Erlang machine used, namely <c>".beam"</c>.</p> + <p>Returns the object code file extension corresponding to + the Erlang machine used, namely <c>.beam</c>.</p> </desc> </func> <func> <name name="stick_dir" arity="1"/> - <fsummary>Mark a directory as sticky</fsummary> + <fsummary>Mark a directory as sticky.</fsummary> <desc> - <p>This function marks <c><anno>Dir</anno></c> as sticky.</p> - <p>Returns <c>ok</c> if successful or <c>error</c> if not.</p> + <p>Marks <c><anno>Dir</anno></c> as sticky.</p> + <p>Returns <c>ok</c> if successful, otherwise <c>error</c>.</p> </desc> </func> <func> <name name="unstick_dir" arity="1"/> - <fsummary>Remove a sticky directory mark</fsummary> + <fsummary>Remove a sticky directory mark.</fsummary> <desc> - <p>This function unsticks a directory which has been marked as + <p>Unsticks a directory that is marked as sticky.</p> - <p>Returns <c>ok</c> if successful or <c>error</c> if not.</p> + <p>Returns <c>ok</c> if successful, otherwise <c>error</c>.</p> </desc> </func> <func> <name name="is_sticky" arity="1"/> - <fsummary>Test whether a module is sticky</fsummary> + <fsummary>Test if a module is sticky.</fsummary> <desc> - <p>This function returns <c>true</c> if <c><anno>Module</anno></c> is the + <p>Returns <c>true</c> if <c><anno>Module</anno></c> is the name of a module that has been loaded from a sticky directory - (or in other words: an attempt to reload the module will fail), + (in other words: an attempt to reload the module will fail), or <c>false</c> if <c><anno>Module</anno></c> is not a loaded module or is not sticky.</p> </desc> </func> <func> <name name="where_is_file" arity="1"/> - <fsummary>Full name of a file located in the code path</fsummary> + <fsummary>Full name of a file located in the code path.</fsummary> <desc> <p>Searches the code path for <c><anno>Filename</anno></c>, a file of arbitrary type. If found, the full name is returned. @@ -859,32 +879,39 @@ rpc:call(Node, code, load_binary, [Module, Filename, Binary]), <name name="clash" arity="0"/> <fsummary>Search for modules with identical names.</fsummary> <desc> - <p>Searches the entire code space for module names with + <p>Searches all directories in the code path for module names with identical names and writes a report to <c>stdout</c>.</p> </desc> </func> <func> <name name="is_module_native" arity="1"/> - <fsummary>Test whether a module has native code</fsummary> + <fsummary>Test if a module has native code.</fsummary> <desc> - <p>This function returns <c>true</c> if <c><anno>Module</anno></c> is - name of a loaded module that has native code loaded, and - <c>false</c> if <c><anno>Module</anno></c> is loaded but does not have - native. If <c><anno>Module</anno></c> is not loaded, this function returns - <c>undefined</c>.</p> + <p>Returns:</p> + <taglist> + <tag><c>true</c></tag> + <item><p>If <c><anno>Module</anno></c> is the + name of a loaded module that has native code loaded</p></item> + <tag><c>false</c></tag> + <item><p>If <c><anno>Module</anno></c> is loaded but does not have + native code</p></item> + <tag><c>undefined</c></tag> + <item><p>If <c><anno>Module</anno></c> is not loaded</p></item> + </taglist> </desc> </func> <func> <name name="get_mode" arity="0"/> - <fsummary>The code_server's mode.</fsummary> + <fsummary>The mode of the code server.</fsummary> <desc> - <p>This function returns an atom describing the code_server's mode: - <c>interactive</c> or <c>embedded</c>. </p> + <p>Returns an atom describing the mode of the code server: + <c>interactive</c> or <c>embedded</c>.</p> <p>This information is useful when an external entity (for example, - an IDE) provides additional code for a running node. If in interactive - mode, it only needs to add to the code path. If in embedded mode, - the code has to be loaded with <c>load_binary/3</c></p> + an IDE) provides additional code for a running node. If the code server is + in interactive mode, it only has to add the path to the code. If the code server + is in embedded mode, the code must be loaded with + <seealso marker="#load_binary/3"><c>load_binary/3</c></seealso>.</p> </desc> </func> </funcs> diff --git a/lib/kernel/doc/src/config.xml b/lib/kernel/doc/src/config.xml index 0ec28d8974..c5f37fd036 100644 --- a/lib/kernel/doc/src/config.xml +++ b/lib/kernel/doc/src/config.xml @@ -11,7 +11,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -19,7 +19,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + </legalnotice> <title>config</title> @@ -33,94 +33,93 @@ <description> <p>A <em>configuration file</em> contains values for configuration parameters for the applications in the system. The <c>erl</c> - command line argument <c>-config Name</c> tells the system to use + command-line argument <c>-config Name</c> tells the system to use data in the system configuration file <c>Name.config</c>.</p> - <p>Configuration parameter values in the configuration file will + <p>Configuration parameter values in the configuration file override the values in the application resource files (see - <c>app(4)</c>). The values in the configuration file can be - overridden by command line flags (see <c>erl(1)</c>).</p> + <seealso marker="app"><c>app(4)</c></seealso>. + The values in the configuration file can be + overridden by command-line flags (see + <seealso marker="erts:erl"><c>erts:erl(1)</c></seealso>.</p> <p>The value of a configuration parameter is retrieved by calling <c>application:get_env/1,2</c>.</p> </description> <section> - <title>FILE SYNTAX</title> - <p>The configuration file should be called <c>Name.config</c> where - <c>Name</c> is an arbitrary name.</p> - <p>The <c>.config</c> file contains one single Erlang term. - The file has the following syntax:</p> + <title>File Syntax</title> + <p>The configuration file is to be called <c>Name.config</c>, where + <c>Name</c> is any name.</p> + <p>File <c>.config</c> contains a single Erlang term and + has the following syntax:</p> <code type="none"> -[{Application1, [{Par11, Val11}, ..]}, - .. - {ApplicationN, [{ParN1, ValN1}, ..]}].</code> - <list type="bulleted"> - <item> - <p><c>Application = atom()</c> is the name of the application.</p> - </item> - <item> - <p><c>Par = atom()</c> is the name of a configuration parameter.</p> - </item> - <item> - <p><c>Val = term()</c> is the value of a configuration - parameter.</p> - </item> - </list> +[{Application1, [{Par11, Val11}, ...]}, + ... + {ApplicationN, [{ParN1, ValN1}, ...]}].</code> + <taglist> + <tag><c>Application = atom()</c></tag> + <item><p>Application name.</p></item> + <tag><c>Par = atom()</c></tag> + <item><p>Name of a configuration parameter.</p></item> + <tag><c>Val = term()</c></tag> + <item><p>Value of a configuration parameter.</p></item> + </taglist> </section> <section> <title>sys.config</title> <p>When starting Erlang in embedded mode, it is assumed that exactly one system configuration file is used, named - <c>sys.config</c>. This file should be located in + <c>sys.config</c>. This file is to be located in <c>$ROOT/releases/Vsn</c>, where <c>$ROOT</c> is the Erlang/OTP root installation directory and <c>Vsn</c> is the release version.</p> <p>Release handling relies on this assumption. When installing a new release version, the new <c>sys.config</c> is read and used to update the application configurations.</p> - <p>This means that specifying another, or additional, <c>.config</c> - files would lead to inconsistent update of application + <p>This means that specifying another <c>.config</c> file, or more + <c>.config</c> files, leads to inconsistent update of application configurations. Therefore, in Erlang 5.4/OTP R10B, the syntax of <c>sys.config</c> was extended to allow pointing out other <c>.config</c> files:</p> <code type="none"> [{Application, [{Par, Val}]} | File].</code> - <list type="bulleted"> - <item> - <p><c>File = string()</c> is the name of another <c>.config</c> - file. The extension <c>.config</c> may be omitted. It is - recommended to use absolute paths. A relative path is - relative the current working directory of the emulator.</p> - </item> - </list> + <taglist> + <tag><c>File = string()</c></tag> + <item>Name of another <c>.config</c> file. + Extension <c>.config</c> can be omitted. It is + recommended to use absolute paths. A relative path is + relative the current working directory of the emulator.</item> + </taglist> <p>When traversing the contents of <c>sys.config</c> and a filename is encountered, its contents are read and merged with the result so far. When an application configuration tuple <c>{Application, Env}</c> is found, it is merged with the result so far. Merging means that new parameters are added and existing - parameter values overwritten. Example:</p> + parameter values overwritten.</p> + <p><em>Example:</em></p> <code type="none"> sys.config: [{myapp,[{par1,val1},{par2,val2}]}, "/home/user/myconfig"]. - myconfig.config: [{myapp,[{par2,val3},{par3,val4}]}].</code> - <p>This will yield the following environment for <c>myapp</c>:</p> + <p>This yields the following environment for <c>myapp</c>:</p> <code type="none"> [{par1,val1},{par2,val3},{par3,val4}]</code> - <p>The behaviour if a file specified in <c>sys.config</c> does not - exist or is erroneous in some other way, is backwards compatible. + <p>The behavior if a file specified in <c>sys.config</c> does not + exist, or is erroneous, is backwards compatible. Starting the runtime system will fail. Installing a new release - version will not fail, but an error message is given and + version will not fail, but an error message is returned and the erroneous file is ignored.</p> </section> <section> - <title>SEE ALSO</title> - <p><c>app(4)</c>, <c>erl(1)</c>, <em>OTP Design Principles</em></p> + <title>See Also</title> + <p><seealso marker="app"><c>app(4)</c></seealso>, + <seealso marker="erts:erl"><c>erts:erl(1)</c></seealso>, + <seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso></p> </section> </fileref> diff --git a/lib/kernel/doc/src/disk_log.xml b/lib/kernel/doc/src/disk_log.xml index 50a1537c24..0b6ee1e6a5 100644 --- a/lib/kernel/doc/src/disk_log.xml +++ b/lib/kernel/doc/src/disk_log.xml @@ -35,149 +35,174 @@ <file>disk_log.sgml</file> </header> <module>disk_log</module> - <modulesummary>A disk based term logging facility</modulesummary> + <modulesummary>A disk-based term logging facility.</modulesummary> <description> - <p><c>disk_log</c> is a disk based term logger which makes - it possible to efficiently log items on files. - Two types of logs are supported, - <em>halt logs</em> and <em>wrap logs</em>. A halt log - appends items to a single file, the size of which may or may - not be limited by the disk log module, whereas a wrap log utilizes - a sequence of wrap log files of limited size. As a wrap log file - has been filled up, further items are logged onto to the next - file in the sequence, starting all over with the first file when - the last file has been filled up. For the sake of efficiency, - items are always written to files as binaries. - </p> - <p>Two formats of the log files are supported, the <em>internal format</em> and the <em>external format</em>. The internal - format supports automatic repair of log files that have not been - properly closed, and makes it possible to efficiently read - logged items in <em>chunks</em> using a set of functions defined - in this module. In fact, this is the only way to read internally - formatted logs. The external format leaves it up to the user to - read the logged deep byte lists. The disk log module cannot - repair externally formatted logs. An item logged to an - internally formatted log must not occupy more than 4 GB of disk - space (the size must fit in 4 bytes). - </p> - <p>For each open disk log there is one process that handles requests - made to the disk log; the disk log process is created when <c>open/1</c> + <p><c>disk_log</c> is a disk-based term logger that enables + efficient logging of items on files.</p> + <p>Two types of logs are supported:</p> + <taglist> + <tag>halt logs</tag> + <item><p>Appends items to a single file, which size can + be limited by the disk log module.</p></item> + <tag>wrap logs</tag> + <item><p>Uses a sequence of wrap log files of limited size. As a + wrap log file is filled up, further items are logged on to the next + file in the sequence, starting all over with the first file when + the last file is filled up.</p></item> + </taglist> + <p>For efficiency reasons, items are always written to files as binaries.</p> + + <p>Two formats of the log files are supported:</p> + <taglist> + <tag>internal format</tag> + <item><p>Supports automatic repair of log files that are not + properly closed and enables efficient reading of logged items in + <em>chunks</em> using a set of functions defined in this module. + This is the only way to read internally formatted logs. + An item logged to an internally formatted log must not occupy more + than 4 GB of disk space (the size must fit in 4 bytes).</p></item> + <tag>external format</tag> + <item><p>Leaves it up to the user to read the logged deep byte lists. + The disk log module cannot repair externally formatted logs.</p></item> + </taglist> + + <p>For each open disk log, one process handles requests + made to the disk log. This process is created when + <seealso marker="#open/1"><c>open/1</c></seealso> is called, provided there exists no process handling the disk log. - A process that opens a disk log can either be an <em>owner</em> + A process that opens a disk log can be an <em>owner</em> or an anonymous <em>user</em> of the disk log. Each owner is - linked to the disk log - process, and the disk log is closed by the owner should the - owner terminate. Owners can subscribe to <em>notifications</em>, - messages of the form <c>{disk_log, Node, Log, Info}</c> that are sent + linked to the disk log process, and an owner can close the disk log + either explicitly (by calling <c>close/1</c> or <c>lclose/1,2</c>) + or by terminating.</p> + <p>Owners can subscribe to <em>notifications</em>, + messages of the form <c>{disk_log, Node, Log, Info}</c>, which are sent from the disk log process when certain events occur, see - the commands below and in particular the <c>open/1</c> option - <seealso marker="#notify">notify</seealso>. - There can be several owners of a log, but a process cannot own a - log more than once. One and the same process may, however, - open the log - as a user more than once. For a disk log process to properly close - its file and terminate, it must be closed by its owners and once by - some non-owner process for each time the log was used anonymously; - the users are counted, and there must not be any users left when the - disk log process terminates. + the functions and in particular the <c>open/1</c> option + <seealso marker="#notify"><c>notify</c></seealso>. + A log can have many owners, but a process cannot own a + log more than once. However, the same process can open the log + as a user more than once.</p> + <p>For a disk log process to close its file properly and terminate, + it must be closed by its owners and once by some non-owner process + for each time the log was used anonymously. The users are counted + and there must not be any users left when the disk log process terminates. </p> - <p>Items can be logged <em>synchronously</em> by using the functions - <c>log/2</c>, <c>blog/2</c>, <c>log_terms/2</c> and - <c>blog_terms/2</c>. For each of these functions, the caller is put - on hold until the items have been logged (but not necessarily + <p>Items can be logged <em>synchronously</em> by using functions + <seealso marker="#log/2"><c>log/2</c></seealso>, + <seealso marker="#blog/2"><c>blog/2</c></seealso>, + <seealso marker="#log_terms/2"><c>log_terms/2</c></seealso>, and + <seealso marker="#blog_terms/2"><c>blog_terms/2</c></seealso>. + For each of these functions, the caller is put + on hold until the items are logged (but not necessarily written, use <c>sync/1</c> to ensure that). By adding an <c>a</c> - to each of the mentioned function names we get functions that log + to each of the mentioned function names, we get functions that log items <em>asynchronously</em>. Asynchronous functions do not wait for - the disk log process to actually write the items to the file, but + the disk log process to write the items to the file, but return the control to the caller more or less immediately. </p> - <p>When using the internal format for logs, the functions - <c>log/2</c>, <c>log_terms/2</c>, <c>alog/2</c>, and - <c>alog_terms/2</c> should be used. These functions log one or - more Erlang terms. By prefixing each of the functions with - a <c>b</c> (for "binary") we get the corresponding <c>blog</c> - functions for the external format. These functions log one or - more deep lists of bytes or, alternatively, binaries of deep lists - of bytes. - For example, to log the string <c>"hello"</c> in ASCII format, we + <p>When using the internal format for logs, use functions + <seealso marker="#log/2"><c>log/2</c></seealso>, + <seealso marker="#log_terms/2"><c>log_terms/2</c></seealso>, + <seealso marker="#alog/2"><c>alog/2</c></seealso>, and + <seealso marker="#alog_terms/2"><c>alog_terms/2</c></seealso>. + These functions log one or more Erlang terms. + By prefixing each of the functions with a <c>b</c> (for "binary"), + we get the corresponding <c>blog()</c> functions for the external format. + These functions log one or more deep lists of bytes or, alternatively, + binaries of deep lists of bytes. + For example, to log the string <c>"hello"</c> in ASCII format, you can use <c>disk_log:blog(Log, "hello")</c>, or <c>disk_log:blog(Log, list_to_binary("hello"))</c>. The two - alternatives are equally efficient. The <c>blog</c> functions - can be used for internally formatted logs as well, but in - this case they must be called with binaries constructed with - calls to <c>term_to_binary/1</c>. There is no check to ensure + alternatives are equally efficient.</p> + <p>The <c>blog()</c> functions can also be used for internally formatted + logs, but in this case they must be called with binaries constructed + with calls to + <seealso marker="erts:erlang#term_to_binary/1"><c>term_to_binary/1</c></seealso>. + There is no check to ensure this, it is entirely the responsibility of the caller. If these functions are called with binaries that do not correspond to - Erlang terms, the <c>chunk/2,3</c> and automatic repair - functions will fail. The corresponding terms (not the binaries) - will be returned when <c>chunk/2,3</c> is called. + Erlang terms, the + <seealso marker="#chunk/2"><c>chunk/2,3</c></seealso> + and automatic repair + functions fail. The corresponding terms (not the binaries) + are returned when <c>chunk/2,3</c> is called. </p> <p>A collection of open disk logs with the same name running on - different nodes is said to be a <em>a distributed disk log</em> - if requests made to any one of the logs are automatically made to - the other logs as well. The members of such a collection will be + different nodes is said to be a <em>distributed disk log</em> + if requests made to any of the logs are automatically made to + the other logs as well. The members of such a collection are called individual distributed disk logs, or just distributed disk logs if there is no risk of confusion. There is no order - between the members of such a collection. For instance, logged - terms are not necessarily written onto the node where the - request was made before written onto the other nodes. One could - note here that there are a few functions that do not make - requests to all members of distributed disk logs, namely - <c>info</c>, <c>chunk</c>, <c>bchunk</c>, <c>chunk_step</c> and - <c>lclose</c>. An open disk log that is not a distributed disk + between the members of such a collection. For example, logged + terms are not necessarily written to the node where the + request was made before written to the other nodes. However, + a few functions do not make requests to all + members of distributed disk logs, namely + <seealso marker="#info/1"><c>info/1</c></seealso>, + <seealso marker="#chunk/2"><c>chunk/2,3</c></seealso>, + <seealso marker="#bchunk/2"><c>bchunk/2,3</c></seealso>, + <seealso marker="#chunk_step/3"><c>chunk_step/3</c></seealso>, and + <seealso marker="#lclose/1"><c>lclose/1,2</c></seealso>.</p> + <p>An open disk log that is not a distributed disk log is said to be a <em>local disk log</em>. A local disk log is - accessible only from the node where the disk log process runs, + only accessible from the node where the disk log process runs, whereas a distributed disk log is accessible from all nodes in - the Erlang system, with exception for those nodes where a local + the Erlang system, except for those nodes where a local disk log with the same name as the distributed disk log exists. All processes on nodes that have access to a local or - distributed disk log can log items or otherwise change, inspect + distributed disk log can log items or otherwise change, inspect, or close the log. </p> <p>It is not guaranteed that all log files of a distributed disk log - contain the same log items; there is no attempt made to synchronize + contain the same log items. No attempt is made to synchronize the contents of the files. However, as long as at least one of - the involved nodes is alive at each time, all items will be logged. + the involved nodes is alive at each time, all items are logged. When logging items to a distributed log, or otherwise trying to change the log, the replies from individual logs are ignored. If all nodes are down, the disk log functions reply with a <c>nonode</c> error. </p> <note> - <p>In some applications it may not be acceptable that + <p>In some applications, it can be unacceptable that replies from individual logs are ignored. An alternative in such - situations is to use several local disk logs instead of one + situations is to use many local disk logs instead of one distributed disk log, and implement the distribution without use - of the disk log module.</p> + of the <c>disk_log</c> module.</p> </note> <p>Errors are reported differently for asynchronous log attempts - and other uses of the disk log module. When used synchronously - the disk log module replies with an error message, but when called - asynchronously, the disk log module does not know where to send - the error message. Instead owners subscribing to notifications will + and other uses of the <c>disk_log</c> module. When used synchronously, + this module replies with an error message, but when called + asynchronously, this module does not know where to send + the error message. Instead, owners subscribing to notifications receive an <c>error_status</c> message. </p> - <p>The disk log module itself does not report errors to the - <c>error_logger</c> module; it is up to the caller to decide - whether the error logger should be employed or not. The function - <c>format_error/1</c> can be used to produce readable messages - from error replies. Information events are however sent to the - error logger in two situations, namely when a log is repaired, - or when a file is missing while reading chunks. + <p>The <c>disk_log</c> module does not report errors to the + <seealso marker="error_logger"><c>error_logger</c></seealso> + module. It is up to the caller to decide + whether to employ the error logger. Function + <seealso marker="#format_error/1"><c>format_error/1</c></seealso> + can be used to produce readable messages from error replies. + However, information events are sent to the error logger in two + situations, namely when a log is repaired, or when a file is missing + while reading chunks. </p> - <p>The error message <c>no_such_log</c> means that the given - disk log is not currently open. Nothing is said about - whether the disk log files exist or not. + <p>Error message <c>no_such_log</c> means that the specified + disk log is not open. Nothing is said about whether the disk log + files exist or not. </p> <note> <p>If an attempt to reopen or truncate a log fails (see - <c>reopen</c> and <c>truncate</c>) the disk log process - immediately terminates. Before the process terminates links to - to owners and blocking processes (see <c>block</c>) are removed. - The effect is that the links work in one direction only; any - process using a disk log has to check for the error message - <c>no_such_log</c> if some other process might truncate or - reopen the log simultaneously.</p> + <seealso marker="#reopen/2"><c>reopen/2,3</c></seealso> + and + <seealso marker ="#truncate/1"><c>truncate/1,2</c></seealso>) + the disk log process terminates immediately. Before the process + terminates, links to owners and blocking processes (see + <seealso marker="#block/1"><c>block/1,2</c></seealso>) are removed. + The effect is that the links work in one direction only. Any + process using a disk log must check for error message + <c>no_such_log</c> if some other process truncates or + reopens the log simultaneously.</p> </note> </description> <datatypes> @@ -221,11 +246,10 @@ <funcs> <func> <name name="accessible_logs" arity="0"/> - <fsummary>Return the accessible disk logs on the current node.</fsummary> + <fsummary>Return the accessible disk logs on the current node.</fsummary> <desc> - <p>The <c>accessible_logs/0</c> function returns - the names of the disk logs accessible on the current node. - The first list contains local disk logs, and the + <p>Returns the names of the disk logs accessible on the current node. + The first list contains local disk logs and the second list contains distributed disk logs. </p> </desc> @@ -233,55 +257,55 @@ <func> <name name="alog" arity="2"/> <name name="balog" arity="2"/> - <fsummary>Asynchronously log an item onto a disk log.</fsummary> + <fsummary>Asynchronously log an item on to a disk log.</fsummary> <type variable="Log"/> <type variable="Term" name_i="1"/> <type variable="Bytes"/> <type name="notify_ret"/> <desc> - <p>The <c>alog/2</c> and <c>balog/2</c> functions asynchronously - append an item to a disk log. The function <c>alog/2</c> is - used for internally formatted logs, and the function <c>balog/2</c> - for externally formatted logs. <c>balog/2</c> can be used - for internally formatted logs as well provided the binary was - constructed with a call to <c>term_to_binary/1</c>. + <p>Asynchronously append an item to a disk log. <c>alog/2</c> is + used for internally formatted logs and <c>balog/2</c> + for externally formatted logs. <c>balog/2</c> can also be used + for internally formatted logs if the binary is + constructed with a call to + <seealso marker="erts:erlang#term_to_binary/1"><c>term_to_binary/1</c></seealso>. </p> - <p>The owners that subscribe to notifications will receive the - message <c>read_only</c>, <c>blocked_log</c> - or <c>format_external</c> in case the item cannot be written + <p>Owners subscribing to notifications receive + message <c>read_only</c>, <c>blocked_log</c>, + or <c>format_external</c> if the item cannot be written on the log, and possibly one of the messages <c>wrap</c>, - <c>full</c> and <c>error_status</c> if an item was written - on the log. The message <c>error_status</c> is sent if there - is something wrong with the header function or a file error - occurred. + <c>full</c>, or <c>error_status</c> if an item is written + on the log. Message <c>error_status</c> is sent if + something is wrong with the header function or if a file error + occurs. </p> </desc> </func> <func> <name name="alog_terms" arity="2"/> <name name="balog_terms" arity="2"/> - <fsummary>Asynchronously log several items onto a disk log.</fsummary> + <fsummary>Asynchronously log many items on to a disk log.</fsummary> <type variable="Log"/> <type variable="TermList" name_i="1"/> <type variable="ByteList"/> <type name="notify_ret"/> <desc> - <p>The <c>alog_terms/2</c> and <c>balog_terms/2</c> functions - asynchronously append a list of items to a disk log. - The function <c>alog_terms/2</c> is used for internally - formatted logs, and the function <c>balog_terms/2</c> - for externally formatted logs. <c>balog_terms/2</c> can be used - for internally formatted logs as well provided the binaries were - constructed with calls to <c>term_to_binary/1</c>. + <p>Asynchronously append a list of items to a disk log. + <c>alog_terms/2</c> is used for internally + formatted logs and <c>balog_terms/2</c> + for externally formatted logs. <c>balog_terms/2</c> can also be used + for internally formatted logs if the binaries are + constructed with calls to + <seealso marker="erts:erlang#term_to_binary/1"><c>term_to_binary/1</c></seealso>. </p> - <p>The owners that subscribe to notifications will receive the - message <c>read_only</c>, <c>blocked_log</c> - or <c>format_external</c> in case the items cannot be written + <p>Owners subscribing to notifications receive + message <c>read_only</c>, <c>blocked_log</c>, + or <c>format_external</c> if the items cannot be written on the log, and possibly one or more of the messages <c>wrap</c>, - <c>full</c> and <c>error_status</c> if items were written - on the log. The message <c>error_status</c> is sent if there - is something wrong with the header function or a file error - occurred. + <c>full</c>, and <c>error_status</c> if items are written + on the log. Message <c>error_status</c> is sent if + something is wrong with the header function or if a file error + occurs. </p> </desc> </func> @@ -294,76 +318,75 @@ <p>With a call to <c>block/1,2</c> a process can block a log. If the blocking process is not an owner of the log, a temporary link is created between the disk log process and the blocking - process. The link is used to ensure that the disk log is - unblocked should the blocking process terminate without + process. The link ensures that the disk log is + unblocked if the blocking process terminates without first closing or unblocking the log. </p> <p>Any process can probe a blocked log with <c>info/1</c> or close it with <c>close/1</c>. The blocking process can also - use the functions <c>chunk/2,3</c>, <c>bchunk/2,3</c>, + use functions <c>chunk/2,3</c>, <c>bchunk/2,3</c>, <c>chunk_step/3</c>, and <c>unblock/1</c> without being - affected by the block. Any other attempt than those hitherto - mentioned to update or read a blocked log suspends the - calling process until the log is unblocked or returns an + affected by the block. Any other attempt than those + mentioned so far to update or read a blocked log suspends the + calling process until the log is unblocked or returns error message <c>{blocked_log, <anno>Log</anno>}</c>, depending on whether the value of <c><anno>QueueLogRecords</anno></c> is <c>true</c> - or <c>false</c>. The default value of <c><anno>QueueLogRecords</anno></c> - is <c>true</c>, which is used by <c>block/1</c>. + or <c>false</c>. <c><anno>QueueLogRecords</anno></c> defaults to + <c>true</c>, which is used by <c>block/1</c>. </p> </desc> </func> <func> <name name="change_header" arity="2"/> - <fsummary>Change the head or head_func option for an owner of a disk log.</fsummary> + <fsummary>Change option head or head_func for an owner of a disk log.</fsummary> <desc> - <p>The <c>change_header/2</c> function changes the value of - the <c>head</c> or <c>head_func</c> option of a disk log.</p> + <p>Changes the value of option <c>head</c> or <c>head_func</c> for an owner of a disk log.</p> </desc> </func> <func> <name name="change_notify" arity="3"/> - <fsummary>Change the notify option for an owner of a disk log.</fsummary> + <fsummary>Change option notify for an owner of a disk log.</fsummary> <desc> - <p>The <c>change_notify/3</c> function changes the value of the - <c>notify</c> option for an owner of a disk log. </p> + <p>Changes the value of option <c>notify</c> for an owner of a disk log. </p> </desc> </func> <func> <name name="change_size" arity="2"/> <fsummary>Change the size of an open disk log.</fsummary> <desc> - <p>The <c>change_size/2</c> function changes the size of an open log. - For a halt log it is always possible to increase the size, - but it is not possible to decrease the size to something less than - the current size of the file. + <p>Changes the size of an open log. + For a halt log, the size can always be increased, + but it cannot be decreased to something less than + the current file size. </p> - <p>For a wrap log it is always possible to increase both the - size and number of files, as long as the number of files does not + <p>For a wrap log, both the size and the number of files can always + be increased, as long as the number of files does not exceed 65000. If the maximum number of files is decreased, the - change will not be valid until the current file is full and the + change is not valid until the current file is full and the log wraps to the next file. - The redundant files will be removed next time the log wraps around, - i.e. starts to log to file number 1. + The redundant files are removed the next time the log wraps around, + that is, starts to log to file number 1. </p> <p>As an example, assume that the old maximum number of files is 10 and that the new maximum number of files is 6. If the current file number is not greater than the new maximum number - of files, the files 7 to 10 will be removed when file number 6 + of files, files 7-10 are removed when file 6 is full and the log starts to write to file number 1 again. - Otherwise the files greater than the current - file will be removed when the current file is full (e.g. if - the current file is 8, the files 9 and 10); the files between - new maximum number of files and the current - file (i.e. files 7 and 8) will be removed next time file number 6 + Otherwise, the files greater than the current + file are removed when the current file is full (for example, if + the current file is 8, files 9 and 10 are removed). The files between + the new maximum number of files and the current + file (that is, files 7 and 8) are removed the next time file 6 is full. </p> - <p>If the size of the files is decreased the change will immediately - affect the current log. It will not of course change the - size of log files already full until next time they are used. + <p>If the size of the files is decreased, the change immediately + affects the current log. It does not change the + size of log files already full until the next time they are used. </p> - <p>If the log size is decreased for instance to save space, - the function <c>inc_wrap_file/1</c> can be used to force the log - to wrap. + <p>If the log size is decreased, for example, to save space, + function + <seealso marker="#inc_wrap_file/1"><c>inc_wrap_file/1</c></seealso> + can be used to force the log to wrap. </p> </desc> </func> @@ -380,93 +403,85 @@ <type name="bchunk_ret"/> <type name="chunk_error_rsn"/> <desc> - <p>The <c>chunk/2,3</c> and <c>bchunk/2,3</c> functions make - it possible to efficiently read the terms which have been - appended to an internally formatted log. It minimizes disk - I/O by reading 64 kilobyte chunks from the file. The - <c>bchunk/2,3</c> functions return the binaries read from - the file; they do not call <c>binary_to_term</c>. Otherwise - the work just like <c>chunk/2,3</c>. + <p>Efficiently reads the terms that are appended + to an internally formatted log. It minimizes disk + I/O by reading 64 kilobyte chunks from the file. Functions + <c>bchunk/2,3</c> return the binaries read from + the file, they do not call <c>binary_to_term()</c>. Apart from that, + they work just like <c>chunk/2,3</c>. </p> - <p>The first time <c>chunk</c> (or <c>bchunk</c>) is called, + <p>The first time <c>chunk()</c> (or <c>bchunk()</c>) is called, an initial continuation, the atom <c>start</c>, must be - provided. If there is a disk log process running on the - current node, terms are read from that log, otherwise an + provided. If a disk log process is running on the + current node, terms are read from that log. Otherwise, an individual distributed log on some other node is chosen, if such a log exists. </p> <p>When <c>chunk/3</c> is called, <c><anno>N</anno></c> controls the maximum number of terms that are read from the log in each - chunk. Default is <c>infinity</c>, which means that all the + chunk. Defaults to <c>infinity</c>, which means that all the terms contained in the 64 kilobyte chunk are read. If less than <c><anno>N</anno></c> terms are returned, this does not necessarily mean - that the end of the file has been reached. + that the end of the file is reached. </p> - <p>The <c>chunk</c> function returns a tuple - <c>{<anno>Continuation2</anno>, <anno>Terms</anno>}</c>, where <c><anno>Terms</anno></c> is a list + <p><c>chunk()</c> returns a tuple + <c>{<anno>Continuation2</anno>, <anno>Terms</anno>}</c>, where + <c><anno>Terms</anno></c> is a list of terms found in the log. <c><anno>Continuation2</anno></c> is yet - another continuation which must be passed on to any - subsequent calls to <c>chunk</c>. With a series of calls to - <c>chunk</c> it is possible to extract all terms from a log. + another continuation, which must be passed on to any + subsequent calls to <c>chunk()</c>. With a series of calls to + <c>chunk()</c>, all terms from a log can be extracted. </p> - <p>The <c>chunk</c> function returns a tuple - <c>{<anno>Continuation2</anno>, <anno>Terms</anno>, <anno>Badbytes</anno>}</c> if the log is opened - in read-only mode and the read chunk is corrupt. <c><anno>Badbytes</anno></c> - is the number of bytes in the file which were found not to be - Erlang terms in the chunk. Note also that the log is not repaired. + <p><c>chunk()</c> returns a tuple + <c>{<anno>Continuation2</anno>, <anno>Terms</anno>, <anno>Badbytes</anno>}</c> + if the log is opened in read-only mode and the read chunk is corrupt. + <c><anno>Badbytes</anno></c> is the number of bytes in the file found not to be + Erlang terms in the chunk. Notice that the log is not repaired. When trying to read chunks from a log opened in read-write mode, - the tuple <c>{corrupt_log_file, <anno>FileName</anno>}</c> is returned if the + tuple <c>{corrupt_log_file, <anno>FileName</anno>}</c> is returned if the read chunk is corrupt. </p> - <p><c>chunk</c> returns <c>eof</c> when the end of the log is - reached, or <c>{error, <anno>Reason</anno>}</c> if an error occurs. Should - a wrap log file be missing, a message is output on the error log. + <p><c>chunk()</c> returns <c>eof</c> when the end of the log is + reached, or <c>{error, <anno>Reason</anno>}</c> if an error occurs. If + a wrap log file is missing, a message is output on the error log. </p> <p>When <c>chunk/2,3</c> is used with wrap logs, the returned - continuation may or may not be valid in the next call to - <c>chunk</c>. This is because the log may wrap and delete - the file into which the continuation points. To make sure - this does not happen, the log can be blocked during the - search. + continuation might not be valid in the next call to + <c>chunk()</c>. This is because the log can wrap and delete + the file into which the continuation points. To prevent this, + the log can be blocked during the search. </p> </desc> </func> <func> <name name="chunk_info" arity="1"/> - <fsummary>Return information about a chunk continuation of a disk log.</fsummary> + <fsummary>Return information about a chunk continuation of a disk log.</fsummary> <desc> - <p>The <c>chunk_info/1</c> function returns the following pair + <p>Returns the pair <c>{node, <anno>Node</anno>}</c>, describing the chunk continuation returned by - <c>chunk/2,3</c>, <c>bchunk/2,3</c>, or <c>chunk_step/3</c>: - </p> - <list type="bulleted"> - <item> - <p><c>{node, <anno>Node</anno>}</c>. Terms are read from - the disk log running on <c><anno>Node</anno></c>.</p> - </item> - </list> + <c>chunk/2,3</c>, <c>bchunk/2,3</c>, or <c>chunk_step/3</c>.</p> + <p>Terms are read from the disk log running on <c><anno>Node</anno></c>.</p> </desc> </func> <func> <name name="chunk_step" arity="3"/> - <fsummary>Step forward or backward among the wrap log files of a disk log.</fsummary> + <fsummary>Step forward or backward among the wrap log files of a disk log.</fsummary> <desc> - <p>The function <c>chunk_step</c> can be used in conjunction - with <c>chunk/2,3</c> and <c>bchunk/2,3</c> to search - through an internally formatted wrap log. It takes as + <p>Can be used with <c>chunk/2,3</c> and <c>bchunk/2,3</c> + to search through an internally formatted wrap log. It takes as argument a continuation as returned by <c>chunk/2,3</c>, <c>bchunk/2,3</c>, or <c>chunk_step/3</c>, and steps forward (or backward) <c><anno>Step</anno></c> files in the wrap log. The - continuation returned points to the first log item in the + continuation returned, points to the first log item in the new current file. </p> - <p>If the atom <c>start</c> is given as continuation, a disk log + <p>If atom <c>start</c> is specified as continuation, a disk log to read terms from is chosen. A local or distributed disk log on the current node is preferred to an individual distributed log on some other node. </p> - <p>If the wrap log is not full because all files have not been - used yet, <c>{error, end_of_log}</c> is returned if trying to + <p>If the wrap log is not full because all files are not yet + used, <c>{error, end_of_log}</c> is returned if trying to step outside the log. </p> </desc> @@ -476,21 +491,20 @@ <fsummary>Close a disk log.</fsummary> <type name="close_error_rsn"/> <desc> - <p><marker id="close_1"></marker>The function <c>close/1</c> closes a + <p><marker id="close_1"></marker>Closes a local or distributed disk log properly. An internally formatted log must be closed before the Erlang system is - stopped, otherwise the log is regarded as unclosed and the - automatic repair procedure will be activated next time the + stopped. Otherwise, the log is regarded as unclosed and the + automatic repair procedure is activated next time the log is opened. </p> - <p>The disk log process in not terminated as long as there are - owners or users of the log. It should be stressed that each - and every owner must close the log, possibly by terminating, - and that any other process - not only the processes that have - opened the log anonymously - can decrement the <c>users</c> + <p>The disk log process is not terminated as long as there are + owners or users of the log. All owners must close the log, + possibly by terminating. Also, any other process, not only the processes + that have opened the log anonymously, can decrement the <c>users</c> counter by closing the log. Attempts to close a log by a process that is - not an owner are simply ignored if there are no users. + not an owner are ignored if there are no users. </p> <p>If the log is blocked by the closing process, the log is also unblocked. @@ -499,12 +513,14 @@ </func> <func> <name name="format_error" arity="1"/> - <fsummary>Return an English description of a disk log error reply.</fsummary> + <fsummary>Return an English description of a disk log error reply.</fsummary> <desc> <p>Given the error returned by any function in this module, - the function <c>format_error</c> returns a descriptive string - of the error in English. For file errors, the function - <c>format_error/1</c> in the <c>file</c> module is called.</p> + this function returns a descriptive string + of the error in English. For file errors, function + <c>format_error/1</c> in module + <seealso marker="file#format_error/1"><c>file</c></seealso> + is called.</p> </desc> </func> <func> @@ -513,16 +529,15 @@ <type name="inc_wrap_error_rsn"/> <type name="invalid_header"/> <desc> - <p>The <c>inc_wrap_file/1</c> function forces the internally formatted - disk log to start logging to the - next log file. It can be used, for instance, in conjunction with + <p>Forces the internally formatted disk log to start logging to the + next log file. It can be used, for example, with <c>change_size/2</c> to reduce the amount of disk space allocated by the disk log. </p> - <p>The owners that subscribe to notifications will normally - receive a <c>wrap</c> message, but in case of - an error with a reason tag of <c>invalid_header</c> or - <c>file_error</c> an <c>error_status</c> message will be sent.</p> + <p>Owners subscribing to notifications normally + receive a <c>wrap</c> message, but if + an error occurs with a reason tag of <c>invalid_header</c> or + <c>file_error</c>, an <c>error_status</c> message is sent.</p> </desc> </func> <func> @@ -530,132 +545,148 @@ <fsummary>Return information about a disk log.</fsummary> <type name="dlog_info"/> <desc> - <p>The <c>info/1</c> function returns a list of <c>{Tag, Value}</c> - pairs describing the log. If there is a disk log process running - on the current node, that log is used as source of information, - otherwise an individual distributed log on - some other node is chosen, if such a log exists. + <p>Returns a list of <c>{Tag, Value}</c> pairs describing the log. + If a disk log process is running on the current node, + that log is used as source of information, otherwise an individual + distributed log on some other node is chosen, if such a log exists. </p> <p>The following pairs are returned for all logs: </p> - <list type="bulleted"> + <taglist> + <tag><c>{name, <anno>Log</anno>}</c></tag> <item> - <p><c>{name, <anno>Log</anno>}</c>, where <c><anno>Log</anno></c> is the name of - the log as given by the <c>open/1</c> option <c>name</c>.</p> + <p><c><anno>Log</anno></c> is the log name + as specified by the <c>open/1</c> option <c>name</c>.</p> </item> + <tag><c>{file, <anno>File</anno>}</c></tag> <item> - <p><c>{file, <anno>File</anno>}</c>. For halt logs <c><anno>File</anno></c> is the + <p>For halt logs <c><anno>File</anno></c> is the filename, and for wrap logs <c><anno>File</anno></c> is the base name.</p> </item> + <tag><c>{type, <anno>Type</anno>}</c></tag> <item> - <p><c>{type, <anno>Type</anno>}</c>, where <c><anno>Type</anno></c> is the type of - the log as given by the <c>open/1</c> option <c>type</c>.</p> + <p><c><anno>Type</anno></c> is the log type + as specified by the <c>open/1</c> option <c>type</c>.</p> </item> + <tag><c>{format, <anno>Format</anno>}</c></tag> <item> - <p><c>{format, <anno>Format</anno>}</c>, where <c><anno>Format</anno></c> is the format - of the log as given by the <c>open/1</c> option <c>format</c>.</p> + <p><c><anno>Format</anno></c> is the log format + as specified by the <c>open/1</c> option <c>format</c>.</p> </item> + <tag><c>{size, <anno>Size</anno>}</c></tag> <item> - <p><c>{size, <anno>Size</anno>}</c>, where <c><anno>Size</anno></c> is the size - of the log as given by the <c>open/1</c> option <c>size</c>, + <p><c><anno>Size</anno></c> is the log size + as specified by the <c>open/1</c> option <c>size</c>, or the size set by <c>change_size/2</c>. The value set by <c>change_size/2</c> is reflected immediately.</p> </item> + <tag><c>{mode, <anno>Mode</anno>}</c></tag> <item> - <p><c>{mode, <anno>Mode</anno>}</c>, where <c><anno>Mode</anno></c> is the mode - of the log as given by the <c>open/1</c> option <c>mode</c>.</p> + <p><c><anno>Mode</anno></c> is the log mode + as specified by the <c>open/1</c> option <c>mode</c>.</p> </item> + <tag><c>{owners, [{pid(), <anno>Notify</anno>}]}</c></tag> <item> - <p><c>{owners, [{pid(), <anno>Notify</anno>}]}</c> where <c><anno>Notify</anno></c> + <p><c><anno>Notify</anno></c> is the value set by the <c>open/1</c> option <c>notify</c> - or the function <c>change_notify/3</c> for the owners of + or function <c>change_notify/3</c> for the owners of the log.</p> </item> + <tag><c>{users, <anno>Users</anno>}</c></tag> <item> - <p><c>{users, <anno>Users</anno>}</c> where <c><anno>Users</anno></c> is the number + <p><c><anno>Users</anno></c> is the number of anonymous users of the log, see the <c>open/1</c> option - <seealso marker="#linkto">linkto</seealso>.</p> + <seealso marker="#linkto"><c>linkto</c></seealso>.</p> </item> + <tag><c>{status, <anno>Status</anno>}</c></tag> <item> - <p><c>{status, <anno>Status</anno>}</c>, where <c><anno>Status</anno></c> is <c>ok</c> - or <c>{blocked, <anno>QueueLogRecords</anno>}</c> as set by the functions + <p><c><anno>Status</anno></c> is <c>ok</c> + or <c>{blocked, <anno>QueueLogRecords</anno>}</c> as set by functions <c>block/1,2</c> and <c>unblock/1</c>.</p> </item> + <tag><c>{node, <anno>Node</anno>}</c></tag> <item> - <p><c>{node, <anno>Node</anno>}</c>. The information returned by the - current invocation of the <c>info/1</c> function has been + <p>The information returned by the + current invocation of function <c>info/1</c> is gathered from the disk log process running on <c><anno>Node</anno></c>.</p> </item> + <tag><c>{distributed, <anno>Dist</anno>}</c></tag> <item> - <p><c>{distributed, <anno>Dist</anno>}</c>. If the log is local on - the current node, then <c><anno>Dist</anno></c> has the value <c>local</c>, + <p>If the log is local on + the current node, <c><anno>Dist</anno></c> has the value <c>local</c>, otherwise all nodes where the log is distributed are returned as a list.</p> </item> - </list> + </taglist> <p>The following pairs are returned for all logs opened in <c>read_write</c> mode: </p> - <list type="bulleted"> + <taglist> + <tag><c>{head, <anno>Head</anno>}</c></tag> <item> - <p><c>{head, <anno>Head</anno>}</c>. Depending of the value of - the <c>open/1</c> options <c>head</c> and <c>head_func</c> - or set by the function <c>change_header/2</c>, the value + <p>Depending on the value of + the <c>open/1</c> options <c>head</c> and <c>head_func</c>, + or set by function <c>change_header/2</c>, the value of <c><anno>Head</anno></c> is <c>none</c> (default), - <c>{head, H}</c> (<c>head</c> option) or <c>{M,F,A}</c> + <c>{head, H}</c> (<c>head</c> option), or <c>{M,F,A}</c> (<c>head_func</c> option).</p> </item> + <tag><c>{no_written_items, <anno>NoWrittenItems</anno>}</c></tag> <item> - <p><c>{no_written_items, <anno>NoWrittenItems</anno>}</c>, where - <c><anno>NoWrittenItems</anno></c> is the number of items + <p><c><anno>NoWrittenItems</anno></c> is the number of items written to the log since the disk log process was created.</p> </item> - </list> + </taglist> <p>The following pair is returned for halt logs opened in <c>read_write</c> mode: </p> - <list type="bulleted"> + <taglist> + <tag><c>{full, <anno>Full</anno>}</c></tag> <item> - <p><c>{full, <anno>Full</anno>}</c>, where <c><anno>Full</anno></c> is <c>true</c> or + <p><c><anno>Full</anno></c> is <c>true</c> or <c>false</c> depending on whether the halt log is full or not.</p> </item> - </list> + </taglist> <p>The following pairs are returned for wrap logs opened in <c>read_write</c> mode: </p> - <list type="bulleted"> + <taglist> + <tag><c>{no_current_bytes, integer() >= 0}</c></tag> <item> - <p><c>{no_current_bytes, integer() >= 0}</c> is the number + <p>The number of bytes written to the current wrap log file.</p> </item> + <tag><c>{no_current_items, integer() >= 0}</c></tag> <item> - <p><c>{no_current_items, integer() >= 0}</c> is the number + <p>The number of items written to the current wrap log file, header inclusive.</p> </item> + <tag><c>{no_items, integer() >= 0}</c></tag> <item> - <p><c>{no_items, integer() >= 0}</c> is the total number + <p>The total number of items in all wrap log files.</p> </item> + <tag><c>{current_file, integer()}</c></tag> <item> - <p><c>{current_file, integer()}</c> is the ordinal for + <p>The ordinal for the current wrap log file in the range <c>1..MaxNoFiles</c>, - where <c>MaxNoFiles</c> is given by the <c>open/1</c> option + where <c>MaxNoFiles</c> is specified by the <c>open/1</c> option <c>size</c> or set by <c>change_size/2</c>.</p> </item> + <tag><c>{no_overflows, {<anno>SinceLogWasOpened</anno>, <anno>SinceLastInfo</anno>}}</c></tag> <item> - <p><c>{no_overflows, {<anno>SinceLogWasOpened</anno>, <anno>SinceLastInfo</anno>}}</c>, - where <c><anno>SinceLogWasOpened</anno></c> (<c><anno>SinceLastInfo</anno></c>) is - the number of times a wrap log file has been filled up and a - new one opened or <c>inc_wrap_file/1</c> has been called since + <p><c><anno>SinceLogWasOpened</anno></c> (<c><anno>SinceLastInfo</anno></c>) + is the number of times a wrap log file has been filled up and a + new one is opened or <c>inc_wrap_file/1</c> has been called since the disk log was last opened (<c>info/1</c> was last called). The first time <c>info/2</c> is called after a log was (re)opened or truncated, the two values are equal.</p> </item> - </list> - <p>Note that the <c>chunk/2,3</c>, <c>bchunk/2,3</c>, and - <c>chunk_step/3</c> functions do not affect any value + </taglist> + <p>Notice that functions <c>chunk/2,3</c>, <c>bchunk/2,3</c>, and + <c>chunk_step/3</c> do not affect any value returned by <c>info/1</c>. </p> </desc> @@ -666,17 +697,16 @@ <fsummary>Close a disk log on one node.</fsummary> <type name="lclose_error_rsn"/> <desc> - <p>The function <c>lclose/1</c> closes a local log or an - individual distributed log on the current node. - The function <c>lclose/2</c> closes an individual - distributed log on the specified node if the node - is not the current one. - <c>lclose(<anno>Log</anno>)</c> is equivalent to + <p><c>lclose/1</c> closes a local log or an individual distributed + log on the current node.</p> + <p><c>lclose/2</c> closes an individual distributed log on the + specified node if the node is not the current one.</p> + <p><c>lclose(<anno>Log</anno>)</c> is equivalent to <c>lclose(<anno>Log</anno>, node())</c>. - See also <seealso marker="#close_1">close/1</seealso>. + See also <seealso marker="#close_1"><c>close/1</c></seealso>. </p> - <p>If there is no log with the given name - on the specified node, <c>no_such_log</c> is returned. + <p>If no log with the specified name exist on the specified node, + <c>no_such_log</c> is returned. </p> </desc> </func> @@ -689,25 +719,27 @@ <type variable="Bytes"/> <type name="log_error_rsn"/> <desc> - <p>The <c>log/2</c> and <c>blog/2</c> functions synchronously - append a term to a disk log. They return <c>ok</c> or - <c>{error, <anno>Reason</anno>}</c> when the term has been written to - disk. If the log is distributed, <c>ok</c> is always - returned, unless all nodes are down. Terms are written by - means of the ordinary <c>write()</c> function of the - operating system. Hence, there is no guarantee that the term - has actually been written to the disk, it might linger in - the operating system kernel for a while. To make sure the - item is actually written to disk, the <c>sync/1</c> function + <p>Synchronously + appends a term to a disk log. Returns <c>ok</c> or + <c>{error, <anno>Reason</anno>}</c> when the term is written to + disk. If the log is distributed, <c>ok</c> is returned, + unless all nodes are down. Terms are written by + the ordinary <c>write()</c> function of the + operating system. Hence, it is not guaranteed that the term + is written to disk, it can linger in + the operating system kernel for a while. To ensure that the + item is written to disk, function + <seealso marker="#sync/1"><c>sync/1</c></seealso> must be called. </p> - <p>The <c>log/2</c> function is used for internally formatted logs, + <p><c>log/2</c> is used for internally formatted logs, and <c>blog/2</c> for externally formatted logs. - <c>blog/2</c> can be used - for internally formatted logs as well provided the binary was - constructed with a call to <c>term_to_binary/1</c>. - </p> - <p>The owners that subscribe to notifications will be notified + <c>blog/2</c> can also be used + for internally formatted logs if the binary is + constructed with a call to + <seealso marker="erts:erlang#term_to_binary/1"> + <c>term_to_binary/1</c></seealso>.</p> + <p>Owners subscribing to notifications are notified of an error with an <c>error_status</c> message if the error reason tag is <c>invalid_header</c> or <c>file_error</c>. </p> @@ -716,27 +748,27 @@ <func> <name name="log_terms" arity="2"/> <name name="blog_terms" arity="2"/> - <fsummary>Log several items onto a disk log.</fsummary> + <fsummary>Log many items onto a disk log.</fsummary> <type variable="Log"/> <type variable="TermList" name_i="1"/> <type variable="BytesList"/> <type name="log_error_rsn"/> <desc> - <p>The <c>log_terms/2</c> and <c>blog_terms/2</c> functions - synchronously append a list of items to the log. The benefit - of using these functions rather than the <c>log/2</c> and - <c>blog/2</c> functions is that of efficiency: the given - list is split into as large sublists as possible (limited by - the size of wrap log files), and each sublist is logged as - one single item, which reduces the overhead. + <p>Synchronously appends a list of items to the log. It is more + efficient to use these functions instead of functions <c>log/2</c> + and <c>blog/2</c>. The specified list is split into as large + sublists as possible (limited by the size of wrap log files), + and each sublist is logged as one single item, which reduces + the overhead. </p> - <p>The <c>log_terms/2</c> function is used for internally formatted + <p><c>log_terms/2</c> is used for internally formatted logs, and <c>blog_terms/2</c> for externally formatted logs. - <c>blog_terms/2</c> can be used - for internally formatted logs as well provided the binaries were - constructed with calls to <c>term_to_binary/1</c>. - </p> - <p>The owners that subscribe to notifications will be notified + <c>blog_terms/2</c> can also be used + for internally formatted logs if the binaries are + constructed with calls to + <seealso marker="erts:erlang#term_to_binary/1"> + <c>term_to_binary/1</c></seealso>.</p> + <p>Owners subscribing to notifications are notified of an error with an <c>error_status</c> message if the error reason tag is <c>invalid_header</c> or <c>file_error</c>. </p> @@ -755,110 +787,119 @@ <type name="dlog_optattr"/> <type name="dlog_size"/> <desc> - <p>The <c><anno>ArgL</anno></c> parameter is a list of options which have - the following meanings:</p> - <list type="bulleted"> + <p>Parameter <c><anno>ArgL</anno></c> is a list of the following + options:</p> + <taglist> + <tag><c>{name, <anno>Log</anno>}</c></tag> <item> - <p><c>{name, <anno>Log</anno>}</c> specifies the name of the log. - This is the name which must be passed on as a parameter in + <p>Specifies the log name. + This name must be passed on as a parameter in all subsequent logging operations. A name must always be supplied. </p> </item> + <tag><c>{file, <anno>FileName</anno>}</c></tag> <item> - <p><c>{file, <anno>FileName</anno>}</c> specifies the name of the - file which will be used for logged terms. If this value is - omitted and the name of the log is either an atom or a string, - the file name will default to <c>lists:concat([<anno>Log</anno>, ".LOG"])</c> for halt logs. For wrap logs, this will be - the base name of the files. Each file in a wrap log - will be called <c><![CDATA[<base_name>.N]]></c>, where <c>N</c> is an - integer. Each wrap log will also have two files called + <p>Specifies the name of the + file to be used for logged terms. If this value is + omitted and the log name is an atom or a string, + the filename defaults to <c>lists:concat([<anno>Log</anno>, ".LOG"])</c> + for halt logs.</p> + <p>For wrap logs, this is the base name of the files. Each file in + a wrap log is called <c><![CDATA[<base_name>.N]]></c>, where <c>N</c> + is an integer. Each wrap log also has two files called <c><![CDATA[<base_name>.idx]]></c> and <c><![CDATA[<base_name>.siz]]></c>. </p> </item> + <tag><c>{linkto, <anno>LinkTo</anno>}</c><marker id="linkto"></marker></tag> <item> - <p><c>{linkto, <anno>LinkTo</anno>}</c>. <marker id="linkto"></marker> -If - <c><anno>LinkTo</anno></c> is a pid, that pid becomes an owner of the - log. If <c><anno>LinkTo</anno></c> is <c>none</c> the log records + <p>If <c><anno>LinkTo</anno></c> is a pid, it becomes an owner of the + log. If <c><anno>LinkTo</anno></c> is <c>none</c>, the log records that it is used anonymously by some process by incrementing the <c>users</c> counter. By default, the - process which calls <c>open/1</c> owns the log. + process that calls <c>open/1</c> owns the log. </p> </item> + <tag><c>{repair, <anno>Repair</anno>}</c></tag> <item> - <p><c>{repair, <anno>Repair</anno>}</c>. If <c><anno>Repair</anno></c> is <c>true</c>, - the current log file will be repaired, if needed. As the + <p>If <c><anno>Repair</anno></c> is <c>true</c>, + the current log file is repaired, if needed. As the restoration is initiated, a message is output on the error log. - If <c>false</c> is given, - no automatic repair will be attempted. Instead, the + If <c>false</c> is specified, + no automatic repair is attempted. Instead, the tuple <c>{error, {need_repair, <anno>Log</anno>}}</c> is returned if an attempt is made to open a corrupt log file. - If <c>truncate</c> is given, the log file will - be truncated, creating an empty log. Default is + If <c>truncate</c> is specified, the log file becomes + truncated, creating an empty log. Defaults to <c>true</c>, which has no effect on logs opened in read-only mode. </p> </item> + <tag><c>{type, <anno>Type</anno>}</c></tag> <item> - <p><c>{type, <anno>Type</anno>}</c> is the type of the log. Default - is <c>halt</c>. + <p>The log type. Defaults to <c>halt</c>. </p> </item> + <tag><c>{format, <anno>Format</anno>}</c></tag> <item> - <p><c>{format, <anno>Format</anno>}</c> specifies the format of the - disk log. Default is <c>internal</c>. + <p>Disk log format. Defaults to <c>internal</c>. </p> </item> + <tag><c>{size, <anno>Size</anno>}</c></tag> <item> - <p><c>{size, <anno>Size</anno>}</c> specifies the size of the log. - When a halt log has reached its maximum size, all attempts to - log more items are rejected. The default size is + <p>Log size.</p> + <p>When a halt log has reached its maximum size, all attempts to + log more items are rejected. Defaults to <c>infinity</c>, which for halt implies that there is no - maximum size. For wrap logs, the <c><anno>Size</anno></c> parameter - may be either a pair - <c>{<anno>MaxNoBytes</anno>, <anno>MaxNoFiles</anno>}</c> or <c>infinity</c>. In the - latter case, if the files of an already existing wrap log + maximum size.</p> + <p>For wrap logs, parameter <c><anno>Size</anno></c> + can be a pair + <c>{<anno>MaxNoBytes</anno>, <anno>MaxNoFiles</anno>}</c> or + <c>infinity</c>. + In the latter case, if the files of an existing wrap log with the same name can be found, the size is read - from the existing wrap log, otherwise an error is returned. - Wrap logs write at most <c><anno>MaxNoBytes</anno></c> bytes on each file - and use <c><anno>MaxNoFiles</anno></c> files before starting all over with - the first wrap log file. Regardless of <c><anno>MaxNoBytes</anno></c>, + from the existing wrap log, otherwise an error is returned.</p> + <p>Wrap logs write at most <c><anno>MaxNoBytes</anno></c> + bytes on each file and use <c><anno>MaxNoFiles</anno></c> + files before starting all over with the first wrap log + file. Regardless of <c><anno>MaxNoBytes</anno></c>, at least the header (if there is one) and one - item is written on each wrap log file before - wrapping to the next file. - When opening an existing wrap log, it is not - necessary to supply a value for the option <c>Size</c>, but any - supplied value must equal the current size of the log, otherwise - the tuple <c>{error, {size_mismatch, <anno>CurrentSize</anno>, <anno>NewSize</anno>}}</c> - is returned. - </p> + item are written on each wrap log file before + wrapping to the next file.</p> + <p>When opening an existing wrap log, it is not + necessary to supply a value for option <c>Size</c>, but any + supplied value must equal the current log size, otherwise + the tuple <c>{error, {size_mismatch, <anno>CurrentSize</anno>, + <anno>NewSize</anno>}}</c> is returned.</p> </item> + <tag><c>{distributed, <anno>Nodes</anno>}</c></tag> <item> - <p><c>{distributed, <anno>Nodes</anno>}</c>. This option can be used for - adding members to a distributed disk log. The - default value is <c>[]</c>, which means that + <p>This option can be used for + adding members to a distributed disk log. + Defaults to <c>[]</c>, which means that the log is local on the current node. </p> </item> + <tag><c>{notify, boolean()}</c><marker id="notify"></marker></tag> <item> - <marker id="notify"></marker> - <p><c>{notify, bool()}</c>. If <c>true</c>, the owners of the - log are notified when certain events occur in the log. - Default is <c>false</c>. The owners are sent one of the + <p>If <c>true</c>, the log owners + are notified when certain log events occur. + Defaults to <c>false</c>. The owners are sent one of the following messages when an event occurs: </p> - <list type="bulleted"> + <taglist> + <tag><c>{disk_log, Node, Log, {wrap, NoLostItems}}</c></tag> <item> - <p><c>{disk_log, Node, Log, {wrap, NoLostItems}}</c> is sent when a wrap log has + <p>Sent when a wrap log has filled up one of its files and a new file is opened. <c>NoLostItems</c> is the number of - previously logged items that have been lost when + previously logged items that were lost when truncating existing files. </p> </item> + <tag><c>{disk_log, Node, Log, {truncated, NoLostItems}}</c></tag> <item> - <p><c>{disk_log, Node, Log, {truncated, NoLostItems}}</c> is sent when a log has been + <p>Sent when a log is truncated or reopened. For halt logs <c>NoLostItems</c> is the number of items written on the log since the disk log process was created. For wrap logs @@ -866,127 +907,132 @@ If wrap log files. </p> </item> + <tag><c>{disk_log, Node, Log, {read_only, Items}}</c></tag> <item> - <p><c>{disk_log, Node, Log, {read_only, Items}}</c> - is sent when an asynchronous log attempt is made to + <p>Sent when an asynchronous log attempt is made to a log file opened in read-only mode. <c>Items</c> is the items from the log attempt. </p> </item> + <tag><c>{disk_log, Node, Log, {blocked_log, Items}}</c></tag> <item> - <p><c>{disk_log, Node, Log, {blocked_log, Items}}</c> - is sent when an asynchronous log attempt is made to + <p>Sent when an asynchronous log attempt is made to a blocked log that does not queue log attempts. <c>Items</c> is the items from the log attempt. </p> </item> + <tag><c>{disk_log, Node, Log, {format_external, Items}}</c></tag> <item> - <p><c>{disk_log, Node, Log, {format_external, Items}}</c> - is sent when <c>alog/2</c> or <c>alog_terms/2</c> is + <p>Sent when function <c>alog/2</c> or <c>alog_terms/2</c> is used for internally formatted logs. <c>Items</c> is the items from the log attempt. </p> </item> + <tag><c>{disk_log, Node, Log, full}</c></tag> <item> - <p><c>{disk_log, Node, Log, full}</c> is sent when + <p>Sent when an attempt to log items to a wrap log would write more - bytes than the limit set by the <c>size</c> option. + bytes than the limit set by option <c>size</c>. </p> </item> + <tag><c>{disk_log, Node, Log, {error_status, Status}}</c></tag> <item> - <p><c>{disk_log, Node, Log, {error_status, Status}}</c> - is sent when the error status changes. The error status + <p>Sent when the error status changes. The error status is defined by the outcome of the last attempt to log - items to a the log or to truncate the log or the last - use of <c>sync/1</c>, <c>inc_wrap_file/1</c> or - <c>change_size/2</c>. <c>Status</c> is one of <c>ok</c> and - <c>{error, Error}</c>, the former being the initial value. + items to the log, or to truncate the log, or the last + use of function <c>sync/1</c>, <c>inc_wrap_file/1</c>, or + <c>change_size/2</c>. <c>Status</c> is either <c>ok</c> or + <c>{error, Error}</c>, the former is the initial value. </p> </item> - </list> + </taglist> </item> + <tag><c>{head, <anno>Head</anno>}</c></tag> <item> - <p><c>{head, <anno>Head</anno>}</c> specifies a header to be + <p>Specifies a header to be written first on the log file. If the log is a wrap log, the item <c><anno>Head</anno></c> is written first in each new file. - <c><anno>Head</anno></c> should be a term if the format is - <c>internal</c>, and a deep list of bytes (or a binary) - otherwise. Default is <c>none</c>, which means that + <c><anno>Head</anno></c> is to be a term if the format is + <c>internal</c>, otherwise a deep list of bytes (or a binary). + Defaults to <c>none</c>, which means that no header is written first on the file. </p> </item> + <tag><c>{head_func, {M,F,A}}</c></tag> <item> - <p><c>{head_func, {M,F,A}}</c> specifies a function + <p>Specifies a function to be called each time a new log file is opened. The call <c>M:F(A)</c> is assumed to return <c>{ok, Head}</c>. The item <c>Head</c> is written first in each file. - <c>Head</c> should be a term if the format is - <c>internal</c>, and a deep list of bytes (or a binary) - otherwise. + <c>Head</c> is to be a term if the format is + <c>internal</c>, otherwise a deep list of bytes (or a binary). </p> </item> + <tag><c>{mode, <anno>Mode</anno>}</c></tag> <item> - <p><c>{mode, <anno>Mode</anno>}</c> specifies if the log is to be - opened in read-only or read-write mode. It defaults to + <p>Specifies if the log is to be + opened in read-only or read-write mode. Defaults to <c>read_write</c>. </p> </item> - </list> - <p>The <c>open/1</c> function returns <c>{ok, <anno>Log</anno>}</c> if the - log file was successfully opened. If the file was - successfully repaired, the tuple <c>{repaired, <anno>Log</anno>, {recovered, <anno>Rec</anno>}, {badbytes, <anno>Bad</anno>}}</c> is returned, where - <c><anno>Rec</anno></c> is the number of whole Erlang terms found in the - file and <c><anno>Bad</anno></c> is the number of bytes in the file which - were non-Erlang terms. If the <c>distributed</c> parameter - was given, <c>open/1</c> returns a list of + </taglist> + <p><c>open/1</c> returns <c>{ok, <anno>Log</anno>}</c> if the + log file is successfully opened. If the file is + successfully repaired, the tuple <c>{repaired, <anno>Log</anno>, + {recovered, <anno>Rec</anno>}, {badbytes, <anno>Bad</anno>}}</c> + is returned, where <c><anno>Rec</anno></c> is the number of + whole Erlang terms found in the file and <c><anno>Bad</anno></c> + is the number of bytes in the file that + are non-Erlang terms. If the parameter <c>distributed</c> + is specified, <c>open/1</c> returns a list of successful replies and a list of erroneous replies. Each reply is tagged with the node name. </p> <p>When a disk log is opened in read-write mode, any existing - log file is checked for. If there is none a new empty + log file is checked for. If there is none, a new empty log is created, otherwise the existing file is opened at the position after the last logged item, and the logging of items - will commence from there. If the format is <c>internal</c> + starts from there. If the format is <c>internal</c> and the existing file is not recognized as an internally - formatted log, a tuple <c>{error, {not_a_log_file, <anno>FileName</anno>}}</c> + formatted log, a tuple + <c>{error, {not_a_log_file, <anno>FileName</anno>}}</c> is returned. </p> - <p>The <c>open/1</c> function cannot be used for changing the - values of options of an already open log; when there are prior + <p><c>open/1</c> cannot be used for changing the + values of options of an open log. When there are prior owners or users of a log, all option values except <c>name</c>, - <c>linkto</c> and <c>notify</c> are just checked against - the values that have been supplied before as option values - to <c>open/1</c>, <c>change_header/2</c>, <c>change_notify/3</c> - or <c>change_size/2</c>. As a consequence, + <c>linkto</c>, and <c>notify</c> are only checked against + the values supplied before as option values + to function <c>open/1</c>, <c>change_header/2</c>, <c>change_notify/3</c>, + or <c>change_size/2</c>. Thus, none of the options except <c>name</c> is mandatory. If some - given value differs from the current value, a tuple + specified value differs from the current value, a tuple <c>{error, {arg_mismatch, <anno>OptionName</anno>, <anno>CurrentValue</anno>, <anno>Value</anno>}}</c> - is returned. Caution: an owner's attempt to open a log - as owner once again is acknowledged with the return value + is returned.</p> + <note><p>If an owner attempts to open a log + as owner once again, it is acknowledged with the return value <c>{ok, <anno>Log</anno>}</c>, but the state of the disk log is not - affected in any way. - </p> - <p>If a log with a given name is local on some node, + affected.</p></note> + <p>If a log with a specified name is local on some node, and one tries to open the log distributed on the same node, - then the tuple <c>{error, {node_already_open, <anno>Log</anno>}}</c> is + the tuple <c>{error, {node_already_open, <anno>Log</anno>}}</c> is returned. The same tuple is returned if the log is distributed on some node, and one tries to open the log locally on the same node. Opening individual distributed disk logs for the first time adds those logs to a (possibly empty) distributed disk log. - The option values supplied are used - on all nodes mentioned by the <c>distributed</c> option. + The supplied option values are used + on all nodes mentioned by option <c>distributed</c>. Individual distributed logs know nothing about each other's option values, so each node can be given unique option values by creating a distributed - log with several calls to <c>open/1</c>. + log with many calls to <c>open/1</c>. </p> - <p>It is possible to open a log file more than once by giving - different values to the option <c>name</c> or by using the + <p>A log file can be opened more than once by giving + different values to option <c>name</c> or by using the same file when distributing a log on different nodes. - It is up to the user of the <c>disk_log</c> - module to ensure that no more than one - disk log process has write access to any file, or the - the file may be corrupted. + It is up to the user of module <c>disk_log</c> + to ensure that not more than one disk log process has write + access to any file, otherwise the file can be corrupted. </p> <p>If an attempt to open a log file for the first time fails, the disk log process terminates with the EXIT message @@ -999,9 +1045,9 @@ If <name name="pid2name" arity="1"/> <fsummary>Return the name of the disk log handled by a pid.</fsummary> <desc> - <p>The <c>pid2name/1</c> function returns the name of the log + <p>Returns the log name given the pid of a disk log process on the current node, or - <c>undefined</c> if the given pid is not a disk log process. + <c>undefined</c> if the specified pid is not a disk log process. </p> <p>This function is meant to be used for debugging only. </p> @@ -1018,25 +1064,25 @@ If <type variable="BHead"/> <type name="reopen_error_rsn"/> <desc> - <p>The <c>reopen</c> functions first rename the log file - to <c><anno>File</anno></c> and then re-create a new log file. - In case of a wrap log, <c><anno>File</anno></c> is used as the base name + <p>Renames the log file + to <c><anno>File</anno></c> and then recreates a new log file. + If a wrap log exists, <c><anno>File</anno></c> is used as the base name of the renamed files. By default the header given to <c>open/1</c> is written first in - the newly opened log file, but if the <c><anno>Head</anno></c> or the - <c><anno>BHead</anno></c> argument is given, this item is used instead. - The header argument is used once only; next time a wrap log file + the newly opened log file, but if argument <c><anno>Head</anno></c> or + <c><anno>BHead</anno></c> is specified, this item is used instead. + The header argument is used only once. Next time a wrap log file is opened, the header given to <c>open/1</c> is used. </p> - <p>The <c>reopen/2,3</c> functions are used for internally formatted + <p><c>reopen/2,3</c> are used for internally formatted logs, and <c>breopen/3</c> for externally formatted logs. </p> - <p>The owners that subscribe to notifications will receive + <p>Owners subscribing to notifications receive a <c>truncate</c> message. </p> <p>Upon failure to reopen the log, the disk log process terminates - with the EXIT message <c>{{failed,Error},[{disk_log,Fun,Arity}]}</c>, - and other processes that have requests queued receive the message + with the EXIT message <c>{{failed,Error},[{disk_log,Fun,Arity}]}</c>. + Other processes having requests queued receive the message <c>{disk_log, Node, {error, disk_log_stopped}}</c>. </p> </desc> @@ -1046,8 +1092,7 @@ If <fsummary>Flush the contents of a disk log to the disk.</fsummary> <type name="sync_error_rsn"/> <desc> - <p>The <c>sync/1</c> function ensures that the contents of the - log are actually written to the disk. + <p>Ensures that the contents of the log are written to the disk. This is usually a rather expensive operation. </p> </desc> @@ -1062,24 +1107,24 @@ If <type variable="BHead"/> <type name="trunc_error_rsn"/> <desc> - <p>The <c>truncate</c> functions remove all items from a disk log. - If the <c><anno>Head</anno></c> or the <c><anno>BHead</anno></c> argument is - given, this item is written first in the newly truncated + <p>Removes all items from a disk log. + If argument <c><anno>Head</anno></c> or <c><anno>BHead</anno></c> is + specified, this item is written first in the newly truncated log, otherwise the header given to <c>open/1</c> is used. - The header argument is only used once; next time a wrap log file + The header argument is used only once. Next time a wrap log file is opened, the header given to <c>open/1</c> is used. </p> - <p>The <c>truncate/1,2</c> functions are used for internally + <p><c>truncate/1,2</c> are used for internally formatted logs, and <c>btruncate/2</c> for externally formatted logs. </p> - <p>The owners that subscribe to notifications will receive + <p>Owners subscribing to notifications receive a <c>truncate</c> message. </p> <p>If the attempt to truncate the log fails, the disk log process terminates with the EXIT message - <c>{{failed,Reason},[{disk_log,Fun,Arity}]}</c>, and - other processes that have requests queued receive the message + <c>{{failed,Reason},[{disk_log,Fun,Arity}]}</c>. + Other processes having requests queued receive the message <c>{disk_log, Node, {error, disk_log_stopped}}</c>. </p> </desc> @@ -1089,7 +1134,7 @@ If <fsummary>Unblock a disk log.</fsummary> <type name="unblock_error_rsn"/> <desc> - <p>The <c>unblock/1</c> function unblocks a log. + <p>Unblocks a log. A log can only be unblocked by the blocking process. </p> </desc> @@ -1098,8 +1143,8 @@ If <section> <title>See Also</title> - <p><seealso marker="file">file(3)</seealso>, - <seealso marker="pg2">pg2(3)</seealso>, - <seealso marker="wrap_log_reader">wrap_log_reader(3)</seealso></p> + <p><seealso marker="file"><c>file(3)</c></seealso>, + <seealso marker="pg2"><c>pg2(3)</c></seealso>, + <seealso marker="wrap_log_reader"><c>wrap_log_reader(3)</c></seealso></p> </section> </erlref> diff --git a/lib/kernel/doc/src/erl_boot_server.xml b/lib/kernel/doc/src/erl_boot_server.xml index 60def984ec..897365f9b9 100644 --- a/lib/kernel/doc/src/erl_boot_server.xml +++ b/lib/kernel/doc/src/erl_boot_server.xml @@ -29,72 +29,73 @@ <rev></rev> </header> <module>erl_boot_server</module> - <modulesummary>Boot Server for Other Erlang Machines</modulesummary> + <modulesummary>Boot server for other Erlang machines.</modulesummary> <description> - <p>This server is used to assist diskless Erlang nodes which fetch + <p>This server is used to assist diskless Erlang nodes that fetch all Erlang code from another machine.</p> <p>This server is used to fetch all code, including the start script, if an Erlang runtime system is started with - the <c>-loader inet</c> command line flag. All hosts specified - with the <c>-hosts Host</c> command line flag must have one + command-line flag <c>-loader inet</c>. All hosts specified + with command-line flag <c>-hosts Host</c> must have one instance of this server running.</p> - <p>This server can be started with the <c>kernel</c> configuration + <p>This server can be started with the <c>Kernel</c> configuration parameter <c>start_boot_server</c>.</p> - <p>The <c>erl_boot_server</c> can both read regular files as well as - files in archives. See <seealso marker="code">code(3)</seealso> - and <seealso marker="erts:erl_prim_loader">erl_prim_loader(3)</seealso>.</p> - <warning><p>The support for loading of code from archive files is - experimental. The sole purpose of releasing it before it is ready - is to obtain early feedback. The file format, semantics, - interfaces etc. may be changed in a future release.</p></warning> + <p>The <c>erl_boot_server</c> can read regular files and + files in archives. See <seealso marker="code"><c>code(3)</c></seealso> + and + <seealso marker="erts:erl_prim_loader"><c>erl_prim_loader(3)</c></seealso> + in <c>ERTS</c>.</p> + <warning><p>The support for loading code from archive files is + experimental. It is released before it is ready + to obtain early feedback. The file format, semantics, + interfaces, and so on, can be changed in a future release.</p></warning> </description> <funcs> <func> - <name name="start" arity="1"/> - <fsummary>Start the boot server</fsummary> + <name name="add_slave" arity="1"/> + <fsummary>Add a slave to the list of allowed slaves.</fsummary> <desc> - <p>Starts the boot server. <c><anno>Slaves</anno></c> is a list of IP - addresses for hosts which are allowed to use this server as a - boot server.</p> + <p>Adds a <c><anno>Slave</anno></c> node to the list of allowed slave hosts.</p> </desc> </func> <func> - <name name="start_link" arity="1"/> - <fsummary>Start the boot server and links the caller</fsummary> + <name name="delete_slave" arity="1"/> + <fsummary>Delete a slave from the list of allowed slaves.</fsummary> <desc> - <p>Starts the boot server and links to the caller. This function - is used to start the server if it is included in a supervision - tree.</p> + <p>Deletes a <c><anno>Slave</anno></c> node from the list of allowed slave + hosts.</p> </desc> </func> <func> - <name name="add_slave" arity="1"/> - <fsummary>Add a slave to the list of allowed slaves</fsummary> + <name name="start" arity="1"/> + <fsummary>Start the boot server.</fsummary> <desc> - <p>Adds a <c><anno>Slave</anno></c> node to the list of allowed slave hosts.</p> + <p>Starts the boot server. <c><anno>Slaves</anno></c> is a list of + IP addresses for hosts, which are allowed to use this server as a + boot server.</p> </desc> </func> <func> - <name name="delete_slave" arity="1"/> - <fsummary>Delete a slave from the list of allowed slaves</fsummary> + <name name="start_link" arity="1"/> + <fsummary>Start the boot server and link to the the caller.</fsummary> <desc> - <p>Deletes a <c><anno>Slave</anno></c> node from the list of allowed slave - hosts.</p> + <p>Starts the boot server and links to the caller. This function + is used to start the server if it is included in a supervision + tree.</p> </desc> </func> <func> <name name="which_slaves" arity="0"/> - <fsummary>Return the current list of allowed slave hosts</fsummary> + <fsummary>Return the current list of allowed slave hosts.</fsummary> <desc> <p>Returns the current list of allowed slave hosts.</p> </desc> </func> </funcs> - <section> <title>SEE ALSO</title> - <p><seealso marker="erts:init">init(3)</seealso>, - <seealso marker="erts:erl_prim_loader">erl_prim_loader(3)</seealso></p> + <p><seealso marker="erts:init"><c>erts:init(3)</c></seealso>, + <seealso marker="erts:erl_prim_loader"><c>erts:erl_prim_loader(3)</c></seealso></p> </section> </erlref> diff --git a/lib/kernel/doc/src/erl_ddll.xml b/lib/kernel/doc/src/erl_ddll.xml index 6421a30dcf..a5ce58ef3e 100644 --- a/lib/kernel/doc/src/erl_ddll.xml +++ b/lib/kernel/doc/src/erl_ddll.xml @@ -29,84 +29,91 @@ <rev></rev> </header> <module>erl_ddll</module> - <modulesummary>Dynamic Driver Loader and Linker</modulesummary> + <modulesummary>Dynamic driver loader and linker.</modulesummary> <description> - <p>The <c>erl_ddll</c> module provides an interface for loading - and unloading <em>erlang linked in drivers</em> in runtime.</p> + <p>This module provides an interface for loading + and unloading <em>Erlang linked-in drivers</em> in runtime.</p> <note> - <p>This is a large reference document. For casual use of the - module, as well as for most real world applications, the - descriptions of the functions <seealso marker="#load/2">load/2</seealso> and <seealso marker="#unload/1">unload/1</seealso> are enough to get - going. </p> + <p>This is a large reference document. For casual use of this + module, and for most real world applications, the + descriptions of functions + <seealso marker="#load/2"><c>load/2</c></seealso> and + <seealso marker="#unload/1"><c>unload/1</c></seealso> + are enough to getting started.</p> </note> - <p>The driver should be provided as a dynamically linked library - in a object code format specific for the platform in use, - i. e. <c>.so</c> files on most Unix systems and <c>.ddl</c> - files on windows. An erlang linked in driver has to provide + <p>The driver is to be provided as a dynamically linked library + in an object code format specific for the platform in use, + that is, <c>.so</c> files on most Unix systems and <c>.ddl</c> + files on Windows. An Erlang linked-in driver must provide specific interfaces to the emulator, so this module is not - designed for loading arbitrary dynamic libraries. For further - information about erlang drivers, refer to the ERTS reference - manual section <seealso marker="erts:erl_driver">erl_driver</seealso>.</p> + designed for loading arbitrary dynamic libraries. For more + information about Erlang drivers, see + <seealso marker="erts:erl_driver"><c>erts:erl_driver</c></seealso> + .</p> <marker id="users"></marker> - <p>When describing a set of functions, (i.e. a module, a part of a - module or an application) executing in a process and wanting to - use a ddll-driver, we use the term <em>user</em>. There can be - several users in one process (different modules needing the same - driver) and several processes running the same code, making up - several <em>users</em> of a driver. In the basic scenario, each - user loads the driver before starting to use it and unloads the - driver when done. The reference counting keeps track of - processes as well as the number of loads by each process, so that - the driver will only be unloaded when no one wants it - (it has no user). The driver also keeps track of ports that are - opened towards it, so that one can delay unloading until all - ports are closed or kill all ports using the driver when it is - unloaded. </p> + <p>When describing a set of functions (that is, a module, a part of a + module, or an application), executing in a process and wanting to + use a ddll-driver, we use the term <em>user</em>. A process can + have many users (different modules needing the same + driver) and many processes running the same code, making up + many <em>users</em> of a driver.</p> + <p>In the basic scenario, each user loads the driver before + starting to use it and unloads the driver when done. + The reference counting keeps track of + processes and the number of loads by each process. This way + the driver is only unloaded when no one wants it (it has no user). + The driver also keeps track of ports that are + opened to it. This enables delay of unloading until all + ports are closed, or killing of all ports that use the driver when + it is unloaded.</p> <marker id="scenarios"></marker> <p>The interface supports two basic scenarios of loading and unloading. Each scenario can also have the option of either killing ports when the driver is unloading, or waiting for the - ports to close themselves. The scenarios are:</p> + ports to close themselves. The scenarios are as follows:</p> <taglist> - <tag><em>Load and unload on a "when needed basis"</em></tag> + <tag><em>Load and Unload on a "When Needed Basis"</em></tag> <item> <p>This (most common) scenario simply supports that each <seealso marker="#users">user</seealso> of the driver loads - it when it is needed and unloads it when the <seealso marker="#users">user</seealso> no longer have any use for - it. The driver is always reference counted and as long as a + it when needed and unloads it when no longer needed. + The driver is always reference counted and as long as a process keeping the driver loaded is still alive, the driver is present in the system.</p> <p>Each <seealso marker="#users">user</seealso> of the driver use <em>literally</em> the same pathname for the driver when - demanding load, but the <seealso marker="#users">users</seealso> are not really concerned - with if the driver is already loaded from the filesystem or - if the object code has to be loaded from filesystem.</p> - <p>Two pairs of functions support this scenario:</p> + demanding load, but the + <seealso marker="#users">users</seealso> are not concerned + with if the driver is already loaded from the file system or + if the object code must be loaded from file system.</p> + <p>The following two pairs of functions support this scenario:</p> <taglist> <tag><em>load/2 and unload/1</em></tag> <item> <p>When using the <c>load/unload</c> interfaces, the - driver will not <em>actually</em> get unloaded until the - <em>last port</em> using the driver is closed. The function - <c>unload/1</c> can return immediately, as the <seealso marker="#users">users</seealso> are not really concerned - with when the actual unloading occurs. The - driver will actually get unloaded when no one needs it any longer.</p> - <p>If a process having the driver loaded dies, it will have - the same effect as if unloading was done. </p> - <p>When loading, the function <c>load/2</c> returns - <c>ok</c> as soon as there is any instance of the driver - present, so that if a driver is waiting to get unloaded - (due to open ports), it will simply change state to no + driver is not unloaded until the + <em>last port</em> using the driver is closed. Function + <c>unload/1</c> can return immediately, as the + <seealso marker="#users">users</seealso> + have no interrest in when the unloading occurs. The + driver is unloaded when no one needs it any longer.</p> + <p>If a process having the driver loaded dies, it has + the same effect as if unloading is done.</p> + <p>When loading, function <c>load/2</c> returns + <c>ok</c> when any instance of the driver is + present. Thus, if a driver is waiting to get unloaded + (because of open ports), it simply changes state to no longer need unloading.</p> </item> <tag><em>load_driver/2 and unload_driver/1</em></tag> <item> - <p>These interfaces is intended to be used when it is considered an - error that ports are open towards a driver that no <seealso marker="#users">user</seealso> - has loaded. The ports still open when the + <p>These interfaces are intended to be used when it is considered an + error that ports are open to a driver that no + <seealso marker="#users">user</seealso> + has loaded. The ports that are still open when the last <seealso marker="#users">user</seealso> calls <c>unload_driver/1</c> or when the last process having the - driver loaded dies, will get killed with reason + driver loaded dies, are killed with reason <c>driver_unloaded</c>.</p> <p>The function names <c>load_driver</c> and <c>unload_driver</c> are kept for backward @@ -114,60 +121,66 @@ </item> </taglist> </item> - <tag><em>Loading and reloading for code replacement</em></tag> + <tag><em>Loading and Reloading for Code Replacement</em></tag> <item> - <p>This scenario occurs when the driver code might need + <p>This scenario can occur if the driver code needs replacement during operation of the Erlang - emulator. Implementing driver code replacement is somewhat - more tedious than beam code replacement, as one driver - cannot be loaded as both "old" and "new" code. All <seealso marker="#users">users</seealso> of a driver must have it + emulator. Implementing driver code replacement is a little + more tedious than Beam code replacement, as one driver + cannot be loaded as both "old" and "new" code. All + <seealso marker="#users">users</seealso> of a driver must have it closed (no open ports) before the old code can be unloaded and the new code can be loaded.</p> - <p>The actual unloading/loading is done as one atomic + <p>The unloading/loading is done as one atomic operation, blocking all processes in the system from using - the driver concerned while in progress.</p> + the driver in question while in progress.</p> <p>The preferred way to do driver code replacement is to let <em>one single process</em> keep track of the driver. When - the process start, the driver is loaded. When replacement + the process starts, the driver is loaded. When replacement is required, the driver is reloaded. Unload is probably never - done, or done when the process exits. If more than one <seealso marker="#users">user</seealso> has a driver loaded when code - replacement is demanded, the replacement cannot occur until - the last "other" <seealso marker="#users">user</seealso> has + done, or done when the process exits. If more than one + <seealso marker="#users">user</seealso> has a driver + loaded when code replacement is demanded, the replacement cannot + occur until the last "other" + <seealso marker="#users">user</seealso> has unloaded the driver.</p> <p>Demanding reload when a reload is already in progress is - always an error. Using the high level functions, it is also - an error to demand reloading when more than one <seealso marker="#users">user</seealso> has the driver loaded. To - simplify driver replacement, avoid designing your system so - that more than than one <seealso marker="#users">user</seealso> has the driver loaded.</p> - <p>The two functions for reloading drivers should be used - together with corresponding load functions, to support the two + always an error. Using the high-level functions, it is also + an error to demand reloading when more than one + <seealso marker="#users">user</seealso> has the driver loaded.</p> + <p>To simplify driver replacement, avoid designing your system so + that more than one + <seealso marker="#users">user</seealso> has the driver loaded.</p> + <p>The two functions for reloading drivers are to be used + together with corresponding load functions to support the two different behaviors concerning open ports:</p> <taglist> <tag><em>load/2 and reload/2</em></tag> <item> - <p>This pair of functions is used when reloading should be - done after the last open port towards the driver is + <p>This pair of functions is used when reloading is to be + done after the last open port to the driver is closed.</p> - <p>As <c>reload/2</c> actually waits for the reloading to - occur, a misbehaving process keeping open ports towards - the driver (or keeping the driver loaded) might cause - infinite waiting for reload. Timeouts has to be provided + <p>As <c>reload/2</c> waits for the reloading to + occur, a misbehaving process keeping open ports to + the driver (or keeping the driver loaded) can cause + infinite waiting for reload. Time-outs must be provided outside of the process demanding the reload or by using - the low-level interface <seealso marker="#try_load/3">try_load/3</seealso> in combination - with driver monitors (see below).</p> + the low-level interface + <seealso marker="#try_load/3"><c>try_load/3</c></seealso> + in combination with driver monitors.</p> </item> <tag><em>load_driver/2 and reload_driver/2</em></tag> <item> - <p>This pair of functions are used when open ports towards - the driver should be killed with reason + <p>This pair of functions are used when open ports to + the driver are to be killed with reason <c>driver_unloaded</c> to allow for new driver code to get loaded.</p> - <p>If, however, another process has the driver loaded, - calling <c>reload_driver</c> returns the error code + <p>However, if another process has the driver loaded, + calling <c>reload_driver</c> returns error code <c>pending_process</c>. As stated earlier, - the recommended design is to not allow other <seealso marker="#users">users</seealso> than the "driver - reloader" to actually demand loading of the concerned - driver.</p> + the recommended design is to not allow other + <seealso marker="#users">users</seealso> than the "driver + reloader" to demand loading of the driver in question.</p> </item> </taglist> </item> @@ -184,903 +197,982 @@ <funcs> <func> <name name="demonitor" arity="1"/> - <fsummary>Remove a monitor for a driver</fsummary> + <fsummary>Remove a monitor for a driver.</fsummary> <desc> <p>Removes a driver monitor in much the same way as - <seealso marker="erts:erlang#erlang:demonitor/1">erlang:demonitor/1</seealso> does with process - monitors. See <seealso marker="#monitor/2">monitor/2</seealso>, <seealso marker="#try_load/3">try_load/3</seealso> and <seealso marker="#try_unload/2">try_unload/2</seealso> for details - about how to create driver monitors.</p> + <seealso marker="erts:erlang#erlang:demonitor/1"><c>erlang:demonitor/1</c></seealso> + in <c>ERTS</c> + does with process monitors. For details about how to create + driver monitors, see + <seealso marker="#monitor/2"><c>monitor/2</c></seealso>, + <seealso marker="#try_load/3"><c>try_load/3</c></seealso>, and + <seealso marker="#try_unload/2"><c>try_unload/2</c></seealso>.</p> <p>The function throws a <c>badarg</c> exception if the - parameter is not a reference(). </p> + parameter is not a <c>reference()</c>.</p> + </desc> + </func> + <func> + <name name="format_error" arity="1"/> + <fsummary>Format an error descriptor.</fsummary> + <desc> + <p>Takes an <c><anno>ErrorDesc</anno></c> returned by load, unload, or + reload functions and returns a string that + describes the error or warning.</p> + <note> + <p>Because of peculiarities in the dynamic loading interfaces on + different platforms, the returned string is only guaranteed + to describe the correct error <em>if format_error/1 is called + in the same instance of the Erlang virtual machine as the error + appeared in</em> (meaning the same operating + system process).</p> + </note> </desc> </func> <func> <name name="info" arity="0"/> - <fsummary>Retrieve information about all drivers</fsummary> + <fsummary>Retrieve information about all drivers.</fsummary> <desc> - <p>Returns a list of tuples <c>{<anno>DriverName</anno>, <anno>InfoList</anno>}</c>, where - <c><anno>InfoList</anno></c> is the result of calling <seealso marker="#info/1">info/1</seealso> for that - <c><anno>DriverName</anno></c>. Only dynamically linked in drivers are + <p>Returns a list of tuples <c>{<anno>DriverName</anno>, <anno>InfoList</anno>}</c>, + where <c><anno>InfoList</anno></c> is the result of calling + <seealso marker="#info/1"><c>info/1</c></seealso> for that + <c><anno>DriverName</anno></c>. Only dynamically linked-in drivers are included in the list.</p> </desc> </func> <func> <name name="info" arity="1"/> - <fsummary>Retrieve information about one driver</fsummary> + <fsummary>Retrieve information about one driver.</fsummary> <desc> - <p>Returns a list of tuples <c>{<anno>Tag</anno>, <anno>Value</anno>}</c>, where - <c><anno>Tag</anno></c> is the information item and <c><anno>Value</anno></c> is the result - of calling <seealso marker="#info/2">info/2</seealso> with this driver name and - this tag. The result being a tuple list containing all - information available about a driver. </p> - <p>The different tags that will appear in the list are:</p> + <p>Returns a list of tuples <c>{<anno>Tag</anno>, <anno>Value</anno>}</c>, + where <c><anno>Tag</anno></c> is the information item and + <c><anno>Value</anno></c> is the result of calling + <seealso marker="#info/2"><c>info/2</c></seealso> with this driver + name and this tag. The result is a tuple list containing all information + available about a driver.</p> + <p>The following tags appears in the list:</p> <list type="bulleted"> - <item>processes</item> - <item>driver_options</item> - <item>port_count</item> - <item>linked_in_driver</item> - <item>permanent</item> - <item>awaiting_load</item> - <item>awaiting_unload</item> + <item><c>processes</c></item> + <item><c>driver_options</c></item> + <item><c>port_count</c></item> + <item><c>linked_in_driver</c></item> + <item><c>permanent</c></item> + <item><c>awaiting_load</c></item> + <item><c>awaiting_unload</c></item> </list> - <p>For a detailed description of each value, please read the - description of <seealso marker="#info/2">info/2</seealso> below.</p> + <p>For a detailed description of each value, see + <seealso marker="#info/2"><c>info/2</c></seealso>.</p> <p>The function throws a <c>badarg</c> exception if the driver is not present in the system.</p> </desc> </func> <func> <name name="info" arity="2"/> - <fsummary>Retrieve specific information about one driver</fsummary> + <fsummary>Retrieve specific information about one driver.</fsummary> <desc> - <p>This function returns specific information about one aspect - of a driver. The <c><anno>Tag</anno></c> parameter specifies which aspect - to get information about. The <c><anno>Value</anno></c> return differs + <p>Returns specific information about one aspect of a driver. + Parameter <c><anno>Tag</anno></c> specifies which aspect + to get information about. The return <c><anno>Value</anno></c> differs between different tags:</p> <taglist> - <tag><em>processes</em></tag> + <tag><c>processes</c></tag> <item> - <p>Return all processes containing <seealso marker="#users">users</seealso> of the specific drivers - as a list of tuples <c>{pid(),integer() >= 0}</c>, where the - <c>integer()</c> denotes the number of users in the process + <p>Returns all processes containing + <seealso marker="#users">users</seealso> of the specific drivers + as a list of tuples <c>{pid(),integer() >= 0}</c>, where + <c>integer()</c> denotes the number of users in process <c>pid()</c>.</p> </item> - <tag><em>driver_options</em></tag> + <tag><c>driver_options</c></tag> <item> - <p>Return a list of the driver options provided when - loading, as well as any options set by the driver itself - during initialization. The currently only valid option - being <c>kill_ports</c>.</p> + <p>Returns a list of the driver options provided when + loading, and any options set by the driver + during initialization. The only valid option + is <c>kill_ports</c>.</p> </item> - <tag><em>port_count</em></tag> + <tag><c>port_count</c></tag> <item> - <p>Return the number of ports (an <c>integer >= 0()</c>) using the driver.</p> + <p>Returns the number of ports (an <c>integer() >= 0</c>) + using the driver.</p> </item> - <tag><em>linked_in_driver</em></tag> + <tag><c>linked_in_driver</c></tag> <item> - <p>Return a <c>boolean()</c>, being <c>true</c> if the driver is a - statically linked in one and <c>false</c> otherwise.</p> + <p>Returns a <c>boolean()</c>, which is <c>true</c> if the driver is a + statically linked-in one, otherwise <c>false</c>.</p> </item> - <tag><em>permanent</em></tag> + <tag><c>permanent</c></tag> <item> - <p>Return a <c>boolean()</c>, being <c>true</c> if the driver has made - itself permanent (and is <em>not</em> a statically - linked in driver). <c>false</c> otherwise.</p> + <p>Returns a <c>boolean()</c>, which is <c>true</c> if the driver has + made itself permanent (and is <em>not</em> a statically + linked-in driver), otherwise <c>false</c>.</p> </item> - <tag><em>awaiting_load</em></tag> + <tag><c>awaiting_load</c></tag> <item> - <p>Return a list of all processes having monitors for - <c>loading</c> active, each process returned as - <c>{pid(),integer() >= 0}</c>, where the <c>integer()</c> is the - number of monitors held by the process <c>pid()</c>.</p> + <p>Returns a list of all processes having monitors for + <c>loading</c> active. Each process is returned as + <c>{pid(),integer() >= 0}</c>, where <c>integer()</c> is the + number of monitors held by process <c>pid()</c>.</p> </item> - <tag><em>awaiting_unload</em></tag> + <tag><c>awaiting_unload</c></tag> <item> - <p>Return a list of all processes having monitors for - <c>unloading</c> active, each process returned as - <c>{pid(),integer() >= 0}</c>, where the <c>integer()</c> is the - number of monitors held by the process <c>pid()</c>.</p> + <p>Returns a list of all processes having monitors for + <c>unloading</c> active. Each process is returned as + <c>{pid(),integer() >= 0}</c>, where <c>integer()</c> is the + number of monitors held by process <c>pid()</c>.</p> </item> </taglist> - <p>If the options <c>linked_in_driver</c> or <c>permanent</c> - return true, all other options will return the value - <c>linked_in_driver</c> or <c>permanent</c> respectively.</p> + <p>If option <c>linked_in_driver</c> or <c>permanent</c> + returns <c>true</c>, all other options return + <c>linked_in_driver</c> or <c>permanent</c>, respectively.</p> <p>The function throws a <c>badarg</c> exception if the driver - is not present in the system or the tag is not supported.</p> + is not present in the system or if the tag is not supported.</p> </desc> </func> <func> <name name="load" arity="2"/> - <fsummary>Load a driver</fsummary> + <fsummary>Load a driver.</fsummary> <desc> - <p>Loads and links the dynamic driver <c><anno>Name</anno></c>. <c><anno>Path</anno></c> + <p>Loads and links the dynamic driver <c><anno>Name</anno></c>. + <c><anno>Path</anno></c> is a file path to the directory containing the driver. <c><anno>Name</anno></c> must be a sharable object/dynamic library. Two drivers with different <c><anno>Path</anno></c> parameters cannot be - loaded under the same name. The <c><anno>Name</anno></c> is a string or + loaded under the same name. <c><anno>Name</anno></c> is a string or atom containing at least one character.</p> - <p>The <c><anno>Name</anno></c> given should correspond to the filename - of the actual dynamically loadable object file residing in - the directory given as <c><anno>Path</anno></c>, but <em>without</em> the - extension (i.e. <c>.so</c>). The driver name provided in + <p>The <c><anno>Name</anno></c> specified is to correspond to the filename + of the dynamically loadable object file residing in + the directory specified as <c><anno>Path</anno></c>, but <em>without</em> the + extension (that is, <c>.so</c>). The driver name provided in the driver initialization routine must correspond with the - filename, in much the same way as erlang module names + filename, in much the same way as Erlang module names correspond to the names of the <c>.beam</c> files.</p> - <p>If the driver has been previously unloaded, but is still - present due to open ports against it, a call to - <c>load/2</c> will stop the unloading and keep the driver - (as long as the <c><anno>Path</anno></c> is the same) and <c>ok</c> is - returned. If one actually wants the object code to be - reloaded, one uses <seealso marker="#reload/2">reload/2</seealso> or the low-level - interface <seealso marker="#try_load/3">try_load/3</seealso> - instead. Please refer to the description of <seealso marker="#scenarios">different scenarios</seealso> for + <p>If the driver was previously unloaded, but is still + present because of open ports to it, a call to + <c>load/2</c> stops the unloading and keeps the driver + (as long as <c><anno>Path</anno></c> is the same), and <c>ok</c> is + returned. If you really want the object code to be + reloaded, use <seealso marker="#reload/2"><c>reload/2</c></seealso> + or the low-level interface + <seealso marker="#try_load/3"><c>try_load/3</c></seealso> instead. + See also the description of + <seealso marker="#scenarios"><c>different scenarios</c></seealso> for loading/unloading in the introduction.</p> <p>If more than one process tries to load an already loaded - driver withe the same <c><anno>Path</anno></c>, or if the same process - tries to load it several times, the function will return - <c>ok</c>. The emulator will keep track of the + driver with the same <c><anno>Path</anno></c>, or if the same process + tries to load it many times, the function returns + <c>ok</c>. The emulator keeps track of the <c>load/2</c> calls, so that a corresponding number of - <c>unload/2</c> calls will have to be done from the same - process before the driver will actually get unloaded. It is + <c>unload/2</c> calls must be done from the same + process before the driver gets unloaded. It is therefore safe for an application to load a driver that is shared between processes or applications when needed. It can safely be unloaded without causing trouble for other - parts of the system. </p> - <p>It is not allowed to load - several drivers with the same name but with different - <c>Path</c> parameters.</p> + parts of the system.</p> + <p>It is not allowed to load multiple drivers with + the same name but with different <c>Path</c> parameters.</p> <note> - <p>Note especially that the <c><anno>Path</anno></c> is interpreted - literally, so that all loaders of the same driver needs to - give the same <em>literal</em><c><anno>Path</anno></c> string, even - though different paths might point out the same directory - in the filesystem (due to use of relative paths and + <p><c><anno>Path</anno></c> is interpreted + literally, so that all loaders of the same driver must + specify the same <em>literal</em> <c><anno>Path</anno></c> string, + although different paths can point out the same directory + in the file system (because of use of relative paths and links).</p> </note> <p>On success, the function returns <c>ok</c>. On failure, the return value is <c>{error,<anno>ErrorDesc</anno>}</c>, where <c><anno>ErrorDesc</anno></c> is an opaque term to be - translated into human readable form by the <seealso marker="#format_error/1">format_error/1</seealso> - function.</p> - <p>For more control over the error handling, again use the - <seealso marker="#try_load/3">try_load/3</seealso> + translated into human readable form by function + <seealso marker="#format_error/1"><c>format_error/1</c></seealso>.</p> + <p>For more control over the error handling, use the + <seealso marker="#try_load/3"><c>try_load/3</c></seealso> interface instead.</p> <p>The function throws a <c>badarg</c> exception if the - parameters are not given as described above. </p> + parameters are not specified as described here.</p> </desc> </func> <func> <name name="load_driver" arity="2"/> - <fsummary>Load a driver</fsummary> + <fsummary>Load a driver.</fsummary> <desc> - <p>Works essentially as <c>load/2</c>, but will load the driver - with other options. All ports that are using the - driver will get killed with the reason - <c>driver_unloaded</c> when the driver is to be unloaded.</p> - <p>The number of loads and unloads by different <seealso marker="#users">users</seealso> influence the actual loading - and unloading of a driver file. The port killing will - therefore only happen when the <em>last</em> <seealso marker="#users">user</seealso> unloads the driver, or the - last process having loaded the driver exits.</p> + <p>Works essentially as <c>load/2</c>, but loads the driver + with other options. All ports using the + driver are killed with reason <c>driver_unloaded</c> + when the driver is to be unloaded.</p> + <p>The number of loads and unloads by different + <seealso marker="#users">users</seealso> influences the loading + and unloading of a driver file. The port killing + therefore only occurs when the <em>last</em> + <seealso marker="#users">user</seealso> unloads the driver, + or when the last process having loaded the driver exits.</p> <p>This interface (or at least the name of the functions) is - kept for backward compatibility. Using <seealso marker="#try_load/3">try_load/3</seealso> with - <c>{driver_options,[kill_ports]} </c> in the option list will - give the same effect regarding the port killing.</p> + kept for backward compatibility. + Using <seealso marker="#try_load/3"><c>try_load/3</c></seealso> with + <c>{driver_options,[kill_ports]}</c> in the option list + gives the same effect regarding the port killing.</p> <p>The function throws a <c>badarg</c> exception if the - parameters are not given as described above. </p> + parameters are not specified as described here.</p> + </desc> + </func> + <func> + <name name="loaded_drivers" arity="0"/> + <fsummary>List loaded drivers.</fsummary> + <desc> + <p>Returns a list of all the available drivers, both + (statically) linked-in and dynamically loaded ones.</p> + <p>The driver names are returned as a list of strings rather + than a list of atoms for historical reasons.</p> + <p>For more information about drivers, see + <seealso marker="#info/0"><c>info</c></seealso>.</p> </desc> </func> <func> <name name="monitor" arity="2"/> - <fsummary>Create a monitor for a driver</fsummary> + <fsummary>Create a monitor for a driver.</fsummary> <desc> - <p>This function creates a driver monitor and works in many - ways as the function <seealso marker="erts:erlang#erlang:monitor/2">erlang:monitor/2</seealso>, + <p>Creates a driver monitor and works in many + ways as + <seealso marker="erts:erlang#erlang:monitor/2"><c>erlang:monitor/2</c></seealso> + in <c>ERTS</c>, does for processes. When a driver changes state, the monitor - results in a monitor-message being sent to the calling - process. The <c><anno>MonitorRef</anno></c> returned by this function is + results in a monitor message that is sent to the calling + process. <c><anno>MonitorRef</anno></c> returned by this function is included in the message sent.</p> - <p>As with process monitors, each driver monitor set will only - generate <em>one single message</em>. The monitor is - "destroyed" after the message is sent and there is then no - need to call <seealso marker="#demonitor/1">demonitor/1</seealso>.</p> - <p>The <c><anno>MonitorRef</anno></c> can also be used in subsequent calls - to <seealso marker="#demonitor/1">demonitor/1</seealso> to + <p>As with process monitors, each driver monitor set only + generates <em>one single message</em>. The monitor is + "destroyed" after the message is sent, so it is then not + needed to call + <seealso marker="#demonitor/1"><c>demonitor/1</c></seealso>.</p> + <p><c><anno>MonitorRef</anno></c> can also be used in subsequent calls + to <seealso marker="#demonitor/1"><c>demonitor/1</c></seealso> to remove a monitor.</p> <p>The function accepts the following parameters:</p> <taglist> - <tag><em><c><anno>Tag</anno></c></em></tag> + <tag><c><anno>Tag</anno></c></tag> <item> - <p>The monitor tag is always <c>driver</c> as this function + <p>The monitor tag is always <c>driver</c>, as this function can only be used to create driver monitors. In the future, driver monitors will be integrated with process monitors, - why this parameter has to be given for consistence.</p> + why this parameter has to be specified for consistence.</p> </item> - <tag><em><c><anno>Item</anno></c></em></tag> + <tag><c><anno>Item</anno></c></tag> <item> - <p>The <c><anno>Item</anno></c> parameter specifies which driver one - wants to monitor (the name of the driver) as well as - which state change one wants to monitor. The parameter + <p>Parameter <c><anno>Item</anno></c> specifies + which driver to monitor (the driver name) and + which state change to monitor. The parameter is a tuple of arity two whose first element is the - driver name and second element is either of:</p> + driver name and second element is one of the following:</p> <taglist> - <tag><em>loaded</em></tag> + <tag><c>loaded</c></tag> <item> - <p>Notify me when the driver is reloaded (or loaded if + <p>Notifies when the driver is reloaded (or loaded if loading is underway). It only makes sense to monitor drivers that are in the process of being loaded or - reloaded. One cannot monitor a future-to-be driver - name for loading, that will only result in a - <c>'DOWN'</c> message being immediately sent. + reloaded. A future driver name for loading cannot be + monitored. That only results in a + <c>DOWN</c> message sent immediately. Monitoring for loading is therefore most useful when - triggered by the <seealso marker="#try_load/3">try_load/3</seealso> function, + triggered by function + <seealso marker="#try_load/3"><c>try_load/3</c></seealso>, where the monitor is created <em>because</em> the driver is in such a pending state.</p> - <p>Setting a driver monitor for <c>loading</c> will - eventually lead to one of the following messages + <p>Setting a driver monitor for <c>loading</c> + eventually leads to one of the following messages being sent:</p> <taglist> - <tag><em>{'UP', reference(), driver, Name, loaded}</em></tag> + <tag><c>{'UP', reference(), driver, Name, loaded}</c></tag> <item> - <p>This message is sent, either immediately if the + <p>This message is sent either immediately if the driver is already loaded and no reloading is pending, or when reloading is executed if reloading is pending. </p> <p>The <seealso marker="#users">user</seealso> is - expected to know if reloading is demanded prior - to creating a monitor for loading.</p> + expected to know if reloading is demanded before + creating a monitor for loading.</p> </item> - <tag><em>{'UP', reference(), driver, Name, permanent}</em></tag> + <tag><c>{'UP', reference(), driver, Name, permanent}</c></tag> <item> - <p>This message will be sent if reloading was + <p>This message is sent if reloading was expected, but the (old) driver made itself - permanent prior to reloading. It will also be + permanent before reloading. It is also sent if the driver was permanent or statically - linked in when trying to create the monitor.</p> + linked-in when trying to create the monitor.</p> </item> - <tag><em>{'DOWN', reference(), driver, Name, load_cancelled}</em></tag> + <tag><c>{'DOWN', reference(), driver, Name, load_cancelled}</c></tag> <item> - <p>This message will arrive if reloading was - underway, but the <seealso marker="#users">user</seealso> having requested - reload cancelled it by either dying or calling - <seealso marker="#try_unload/2">try_unload/2</seealso> + <p>This message arrives if reloading was + underway, but the requesting + <seealso marker="#users">user</seealso> + cancelled it by dying or calling + <seealso marker="#try_unload/2"><c>try_unload/2</c></seealso> (or <c>unload/1</c>/<c>unload_driver/1</c>) again before it was reloaded.</p> </item> - <tag><em>{'DOWN', reference(), driver, Name, {load_failure, Failure}}</em></tag> + <tag><c>{'DOWN', reference(), driver, Name, {load_failure, Failure}}</c></tag> <item> - <p>This message will arrive if reloading was + <p>This message arrives if reloading was underway but the loading for some reason failed. The <c>Failure</c> term is one of the - errors that can be returned from <seealso marker="#try_load/3">try_load/3</seealso>. The - error term can be passed to <seealso marker="#format_error/1">format_error/1</seealso> - for translation into human readable form. Note - that the translation has to be done in the same - running erlang virtual machine as the error + errors that can be returned from + <seealso marker="#try_load/3"><c>try_load/3</c></seealso>. + The error term can be passed to + <seealso marker="#format_error/1"><c>format_error/1</c></seealso> + for translation into human readable form. Notice + that the translation must be done in the same + running Erlang virtual machine as the error was detected in.</p> </item> </taglist> </item> - <tag><em>unloaded</em></tag> + <tag><c>unloaded</c></tag> <item> - <p>Monitor when a driver gets unloaded. If one + <p>Monitors when a driver gets unloaded. If one monitors a driver that is not present in the system, - one will immediately get notified that the driver got + one immediately gets notified that the driver got unloaded. There is no guarantee that the driver was - actually ever loaded.</p> - <p>A driver monitor for unload will eventually result + ever loaded.</p> + <p>A driver monitor for unload eventually results in one of the following messages being sent:</p> <taglist> - <tag><em>{'DOWN', reference(), driver, Name, unloaded}</em></tag> + <tag><c>{'DOWN', reference(), driver, Name, unloaded}</c></tag> <item> - <p>The driver instance monitored is now - unloaded. As the unload might have been due to a - <c>reload/2</c> request, the driver might once + <p>The monitored driver instance is now + unloaded. As the unload can be a result of a + <c>reload/2</c> request, the driver can once again have been loaded when this message arrives.</p> </item> - <tag><em>{'UP', reference(), driver, Name, unload_cancelled}</em></tag> + <tag><c>{'UP', reference(), driver, Name, unload_cancelled}</c></tag> <item> - <p>This message will be sent if unloading was + <p>This message is sent if unloading was expected, but while the driver was waiting for - all ports to get closed, a new <seealso marker="#users">user</seealso> of the driver - appeared and the unloading was cancelled.</p> - <p>This message appears when an <c>{ok, pending_driver}</c>) was returned from <seealso marker="#try_unload/2">try_unload/2</seealso>) - for the last <seealso marker="#users">user</seealso> of the driver and - then a <c>{ok, already_loaded}</c> is returned - from a call to <seealso marker="#try_load/3">try_load/3</seealso>.</p> - <p>If one wants to <em>really</em> monitor when the - driver gets unloaded, this message will distort - the picture, no unloading was really done. - The <c>unloaded_only</c> option creates a monitor - similar to an <c>unloaded</c> monitor, but does - never result in this message.</p> + all ports to get closed, a new + <seealso marker="#users">user</seealso> of the driver + appeared, and the unloading was cancelled.</p> + <p>This message appears if <c>{ok, pending_driver}</c> + was returned from + <seealso marker="#try_unload/2"><c>try_unload/2</c></seealso> + for the last <seealso marker="#users">user</seealso> + of the driver, and then <c>{ok, already_loaded}</c> is returned + from a call to + <seealso marker="#try_load/3"><c>try_load/3</c></seealso>.</p> + <p>If one <em>really</em> wants to monitor when the + driver gets unloaded, this message distorts + the picture, because no unloading was done. + Option <c>unloaded_only</c> creates a monitor + similar to an <c>unloaded</c> monitor, but + never results in this message.</p> </item> - <tag><em>{'UP', reference(), driver, Name, permanent}</em></tag> + <tag><c>{'UP', reference(), driver, Name, permanent}</c></tag> <item> - <p>This message will be sent if unloading was + <p>This message is sent if unloading was expected, but the driver made itself - permanent prior to unloading. It will also be + permanent before unloading. It is also sent if trying to monitor a permanent or - statically linked in driver.</p> + statically linked-in driver.</p> </item> </taglist> </item> - <tag><em>unloaded_only</em></tag> + <tag><c>unloaded_only</c></tag> <item> <p>A monitor created as <c>unloaded_only</c> behaves - exactly as one created as <c>unloaded</c> with the - exception that the <c>{'UP', reference(), driver, Name, unload_cancelled}</c> message will never be - sent, but the monitor instead persists until the - driver <em>really</em> gets unloaded.</p> + exactly as one created as <c>unloaded</c> + except that the + <c>{'UP', reference(), driver, Name, unload_cancelled}</c> + message is never sent, but the monitor instead persists until + the driver <em>really</em> gets unloaded.</p> </item> </taglist> </item> </taglist> <p>The function throws a <c>badarg</c> exception if the - parameters are not given as described above. </p> + parameters are not specified as described here.</p> </desc> </func> <func> <name name="reload" arity="2"/> - <fsummary>Replace a driver</fsummary> + <fsummary>Replace a driver.</fsummary> <desc> <p>Reloads the driver named <c><anno>Name</anno></c> from a possibly - different <c><anno>Path</anno></c> than was previously used. This - function is used in the code change <seealso marker="#scenarios">scenario</seealso> described in the + different <c><anno>Path</anno></c> than previously used. This + function is used in the code change + <seealso marker="#scenarios"><c>scenario</c></seealso> described in the introduction.</p> <p>If there are other <seealso marker="#users">users</seealso> - of this driver, the function will return <c>{error, pending_process}</c>, but if there are no more users, the - function call will hang until all open ports are closed.</p> + of this driver, the function returns <c>{error, pending_process}</c>, + but if there are no other users, the function call hangs until all + open ports are closed.</p> <note> - <p>Avoid mixing - several <seealso marker="#users">users</seealso> - with driver reload requests.</p> - </note> - <p>If one wants to avoid hanging on open ports, one should use - the <seealso marker="#try_load/3">try_load/3</seealso> - function instead.</p> - <p>The <c><anno>Name</anno></c> and <c><anno>Path</anno></c> parameters have exactly the - same meaning as when calling the plain <seealso marker="#load/2">load/2</seealso> function.</p> - <note> - <p>Avoid mixing - several <seealso marker="#users">users</seealso> - with driver reload requests.</p> + <p>Avoid mixing multiple + <seealso marker="#users">users</seealso> + with driver reload requests.</p> </note> + <p>To avoid hanging on open ports, use function + <seealso marker="#try_load/3"><c>try_load/3</c></seealso> + instead.</p> + <p>The <c><anno>Name</anno></c> and <c><anno>Path</anno></c> parameters + have exactly the same meaning as when calling the plain function + <seealso marker="#load/2"><c>load/2</c></seealso>.</p> + <p>On success, the function returns <c>ok</c>. On - failure, the function returns an opaque error, with the - exception of the <c>pending_process</c> error described - above. The opaque errors are to be translated into human - readable form by the <seealso marker="#format_error/1">format_error/1</seealso> function.</p> - <p>For more control over the error handling, again use the - <seealso marker="#try_load/3">try_load/3</seealso> + failure, the function returns an opaque error, + except the <c>pending_process</c> error described + earlier. The opaque errors are to be translated into human + readable form by function + <seealso marker="#format_error/1"><c>format_error/1</c></seealso>.</p> + <p>For more control over the error handling, use the + <seealso marker="#try_load/3"><c>try_load/3</c></seealso> interface instead.</p> <p>The function throws a <c>badarg</c> exception if the - parameters are not given as described above. </p> + parameters are not specified as described here.</p> </desc> </func> <func> <name name="reload_driver" arity="2"/> - <fsummary>Replace a driver</fsummary> + <fsummary>Replace a driver.</fsummary> <desc> - <p>Works exactly as <seealso marker="#reload/2">reload/2</seealso>, but for drivers - loaded with the <seealso marker="#load_driver/2">load_driver/2</seealso> interface. </p> - <p>As this interface implies that ports are being killed when - the last user disappears, the function wont hang waiting for + <p>Works exactly as <seealso marker="#reload/2"><c>reload/2</c></seealso>, + but for drivers loaded with the + <seealso marker="#load_driver/2"><c>load_driver/2</c></seealso> interface.</p> + <p>As this interface implies that ports are killed when + the last user disappears, the function does not hang waiting for ports to get closed.</p> - <p>For further details, see the <seealso marker="#scenarios">scenarios</seealso> in the module - description and refer to the <seealso marker="#reload/2">reload/2</seealso> function description.</p> + <p>For more details, see + <seealso marker="#scenarios"><c>scenarios</c></seealso> in this module + description and the function description for + <seealso marker="#reload/2"><c>reload/2</c></seealso>.</p> <p>The function throws a <c>badarg</c> exception if the - parameters are not given as described above. </p> + parameters are not specified as described here.</p> </desc> </func> <func> <name name="try_load" arity="3"/> - <fsummary>Load a driver</fsummary> + <fsummary>Load a driver.</fsummary> <desc> - <p>This function provides more control than the + <p>Provides more control than the <c>load/2</c>/<c>reload/2</c> and <c>load_driver/2</c>/<c>reload_driver/2</c> interfaces. It - will never wait for completion of other operations related - to the driver, but immediately return the status of the - driver as either:</p> + never waits for completion of other operations related + to the driver, but immediately returns the status of the + driver as one of the following:</p> <taglist> - <tag><em>{ok, loaded}</em></tag> + <tag><c>{ok, loaded}</c></tag> <item> - <p>The driver was actually loaded and is immediately - usable.</p> + <p>The driver was loaded and is immediately usable.</p> </item> - <tag><em>{ok, already_loaded}</em></tag> + <tag><c>{ok, already_loaded}</c></tag> <item> <p>The driver was already loaded by another process - and/or is in use by a living port. The load by you is + or is in use by a living port, or both. The load by you is registered and a corresponding <c>try_unload</c> is expected sometime in the future.</p> </item> - <tag><em>{ok, pending_driver}</em>or <em>{ok, pending_driver, reference()}</em></tag> + <tag><c>{ok, pending_driver}</c>or <c>{ok, pending_driver, reference()}</c></tag> <item> <p>The load request is registered, but the loading is - delayed due to the fact that an earlier instance of the - driver is still waiting to get unloaded (there are open - ports using it). Still, unload is expected when you are - done with the driver. This return value will - <em>mostly</em> happen when the + delayed because an earlier instance of the + driver is still waiting to get unloaded (open + ports use it). Still, unload is expected when you are + done with the driver. This return value + <em>mostly</em> occurs when options <c>{reload,pending_driver}</c> or - <c>{reload,pending}</c> options are used, but - <em>can</em> happen when another <seealso marker="#users">user</seealso> is unloading a driver in - parallel and the <c>kill_ports</c> driver option is - set. In other words, this return value will always need - to be handled!</p> + <c>{reload,pending}</c> are used, but + <em>can</em> occur when another + <seealso marker="#users">user</seealso> is unloading a + driver in parallel and driver option <c>kill_ports</c> is set. + In other words, this return value always needs + to be handled.</p> </item> - <tag><em>{ok, pending_process}</em>or <em>{ok, pending_process, reference()}</em></tag> + <tag><c>{ok, pending_process}</c>or <c>{ok, pending_process, reference()}</c></tag> <item> <p>The load request is registered, but the loading is - delayed due to the fact that an earlier instance of the + delayed because an earlier instance of the driver is still waiting to get unloaded by another <seealso marker="#users">user</seealso> (not only by a port, in which case <c>{ok,pending_driver}</c> would have been returned). Still, unload is expected when you - are done with the driver. This return value will - <em>only</em> happen when the <c>{reload,pending}</c> - option is used.</p> + are done with the driver. This return value + <em>only</em> occurs when option <c>{reload,pending}</c> + is used.</p> </item> </taglist> <p>When the function returns <c>{ok, pending_driver}</c> or - <c>{ok, pending_process}</c>, one might want to get information - about when the driver is <em>actually</em> loaded. This can - be achieved by using the <c>{monitor, <anno>MonitorOption</anno>}</c> option.</p> - <p>When monitoring is requested, and a corresponding <c>{ok, pending_driver}</c> or <c>{ok, pending_process}</c> would be - returned, the function will instead return a tuple <c>{ok, <anno>PendingStatus</anno>, reference()}</c> and the process will, at a later - time when the driver actually gets loaded, get a monitor - message. The monitor message one can expect is described in - the <seealso marker="#monitor/2">monitor/2</seealso> - function description. </p> + <c>{ok, pending_process}</c>, one can get information + about when the driver is <em>actually</em> loaded by using + option <c>{monitor, <anno>MonitorOption</anno>}</c>.</p> + <p>When monitoring is requested, and a corresponding + <c>{ok, pending_driver}</c> or <c>{ok, pending_process}</c> would + be returned, the function instead returns a tuple + <c>{ok, <anno>PendingStatus</anno>, reference()}</c> + and the process then gets a monitor message later, when the + driver gets loaded. The monitor message to expect is described in + the function description of + <seealso marker="#monitor/2"><c>monitor/2</c></seealso>.</p> <note> - <p>Note that in case of loading, monitoring can - <em>not</em> only get triggered by using the <c>{reload, <anno>ReloadOption</anno>}</c> option, but also in special cases where - the load-error is transient, why <c>{monitor, pending_driver}</c> should be used under basically - <em>all</em> real world circumstances!</p> + <p>In case of loading, monitoring can <em>not</em> only get + triggered by using option <c>{reload, <anno>ReloadOption</anno>}</c>, + but also in special cases where the load error is transient. Thus, + <c>{monitor, pending_driver}</c> is to be used under basically + <em>all</em> real world circumstances.</p> </note> <p>The function accepts the following parameters:</p> <taglist> - <tag><em><c><anno>Path</anno></c></em></tag> + <tag><c><anno>Path</anno></c></tag> <item> - <p>The filesystem path to the directory where the driver - object file is situated. The filename of the object file + <p>The file system path to the directory where the driver + object file is located. The filename of the object file (minus extension) must correspond to the driver name - (used in the name parameter) and the driver must - identify itself with the very same name. The - <c><anno>Path</anno></c> might be provided as an <em>iolist()</em>, + (used in parameter <c><anno>Name</anno></c>) and the driver must + identify itself with the same name. + <c><anno>Path</anno></c> can be provided as an <em>iolist()</em>, meaning it can be a list of other <c>iolist()</c>s, characters - (eight bit integers) or binaries, all to be flattened + (8-bit integers), or binaries, all to be flattened into a sequence of characters.</p> <p>The (possibly flattened) <c><anno>Path</anno></c> parameter must be - consistent throughout the system, a driver should, by + consistent throughout the system. A driver is to, by all <seealso marker="#users">users</seealso>, be loaded - using the same <em>literal</em><c><anno>Path</anno></c>. The - exception is when <em>reloading</em> is requested, in - which case the <c><anno>Path</anno></c> may be specified - differently. Note that all <seealso marker="#users">users</seealso> trying to load the - driver at a later time will need to use the <em>new</em><c><anno>Path</anno></c> if the <c><anno>Path</anno></c> is changed using a - <c>reload</c> option. This is yet another reason + using the same <em>literal</em> <c><anno>Path</anno></c>. + The exception is when <em>reloading</em> is requested, + in which case <c><anno>Path</anno></c> can be specified + differently. Notice that all + <seealso marker="#users">users</seealso> trying to load the + driver later need to use the + <em>new</em> <c><anno>Path</anno></c> if <c><anno>Path</anno></c> + is changed using a <c>reload</c> option. This is yet another reason to have <em>only one loader</em> of a driver one wants to - upgrade in a running system! </p> + upgrade in a running system.</p> </item> - <tag><em><c><anno>Name</anno></c></em></tag> + <tag><c><anno>Name</anno></c></tag> <item> - <p>The name parameter is the name of the driver to be used - in subsequent calls to <seealso marker="erts:erlang#open_port/2">open_port</seealso>. The - name can be specified either as an <c>iolist()</c> or - as an <c>atom()</c>. The name given when loading is used - to find the actual object file (with the - help of the <c><anno>Path</anno></c> and the system implied - extension suffix, i.e. <c>.so</c>). The name by which - the driver identifies itself must also be consistent - with this <c><anno>Name</anno></c> parameter, much as a beam-file's - module name much correspond to its filename.</p> + <p>This parameter is the name of the driver + to be used in subsequent calls to function + <seealso marker="erts:erlang#open_port/2"><c>erlang:open_port</c></seealso> + in <c>ERTS</c>. + The name can be specified as an <c>iolist()</c> or + an <c>atom()</c>. The name specified when loading is used + to find the object file (with the help of <c><anno>Path</anno></c> + and the system-implied extension suffix, that is, <c>.so</c>). + The name by which the driver identifies itself must also be consistent + with this <c><anno>Name</anno></c> parameter, much as + the module name of a Beam file much corresponds to its filename.</p> </item> - <tag><em><c><anno>OptionList</anno></c></em></tag> + <tag><c><anno>OptionList</anno></c></tag> <item> - <p>A number of options can be specified to control the - loading operation. The options are given as a list of - two-tuples, the tuples having the following values and + <p>Some options can be specified to control the + loading operation. The options are specified as a list of + two-tuples. The tuples have the following values and meanings:</p> <taglist> - <tag><em>{driver_options, <c><anno>DriverOptionList</anno></c>}</em></tag> + <tag><c>{driver_options, <anno>DriverOptionList</anno>}</c></tag> <item> - <p>This option is to provide options that will change - its general behavior and will "stick" to the driver + <p>This is to provide options that changes + its general behavior and "sticks" to the driver throughout its lifespan.</p> - <p>The driver options for a given driver name need - always to be consistent, <em>even when the driver is reloaded</em>, meaning that they are as much a part - of the driver as the actual name.</p> - <p>Currently the only allowed driver option is + <p>The driver options for a specified driver name need + always to be consistent, <em>even when the driver is reloaded</em>, + meaning that they are as much a part of the driver as the name.</p> + <p>The only allowed driver option is <c>kill_ports</c>, which means that all ports opened - towards the driver are killed with the exit-reason + to the driver are killed with exit reason <c>driver_unloaded</c> when no process any longer has the driver loaded. This situation arises either - when the last <seealso marker="#users">user</seealso> calls <seealso marker="#try_unload/2">try_unload/2</seealso>, or - the last process having loaded the driver exits.</p> + when the last <seealso marker="#users">user</seealso> calls + <seealso marker="#try_unload/2"><c>try_unload/2</c></seealso>, or + when the last process having loaded the driver exits.</p> </item> - <tag><em>{monitor, <c><anno>MonitorOption</anno></c>}</em></tag> + <tag><c>{monitor, <anno>MonitorOption</anno>}</c></tag> <item> <p>A <c><anno>MonitorOption</anno></c> tells <c>try_load/3</c> to trigger a driver monitor under certain conditions. When the monitor is triggered, the - function will return a three-tuple <c>{ok, <anno>PendingStatus</anno>, reference()}</c>, where the <c>reference()</c> is - the monitor ref for the driver monitor.</p> - <p>Only one <c><anno>MonitorOption</anno></c> can be specified and - it is either the atom <c>pending</c>, which means - that a monitor should be created whenever a load - operation is delayed, and the atom - <c>pending_driver</c>, in which a monitor is - created whenever the operation is delayed due to - open ports towards an otherwise unused driver. The - <c>pending_driver</c> option is of little use, but - is present for completeness, it is very well defined - which reload-options might give rise to which - delays. It might, however, be a good idea to use the - same <c><anno>MonitorOption</anno></c> as the <c><anno>ReloadOption</anno></c> - if present.</p> - <p>If reloading is not requested, it might still be - useful to specify the <c>monitor</c> option, as - forced unloads (<c>kill_ports</c> driver option or - the <c>kill_ports</c> option to <seealso marker="#try_unload/2">try_unload/2</seealso>) will - trigger a transient state where driver loading + function returns a three-tuple + <c>{ok, <anno>PendingStatus</anno>, reference()}</c>, where + <c>reference()</c> is the monitor reference for the driver monitor.</p> + <p>Only one <c><anno>MonitorOption</anno></c> can be specified. + It is one of the following:</p> + <list type="bulleted"> + <item> + <p>The atom <c>pending</c>, which means + that a monitor is to be created whenever a load + operation is delayed,</p> + </item> + <item> + <p>The atom <c>pending_driver</c>, in which a monitor + is created whenever the operation is delayed because + of open ports to an otherwise unused driver.</p> + </item> + </list> + <p>Option <c>pending_driver</c> is of little use, but + is present for completeness, as it is well defined which + reload options that can give rise to which delays. + However, it can be a good idea to use the same + <c><anno>MonitorOption</anno></c> as the + <c><anno>ReloadOption</anno></c>, if present.</p> + <p>If reloading is not requested, it can still be + useful to specify option <c>monitor</c>, as + forced unloads (driver option <c>kill_ports</c> or + option <c>kill_ports</c> to + <seealso marker="#try_unload/2"><c>try_unload/2</c></seealso>) + trigger a transient state where driver loading cannot be performed until all closing ports are - actually closed. So, as <c>try_unload</c> can, in - almost all situations, return <c>{ok, pending_driver}</c>, one should always specify at least - <c>{monitor, pending_driver}</c> in production - code (see the monitor discussion above). </p> + closed. Thus, as <c>try_unload</c> can, in + almost all situations, return <c>{ok, pending_driver}</c>, + always specify at least <c>{monitor, pending_driver}</c> + in production code (see the monitor discussion earlier).</p> </item> - <tag><em>{reload, <c><anno>ReloadOption</anno></c>}</em></tag> + <tag><c>{reload, <anno>ReloadOption</anno>}</c></tag> <item> - <p>This option is used when one wants to + <p>This option is used to <em>reload</em> a driver from disk, most often in a code upgrade scenario. Having a <c>reload</c> option - also implies that the <c><anno>Path</anno></c> parameter need - <em>not</em> be consistent with earlier loads of + also implies that parameter <c><anno>Path</anno></c> does + <em>not</em> need to be consistent with earlier loads of the driver.</p> - <p>To reload a driver, the process needs to have previously - loaded the driver, i.e there has to be an active <seealso marker="#users">user</seealso> of the driver in the process. </p> - <p>The <c>reload</c> option can be either the atom - <c>pending</c>, in which reloading is requested for - any driver and will be effectuated when <em>all</em> - ports opened against the driver are closed. The - replacement of the driver will in this case take - place regardless of if there are still - pending <seealso marker="#users">users</seealso> - having the driver loaded! - The option also triggers port-killing (if the - <c>kill_ports</c> driver option is used) even though - there are pending users, making it usable for forced - driver replacement, but laying a lot of - responsibility on the driver <seealso marker="#users">users</seealso>. The pending option is - seldom used as one does not want other <seealso marker="#users">users</seealso> to have loaded the - driver when code change is underway. </p> - <p>The more useful option is <c>pending_driver</c>, - which means that reloading will be queued if the - driver is <em>not</em> loaded by any other <seealso marker="#users">users</seealso>, but the driver has - opened ports, in which case <c>{ok, pending_driver}</c> will be returned (a - <c>monitor</c> option is of course recommended).</p> - <p>If the driver is unloaded (not present in the - system), the error code - <c>not_loaded</c> will be returned. The - <c>reload</c> option is intended for when the user + <p>To reload a driver, the process must have loaded the driver + before, that is, there must be an active + <seealso marker="#users">user</seealso> of the driver + in the process.</p> + <p>The <c>reload</c> option can be either of the following:</p> + <taglist> + <tag><c>pending</c></tag> + <item> + <p>With the atom <c>pending</c>, reloading is requested + for any driver and is effectuated when <em>all</em> + ports opened to the driver are closed. The driver + replacement in this case takes + place regardless if there are still + pending <seealso marker="#users">users</seealso> + having the driver loaded.</p> + <p>The option also triggers port-killing (if driver + option <c>kill_ports</c> is used) although + there are pending users, making it usable for forced + driver replacement, but laying much + responsibility on the driver + <seealso marker="#users">users</seealso>. + The pending option is seldom used as one does not want other + <seealso marker="#users">users</seealso> to have loaded + the driver when code change is underway.</p></item> + <tag><c>pending_driver</c></tag> + <item> + <p>This option is more useful. Here, reloading is queued + if the driver is <em>not</em> loaded by any other + <seealso marker="#users">users</seealso>, but the + driver has opened ports, in which case + <c>{ok, pending_driver}</c> is returned + (a <c>monitor</c> option is recommended).</p></item> + </taglist> + <p>If the driver is unloaded (not present in the system), + error code <c>not_loaded</c> is returned. Option + <c>reload</c> is intended for when the user has already loaded the driver in advance.</p> </item> </taglist> </item> </taglist> - <p>The function might return numerous errors, of which some - only can be returned given a certain combination of options.</p> - <p>A number of errors are opaque and can only be interpreted by - passing them to the <seealso marker="#format_error/1">format_error/1</seealso> function, + <p>The function can return numerous errors, some + can only be returned given a certain combination of options.</p> + <p>Some errors are opaque and can only be interpreted by + passing them to function + <seealso marker="#format_error/1"><c>format_error/1</c></seealso>, but some can be interpreted directly:</p> <taglist> - <tag><em>{error,linked_in_driver}</em></tag> + <tag><c>{error,linked_in_driver}</c></tag> <item> - <p>The driver with the specified name is an erlang - statically linked in driver, which cannot be manipulated + <p>The driver with the specified name is an Erlang + statically linked-in driver, which cannot be manipulated with this API.</p> </item> - <tag><em>{error,inconsistent}</em></tag> + <tag><c>{error,inconsistent}</c></tag> <item> - <p>The driver has already been loaded with either other - <c><anno>DriverOptionList</anno></c> or a different <em>literal</em><c>Path</c> argument.</p> - <p>This can happen even if a <c>reload</c> option is given, - if the <c>DriverOptionList</c> differ from the current.</p> + <p>The driver is already loaded with other + <c><anno>DriverOptionList</anno></c> or a different + <em>literal</em> <c>Path</c> argument.</p> + <p>This can occur even if a <c>reload</c> option is specified, + if <c>DriverOptionList</c> differs from the current.</p> </item> - <tag><em>{error, permanent}</em></tag> + <tag><c>{error, permanent}</c></tag> <item> <p>The driver has requested itself to be permanent, making - it behave like an erlang linked in driver and it can no + it behave like an Erlang linked-in driver and can no longer be manipulated with this API.</p> </item> - <tag><em>{error, pending_process}</em></tag> + <tag><c>{error, pending_process}</c></tag> <item> - <p>The driver is loaded by other <seealso marker="#users">users</seealso> when the <c>{reload, pending_driver}</c> option was given.</p> + <p>The driver is loaded by other + <seealso marker="#users">users</seealso> when + option <c>{reload, pending_driver}</c> was specified.</p> </item> - <tag><em>{error, pending_reload}</em></tag> + <tag><c>{error, pending_reload}</c></tag> <item> - <p>Driver reload is already requested by another <seealso marker="#users">user</seealso> when the <c>{reload, <anno>ReloadOption</anno>}</c> option was given.</p> + <p>Driver reload is already requested by another + <seealso marker="#users">user</seealso> when option + <c>{reload, <anno>ReloadOption</anno>}</c> was specified.</p> </item> - <tag><em>{error, not_loaded_by_this_process}</em></tag> + <tag><c>{error, not_loaded_by_this_process}</c></tag> <item> - <p>Appears when the <c>reload</c> option is given. The - driver <c><anno>Name</anno></c> is present in the system, but there is no - <seealso marker="#users">user</seealso> of it in this + <p>Appears when option <c>reload</c> is specified. The + driver <c><anno>Name</anno></c> is present in the system, but there + is no <seealso marker="#users">user</seealso> of it in this process.</p> </item> - <tag><em>{error, not_loaded}</em></tag> + <tag><c>{error, not_loaded}</c></tag> <item> - <p>Appears when the <c>reload</c> option is given. The + <p>Appears when option <c>reload</c> is specified. The driver <c><anno>Name</anno></c> is not in the system. Only drivers loaded by this process can be reloaded.</p> </item> </taglist> - <p>All other error codes are to be translated by the <seealso marker="#format_error/1">format_error/1</seealso> - function. Note that calls to <c>format_error</c> should be - performed from the same running instance of the erlang - virtual machine as the error was detected in, due to system - dependent behavior concerning error values.</p> - <p>If the arguments or options are malformed, the function will - throw a <c>badarg</c> exception.</p> + <p>All other error codes are to be translated by function + <seealso marker="#format_error/1"><c>format_error/1</c></seealso>. + Notice that calls to <c>format_error</c> are to be + performed from the same running instance of the Erlang + virtual machine as the error is detected in, because of + system-dependent behavior concerning error values.</p> + <p>If the arguments or options are malformed, the function + throws a <c>badarg</c> exception.</p> </desc> </func> <func> <name name="try_unload" arity="2"/> - <fsummary>Unload a driver</fsummary> + <fsummary>Unload a driver.</fsummary> <desc> - <p>This is the low level function to unload (or decrement + <p>This is the low-level function to unload (or decrement reference counts of) a driver. It can be used to force port killing, in much the same way as the driver option - <c>kill_ports</c> implicitly does, and it can trigger a - monitor either due to other <seealso marker="#users">users</seealso> still having the driver - loaded or that there are open ports using the driver.</p> + <c>kill_ports</c> implicitly does. Also, it can trigger a + monitor either because other + <seealso marker="#users">users</seealso> still have the driver + loaded or because open ports use the driver.</p> <p>Unloading can be described as the process of telling the emulator that this particular part of the code in this - particular process (i.e. this <seealso marker="#users">user</seealso>) no longer needs the - driver. That can, if there are no other users, trigger - actual unloading of the driver, in which case the driver - name disappears from the system and (if possible) the memory - occupied by the driver executable code is reclaimed. If the - driver has the <c>kill_ports</c> option set, or if - <c>kill_ports</c> was specified as an option to this - function, all pending ports using this driver will get - killed when unloading is done by the last <seealso marker="#users">user</seealso>. If no port-killing is - involved and there are open ports, the actual unloading - is delayed until there are no more open ports using the - driver. If, in this case, another <seealso marker="#users">user</seealso> (or even this user) loads the - driver again before the driver is actually unloaded, the - unloading will never take place.</p> - <p>To allow the <seealso marker="#users">user</seealso> that - <em>requests unloading</em> to wait for <em>actual unloading</em> to - take place, <c>monitor</c> triggers can be specified in much - the same way as when loading. As <seealso marker="#users">users</seealso> of this function however - seldom are interested in more than decrementing the - reference counts, monitoring is more seldom needed. If the - <c>kill_ports</c> option is used however, monitor trigging is - crucial, as the ports are not guaranteed to have been killed - until the driver is unloaded, why a monitor should be - triggered for at least the <c>pending_driver</c> case.</p> - <p>The possible monitor messages that can be expected are the - same as when using the <c>unloaded</c> option to the - <seealso marker="#monitor/2">monitor/2</seealso> function.</p> - <p>The function will return one of the following statuses upon + particular process (that is, this + <seealso marker="#users">user</seealso>) no longer needs + the driver. That can, if there are no other users, trigger + unloading of the driver, in which case the driver name + disappears from the system and (if possible) the memory + occupied by the driver executable code is reclaimed.</p> + <p>If the driver has option <c>kill_ports</c> set, or if + <c>kill_ports</c> is specified as an option to this + function, all pending ports using this driver are + killed when unloading is done by the last + <seealso marker="#users">user</seealso>. If no port-killing + is involved and there are open ports, the unloading + is delayed until no more open ports use the + driver. If, in this case, another + <seealso marker="#users">user</seealso> (or even this user) + loads the driver again before the driver is unloaded, the + unloading never takes place.</p> + <p>To allow the <seealso marker="#users">user</seealso> to + <em>request unloading</em> to wait for <em>actual unloading</em>, + <c>monitor</c> triggers can be specified in much the same way as + when loading. However, as <seealso marker="#users">users</seealso> + of this function seldom are interested in more than decrementing the + reference counts, monitoring is seldom needed.</p> + <note><p> If option <c>kill_ports</c> is used, monitor trigging is crucial, + as the ports are not guaranteed to be killed until the driver is unloaded. + Thus, a monitor must be triggered for at least the <c>pending_driver</c> + case.</p></note> + <p>The possible monitor messages to expect are the + same as when using option <c>unloaded</c> to function + <seealso marker="#monitor/2"><c>monitor/2</c></seealso>.</p> + <p>The function returns one of the following statuses upon success:</p> <taglist> - <tag><em>{ok, unloaded}</em></tag> + <tag><c>{ok, unloaded}</c></tag> <item> <p>The driver was immediately unloaded, meaning that the driver name is now free to use by other drivers and, if the underlying OS permits it, the memory occupied by the driver object code is now reclaimed.</p> <p>The driver can only be unloaded when there are no open - ports using it and there are no more <seealso marker="#users">users</seealso> requiring it to be + ports using it and no more + <seealso marker="#users">users</seealso> require it to be loaded.</p> </item> - <tag><em>{ok, pending_driver}</em>or <em>{ok, pending_driver, reference()}</em></tag> + <tag><c>{ok, pending_driver}</c>or + <c>{ok, pending_driver, reference()}</c></tag> <item> - <p>This return value indicates that this call removed the - last <seealso marker="#users">user</seealso> from the + <p>Indicates that this call removed the last + <seealso marker="#users">user</seealso> from the driver, but there are still open ports using it. - When all ports are closed and no new <seealso marker="#users">users</seealso> have arrived, the driver - will actually be reloaded and the name and memory + When all ports are closed and no new + <seealso marker="#users">users</seealso> have arrived, + the driver is reloaded and the name and memory reclaimed.</p> - <p>This return value is valid even when the option - <c>kill_ports</c> was used, as killing ports may not be - a process that completes immediately. The condition is, - in that case, however transient. Monitors are as always - useful to detect when the driver is really unloaded.</p> + <p>This return value is valid even if option <c>kill_ports</c> + was used, as killing ports can be a process that does not + complete immediately. However, the condition is in that case + transient. Monitors are always useful to detect when the driver + is really unloaded.</p> </item> - <tag><em>{ok, pending_process}</em>or <em>{ok, pending_process, reference()}</em></tag> + <tag><c>{ok, pending_process}</c>or + <c>{ok, pending_process, reference()}</c></tag> <item> - <p>The unload request is registered, but there are still - other <seealso marker="#users">users</seealso> holding - the driver. Note that the term <c>pending_process</c> - might refer to the running process, there might be more + <p>The unload request is registered, but + other <seealso marker="#users">users</seealso> still hold + the driver. Notice that the term <c>pending_process</c> + can refer to the running process; there can be more than one <seealso marker="#users">user</seealso> in the same process.</p> - <p>This is a normal, healthy return value if the call was + <p>This is a normal, healthy, return value if the call was just placed to inform the emulator that you have no - further use of the driver. It is actually the most - common return value in the most common <seealso marker="#scenarios">scenario</seealso> + further use of the driver. It is the most + common return value in the most common + <seealso marker="#scenarios"><c>scenario</c></seealso> described in the introduction.</p> </item> </taglist> <p>The function accepts the following parameters:</p> <taglist> - <tag><em><c><anno>Name</anno></c></em></tag> + <tag><c><anno>Name</anno></c></tag> <item> - <p>The name parameter is the name of the driver to be - unloaded. The name can be specified either as an - <c>iolist()</c> or as an <c>atom()</c>. </p> + <p><c><anno>Name</anno></c> is the name of the + driver to be unloaded. The name can be specified as an + <c>iolist()</c> or as an <c>atom()</c>.</p> </item> - <tag><em><c><anno>OptionList</anno></c></em></tag> + <tag><c><anno>OptionList</anno></c></tag> <item> - <p>The <c><anno>OptionList</anno></c> argument can be used to specify - certain behavior regarding ports as well as triggering + <p>Argument <c><anno>OptionList</anno></c> can be used to specify + certain behavior regarding ports and triggering monitors under certain conditions:</p> <taglist> - <tag><em>kill_ports</em></tag> + <tag><c>kill_ports</c></tag> <item> - <p>Force killing of all ports opened using this driver, - with the exit reason <c>driver_unloaded</c>, if you are - the <em>last</em><seealso marker="#users">user</seealso> of the driver.</p> - <p>If there are other <seealso marker="#users">users</seealso> having the driver - loaded, this option will have no effect.</p> - <p>If one wants the consistent behavior of killing ports + <p>Forces killing of all ports opened using this driver, + with exit reason <c>driver_unloaded</c>, if you are + the <em>last</em> <seealso marker="#users">user</seealso> + of the driver.</p> + <p>If other <seealso marker="#users">users</seealso> + have the driver loaded, this option has no effect.</p> + <p>To get the consistent behavior of killing ports when the last <seealso marker="#users">user</seealso> - unloads, one should use the driver option + unloads, use driver option <c>kill_ports</c> when loading the driver instead.</p> </item> - <tag><em>{monitor, <c><anno>MonitorOption</anno></c>}</em></tag> + <tag><c>{monitor, <anno>MonitorOption</anno>}</c></tag> <item> - <p>This option creates a driver monitor if the condition - given in <c><anno>MonitorOption</anno></c> is true. The valid + <p>Creates a driver monitor if the condition + specified in <c><anno>MonitorOption</anno></c> is true. The valid options are:</p> <taglist> - <tag><em>pending_driver</em></tag> + <tag><c>pending_driver</c></tag> <item> - <p>Create a driver monitor if the return value is to + <p>Creates a driver monitor if the return value is to be <c>{ok, pending_driver}</c>.</p> </item> - <tag><em>pending</em></tag> + <tag><c>pending</c></tag> <item> - <p>Create a monitor if the return value will be either + <p>Creates a monitor if the return value is <c>{ok, pending_driver}</c> or <c>{ok, pending_process}</c>.</p> </item> </taglist> - <p>The <c>pending_driver</c> <c><anno>MonitorOption</anno></c> is by far - the most useful and it has to be used to ensure that the - driver has really been unloaded and the ports closed - whenever the <c>kill_ports</c> option is used or the - driver may have been loaded with the <c>kill_ports</c> - driver option.</p> - <p>By using the monitor-triggers in the call to - <c>try_unload</c> one can be sure that the monitor is - actually added before the unloading is executed, meaning - that the monitor will always get properly triggered, - which would not be the case if one called - <c>erl_ddll:monitor/2</c> separately.</p> + <p>The <c>pending_driver</c> <c><anno>MonitorOption</anno></c> is + by far the most useful. It must be used to ensure that the + driver really is unloaded and the ports closed + whenever option <c>kill_ports</c> is used, or the + driver can have been loaded with driver option + <c>kill_ports</c>.</p> + <p>Using the monitor triggers in the call to + <c>try_unload</c> ensures that the monitor is + added before the unloading is executed, meaning + that the monitor is always properly triggered, + which is not the case if <c>monitor/2</c> is called + separately.</p> </item> </taglist> </item> </taglist> - <p>The function may return several error conditions, of which - all are well specified (no opaque values):</p> + <p>The function can return the following error conditions, + all well specified (no opaque values):</p> <taglist> - <tag><em>{error, linked_in_driver}</em></tag> + <tag><c>{error, linked_in_driver}</c></tag> <item> - <p>You were trying to unload an erlang statically linked in + <p>You were trying to unload an Erlang statically linked-in driver, which cannot be manipulated with this interface (and cannot be unloaded at all).</p> </item> - <tag><em>{error, not_loaded}</em></tag> + <tag><c>{error, not_loaded}</c></tag> <item> <p>The driver <c><anno>Name</anno></c> is not present in the system.</p> </item> - <tag><em>{error, not_loaded_by_this_process}</em></tag> + <tag><c>{error, not_loaded_by_this_process}</c></tag> <item> <p>The driver <c><anno>Name</anno></c> is present in the system, but there is no <seealso marker="#users">user</seealso> of it in this process. </p> <p>As a special case, drivers can be unloaded from - processes that has done no corresponding call to - <c>try_load/3</c> if, and only if, there are <em>no users of the driver at all</em>, which may happen if the + processes that have done no corresponding call to + <c>try_load/3</c> if, and only if, there are + <em>no users of the driver at all</em>, which can occur if the process containing the last user dies.</p> </item> - <tag><em>{error, permanent}</em></tag> + <tag><c>{error, permanent}</c></tag> <item> <p>The driver has made itself permanent, in which case it can no longer be manipulated by this interface (much - like a statically linked in driver).</p> + like a statically linked-in driver).</p> </item> </taglist> <p>The function throws a <c>badarg</c> exception if the - parameters are not given as described above. </p> + parameters are not specified as described here.</p> </desc> </func> <func> <name name="unload" arity="1"/> - <fsummary>Unload a driver</fsummary> + <fsummary>Unload a driver.</fsummary> <desc> <p>Unloads, or at least dereferences the driver named - <c><anno>Name</anno></c>. If the caller is the last <seealso marker="#users">user</seealso> of the driver, and there - are no more open ports using the driver, the driver will - actually get unloaded. In all other cases, actual unloading - will be delayed until all ports are closed and there are no - remaining <seealso marker="#users">users</seealso>.</p> - <p>If there are other <seealso marker="#users">users</seealso> of the driver, the reference - counts of the driver is merely decreased, so that the caller - is no longer considered a user of the driver. For usage - scenarios, see the <seealso marker="#scenarios">description</seealso> in the beginning - of this document. </p> + <c><anno>Name</anno></c>. If the caller is the last + <seealso marker="#users">user</seealso> of the driver, + and no more open ports use the driver, the driver + gets unloaded. Otherwise, unloading + is delayed until all ports are closed and no + <seealso marker="#users">users</seealso> remain.</p> + <p>If there are other <seealso marker="#users">users</seealso> + of the driver, the reference counts of the driver is merely decreased, + so that the caller is no longer considered a + <seealso marker="#users">user</seealso> of the driver. For use + scenarios, see the <seealso marker="#scenarios"><c>description</c></seealso> + in the beginning of this module.</p> <p>The <c><anno>ErrorDesc</anno></c> returned is an opaque value to be - passed further on to the <seealso marker="#format_error/1">format_error/1</seealso> - function. For more control over the operation, use the - <seealso marker="#try_unload/2">try_unload/2</seealso> + passed further on to function + <seealso marker="#format_error/1"><c>format_error/1</c></seealso>. + For more control over the operation, use the + <seealso marker="#try_unload/2"><c>try_unload/2</c></seealso> interface.</p> <p>The function throws a <c>badarg</c> exception if the - parameters are not given as described above. </p> + parameters are not specified as described here.</p> </desc> </func> <func> <name name="unload_driver" arity="1"/> - <fsummary>Unload a driver</fsummary> + <fsummary>Unload a driver.</fsummary> <desc> <p>Unloads, or at least dereferences the driver named - <c><anno>Name</anno></c>. If the caller is the last <seealso marker="#users">user</seealso> of the driver, all - remaining open ports using the driver will get killed with - the reason <c>driver_unloaded</c> and the driver will - eventually get unloaded.</p> + <c><anno>Name</anno></c>. If the caller is the last + <seealso marker="#users">user</seealso> of the driver, all + remaining open ports using the driver are killed with + reason <c>driver_unloaded</c> and the driver + eventually gets unloaded.</p> <p>If there are other <seealso marker="#users">users</seealso> of the driver, the reference counts of the driver is merely decreased, so that the caller is no longer considered a <seealso marker="#users">user</seealso>. For - usage scenarios, see the <seealso marker="#scenarios">description</seealso> in the beginning - of this document.</p> + use scenarios, see the + <seealso marker="#scenarios"><c>description</c></seealso> in the + beginning of this module.</p> <p>The <c><anno>ErrorDesc</anno></c> returned is an opaque value to be - passed further on to the <seealso marker="#format_error/1">format_error/1</seealso> - function. For more control over the operation, use the - <seealso marker="#try_unload/2">try_unload/2</seealso> + passed further on to function + <seealso marker="#format_error/1"><c>format_error/1</c></seealso>. + For more control over the operation, use the + <seealso marker="#try_unload/2"><c>try_unload/2</c></seealso> interface.</p> <p>The function throws a <c>badarg</c> exception if the - parameters are not given as described above. </p> - </desc> - </func> - <func> - <name name="loaded_drivers" arity="0"/> - <fsummary>List loaded drivers</fsummary> - <desc> - <p>Returns a list of all the available drivers, both - (statically) linked-in and dynamically loaded ones.</p> - <p>The driver names are returned as a list of strings rather - than a list of atoms for historical reasons.</p> - <p>More information about drivers can be obtained using one of - the <seealso marker="#info/0">info</seealso> functions.</p> - </desc> - </func> - <func> - <name name="format_error" arity="1"/> - <fsummary>Format an error descriptor</fsummary> - <desc> - <p>Takes an <c><anno>ErrorDesc</anno></c> returned by load, unload or - reload functions and returns a string which - describes the error or warning.</p> - <note> - <p>Due to peculiarities in the dynamic loading interfaces on - different platform, the returned string is only guaranteed - to describe the correct error <em>if format_error/1 is called in the same instance of the erlang virtual machine as the error appeared in</em> (meaning the same operating - system process)!</p> - </note> + parameters are not specified as described here.</p> </desc> </func> </funcs> - <section> - <title>SEE ALSO</title> - <p>erl_driver(4), driver_entry(4)</p> + <title>See Also</title> + <p><seealso marker="erts:erl_driver"><c>erts:erl_driver(4)</c></seealso>, + <seealso marker="erts:driver_entry"><c>erts:driver_entry(4)</c></seealso></p> </section> </erlref> - diff --git a/lib/kernel/doc/src/erl_prim_loader_stub.xml b/lib/kernel/doc/src/erl_prim_loader_stub.xml index f1b2827b37..f3189e2a66 100644 --- a/lib/kernel/doc/src/erl_prim_loader_stub.xml +++ b/lib/kernel/doc/src/erl_prim_loader_stub.xml @@ -37,7 +37,7 @@ The module erl_prim_loader is moved to the runtime system application. Please see <seealso marker="erts:erl_prim_loader">erl_prim_loader(3)</seealso> in the - erts reference manual instead. + ERTS reference manual instead. </p></description> </erlref> diff --git a/lib/kernel/doc/src/erlang_stub.xml b/lib/kernel/doc/src/erlang_stub.xml index 7e8899fdee..afd353e438 100644 --- a/lib/kernel/doc/src/erlang_stub.xml +++ b/lib/kernel/doc/src/erlang_stub.xml @@ -37,7 +37,7 @@ The module erlang is moved to the runtime system application. Please see <seealso marker="erts:erlang">erlang(3)</seealso> in the - erts reference manual instead. + ERTS reference manual instead. </p></description> </erlref> diff --git a/lib/kernel/doc/src/error_handler.xml b/lib/kernel/doc/src/error_handler.xml index 51e283b74f..e5639487dc 100644 --- a/lib/kernel/doc/src/error_handler.xml +++ b/lib/kernel/doc/src/error_handler.xml @@ -31,35 +31,48 @@ <rev></rev> </header> <module>error_handler</module> - <modulesummary>Default System Error Handler</modulesummary> + <modulesummary>Default system error handler.</modulesummary> <description> - <p>The error handler module defines what happens when certain types + <p>This module defines what happens when certain types of errors occur.</p> </description> <funcs> <func> + <name name="raise_undef_exception" arity="3"/> + <fsummary>Raise an undef exception.</fsummary> + <type_desc variable="Args"> + A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> + </type_desc> + <desc> + <p>Raises an <c>undef</c> exception with a stacktrace, indicating + that <c><anno>Module</anno>:<anno>Function</anno>/N</c> is + undefined. + </p> + </desc> + </func> + <func> <name name="undefined_function" arity="3"/> - <fsummary>Called when an undefined function is encountered</fsummary> + <fsummary>Called when an undefined function is encountered.</fsummary> <type_desc variable="Args"> A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> </type_desc> <desc> - <p>This function is called by the run-time system if a call is made to + <p>This function is called by the runtime system if a call is made to <c><anno>Module</anno>:<anno>Function</anno>(Arg1,.., ArgN)</c> and - <c><anno>Module</anno>:<anno>Function</anno>/N</c> is undefined. Note that - <c>undefined_function/3</c> is evaluated inside the process + <c><anno>Module</anno>:<anno>Function</anno>/N</c> is undefined. + Notice that this function is evaluated inside the process making the original call.</p> - <p>This function will first attempt to autoload + <p>This function first attempts to autoload <c><anno>Module</anno></c>. If that is not possible, - an <c>undef</c> exception will be raised.</p> + an <c>undef</c> exception is raised.</p> - <p>If it was possible to load <c><anno>Module</anno></c> - and the function <c><anno>Function</anno>/N</c> is exported, - it will be called.</p> + <p>If it is possible to load <c><anno>Module</anno></c> + and function <c><anno>Function</anno>/N</c> is exported, + it is called.</p> - <p>Otherwise, if the function <c>'$handle_undefined_function'/2</c> - is exported, it will be called as + <p>Otherwise, if function <c>'$handle_undefined_function'/2</c> + is exported, it is called as <c>'$handle_undefined_function'(</c><anno>Function</anno>, <anno>Args</anno>). </p> @@ -67,61 +80,48 @@ <p>Defining <c>'$handle_undefined_function'/2</c> in ordinary application code is highly discouraged. It is very easy to make subtle errors that can take a long time to - debug. Furthermore, none of the tools for static code + debug. Furthermore, none of the tools for static code analysis (such as Dialyzer and Xref) supports the use of <c>'$handle_undefined_function'/2</c> and no such support will be added. Only use this function after having carefully - considered other, less dangerous, solutions. One example of + considered other, less dangerous, solutions. One example of potential legitimate use is creating stubs for other sub-systems during testing and debugging. </p> </warning> - <p>Otherwise an <c>undef</c> exception will be raised.</p> - </desc> - </func> - <func> - <name name="raise_undef_exception" arity="3"/> - <fsummary>Raise an undef exception</fsummary> - <type_desc variable="Args"> - A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> - </type_desc> - <desc> - <p>Raise an <c>undef</c> exception with a stacktrace indicating - that <c><anno>Module</anno>:<anno>Function</anno>/N</c> is - undefined. - </p> + <p>Otherwise an <c>undef</c> exception is raised.</p> </desc> </func> <func> <name name="undefined_lambda" arity="3"/> - <fsummary>Called when an undefined lambda (fun) is encountered</fsummary> + <fsummary>Called when an undefined lambda (fun) is encountered.</fsummary> <type_desc variable="Args"> A (possibly empty) list of arguments <c>Arg1,..,ArgN</c> </type_desc> <desc> <p>This function is evaluated if a call is made to - <c><anno>Fun</anno>(Arg1,.., ArgN)</c> when the module defining the fun is - not loaded. The function is evaluated inside the process + <c><anno>Fun</anno>(Arg1,.., ArgN)</c> when the module defining + the fun is not loaded. The function is evaluated inside the process making the original call.</p> <p>If <c><anno>Module</anno></c> is interpreted, the interpreter is invoked and the return value of the interpreted <c><anno>Fun</anno>(Arg1,.., ArgN)</c> call is returned.</p> <p>Otherwise, it returns, if possible, the value of - <c>apply(<anno>Fun</anno>, <anno>Args</anno>)</c> after an attempt has been made to - autoload <c><anno>Module</anno></c>. If this is not possible, the call - fails with exit reason <c>undef</c>.</p> + <c>apply(<anno>Fun</anno>, <anno>Args</anno>)</c> after an attempt + is made to autoload <c><anno>Module</anno></c>. If this is not possible, + the call fails with exit reason <c>undef</c>.</p> </desc> </func> </funcs> <section> <title>Notes</title> - <p>The code in <c>error_handler</c> is complex and should not be - changed without fully understanding the interaction between + <p>The code in <c>error_handler</c> is complex. Do not + change it without fully understanding the interaction between the error handler, the <c>init</c> process of the code server, and the I/O mechanism of the code.</p> - <p>Changes in the code which may seem small can cause a deadlock - as unforeseen consequences may occur. The use of <c>input</c> is + <p>Code changes that seem small can cause a deadlock, + as unforeseen consequences can occur. The use of <c>input</c> is dangerous in this type of code.</p> </section> </erlref> diff --git a/lib/kernel/doc/src/error_logger.xml b/lib/kernel/doc/src/error_logger.xml index 74830e9d14..a8273e59e2 100644 --- a/lib/kernel/doc/src/error_logger.xml +++ b/lib/kernel/doc/src/error_logger.xml @@ -29,41 +29,44 @@ <rev></rev> </header> <module>error_logger</module> - <modulesummary>Erlang Error Logger</modulesummary> + <modulesummary>Erlang error logger.</modulesummary> <description> <p>The Erlang <em>error logger</em> is an event manager (see <seealso marker="doc/design_principles:des_princ">OTP Design Principles</seealso> and - <seealso marker="stdlib:gen_event">gen_event(3)</seealso>), - registered as <c>error_logger</c>. Error, warning and info events + <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>), + registered as <c>error_logger</c>. Errors, warnings, and info events are sent to the error logger from the Erlang runtime system and the different Erlang/OTP applications. The events are, by default, - logged to tty. Note that an event from a process <c>P</c> is + logged to the terminal. Notice that an event from a process <c>P</c> is logged at the node of the group leader of <c>P</c>. This means that log output is directed to the node from which a process was created, which not necessarily is the same node as where it is executing.</p> - <p>Initially, <c>error_logger</c> only has a primitive event + <p>Initially, <c>error_logger</c> has only a primitive event handler, which buffers and prints the raw event messages. During - system startup, the application Kernel replaces this with a - <em>standard event handler</em>, by default one which writes - nicely formatted output to tty. Kernel can also be configured so - that events are logged to file instead, or not logged at all, see - <seealso marker="kernel_app">kernel(6)</seealso>.</p> - <p>Also the SASL application, if started, adds its own event - handler, which by default writes supervisor, crash and progress - reports to tty. See - <seealso marker="sasl:sasl_app">sasl(6)</seealso>.</p> - <p>It is recommended that user defined applications should report - errors through the error logger, in order to get uniform reports. - User defined event handlers can be added to handle application - specific events. (<c>add_report_handler/1,2</c>). Also, there is - a useful event handler in STDLIB for multi-file logging of events, - see <c>log_mf_h(3)</c>.</p> + system startup, the <c>Kernel</c> application replaces this with a + <em>standard event handler</em>, by default one that writes + nicely formatted output to the terminal. <c>Kernel</c> can also be + configured so that events are logged to a file instead, or not logged at all, + see <seealso marker="kernel_app"><c>kernel(6)</c></seealso>.</p> + <p>Also the <c>SASL</c> application, if started, adds its own event + handler, which by default writes supervisor, crash, and progress + reports to the terminal. See + <seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso>.</p> + <p>It is recommended that user-defined applications report + errors through the error logger to get uniform reports. + User-defined event handlers can be added to handle application-specific + events, see + <seealso marker="#add_report_handler/1"><c>add_report_handler/1,2</c></seealso>. + Also, a useful event handler is provided in <c>STDLIB</c> for multi-file + logging of events, see + <seealso marker="stdlib:log_mf_h"><c>stdlib:log_mf_h(3)</c></seealso>.</p> <p>Warning events were introduced in Erlang/OTP R9C and are enabled - by default as of 18.0. To retain backwards compatibility with existing - user defined event handlers, these may be tagged as errors or info - using the command line flag <c><![CDATA[+W <e | i | w>]]></c>, thus - showing up as error or info reports in the logs.</p> + by default as from Erlang/OTP 18.0. To retain backwards compatibility + with existing user-defined event handlers, the warning events can be + tagged as <c>errors</c> or <c>info</c> using command-line flag + <c><![CDATA[+W <e | i | w>]]></c>, thus showing up as + <c>ERROR REPORT</c> or <c>INFO REPORT</c> in the logs.</p> </description> <datatypes> <datatype> @@ -72,15 +75,44 @@ </datatypes> <funcs> <func> + <name name="add_report_handler" arity="1"/> + <name name="add_report_handler" arity="2"/> + <fsummary>Add an event handler to the error logger.</fsummary> + <desc> + <p>Adds a new event handler to the error logger. The event + handler must be implemented as a <c>gen_event</c> callback + module, see + <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p> + <p><c><anno>Handler</anno></c> is typically the name of the callback module + and <c><anno>Args</anno></c> is an optional term (defaults to []) passed + to the initialization callback function <c><anno>Handler</anno>:init/1</c>. + The function returns <c>ok</c> if successful.</p> + <p>The event handler must be able to handle the events in this module, see + section <seealso marker="#events">Events</seealso>.</p> + </desc> + </func> + <func> + <name name="delete_report_handler" arity="1"/> + <fsummary>Delete an event handler from the error logger.</fsummary> + <desc> + <p>Deletes an event handler from the error logger by calling + <c>gen_event:delete_handler(error_logger, <anno>Handler</anno>, [])</c>, + see <seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>.</p> + </desc> + </func> + <func> <name name="error_msg" arity="1"/> <name name="error_msg" arity="2"/> <name name="format" arity="2"/> - <fsummary>Send an standard error event to the error logger</fsummary> + <fsummary>Send a standard error event to the error logger.</fsummary> <desc> <p>Sends a standard error event to the error logger. - The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments are the same as - the arguments of <c>io:format/2</c>. The event is handled by - the standard event handler.</p> + The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments + are the same as the arguments of + <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso> + in <c>STDLIB</c>. + The event is handled by the standard event handler.</p> + <p><em>Example:</em></p> <pre> 1> <input>error_logger:error_msg("An error occurred in ~p~n", [a_module]).</input> @@ -90,16 +122,19 @@ ok</pre> <warning> <p>If called with bad arguments, this function can crash the standard event handler, meaning no further events are - logged. When in doubt, use <c>error_report/1</c> instead.</p> + logged. When in doubt, use + <seealso marker="#error_report/1"><c>error_report/1</c></seealso> + instead.</p> </warning> </desc> </func> <func> <name name="error_report" arity="1"/> - <fsummary>Send a standard error report event to the error logger</fsummary> + <fsummary>Send a standard error report event to the error logger.</fsummary> <desc> <p>Sends a standard error report event to the error logger. The event is handled by the standard event handler.</p> + <p><em>Example:</em></p> <pre> 2> <input>error_logger:error_report([{tag1,data1},a_term,{tag2,data}]).</input> @@ -117,100 +152,27 @@ ok</pre> </func> <func> <name name="error_report" arity="2"/> - <fsummary>Send a user defined error report event to the error logger</fsummary> + <fsummary>Send a user-defined error report event to the error logger.</fsummary> <desc> - <p>Sends a user defined error report event to the error logger. + <p>Sends a user-defined error report event to the error logger. An event handler to handle the event is supposed to have been added. The event is ignored by the standard event handler.</p> <p>It is recommended that <c><anno>Report</anno></c> follows the same - structure as for <c>error_report/1</c>.</p> - </desc> - </func> - <func> - <name name="warning_map" arity="0"/> - <fsummary>Return the current mapping for warning events</fsummary> - <desc> - <p>Returns the current mapping for warning events. Events sent - using <c>warning_msg/1,2</c> or <c>warning_report/1,2</c> - are tagged as errors, warnings (default) or info, depending - on the value of the command line flag <c>+W</c>.</p> - <pre> -os$ <input>erl</input> -Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll] - -Eshell V5.4.8 (abort with ^G) -1> <input>error_logger:warning_map().</input> -warning -2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [warning]).</input> - -=WARNING REPORT==== 11-Aug-2005::15:31:55 === -Warnings tagged as: warning -ok -3> -User switch command - --> q -os$ <input>erl +W e</input> -Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll] - -Eshell V5.4.8 (abort with ^G) -1> <input>error_logger:warning_map().</input> -error -2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [error]).</input> - -=ERROR REPORT==== 11-Aug-2005::15:31:23 === -Warnings tagged as: error -ok</pre> - </desc> - </func> - <func> - <name name="warning_msg" arity="1"/> - <name name="warning_msg" arity="2"/> - <fsummary>Send a standard warning event to the error logger</fsummary> - <desc> - <p>Sends a standard warning event to the error logger. - The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments are the same as - the arguments of <c>io:format/2</c>. The event is handled by - the standard event handler. It is tagged either as an error, - warning or info, see - <seealso marker="#warning_map/0">warning_map/0</seealso>.</p> - <warning> - <p>If called with bad arguments, this function can crash - the standard event handler, meaning no further events are - logged. When in doubt, use <c>warning_report/1</c> instead.</p> - </warning> - </desc> - </func> - <func> - <name name="warning_report" arity="1"/> - <fsummary>Send a standard warning report event to the error logger</fsummary> - <desc> - <p>Sends a standard warning report event to the error logger. - The event is handled by the standard event handler. It is - tagged either as an error, warning or info, see - <seealso marker="#warning_map/0">warning_map/0</seealso>.</p> - </desc> - </func> - <func> - <name name="warning_report" arity="2"/> - <fsummary>Send a user defined warning report event to the error logger</fsummary> - <desc> - <p>Sends a user defined warning report event to the error - logger. An event handler to handle the event is supposed to - have been added. The event is ignored by the standard event - handler. It is tagged either as an error, warning or info, - depending on the value of - <seealso marker="#warning_map/0">warning_map/0</seealso>.</p> + structure as for + <seealso marker="#error_report/1"><c>error_report/1</c></seealso>.</p> </desc> </func> <func> <name name="info_msg" arity="1"/> <name name="info_msg" arity="2"/> - <fsummary>Send a standard information event to the error logger</fsummary> + <fsummary>Send a standard information event to the error logger.</fsummary> <desc> <p>Sends a standard information event to the error logger. - The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments are the same as - the arguments of <c>io:format/2</c>. The event is handled by - the standard event handler.</p> + The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments + are the same as the arguments of + <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso> + in <c>STDLIB</c>. The event is handled by the standard event handler.</p> + <p><em>Example:</em></p> <pre> 1> <input>error_logger:info_msg("Something happened in ~p~n", [a_module]).</input> @@ -226,10 +188,11 @@ ok</pre> </func> <func> <name name="info_report" arity="1"/> - <fsummary>Send a standard information report event to the error logger</fsummary> + <fsummary>Send a standard information report event to the error logger.</fsummary> <desc> <p>Sends a standard information report event to the error logger. The event is handled by the standard event handler.</p> + <p><em>Example:</em></p> <pre> 2> <input>error_logger:info_report([{tag1,data1},a_term,{tag2,data}]).</input> @@ -247,59 +210,22 @@ ok</pre> </func> <func> <name name="info_report" arity="2"/> - <fsummary>Send a user defined information report event to the error logger</fsummary> + <fsummary>Send a user-defined information report event to the error logger.</fsummary> <desc> - <p>Sends a user defined information report event to the error + <p>Sends a user-defined information report event to the error logger. An event handler to handle the event is supposed to have been added. The event is ignored by the standard event handler.</p> <p>It is recommended that <c><anno>Report</anno></c> follows the same - structure as for <c>info_report/1</c>.</p> - </desc> - </func> - <func> - <name name="add_report_handler" arity="1"/> - <name name="add_report_handler" arity="2"/> - <fsummary>Add an event handler to the error logger</fsummary> - <desc> - <p>Adds a new event handler to the error logger. The event - handler must be implemented as a <c>gen_event</c> callback - module, see - <seealso marker="stdlib:gen_event">gen_event(3)</seealso>.</p> - <p><c><anno>Handler</anno></c> is typically the name of the callback module - and <c><anno>Args</anno></c> is an optional term (defaults to []) passed - to the initialization callback function <c><anno>Handler</anno>:init/1</c>. - The function returns <c>ok</c> if successful.</p> - <p>The event handler must be able to handle the - <seealso marker="#events">events</seealso> described below.</p> - </desc> - </func> - <func> - <name name="delete_report_handler" arity="1"/> - <fsummary>Delete an event handler from the error logger</fsummary> - <desc> - <p>Deletes an event handler from the error logger by calling - <c>gen_event:delete_handler(error_logger, <anno>Handler</anno>, [])</c>, - see <seealso marker="stdlib:gen_event">gen_event(3)</seealso>.</p> - </desc> - </func> - <func> - <name name="tty" arity="1"/> - <fsummary>Enable or disable printouts to the tty</fsummary> - <desc> - <p>Enables (<c><anno>Flag</anno> == true</c>) or disables - (<c><anno>Flag</anno> == false</c>) printout of standard events to the tty.</p> - <p>This is done by adding or deleting the standard event handler - for output to tty, thus calling this function overrides - the value of the Kernel <c>error_logger</c> configuration - parameter.</p> + structure as for + <seealso marker="#info_report/1"><c>info_report/1</c></seealso>.</p> </desc> </func> <func> <name name="logfile" arity="1" clause_i="1"/> <name name="logfile" arity="1" clause_i="2"/> <name name="logfile" arity="1" clause_i="3"/> - <fsummary>Enable or disable error printouts to a file</fsummary> + <fsummary>Enable or disable error printouts to a file.</fsummary> <type variable="Filename"/> <type variable="OpenReason" name_i="1"/> <type variable="CloseReason" name_i="2"/> @@ -308,22 +234,22 @@ ok</pre> <desc> <p>Enables or disables printout of standard events to a file.</p> <p>This is done by adding or deleting the standard event handler - for output to file, thus calling this function overrides - the value of the Kernel <c>error_logger</c> configuration + for output to file. Thus, calling this function overrides + the value of the <c>Kernel</c> <c>error_logger</c> configuration parameter.</p> - <p>Enabling file logging can be used in combination with calling - <c>tty(false)</c>, in order to have a silent system, where + <p>Enabling file logging can be used together with calling + <c>tty(false)</c>, to have a silent system where all standard events are logged to a file only. - There can only be one active log file at a time.</p> - <p><c>Request</c> is one of:</p> + Only one log file can be active at a time.</p> + <p><c>Request</c> is one of the following:</p> <taglist> <tag><c>{open, <anno>Filename</anno>}</c></tag> <item> - <p>Opens the log file <c><anno>Filename</anno></c>. Returns <c>ok</c> if + <p>Opens log file <c><anno>Filename</anno></c>. Returns <c>ok</c> if successful, or <c>{error, allready_have_logfile}</c> if logging to file is already enabled, or an error tuple if - another error occurred. For example, if <c><anno>Filename</anno></c> - could not be opened.</p> + another error occurred (for example, if <c><anno>Filename</anno></c> + cannot be opened).</p> </item> <tag><c>close</c></tag> <item> @@ -339,6 +265,97 @@ ok</pre> </taglist> </desc> </func> + <func> + <name name="tty" arity="1"/> + <fsummary>Enable or disable printouts to the terminal.</fsummary> + <desc> + <p>Enables (<c><anno>Flag</anno> == true</c>) or disables + (<c><anno>Flag</anno> == false</c>) printout of standard events + to the terminal.</p> + <p>This is done by adding or deleting the standard event handler + for output to the terminal. Thus, calling this function overrides + the value of the <c>Kernel</c> <c>error_logger</c> configuration parameter.</p> + </desc> + </func> + <func> + <name name="warning_map" arity="0"/> + <fsummary>Return the current mapping for warning events.</fsummary> + <desc> + <p>Returns the current mapping for warning events. Events sent + using <c>warning_msg/1,2</c> or <c>warning_report/1,2</c> + are tagged as errors, warnings (default), or info, depending + on the value of command-line flag <c>+W</c>.</p> + <p><em>Example:</em></p> + <pre> +os$ <input>erl</input> +Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll] + +Eshell V5.4.8 (abort with ^G) +1> <input>error_logger:warning_map().</input> +warning +2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [warning]).</input> + +=WARNING REPORT==== 11-Aug-2005::15:31:55 === +Warnings tagged as: warning +ok +3> +User switch command + --> q +os$ <input>erl +W e</input> +Erlang (BEAM) emulator version 5.4.8 [hipe] [threads:0] [kernel-poll] + +Eshell V5.4.8 (abort with ^G) +1> <input>error_logger:warning_map().</input> +error +2> <input>error_logger:warning_msg("Warnings tagged as: ~p~n", [error]).</input> + +=ERROR REPORT==== 11-Aug-2005::15:31:23 === +Warnings tagged as: error +ok</pre> + </desc> + </func> + <func> + <name name="warning_msg" arity="1"/> + <name name="warning_msg" arity="2"/> + <fsummary>Send a standard warning event to the error logger.</fsummary> + <desc> + <p>Sends a standard warning event to the error logger. + The <c><anno>Format</anno></c> and <c><anno>Data</anno></c> arguments + are the same as the arguments of + <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso> + in <c>STDLIB</c>. + The event is handled by the standard event handler. It is tagged + as an error, warning, or info, see + <seealso marker="#warning_map/0"><c>warning_map/0</c></seealso>.</p> + <warning> + <p>If called with bad arguments, this function can crash + the standard event handler, meaning no further events are + logged. When in doubt, use <c>warning_report/1</c> instead.</p> + </warning> + </desc> + </func> + <func> + <name name="warning_report" arity="1"/> + <fsummary>Send a standard warning report event to the error logger.</fsummary> + <desc> + <p>Sends a standard warning report event to the error logger. + The event is handled by the standard event handler. It is + tagged as an error, warning, or info, see + <seealso marker="#warning_map/0"><c>warning_map/0</c></seealso>.</p> + </desc> + </func> + <func> + <name name="warning_report" arity="2"/> + <fsummary>Send a user-defined warning report event to the error logger.</fsummary> + <desc> + <p>Sends a user-defined warning report event to the error + logger. An event handler to handle the event is supposed to + have been added. The event is ignored by the standard event + handler. It is tagged as an error, warning, or info, + depending on the value of + <seealso marker="#warning_map/0"><c>warning_map/0</c></seealso>.</p> + </desc> + </func> </funcs> <section> @@ -346,8 +363,8 @@ ok</pre> <title>Events</title> <p>All event handlers added to the error logger must handle the following events. <c>Gleader</c> is the group leader pid of - the process which sent the event, and <c>Pid</c> is the process - which sent the event.</p> + the process that sent the event, and <c>Pid</c> is the process + that sent the event.</p> <taglist> <tag><c>{error, Gleader, {Pid, Format, Data}}</c></tag> <item> @@ -364,18 +381,18 @@ ok</pre> </item> <tag><c>{warning_msg, Gleader, {Pid, Format, Data}}</c></tag> <item> - <p>Generated when <c>warning_msg/1,2</c> is called, provided - that warnings are set to be tagged as warnings.</p> + <p>Generated when <c>warning_msg/1,2</c> is called + if warnings are set to be tagged as warnings.</p> </item> <tag><c>{warning_report, Gleader, {Pid, std_warning, Report}}</c></tag> <item> - <p>Generated when <c>warning_report/1</c> is called, provided - that warnings are set to be tagged as warnings.</p> + <p>Generated when <c>warning_report/1</c> is called + if warnings are set to be tagged as warnings.</p> </item> <tag><c>{warning_report, Gleader, {Pid, Type, Report}}</c></tag> <item> - <p>Generated when <c>warning_report/2</c> is called, provided - that warnings are set to be tagged as warnings.</p> + <p>Generated when <c>warning_report/2</c> is called + if warnings are set to be tagged as warnings.</p> </item> <tag><c>{info_msg, Gleader, {Pid, Format, Data}}</c></tag> <item> @@ -390,17 +407,19 @@ ok</pre> <p>Generated when <c>info_report/2</c> is called.</p> </item> </taglist> - <p>Note that also a number of system internal events may be - received, a catch-all clause last in the definition of + <p>Notice that some system-internal events can also be + received. Therefore a catch-all clause last in the definition of the event handler callback function <c>Module:handle_event/2</c> - is necessary. This also holds true for - <c>Module:handle_info/2</c>, as there are a number of system - internal messages the event handler must take care of as well.</p> + is necessary. This also applies for + <c>Module:handle_info/2</c>, as the event handler must also take care of + some system-internal messages.</p> </section> - <section> - <title>SEE ALSO</title> - <p>gen_event(3), log_mf_h(3), kernel(6), sasl(6)</p> + <title>See Also</title> + <p><seealso marker="stdlib:gen_event"><c>stdlib:gen_event(3)</c></seealso>, + <seealso marker="stdlib:log_mf_h"><c>stdlib:log_mf_h(3)</c></seealso> + <seealso marker="kernel_app"><c>kernel(6)</c></seealso> + <seealso marker="sasl:sasl_app"><c>sasl(6)</c></seealso></p> </section> </erlref> diff --git a/lib/kernel/doc/src/file.xml b/lib/kernel/doc/src/file.xml index bc580dca79..a73038ac58 100644 --- a/lib/kernel/doc/src/file.xml +++ b/lib/kernel/doc/src/file.xml @@ -29,56 +29,57 @@ <rev></rev> </header> <module>file</module> - <modulesummary>File Interface Module</modulesummary> + <modulesummary>File interface module.</modulesummary> <description> - <p>The module <c>file</c> provides an interface to the file system.</p> - <p>On operating systems with thread support, it is possible to let - file operations be performed in threads of their own, allowing + <p>This module provides an interface to the file system.</p> + + <p>On operating systems with thread support, + file operations can be performed in threads of their own, allowing other Erlang processes to continue executing in parallel with - the file operations. See the command line flag - <c>+A</c> in <seealso marker="erts:erl">erl(1)</seealso>.</p> + the file operations. See command-line flag + <c>+A</c> in <seealso marker="erts:erl"><c>erl(1)</c></seealso>.</p> - <p>With regard to file name encoding, the Erlang VM can operate in - two modes. The current mode can be queried using the <seealso - marker="#native_name_encoding">native_name_encoding/0</seealso> - function. It returns either <c>latin1</c> or <c>utf8</c>.</p> + <p>Regarding filename encoding, the Erlang VM can operate in + two modes. The current mode can be queried using function + <seealso marker="#native_name_encoding"><c>native_name_encoding/0</c></seealso>. + It returns <c>latin1</c> or <c>utf8</c>.</p> - <p>In the <c>latin1</c> mode, the Erlang VM does not change the - encoding of file names. In the <c>utf8</c> mode, file names can - contain Unicode characters greater than 255 and the VM will - convert file names back and forth to the native file name encoding + <p>In <c>latin1</c> mode, the Erlang VM does not change the + encoding of filenames. In <c>utf8</c> mode, filenames can + contain Unicode characters greater than 255 and the VM + converts filenames back and forth to the native filename encoding (usually UTF-8, but UTF-16 on Windows).</p> <p>The default mode depends on the operating system. Windows and - MacOS X enforce consistent file name encoding and therefore the - VM uses the <c>utf8</c> mode.</p> + MacOS X enforce consistent filename encoding and therefore the + VM uses <c>utf8</c> mode.</p> - <p>On operating systems with transparent naming (i.e. all Unix - systems except MacOS X), the default will be <c>utf8</c> if the - terminal supports UTF-8, otherwise <c>latin1</c>. The default may - be overridden using the <c>+fnl</c> (to force <c>latin1</c> mode) - or <c>+fnu</c> (to force <c>utf8</c> mode) when starting <seealso - marker="erts:erl">erl</seealso>.</p> + <p>On operating systems with transparent naming (for example, all Unix + systems except MacOS X), default is <c>utf8</c> if the + terminal supports UTF-8, otherwise <c>latin1</c>. The default can + be overridden using <c>+fnl</c> (to force <c>latin1</c> mode) + or <c>+fnu</c> (to force <c>utf8</c> mode) when starting + <seealso marker="erts:erl"><c>erts:erl</c></seealso>.</p> - <p>On operating systems with transparent naming, files could be - inconsistently named, i.e. some files are encoded in UTF-8 while - others are encoded in (for example) iso-latin1. To be able to - handle file systems with inconsistent naming when running in the - <c>utf8</c> mode, the concept of "raw file names" has been - introduced.</p> + <p>On operating systems with transparent naming, files can be + inconsistently named, for example, some files are encoded in UTF-8 while + others are encoded in ISO Latin-1. The concept of <em>raw filenames</em> is + introduced to handle file systems with inconsistent naming when running in + <c>utf8</c> mode.</p> - <p>A raw file name is a file name given as a binary. The Erlang VM - will perform no translation of a file name given as a binary on + <p>A <em>raw filename</em> is a filename specified as a binary. The Erlang VM + does not translate a filename specified as a binary on systems with transparent naming.</p> - <p>When running in the <c>utf8</c> mode, the - <c>file:list_dir/1</c> and <c>file:read_link/1</c> functions will - never return raw file names. Use the <seealso - marker="#list_dir_all">list_dir_all/1</seealso> and <seealso - marker="#read_link_all">read_link_all/1</seealso> functions to - return all file names including raw file names.</p> + <p>When running in <c>utf8</c> mode, functions + <seealso marker="#list_dir/1"><c>list_dir/1</c></seealso> and + <seealso marker="#read_link/1"><c>read_link/1</c></seealso> + never return raw filenames. To return all filenames including raw filenames, + use functions + <seealso marker="#list_dir_all"><c>list_dir_all/1</c></seealso> and + <seealso marker="#read_link_all"><c>read_link_all/1</c></seealso>.</p> - <p>Also see <seealso marker="stdlib:unicode_usage#notes-about-raw-filenames">Notes about raw file names</seealso>.</p> + <p>See also section <seealso marker="stdlib:unicode_usage#notes-about-raw-filenames">Notes About Raw Filenames</seealso> in the <c>STDLIB</c> User´s Giude.</p> </description> @@ -90,8 +91,8 @@ <name>fd()</name> <desc> <p><marker id="type-fd"/> - A file descriptor representing a file opened in <seealso - marker="#raw">raw</seealso> mode.</p> + A file descriptor representing a file opened in + <seealso marker="#raw"><c>raw</c></seealso> mode.</p> </desc> </datatype> <datatype> @@ -104,7 +105,7 @@ <name name="io_device"/> <desc> <p>As returned by - <seealso marker="#open/2">file:open/2</seealso>; + <seealso marker="#open/2"><c>open/2</c></seealso>; <c>pid()</c> is a process handling I/O-protocols.</p> </desc> </datatype> @@ -112,7 +113,7 @@ <name name="name"/> <desc> <p>If VM is in Unicode filename mode, <c>string()</c> and <c>char()</c> - are allowed to be > 255. + are allowed to be > 255. </p> </desc> </datatype> @@ -120,12 +121,12 @@ <name name="name_all"/> <desc> <p>If VM is in Unicode filename mode, <c>string()</c> and <c>char()</c> - are allowed to be > 255. + are allowed to be > 255. <c><anno>RawFilename</anno></c> is a filename not subject to Unicode translation, meaning that it can contain characters not conforming to - the Unicode encoding expected from the filesystem - (i.e. non-UTF-8 characters although the VM is started + the Unicode encoding expected from the file system + (that is, non-UTF-8 characters although the VM is started in Unicode filename mode). </p> </desc> @@ -133,7 +134,7 @@ <datatype> <name name="posix"/> <desc> - <p>An atom which is named from the POSIX error codes used in + <p>An atom that is named from the POSIX error codes used in Unix, and in the runtime libraries of most C compilers.</p> </desc> </datatype> @@ -160,7 +161,7 @@ <funcs> <func> <name name="advise" arity="4"/> - <fsummary>Predeclare an access pattern for file data</fsummary> + <fsummary>Predeclare an access pattern for file data.</fsummary> <type name="posix_file_advise"/> <desc> <p><c>advise/4</c> can be used to announce an intention to access file @@ -171,80 +172,80 @@ </func> <func> <name name="allocate" arity="3"/> - <fsummary>Allocate file space</fsummary> + <fsummary>Allocate file space.</fsummary> <desc> <p><c>allocate/3</c> can be used to preallocate space for a file.</p> - <p>This function only succeeds in platforms that implement this + <p>This function only succeeds in platforms that provide this feature. When it succeeds, space is preallocated for the file but the file size might not be updated. This behaviour depends on the - preallocation implementation. To guarantee the file size is updated - one must truncate the file to the new size.</p> + preallocation implementation. To guarantee that the file size is updated, + truncate the file to the new size.</p> </desc> </func> <func> <name name="change_group" arity="2"/> - <fsummary>Change group of a file</fsummary> + <fsummary>Change group of a file.</fsummary> <desc> <p>Changes group of a file. See - <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> + <seealso marker="#write_file_info/2"><c>write_file_info/2</c></seealso>.</p> </desc> </func> <func> <name name="change_mode" arity="2"/> - <fsummary>Change permissions of a file</fsummary> + <fsummary>Change permissions of a file.</fsummary> <desc> <p>Changes permissions of a file. See - <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> + <seealso marker="#write_file_info/2"><c>write_file_info/2</c></seealso>.</p> </desc> </func> <func> <name name="change_owner" arity="2"/> - <fsummary>Change owner of a file</fsummary> + <fsummary>Change owner of a file.</fsummary> <desc> <p>Changes owner of a file. See - <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> + <seealso marker="#write_file_info/2"><c>write_file_info/2</c></seealso>.</p> </desc> </func> <func> <name name="change_owner" arity="3"/> - <fsummary>Change owner and group of a file</fsummary> + <fsummary>Change owner and group of a file.</fsummary> <desc> <p>Changes owner and group of a file. See - <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> + <seealso marker="#write_file_info/2"><c>write_file_info/2</c></seealso>.</p> </desc> </func> <func> <name name="change_time" arity="2"/> - <fsummary>Change the modification time of a file</fsummary> + <fsummary>Change the modification time of a file.</fsummary> <desc> <p>Changes the modification and access times of a file. See - <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> + <seealso marker="#write_file_info/2"><c>write_file_info/2</c></seealso>.</p> </desc> </func> <func> <name name="change_time" arity="3"/> - <fsummary>Change the modification and last access time of a file</fsummary> + <fsummary>Change the modification and last access time of a file.</fsummary> <desc> <p>Changes the modification and last access times of a file. See - <seealso marker="#write_file_info/2">write_file_info/2</seealso>.</p> + <seealso marker="#write_file_info/2"><c>write_file_info/2</c></seealso>.</p> </desc> </func> <func> <name name="close" arity="1"/> - <fsummary>Close a file</fsummary> + <fsummary>Close a file.</fsummary> <desc> <p>Closes the file referenced by <c><anno>IoDevice</anno></c>. It mostly - returns <c>ok</c>, expect for some severe errors such as out + returns <c>ok</c>, except for some severe errors such as out of memory.</p> - <p>Note that if the option <c>delayed_write</c> was - used when opening the file, <c>close/1</c> might return an + <p>Notice that if option <c>delayed_write</c> was + used when opening the file, <c>close/1</c> can return an old write error and not even try to close the file. See - <seealso marker="#open/2">open/2</seealso>.</p> + <seealso marker="#open/2"><c>open/2</c></seealso>.</p> </desc> </func> <func> <name name="consult" arity="1"/> - <fsummary>Read Erlang terms from a file</fsummary> + <fsummary>Read Erlang terms from a file.</fsummary> <desc> <p>Reads Erlang terms, separated by '.', from <c><anno>Filename</anno></c>. Returns one of the following:</p> @@ -256,42 +257,44 @@ <tag><c>{error, atom()}</c></tag> <item> <p>An error occurred when opening the file or reading it. - See <seealso marker="#open/2">open/2</seealso> for a list - of typical error codes.</p> + For a list of typical error codes, see + <seealso marker="#open/2"><c>open/2</c></seealso>.</p> </item> <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang terms in - the file. Use <c>format_error/1</c> to convert - the three-element tuple to an English description of - the error.</p> + the file. To convert the three-element tuple to an English + description of the error, use + <seealso marker="#format_error/1"><c>format_error/1</c></seealso>.</p> </item> </taglist> - <p>Example:</p> -<code type="none">f.txt: {person, "kalle", 25}. + <p><em>Example:</em></p> +<code type="none"> +f.txt: {person, "kalle", 25}. {person, "pelle", 30}.</code> -<pre>1> <input>file:consult("f.txt").</input> +<pre> +1> <input>file:consult("f.txt").</input> {ok,[{person,"kalle",25},{person,"pelle",30}]}</pre> - <p>The encoding of of <c><anno>Filename</anno></c> can be set - by a comment as described in <seealso - marker="stdlib:epp#encoding">epp(3)</seealso>.</p> + <p>The encoding of <c><anno>Filename</anno></c> can be set + by a comment, as described in + <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p> </desc> </func> <func> <name name="copy" arity="2"/> <name name="copy" arity="3"/> - <fsummary>Copy file contents</fsummary> + <fsummary>Copy file contents.</fsummary> <desc> <p>Copies <c><anno>ByteCount</anno></c> bytes from <c><anno>Source</anno></c> to <c><anno>Destination</anno></c>. <c><anno>Source</anno></c> and <c><anno>Destination</anno></c> refer - to either filenames or IO devices from e.g. <c>open/2</c>. + to either filenames or IO devices from, for example, <c>open/2</c>. <c><anno>ByteCount</anno></c> defaults to <c>infinity</c>, denoting an infinite number of bytes.</p> - <p>The argument <c><anno>Modes</anno></c> is a list of possible modes, - see <seealso marker="#open/2">open/2</seealso>, and defaults to - [].</p> + <p>Argument <c><anno>Modes</anno></c> is a list of possible modes, + see <seealso marker="#open/2"><c>open/2</c></seealso>, and defaults to + <c>[]</c>.</p> <p>If both <c><anno>Source</anno></c> and <c><anno>Destination</anno></c> refer to filenames, the files are opened with <c>[read, binary]</c> @@ -303,25 +306,51 @@ <p>If <c><anno>Destination</anno></c> refers to a filename, it is opened with <c>write</c> mode prepended to the mode list before the copy, and closed when done.</p> - <p>Returns <c>{ok, <anno>BytesCopied</anno>}</c> where + <p>Returns <c>{ok, <anno>BytesCopied</anno>}</c>, where <c><anno>BytesCopied</anno></c> is - the number of bytes that actually was copied, which may be + the number of bytes that was copied, which can be less than <c><anno>ByteCount</anno></c> if end of file was encountered on the source. If the operation fails, <c>{error, <anno>Reason</anno>}</c> is returned.</p> - <p>Typical error reasons: As for <c>open/2</c> if a file had to - be opened, and as for <c>read/2</c> and <c>write/2</c>.</p> + <p>Typical error reasons: as for + <seealso marker="#open/2"><c>open/2</c></seealso> if a file + had to be opened, and as for + <seealso marker="#read/2"><c>read/2</c></seealso> and + <seealso marker="#write/2"><c>write/2</c></seealso>.</p> + </desc> + </func> + <func> + <name name="datasync" arity="1"/> + <fsummary>Synchronize the in-memory data of a file, ignoring most of its metadata, with that on the physical medium.</fsummary> + <desc> + <p>Ensures that any buffers kept by the operating system + (not by the Erlang runtime system) are written to disk. In + many ways it resembles <c>fsync</c> but it does not update + some of the metadata of the file, such as the access time. On + some platforms this function has no effect.</p> + <p>Applications that access databases or log files often write + a tiny data fragment (for example, one line in a log file) and then + call <c>fsync()</c> immediately to ensure that the written + data is physically stored on the hard disk. Unfortunately, <c>fsync()</c> + always initiates two write operations: one for the newly + written data and another one to update the modification + time stored in the <c>inode</c>. If the modification time is not a part + of the transaction concept, <c>fdatasync()</c> can be used to avoid + unnecessary <c>inode</c> disk write operations.</p> + <p>Available only in some POSIX systems, this call results in a + call to <c>fsync()</c>, or has no effect in systems not providing + the <c>fdatasync()</c> syscall.</p> </desc> </func> <func> <name name="del_dir" arity="1"/> - <fsummary>Delete a directory</fsummary> + <fsummary>Delete a directory.</fsummary> <desc> - <p>Tries to delete the directory <c><anno>Dir</anno></c>. + <p>Tries to delete directory <c><anno>Dir</anno></c>. The directory must be empty before it can be deleted. Returns <c>ok</c> if successful.</p> - <p>Typical error reasons are:</p> + <p>Typical error reasons:</p> <taglist> <tag><c>eacces</c></tag> <item> @@ -351,11 +380,11 @@ </func> <func> <name name="delete" arity="1"/> - <fsummary>Delete a file</fsummary> + <fsummary>Delete a file.</fsummary> <desc> - <p>Tries to delete the file <c><anno>Filename</anno></c>. + <p>Tries to delete file <c><anno>Filename</anno></c>. Returns <c>ok</c> if successful.</p> - <p>Typical error reasons are:</p> + <p>Typical error reasons:</p> <taglist> <tag><c>enoent</c></tag> <item> @@ -367,32 +396,32 @@ </item> <tag><c>eperm</c></tag> <item> - <p>The file is a directory and the user is not super-user.</p> + <p>The file is a directory and the user is not superuser.</p> </item> <tag><c>enotdir</c></tag> <item> - <p>A component of the file name is not a directory. On some + <p>A component of the filename is not a directory. On some platforms, <c>enoent</c> is returned instead.</p> </item> <tag><c>einval</c></tag> <item> - <p><c><anno>Filename</anno></c> had an improper type, such as tuple.</p> + <p><c><anno>Filename</anno></c> has an improper type, such as tuple.</p> </item> </taglist> <warning> - <p>In a future release, a bad type for the - <c><anno>Filename</anno></c> argument will probably generate + <p>In a future release, a bad type for argument + <c><anno>Filename</anno></c> will probably generate an exception.</p> </warning> </desc> </func> <func> <name name="eval" arity="1"/> - <fsummary>Evaluate Erlang expressions in a file</fsummary> + <fsummary>Evaluate Erlang expressions in a file.</fsummary> <desc> <p>Reads and evaluates Erlang expressions, separated by '.' (or - ',', a sequence of expressions is also an expression), from - <c><anno>Filename</anno></c>. The actual result of the evaluation + ',', a sequence of expressions is also an expression) from + <c><anno>Filename</anno></c>. The result of the evaluation is not returned; any expression sequence in the file must be there for its side effect. Returns one of the following:</p> <taglist> @@ -403,35 +432,36 @@ <tag><c>{error, atom()}</c></tag> <item> <p>An error occurred when opening the file or reading it. - See <c>open/2</c> for a list of typical error codes.</p> + For a list of typical error codes, see + <seealso marker="#open/2"><c>open/2</c></seealso>.</p> </item> <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang - expressions in the file. Use <c>format_error/1</c> to - convert the three-element tuple to an English description - of the error.</p> + expressions in the file. To convert the three-element tuple + to an English description of the error, use + <seealso marker="#format_error/1"><c>format_error/1</c></seealso>.</p> </item> </taglist> - <p>The encoding of of <c><anno>Filename</anno></c> can be set - by a comment as described in <seealso - marker="stdlib:epp#encoding">epp(3)</seealso>.</p> + <p>The encoding of <c><anno>Filename</anno></c> can be set + by a comment, as described in + <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p> </desc> </func> <func> <name name="eval" arity="2"/> - <fsummary>Evaluate Erlang expressions in a file</fsummary> + <fsummary>Evaluate Erlang expressions in a file.</fsummary> <desc> - <p>The same as <c>eval/1</c> but the variable bindings - <c><anno>Bindings</anno></c> are used in the evaluation. See - <seealso marker="stdlib:erl_eval">erl_eval(3)</seealso> about - variable bindings.</p> + <p>The same as <c>eval/1</c>, but the variable bindings + <c><anno>Bindings</anno></c> are used in the evaluation. For information + about the variable bindings, see + <seealso marker="stdlib:erl_eval"><c>stdlib:erl_eval(3)</c></seealso>.</p> </desc> </func> <func> <name name="format_error" arity="1"/> - <fsummary>Return a descriptive string for an error reason</fsummary> + <fsummary>Return a descriptive string for an error reason.</fsummary> <desc> <p>Given the error reason returned by any function in this module, returns a descriptive string of the error in English.</p> @@ -439,17 +469,17 @@ </func> <func> <name name="get_cwd" arity="0"/> - <fsummary>Get the current working directory</fsummary> + <fsummary>Get the current working directory.</fsummary> <desc> <p>Returns <c>{ok, <anno>Dir</anno>}</c>, where <c><anno>Dir</anno></c> is the current working directory of the file server.</p> <note> <p>In rare circumstances, this function can fail on Unix. - It may happen if read permission does not exist for + It can occur if read permission does not exist for the parent directories of the current directory.</p> </note> - <p>Typical error reasons are:</p> + <p>A typical error reason:</p> <taglist> <tag><c>eacces</c></tag> <item> @@ -461,17 +491,19 @@ </func> <func> <name name="get_cwd" arity="1"/> - <fsummary>Get the current working directory for the drive specified</fsummary> + <fsummary>Get the current working directory for the specified drive.</fsummary> <desc> - <p><c><anno>Drive</anno></c> should be of the form - "<c>Letter</c><c>:</c>", - for example "c:". Returns <c>{ok, <anno>Dir</anno>}</c> or + <p>Returns <c>{ok, <anno>Dir</anno>}</c> or <c>{error, <anno>Reason</anno>}</c>, where <c><anno>Dir</anno></c> - is the current - working directory of the drive specified.</p> - <p>This function returns <c>{error, enotsup}</c> on platforms - which have no concept of current drive (Unix, for example).</p> - <p>Typical error reasons are:</p> + is the current working directory of the specified drive.</p> + + <p><c><anno>Drive</anno></c> is to be of the form + "<c>Letter</c><c>:</c>", for example, "c:".</p> + + <p>Returns <c>{error, enotsup}</c> on platforms + that have no concept of current drive (Unix, for example).</p> + + <p>Typical error reasons:</p> <taglist> <tag><c>enotsup</c></tag> <item> @@ -490,16 +522,16 @@ </func> <func> <name name="list_dir" arity="1"/> - <fsummary>List files in a directory</fsummary> + <fsummary>List files in a directory.</fsummary> <desc> <p>Lists all files in a directory, <em>except</em> files - with "raw" names. Returns - <c>{ok, <anno>Filenames</anno>}</c> if successful. - Otherwise, it returns <c>{error, <anno>Reason</anno>}</c>. + with raw filenames. Returns + <c>{ok, <anno>Filenames</anno>}</c> if successful, + otherwise <c>{error, <anno>Reason</anno>}</c>. <c><anno>Filenames</anno></c> is a list of the names of all the files in the directory. The names are not sorted.</p> - <p>Typical error reasons are:</p> + <p>Typical error reasons:</p> <taglist> <tag><c>eacces</c></tag> <item> @@ -513,24 +545,24 @@ <tag><c>{no_translation, <anno>Filename</anno>}</c></tag> <item> <p><c><anno>Filename</anno></c> is a <c>binary()</c> with - characters coded in ISO-latin-1 and the VM was started - with the parameter <c>+fnue</c>.</p> + characters coded in ISO Latin-1 and the VM was started + with parameter <c>+fnue</c>.</p> </item> </taglist> </desc> </func> <func> <name name="list_dir_all" arity="1"/> - <fsummary>List all files in a directory</fsummary> + <fsummary>List all files in a directory.</fsummary> <desc> <p><marker id="list_dir_all"/>Lists all the files in a directory, - including files with "raw" names. - Returns <c>{ok, <anno>Filenames</anno>}</c> if successful. - Otherwise, it returns <c>{error, <anno>Reason</anno>}</c>. + including files with raw filenames. + Returns <c>{ok, <anno>Filenames</anno>}</c> if successful, + otherwise <c>{error, <anno>Reason</anno>}</c>. <c><anno>Filenames</anno></c> is a list of the names of all the files in the directory. The names are not sorted.</p> - <p>Typical error reasons are:</p> + <p>Typical error reasons:</p> <taglist> <tag><c>eacces</c></tag> <item> @@ -546,12 +578,12 @@ </func> <func> <name name="make_dir" arity="1"/> - <fsummary>Make a directory</fsummary> + <fsummary>Make a directory.</fsummary> <desc> - <p>Tries to create the directory <c><anno>Dir</anno></c>. Missing parent + <p>Tries to create directory <c><anno>Dir</anno></c>. Missing parent directories are <em>not</em> created. Returns <c>ok</c> if successful.</p> - <p>Typical error reasons are:</p> + <p>Typical error reasons:</p> <taglist> <tag><c>eacces</c></tag> <item> @@ -560,7 +592,7 @@ </item> <tag><c>eexist</c></tag> <item> - <p>There is already a file or directory named <c><anno>Dir</anno></c>.</p> + <p>A file or directory named <c><anno>Dir</anno></c> exists already.</p> </item> <tag><c>enoent</c></tag> <item> @@ -568,7 +600,7 @@ </item> <tag><c>enospc</c></tag> <item> - <p>There is a no space left on the device.</p> + <p>No space is left on the device.</p> </item> <tag><c>enotdir</c></tag> <item> @@ -580,13 +612,13 @@ </func> <func> <name name="make_link" arity="2"/> - <fsummary>Make a hard link to a file</fsummary> + <fsummary>Make a hard link to a file.</fsummary> <desc> <p>Makes a hard link from <c><anno>Existing</anno></c> to - <c><anno>New</anno></c>, on - platforms that support links (Unix and Windows). This function returns - <c>ok</c> if the link was successfully created, or - <c>{error, <anno>Reason</anno>}</c>. On platforms that do not support + <c><anno>New</anno></c> on + platforms supporting links (Unix and Windows). This function returns + <c>ok</c> if the link was successfully created, otherwise + <c>{error, <anno>Reason</anno>}</c>. On platforms not supporting links, <c>{error,enotsup}</c> is returned.</p> <p>Typical error reasons:</p> <taglist> @@ -609,17 +641,16 @@ </func> <func> <name name="make_symlink" arity="2"/> - <fsummary>Make a symbolic link to a file or directory</fsummary> + <fsummary>Make a symbolic link to a file or directory.</fsummary> <desc> - <p>This function creates a symbolic link <c><anno>New</anno></c> to - the file or directory <c><anno>Existing</anno></c>, on platforms that - support symbolic links (most Unix systems and Windows beginning with + <p>Creates a symbolic link <c><anno>New</anno></c> to + the file or directory <c><anno>Existing</anno></c> on platforms + supporting symbolic links (most Unix systems and Windows, beginning with Vista). - <c><anno>Existing</anno></c> need not exist. - This function returns <c>ok</c> if the link was - successfully created, or <c>{error, <anno>Reason</anno>}</c>. - On platforms - that do not support symbolic links, <c>{error, enotsup}</c> + <c><anno>Existing</anno></c> does not need to exist. + Returns <c>ok</c> if the link is + successfully created, otherwise <c>{error, <anno>Reason</anno>}</c>. + On platforms not supporting symbolic links, <c>{error, enotsup}</c> is returned.</p> <p>Typical error reasons:</p> <taglist> @@ -646,23 +677,23 @@ </func> <func> <name name="native_name_encoding" arity="0"/> - <fsummary>Return the VM's configured filename encoding</fsummary> + <fsummary>Return the configured filename encoding of the VM.</fsummary> <desc> - <p><marker id="native_name_encoding"/>This function returns - the file name encoding mode. If it is <c>latin1</c>, the - system does no translation of file names. If it is - <c>utf8</c>, file names will be converted back and forth to - the native file name encoding (usually UTF-8, but UTF-16 on + <p><marker id="native_name_encoding"/>Returns + the filename encoding mode. If it is <c>latin1</c>, the + system translates no filenames. If it is + <c>utf8</c>, filenames are converted back and forth to + the native filename encoding (usually UTF-8, but UTF-16 on Windows).</p> </desc> </func> <func> <name name="open" arity="2"/> - <fsummary>Open a file</fsummary> + <fsummary>Open a file.</fsummary> <desc> - <p>Opens the file <c><anno>File</anno></c> in the mode determined - by <c><anno>Modes</anno></c>, which may contain one or more of the - following items:</p> + <p>Opens file <c><anno>File</anno></c> in the mode determined + by <c><anno>Modes</anno></c>, which can contain one or more of the + following options:</p> <taglist> <tag><c>read</c></tag> <item> @@ -671,98 +702,100 @@ <tag><c>write</c></tag> <item> <p>The file is opened for writing. It is created if it does - not exist. If the file exists, and if <c>write</c> is not - combined with <c>read</c>, the file will be truncated.</p> + not exist. If the file exists and <c>write</c> is not + combined with <c>read</c>, the file is truncated.</p> </item> <tag><c>append</c></tag> <item> - <p>The file will be opened for writing, and it will be - created if it does not exist. Every write operation to a - file opened with <c>append</c> will take place at - the end of the file.</p> + <p>The file is opened for writing. It is created if it does + not exist. Every write operation to a file opened with + <c>append</c> takes place at the end of the file.</p> </item> <tag><c>exclusive</c></tag> <item> - <p>The file, when opened for writing, is created if it - does not exist. If the file exists, open will return - <c>{error, eexist}</c>.</p> + <p>The file is opened for writing. It is created if it does + not exist. If the file exists, <c>{error, eexist}</c> + is returned.</p> <warning><p>This option does not guarantee exclusiveness on - file systems that do not support O_EXCL properly, + file systems not supporting <c>O_EXCL</c> properly, such as NFS. Do not depend on this option unless you know that the file system supports it (in general, local - file systems should be safe).</p></warning> + file systems are safe).</p></warning> </item> <tag><c>raw</c></tag> <item> <p><marker id="raw"/> - The <c>raw</c> option allows faster access to a file, - because no Erlang process is needed to handle the file. + Allows faster access to a file, + as no Erlang process is needed to handle the file. However, a file opened in this way has the following limitations:</p> <list type="bulleted"> - <item>The functions in the <c>io</c> module cannot be used, - because they can only talk to an Erlang process. - Instead, use the <c>read/2</c>, <c>read_line/1</c> and - <c>write/2</c> - functions.</item> - <item>Especially if <c>read_line/1</c> is to be used on a <c>raw</c> file, it is recommended to combine this option with the <c>{read_ahead, Size}</c> option as line oriented I/O is inefficient without buffering.</item> - <item>Only the Erlang process which opened the file can use - it.</item> - <item>A remote Erlang file server cannot be used; - the computer on which the Erlang node is running must + <item><p>The functions in the <c>io</c> module cannot be used, + as they can only talk to an Erlang process. + Instead, use functions + <seealso marker="#read/2"><c>read/2</c></seealso>, + <seealso marker="#read_line/1"><c>read_line/1</c></seealso>, and + <seealso marker="#write/2"><c>write/2</c></seealso>.</p></item> + <item><p>Especially if <c>read_line/1</c> is to be used on a <c>raw</c> + file, it is recommended to combine this option with option + <c>{read_ahead, Size}</c> as line-oriented I/O is inefficient + without buffering.</p></item> + <item><p>Only the Erlang process that opened the file can use + it.</p></item> + <item><p>A remote Erlang file server cannot be used. + The computer on which the Erlang node is running must have access to the file system (directly or through - NFS).</item> + NFS).</p></item> </list> </item> <tag><c>binary</c></tag> <item> - <p>When this option has been given, read operations on the file - will return binaries rather than lists.</p> + <p>Read operations on the file return binaries rather than lists.</p> </item> <tag><c>{delayed_write, Size, Delay}</c></tag> <item> - <p>If this option is used, the data in subsequent - <c>write/2</c> calls is buffered until there are at least - <c>Size</c> bytes buffered, or until the oldest buffered + <p>Data in subsequent + <c>write/2</c> calls is buffered until at least + <c>Size</c> bytes are buffered, or until the oldest buffered data is <c>Delay</c> milliseconds old. Then all buffered data is written in one operating system call. The buffered data is also flushed before some other file operation than <c>write/2</c> is executed.</p> <p>The purpose of this option is to increase performance - by reducing the number of operating system calls, so the - <c>write/2</c> calls should be for sizes significantly - less than <c>Size</c>, and not interspersed by to many - other file operations, for this to happen.</p> + by reducing the number of operating system calls. Thus, the + <c>write/2</c> calls must be for sizes significantly + less than <c>Size</c>, and not interspersed by too many + other file operations.</p> <p>When this option is used, the result of <c>write/2</c> - calls may prematurely be reported as successful, and if - a write error should actually occur the error is - reported as the result of the next file operation, which - is not executed.</p> + calls can prematurely be reported as successful, and if + a write error occurs, the error is reported as the result + of the next file operation, which is not executed.</p> <p>For example, when <c>delayed_write</c> is used, after a - number of <c>write/2</c> calls, <c>close/1</c> might - return <c>{error, enospc}</c> because there was not enough - space on the disc for previously written data, and - <c>close/1</c> should probably be called again since the + number of <c>write/2</c> calls, <c>close/1</c> can + return <c>{error, enospc}</c>, as there is not enough + space on the disc for previously written data. + <c>close/1</c> must probably be called again, as the file is still open.</p> </item> <tag><c>delayed_write</c></tag> <item> <p>The same as <c>{delayed_write, Size, Delay}</c> with reasonable default values for <c>Size</c> and - <c>Delay</c>. (Roughly some 64 KBytes, 2 seconds)</p> + <c>Delay</c> (roughly some 64 KB, 2 seconds).</p> </item> <tag><c>{read_ahead, Size}</c></tag> <item> - <p>This option activates read data buffering. If + <p>Activates read data buffering. If <c>read/2</c> calls are for significantly less than - <c>Size</c> bytes, read operations towards the operating + <c>Size</c> bytes, read operations to the operating system are still performed for blocks of <c>Size</c> bytes. The extra data is buffered and returned in subsequent <c>read/2</c> calls, giving a performance gain - since the number of operating system calls is reduced.</p> - <p>The <c>read_ahead</c> buffer is also highly utilized - by the <c>read_line/1</c> function in <c>raw</c> mode, - why this option is recommended (for performance reasons) + as the number of operating system calls is reduced.</p> + <p>The <c>read_ahead</c> buffer is also highly used + by function <c>read_line/1</c> in <c>raw</c> mode, + therefore this option is recommended + (for performance reasons) when accessing raw files using that function.</p> <p>If <c>read/2</c> calls are for sizes not significantly less than, or even greater than <c>Size</c> bytes, no @@ -771,93 +804,141 @@ <tag><c>read_ahead</c></tag> <item> <p>The same as <c>{read_ahead, Size}</c> with a reasonable - default value for <c>Size</c>. (Roughly some 64 KBytes)</p> + default value for <c>Size</c> (roughly some 64 KB).</p> </item> <tag><c>compressed</c></tag> <item> <p>Makes it possible to read or write gzip compressed - files. The <c>compressed</c> option must be combined - with either <c>read</c> or <c>write</c>, but not both. - Note that the file size obtained with - <c>read_file_info/1</c> will most probably not match the - number of bytes that can be read from a compressed file.</p> + files. Option <c>compressed</c> must be combined + with <c>read</c> or <c>write</c>, but not both. + Notice that the file size obtained with + <seealso marker="#read_file_info/1"><c>read_file_info/1</c></seealso> + does probably not match the number of bytes that can be + read from a compressed file.</p> </item> <tag><c>{encoding, Encoding}</c></tag> <item> - <p>Makes the file perform automatic translation of characters to and from a specific (Unicode) encoding. Note that the data supplied to file:write or returned by file:read still is byte oriented, this option only denotes how data is actually stored in the disk file.</p> - <p>Depending on the encoding, different methods of reading and writing data is preferred. The default encoding of <c>latin1</c> implies using this (the file) module for reading and writing data, as the interfaces provided here work with byte-oriented data, while using other (Unicode) encodings makes the <seealso marker="stdlib:io">io(3)</seealso> module's <c>get_chars</c>, <c>get_line</c> and <c>put_chars</c> functions more suitable, as they can work with the full Unicode range.</p> - <p>If data is sent to an <c>io_device()</c> in a format that cannot be converted to the specified encoding, or if data is read by a function that returns data in a format that cannot cope with the character range of the data, an error occurs and the file will be closed.</p> - <p>The allowed values for <c>Encoding</c> are:</p> + <p>Makes the file perform automatic translation of characters to + and from a specific (Unicode) encoding. Notice that the data supplied + to + <seealso marker="#write/2"><c>write/2</c></seealso> + or returned by + <seealso marker="#read/2"><c>read/2</c></seealso> + still is byte-oriented; this option + denotes only how data is stored in the disk file.</p> + <p>Depending on the encoding, different methods of reading and writing + data is preferred. The default encoding of <c>latin1</c> implies using + this module (<c>file</c>) for reading and writing data as the interfaces + provided here work with byte-oriented data. Using other (Unicode) + encodings makes the + <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> functions + <c>get_chars</c>, <c>get_line</c>, and <c>put_chars</c> more suitable, + as they can work with the full Unicode range.</p> + <p>If data is sent to an <c>io_device()</c> in a format that cannot be + converted to the specified encoding, or if data is read by a function + that returns data in a format that cannot cope with the character range + of the data, an error occurs and the file is closed.</p> + <p>Allowed values for <c>Encoding</c>:</p> <taglist> <tag><c>latin1</c></tag> <item> - <p>The default encoding. Bytes supplied to i.e. file:write are written as is on the file, likewise bytes read from the file are returned to i.e. file:read as is. If the <seealso marker="stdlib:io">io(3)</seealso> module is used for writing, the file can only cope with Unicode characters up to codepoint 255 (the ISO-latin-1 range).</p> + <p>The default encoding. Bytes supplied to the file, that is, + <seealso marker="#write/2"><c>write/2</c></seealso> + are written "as is" on the file. Likewise, bytes read from the file, + that is, + <seealso marker="#read/2"><c>read/2</c></seealso> are + returned "as is". If module + <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> is used for + writing, the file can only cope with Unicode characters up to code point + 255 (the ISO Latin-1 range).</p> </item> - <tag><c>unicode</c> or <c>utf8</c></tag> + <tag><c>unicode or utf8</c></tag> <item> - <p>Characters are translated to and from the UTF-8 encoding before being written to or read from the file. A file opened in this way might be readable using the file:read function, as long as no data stored on the file lies beyond the ISO-latin-1 range (0..255), but failure will occur if the data contains Unicode codepoints beyond that range. The file is best read with the functions in the Unicode aware <seealso marker="stdlib:io">io(3)</seealso> module.</p> - <p>Bytes written to the file by any means are translated to UTF-8 encoding before actually being stored on the disk file.</p> + <p>Characters are translated to and from UTF-8 encoding before they are + written to or read from the file. A file opened in this way can be + readable using function + <seealso marker="#read/2"><c>read/2</c></seealso>, + as long as no data stored on + the file lies beyond the ISO Latin-1 range (0..255), but failure occurs + if the data contains Unicode code points beyond that range. The file is + best read with the functions in the Unicode aware module + <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso>.</p> + <p>Bytes written to the file by any means are translated to UTF-8 encoding + before being stored on the disk file.</p> </item> - <tag><c>utf16</c> or <c>{utf16,big}</c></tag> + <tag><c>utf16 or {utf16,big}</c></tag> <item> - <p>Works like <c>unicode</c>, but translation is done to and from big endian UTF-16 instead of UTF-8.</p> + <p>Works like <c>unicode</c>, but translation is done to and from big + endian UTF-16 instead of UTF-8.</p> </item> <tag><c>{utf16,little}</c></tag> <item> - <p>Works like <c>unicode</c>, but translation is done to and from little endian UTF-16 instead of UTF-8.</p> + <p>Works like <c>unicode</c>, but translation is done to and from little + endian UTF-16 instead of UTF-8.</p> </item> - <tag><c>utf32</c> or <c>{utf32,big}</c></tag> + <tag><c>utf32 or {utf32,big}</c></tag> <item> - <p>Works like <c>unicode</c>, but translation is done to and from big endian UTF-32 instead of UTF-8.</p> + <p>Works like <c>unicode</c>, but translation is done to and from big + endian UTF-32 instead of UTF-8.</p> </item> <tag><c>{utf32,little}</c></tag> <item> - <p>Works like <c>unicode</c>, but translation is done to and from little endian UTF-32 instead of UTF-8.</p> + <p>Works like <c>unicode</c>, but translation is done to and from little + endian UTF-32 instead of UTF-8.</p> </item> </taglist> - <p>The Encoding can be changed for a file "on the fly" by using the <seealso marker="stdlib:io#setopts/2">io:setopts/2</seealso> function, why a file can be analyzed in latin1 encoding for i.e. a BOM, positioned beyond the BOM and then be set for the right encoding before further reading.See the <seealso marker="stdlib:unicode">unicode(3)</seealso> module for functions identifying BOM's.</p> + <p>The Encoding can be changed for a file "on the fly" by using function + <seealso marker="stdlib:io#setopts/2"><c>io:setopts/2</c></seealso>. + So a file can be analyzed in latin1 encoding for, for example, a BOM, + positioned beyond the BOM and then be set for the right encoding before + further reading. For functions identifying BOMs, see module + <seealso marker="stdlib:unicode"><c>stdlib:unicode(3)</c></seealso>. </p> <p>This option is not allowed on <c>raw</c> files.</p> </item> <tag><c>ram</c></tag> <item> - <p><c>File</c> must be <c>iodata()</c>. Returns an <c>fd()</c> which lets the <c>file</c> module operate on the data in-memory as if it is a file.</p> + <p><c>File</c> must be <c>iodata()</c>. Returns an <c>fd()</c>, which lets + module <c>file</c> operate on the data in-memory as if it is a file.</p> </item> <tag><c>sync</c></tag> <item> - <p>On platforms that support it, enables the POSIX <c>O_SYNC</c> synchronous I/O flag or its platform-dependent - equivalent (e.g., <c>FILE_FLAG_WRITE_THROUGH</c> on Windows) so that writes to the file block until the data has - been physically written to disk. Be aware, though, that the exact semantics of this flag differ from platform to - platform; for example, neither Linux nor Windows guarantees that all file metadata are also written before the call - returns. For precise semantics, check the details of your platform's documentation. On platforms with no - support for POSIX <c>O_SYNC</c> or equivalent, use of the <c>sync</c> flag causes <c>open</c> to return - <c>{error, enotsup}</c>.</p> + <p>On platforms supporting it, enables the POSIX <c>O_SYNC</c> synchronous + I/O flag or its platform-dependent equivalent (for example, + <c>FILE_FLAG_WRITE_THROUGH</c> on Windows) so that writes to the file + block until the data is physically written to disk. However, be aware + that the exact semantics of this flag differ from platform to + platform. For example, none of Linux or Windows guarantees that all file + metadata are also written before the call returns. For precise semantics, + check the details of your platform documentation. On platforms with no + support for POSIX <c>O_SYNC</c> or equivalent, use of the <c>sync</c> + flag causes <c>open</c> to return <c>{error, enotsup}</c>.</p> </item> </taglist> <p>Returns:</p> <taglist> <tag><c>{ok, <anno>IoDevice</anno>}</c></tag> <item> - <p>The file has been opened in the requested mode. + <p>The file is opened in the requested mode. <c><anno>IoDevice</anno></c> is a reference to the file.</p> </item> <tag><c>{error, <anno>Reason</anno>}</c></tag> <item> - <p>The file could not be opened.</p> + <p>The file cannot be opened.</p> </item> </taglist> - <p><c><anno>IoDevice</anno></c> is really the pid of the process which + <p><c><anno>IoDevice</anno></c> is really the pid of the process that handles the file. This process is linked to the process - which originally opened the file. If any process to which - the <c><anno>IoDevice</anno></c> is linked terminates, the file will - be closed and the process itself will be terminated. + that originally opened the file. If any process to which + the <c><anno>IoDevice</anno></c> is linked terminates, the file is + closed and the process itself is terminated. An <c><anno>IoDevice</anno></c> returned from this call can be used - as an argument to the IO functions (see - <seealso marker="stdlib:io">io(3)</seealso>).</p> + as an argument to the I/O functions (see + <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso>).</p> <note> - <p>In previous versions of <c>file</c>, modes were given + <p>In previous versions of <c>file</c>, modes were specified as one of the atoms <c>read</c>, <c>write</c>, or <c>read_write</c> instead of a list. This is still allowed - for reasons of backwards compatibility, but should not be + for reasons of backwards compatibility, but is not to be used for new code. Also note that <c>read_write</c> is not allowed in a mode list.</p> </note> @@ -874,17 +955,17 @@ </item> <tag><c>eisdir</c></tag> <item> - <p>The named file is not a regular file. It may be a - directory, a fifo, or a device.</p> + <p>The named file is not a regular file. It can be a + directory, a FIFO, or a device.</p> </item> <tag><c>enotdir</c></tag> <item> - <p>A component of the file name is not a directory. On some + <p>A component of the filename is not a directory. On some platforms, <c>enoent</c> is returned instead.</p> </item> <tag><c>enospc</c></tag> <item> - <p>There is a no space left on the device (if <c>write</c> + <p>There is no space left on the device (if <c>write</c> access was specified).</p> </item> </taglist> @@ -892,182 +973,186 @@ </func> <func> <name name="path_consult" arity="2"/> - <fsummary>Read Erlang terms from a file</fsummary> + <fsummary>Read Erlang terms from a file.</fsummary> <desc> <p>Searches the path <c><anno>Path</anno></c> (a list of directory names) until the file <c><anno>Filename</anno></c> is found. If <c><anno>Filename</anno></c> is an absolute filename, <c><anno>Path</anno></c> is ignored. - Then reads Erlang terms, separated by '.', from the file. - Returns one of the following:</p> + Then reads Erlang terms, separated by '.', from the file.</p> + <p>Returns one of the following:</p> <taglist> <tag><c>{ok, <anno>Terms</anno>, <anno>FullName</anno>}</c></tag> <item> - <p>The file was successfully read. <c><anno>FullName</anno></c> is + <p>The file is successfully read. <c><anno>FullName</anno></c> is the full name of the file.</p> </item> <tag><c>{error, enoent}</c></tag> <item> - <p>The file could not be found in any of the directories in + <p>The file cannot be found in any of the directories in <c><anno>Path</anno></c>.</p> </item> <tag><c>{error, atom()}</c></tag> <item> <p>An error occurred when opening the file or reading it. - See <seealso marker="#open/2">open/2</seealso> for a list - of typical error codes.</p> + For a list of typical error codes, see + <seealso marker="#open/2"><c>open/2</c></seealso>.</p> </item> <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang terms in - the file. Use <c>format_error/1</c> to convert - the three-element tuple to an English description of + the file. Use + <seealso marker="#format_error/1"><c>format_error/1</c></seealso> + to convert the three-element tuple to an English description of the error.</p> </item> </taglist> - <p>The encoding of of <c><anno>Filename</anno></c> can be set - by a comment as described in <seealso - marker="stdlib:epp#encoding">epp(3)</seealso>.</p> + <p>The encoding of <c><anno>Filename</anno></c> can be set + by a comment as described in + <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p> </desc> </func> <func> <name name="path_eval" arity="2"/> - <fsummary>Evaluate Erlang expressions in a file</fsummary> + <fsummary>Evaluate Erlang expressions in a file.</fsummary> <desc> <p>Searches the path <c><anno>Path</anno></c> (a list of directory names) until the file <c><anno>Filename</anno></c> is found. - If <c><anno>Filename</anno></c> is an absolute file name, + If <c><anno>Filename</anno></c> is an absolute filename, <c><anno>Path</anno></c> is ignored. Then reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from the file. - The actual result of evaluation is not returned; any + The result of evaluation is not returned; any expression sequence in the file must be there for its side - effect. Returns one of the following:</p> + effect.</p> + <p>Returns one of the following:</p> <taglist> <tag><c>{ok, <anno>FullName</anno>}</c></tag> <item> - <p>The file was read and evaluated. <c><anno>FullName</anno></c> is + <p>The file is read and evaluated. <c><anno>FullName</anno></c> is the full name of the file.</p> </item> <tag><c>{error, enoent}</c></tag> <item> - <p>The file could not be found in any of the directories in + <p>The file cannot be found in any of the directories in <c><anno>Path</anno></c>.</p> </item> <tag><c>{error, atom()}</c></tag> <item> <p>An error occurred when opening the file or reading it. - See <seealso marker="#open/2">open/2</seealso> for a list - of typical error codes.</p> + For a list of typical error codes, see + <seealso marker="#open/2"><c>open/2</c></seealso>.</p> </item> <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang - expressions in the file. Use <c>format_error/1</c> to - convert the three-element tuple to an English description + expressions in the file. Use + <seealso marker="#format_error/1"><c>format_error/1</c></seealso> + to convert the three-element tuple to an English description of the error.</p> </item> </taglist> - <p>The encoding of of <c><anno>Filename</anno></c> can be set - by a comment as described in <seealso - marker="stdlib:epp#encoding">epp(3)</seealso>.</p> + <p>The encoding of <c><anno>Filename</anno></c> can be set + by a comment as described in + <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p> </desc> </func> <func> <name name="path_open" arity="3"/> - <fsummary>Open a file</fsummary> + <fsummary>Open a file.</fsummary> <desc> <p>Searches the path <c><anno>Path</anno></c> (a list of directory names) until the file <c><anno>Filename</anno></c> is found. If <c><anno>Filename</anno></c> - is an absolute file name, <c><anno>Path</anno></c> is ignored. - Then opens the file in the mode determined by <c><anno>Modes</anno></c>. - Returns one of the following:</p> + is an absolute filename, <c><anno>Path</anno></c> is ignored. + Then opens the file in the mode determined by <c><anno>Modes</anno></c>.</p> + <p>Returns one of the following:</p> <taglist> <tag><c>{ok, <anno>IoDevice</anno>, <anno>FullName</anno>}</c></tag> <item> - <p>The file has been opened in the requested mode. + <p>The file is opened in the requested mode. <c><anno>IoDevice</anno></c> is a reference to the file and <c><anno>FullName</anno></c> is the full name of the file.</p> </item> <tag><c>{error, enoent}</c></tag> <item> - <p>The file could not be found in any of the directories in + <p>The file cannot be found in any of the directories in <c><anno>Path</anno></c>.</p> </item> <tag><c>{error, atom()}</c></tag> <item> - <p>The file could not be opened.</p> + <p>The file cannot be opened.</p> </item> </taglist> </desc> </func> <func> <name name="path_script" arity="2"/> - <fsummary>Evaluate and return the value of Erlang expressions in a file</fsummary> + <fsummary>Evaluate and return the value of Erlang expressions in a file.</fsummary> <desc> <p>Searches the path <c><anno>Path</anno></c> (a list of directory names) until the file <c><anno>Filename</anno></c> is found. - If <c><anno>Filename</anno></c> is an absolute file name, + If <c><anno>Filename</anno></c> is an absolute filename, <c><anno>Path</anno></c> is ignored. Then reads and evaluates Erlang expressions, separated by '.' (or ',', a - sequence of expressions is also an expression), from the file. - Returns one of the following:</p> + sequence of expressions is also an expression), from the file.</p> + <p>Returns one of the following:</p> <taglist> <tag><c>{ok, <anno>Value</anno>, <anno>FullName</anno>}</c></tag> <item> - <p>The file was read and evaluated. <c><anno>FullName</anno></c> is + <p>The file is read and evaluated. <c><anno>FullName</anno></c> is the full name of the file and <c><anno>Value</anno></c> the value of the last expression.</p> </item> <tag><c>{error, enoent}</c></tag> <item> - <p>The file could not be found in any of the directories in + <p>The file cannot be found in any of the directories in <c><anno>Path</anno></c>.</p> </item> <tag><c>{error, atom()}</c></tag> <item> <p>An error occurred when opening the file or reading it. - See <seealso marker="#open/2">open/2</seealso> for a list - of typical error codes.</p> + For a list of typical error codes, see + <seealso marker="#open/2"><c>open/2</c></seealso>.</p> </item> <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang - expressions in the file. Use <c>format_error/1</c> to - convert the three-element tuple to an English description + expressions in the file. Use + <seealso marker="#format_error/1"><c>format_error/1</c></seealso> + to convert the three-element tuple to an English description of the error.</p> </item> </taglist> - <p>The encoding of of <c><anno>Filename</anno></c> can be set - by a comment as described in <seealso - marker="stdlib:epp#encoding">epp(3)</seealso>.</p> + <p>The encoding of <c><anno>Filename</anno></c> can be set + by a comment as described in + <seealso marker="stdlib:epp#encoding"><c>stdlib:epp(3)</c></seealso>.</p> </desc> </func> <func> <name name="path_script" arity="3"/> - <fsummary>Evaluate and return the value of Erlang expressions in a file</fsummary> + <fsummary>Evaluate and return the value of Erlang expressions in a file.</fsummary> <desc> <p>The same as <c>path_script/2</c> but the variable bindings <c><anno>Bindings</anno></c> are used in the evaluation. See - <seealso marker="stdlib:erl_eval">erl_eval(3)</seealso> about + <seealso marker="stdlib:erl_eval"><c>erl_eval(3)</c></seealso> about variable bindings.</p> </desc> </func> <func> <name name="pid2name" arity="1"/> - <fsummary>Return the name of the file handled by a pid</fsummary> + <fsummary>Return the name of the file handled by a pid.</fsummary> <desc> - <p>If <c><anno>Pid</anno></c> is an IO device, that is, a pid returned from + <p>If <c><anno>Pid</anno></c> is an I/O device, that is, a pid returned from <c>open/2</c>, this function returns the filename, or rather:</p> <taglist> <tag><c>{ok, <anno>Filename</anno>}</c></tag> <item> - <p>If this node's file server is not a slave, the file was - opened by this node's file server, (this implies that + <p>If the file server of this node is not a slave, the file was + opened by the file server of this node (this implies that <c><anno>Pid</anno></c> must be a local pid) and the file is not closed. <c><anno>Filename</anno></c> is the filename in flat string format.</p> @@ -1084,13 +1169,12 @@ </func> <func> <name name="position" arity="2"/> - <fsummary>Set position in a file</fsummary> + <fsummary>Set position in a file.</fsummary> <desc> <p>Sets the position of the file referenced by <c><anno>IoDevice</anno></c> - to <c><anno>Location</anno></c>. Returns - <c>{ok, <anno>NewPosition</anno>}</c> (as - absolute offset) if successful, otherwise - <c>{error, <anno>Reason</anno>}</c>. <c><anno>Location</anno></c> is + to <c><anno>Location</anno></c>. Returns <c>{ok, <anno>NewPosition</anno>}</c> + (as absolute offset) if successful, otherwise + <c>{error, <anno>Reason</anno>}</c>. <c><anno>Location</anno></c> is one of the following:</p> <taglist> <tag><c>Offset</c></tag> @@ -1114,14 +1198,21 @@ <p>The same as above with <c>Offset</c> 0.</p> </item> </taglist> - <p>Note that offsets are counted in bytes, not in characters. If the file is opened using some other <c>encoding</c> than <c>latin1</c>, one byte does not correspond to one character. Positioning in such a file can only be done to known character boundaries, i.e. to a position earlier retrieved by getting a current position, to the beginning/end of the file or to some other position <em>known</em> to be on a correct character boundary by some other means (typically beyond a byte order mark in the file, which has a known byte-size).</p> - <p>Typical error reasons are:</p> + <p>Notice that offsets are counted in bytes, not in characters. If the file + is opened using some other <c>encoding</c> than <c>latin1</c>, one byte + does not correspond to one character. Positioning in such a file can only + be done to known character boundaries. That is, to a position earlier retrieved + by getting a current position, to the beginning/end of the file or to some + other position <em>known</em> to be on a correct character boundary by some + other means (typically beyond a byte order mark in the file, which has a + known byte-size).</p> + <p>A typical error reason is:</p> <taglist> <tag><c>einval</c></tag> <item> - <p>Either <c><anno>Location</anno></c> was illegal, or it + <p>Either <c><anno>Location</anno></c> is illegal, or it is evaluated to a - negative offset in the file. Note that if the resulting + negative offset in the file. Notice that if the resulting position is a negative value, the result is an error, and after the call the file position is undefined.</p> </item> @@ -1130,7 +1221,7 @@ </func> <func> <name name="pread" arity="2"/> - <fsummary>Read from a file at certain positions</fsummary> + <fsummary>Read from a file at certain positions.</fsummary> <desc> <p>Performs a sequence of <c>pread/3</c> in one operation, which is more efficient than calling them one at a time. @@ -1139,70 +1230,94 @@ where each <c><anno>Data</anno></c>, the result of the corresponding <c>pread</c>, is either a list or a binary depending on the mode of the file, or <c>eof</c> if the requested position - was beyond end of file.</p> - <p>As the position is given as a byte-offset, special caution has to be taken when working with files where <c>encoding</c> is set to something else than <c>latin1</c>, as not every byte position will be a valid character boundary on such a file.</p> + is beyond end of file.</p> + <p>As the position is specified as a byte-offset, take special caution + when working with files where <c>encoding</c> is set to something else + than <c>latin1</c>, as not every byte position is a valid character + boundary on such a file.</p> </desc> </func> <func> <name name="pread" arity="3"/> - <fsummary>Read from a file at a certain position</fsummary> + <fsummary>Read from a file at a certain position.</fsummary> <desc> <p>Combines <c>position/2</c> and <c>read/2</c> in one operation, which is more efficient than calling them one at a - time. If <c><anno>IoDevice</anno></c> has been opened in raw mode, - some restrictions apply: <c><anno>Location</anno></c> is only allowed - to be an - integer; and the current position of the file is undefined - after the operation.</p> - <p>As the position is given as a byte-offset, special caution has to be taken when working with files where <c>encoding</c> is set to something else than <c>latin1</c>, as not every byte position will be a valid character boundary on such a file.</p> + time. If <c><anno>IoDevice</anno></c> is opened in <c>raw</c> mode, + some restrictions apply:</p> + <list type="bulleted"> + <item><c><anno>Location</anno></c> is only allowed to be an + integer.</item> + <item>The current position of the file is undefined after the + operation.</item> + </list> + <p>As the position is specified as a byte-offset, take special caution + when working with files where <c>encoding</c> is set to something else + than <c>latin1</c>, as not every byte position is a valid character + boundary on such a file.</p> </desc> </func> <func> <name name="pwrite" arity="2"/> - <fsummary>Write to a file at certain positions</fsummary> + <fsummary>Write to a file at certain positions.</fsummary> <desc> <p>Performs a sequence of <c>pwrite/3</c> in one operation, which is more efficient than calling them one at a time. Returns <c>ok</c> or <c>{error, {<anno>N</anno>, <anno>Reason</anno>}}</c>, where - <c><anno>N</anno></c> is the number of successful writes that was done + <c><anno>N</anno></c> is the number of successful writes done before the failure.</p> - <p>When positioning in a file with other <c>encoding</c> than <c>latin1</c>, caution must be taken to set the position on a correct character boundary, see <seealso marker="#position/2">position/2</seealso> for details.</p> + <p>When positioning in a file with other <c>encoding</c> than <c>latin1</c>, + caution must be taken to set the position on a correct character boundary. + For details, see <seealso marker="#position/2"><c>position/2</c></seealso>.</p> </desc> </func> <func> <name name="pwrite" arity="3"/> - <fsummary>Write to a file at a certain position</fsummary> + <fsummary>Write to a file at a certain position.</fsummary> <desc> <p>Combines <c>position/2</c> and <c>write/2</c> in one operation, which is more efficient than calling them one at a - time. If <c><anno>IoDevice</anno></c> has been opened in raw mode, - some restrictions apply: <c><anno>Location</anno></c> is only allowed - to be an - integer; and the current position of the file is undefined - after the operation.</p> - <p>When positioning in a file with other <c>encoding</c> than <c>latin1</c>, caution must be taken to set the position on a correct character boundary, see <seealso marker="#position/2">position/2</seealso> for details.</p> + time. If <c><anno>IoDevice</anno></c> has been opened in <c>raw</c> mode, + some restrictions apply:</p> + <list type="bulleted"> + <item><c><anno>Location</anno></c> is only allowed to be an + integer.</item> + <item>The current position of the file is undefined after the + operation.</item> + </list> + <p>When positioning in a file with other <c>encoding</c> than <c>latin1</c>, + caution must be taken to set the position on a correct character boundary. + For details, see <seealso marker="#position/2"><c>position/2</c></seealso>.</p> </desc> </func> <func> <name name="read" arity="2"/> - <fsummary>Read from a file</fsummary> + <fsummary>Read from a file.</fsummary> <desc> <p>Reads <c><anno>Number</anno></c> bytes/characters from the file referenced by <c><anno>IoDevice</anno></c>. The functions - <c>read/2</c>, <c>pread/3</c> - and <c>read_line/1</c> are the only ways to read from a file - opened in raw mode (although they work for normally opened - files, too).</p> - <p>For files where <c>encoding</c> is set to something else than <c>latin1</c>, one character might be represented by more than one byte on the file. The parameter <c>Number</c> always denotes the number of <em>characters</em> read from the file, while the position in the file might be moved much more than this number when reading a Unicode file.</p> - <p>Also, if <c>encoding</c> is set to something else than <c>latin1</c>, the <c>read/3</c> call will fail if the data contains characters larger than 255, which is why the <seealso marker="stdlib:io">io(3)</seealso> module is to be preferred when reading such a file.</p> + <seealso marker="#read/2"><c>read/2</c></seealso>, + <seealso marker="#pread/3"><c>pread/3</c></seealso>, and + <seealso marker="#read_line/1"><c>read_line/1</c></seealso> + are the only ways to read from a file opened in <c>raw</c> mode + (although they work for normally opened files, too).</p> + <p>For files where <c>encoding</c> is set to something else than <c>latin1</c>, + one character can be represented by more than one byte on the file. + The parameter <c>Number</c> always denotes the number of <em>characters</em> + read from the file, while the position in the file can be moved much more than + this number when reading a Unicode file.</p> + <p>Also, if <c>encoding</c> is set to something else than <c>latin1</c>, + the <c>read/3</c> call fails if the data contains characters larger than 255, + which is why module <seealso marker="stdlib:io"><c>io(3)</c></seealso> + is to be preferred when reading such a file.</p> <p>The function returns:</p> <taglist> <tag><c>{ok, <anno>Data</anno>}</c></tag> <item> <p>If the file was opened in binary mode, the read bytes are returned in a binary, otherwise in a list. The list or - binary will be shorter than the number of bytes requested + binary is shorter than the number of bytes requested if end of file was reached.</p> </item> <tag><c>eof</c></tag> @@ -1223,14 +1338,16 @@ </item> <tag><c>{no_translation, unicode, latin1}</c></tag> <item> - <p>The file was opened with another <c>encoding</c> than <c>latin1</c> and the data in the file can not be translated to the byte-oriented data that this function returns.</p> + <p>The file is opened with another <c>encoding</c> than <c>latin1</c> and + the data in the file cannot be translated to the byte-oriented data that + this function returns.</p> </item> </taglist> </desc> </func> <func> <name name="read_file" arity="1"/> - <fsummary>Read a file</fsummary> + <fsummary>Read a file.</fsummary> <desc> <p>Returns <c>{ok, <anno>Binary</anno>}</c>, where <c><anno>Binary</anno></c> is a binary @@ -1254,7 +1371,7 @@ </item> <tag><c>enotdir</c></tag> <item> - <p>A component of the file name is not a directory. On some + <p>A component of the filename is not a directory. On some platforms, <c>enoent</c> is returned instead.</p> </item> <tag><c>enomem</c></tag> @@ -1267,34 +1384,38 @@ <func> <name name="read_file_info" arity="1"/> <name name="read_file_info" arity="2"/> - <fsummary>Get information about a file</fsummary> + <fsummary>Retrieve information about a file.</fsummary> <desc> <p>Retrieves information about a file. Returns <c>{ok, <anno>FileInfo</anno>}</c> if successful, otherwise <c>{error, <anno>Reason</anno>}</c>. <c><anno>FileInfo</anno></c> is a record - <c>file_info</c>, defined in the Kernel include file + <c>file_info</c>, defined in the <c>Kernel</c> include file <c>file.hrl</c>. Include the following directive in the module from which the function is called:</p> <code type="none"> --include_lib("kernel/include/file.hrl").</code> - <p>The time type returned in <c>atime</c>, <c>mtime</c> and <c>ctime</c> - is dependent on the time type set in <c>Opts :: {time, Type}</c>. - Type <c>local</c> will return local time, <c>universal</c> will - return universal time and <c>posix</c> will return seconds since - or before unix time epoch which is 1970-01-01 00:00 UTC. - Default is <c>{time, local}</c>. - </p> - <p>If the <c>raw</c> option is set, the file server will not be called - and only informations about local files will be returned.</p> - <note> - <p> - Since file times is stored in posix time on most OS it is - faster to query file information with the <c>posix</c> option. - </p> - </note> + -include_lib("kernel/include/file.hrl").</code> + <p>The time type returned in <c>atime</c>, <c>mtime</c>, and <c>ctime</c> + is dependent on the time type set in <c>Opts :: {time, Type}</c> as + follows:</p> + <taglist> + <tag><c>local</c></tag> + <item><p>Returns local time.</p></item> + <tag><c>universal</c></tag> + <item><p>Returns universal time.</p></item> + <tag><c>posix</c></tag> + <item><p>Returns seconds since or before Unix time epoch, + which is 1970-01-01 00:00 UTC.</p></item> + </taglist> + <p>Default is <c>{time, local}</c>.</p> + <p>If the option <c>raw</c> is set, the file server is not called + and only information about local files is returned.</p> + <note> + <p>As file times are stored in POSIX time on most OS, it is faster to + query file information with option <c>posix</c>.</p> + </note> - <p>The record <c>file_info</c> contains the following fields.</p> + <p>The record <c>file_info</c> contains the following fields:</p> <taglist> <tag><c>size = integer() >= 0</c></tag> <item> @@ -1308,19 +1429,25 @@ <item> <p>The current system access to the file.</p> </item> - <tag><c>atime = </c><seealso marker="#type-date_time">date_time()</seealso><c> | integer() >= 0</c></tag> + <tag><c>atime = </c> + <seealso marker="#type-date_time"><c>date_time()</c></seealso><c> | + integer() >= 0</c></tag> <item> <p>The last time the file was read.</p> </item> - <tag><c>mtime = </c><seealso marker="#type-date_time">date_time()</seealso><c> | integer() >= 0</c></tag> + <tag><c>mtime = </c> + <seealso marker="#type-date_time"><c>date_time()</c></seealso><c> | + integer() >= 0</c></tag> <item> <p>The last time the file was written.</p> </item> - <tag><c>ctime = </c><seealso marker="#type-date_time">date_time()</seealso><c> | integer() >=0</c></tag> + <tag><c>ctime = </c> + <seealso marker="#type-date_time"><c>date_time()</c></seealso><c> | + integer() >=0</c></tag> <item> <p>The interpretation of this time field depends on the operating system. On Unix, it is the last time - the file or the inode was changed. In Windows, it is + the file or the <c>inode</c> was changed. In Windows, it is the create time.</p> </item> <tag><c>mode = integer() >= 0</c></tag> @@ -1328,36 +1455,36 @@ <p>The file permissions as the sum of the following bit values:</p> <taglist> - <tag>8#00400</tag> - <item>read permission: owner</item> - <tag>8#00200</tag> - <item>write permission: owner</item> - <tag>8#00100</tag> - <item>execute permission: owner</item> - <tag>8#00040</tag> - <item>read permission: group</item> - <tag>8#00020</tag> - <item>write permission: group</item> - <tag>8#00010</tag> - <item>execute permission: group</item> - <tag>8#00004</tag> - <item>read permission: other</item> - <tag>8#00002</tag> - <item>write permission: other</item> - <tag>8#00001</tag> - <item>execute permission: other</item> - <tag>16#800</tag> - <item>set user id on execution</item> - <tag>16#400</tag> - <item>set group id on execution</item> + <tag><c>8#00400</c></tag> + <item><p>read permission: owner</p></item> + <tag><c>8#00200</c></tag> + <item><p>write permission: owner</p></item> + <tag><c>8#00100</c></tag> + <item><p>execute permission: owner</p></item> + <tag><c>8#00040</c></tag> + <item><p>read permission: group</p></item> + <tag><c>8#00020</c></tag> + <item><p>write permission: group</p></item> + <tag><c>8#00010</c></tag> + <item><p>execute permission: group</p></item> + <tag><c>8#00004</c></tag> + <item><p>read permission: other</p></item> + <tag><c>8#00002</c></tag> + <item><p>write permission: other</p></item> + <tag><c>8#00001</c></tag> + <item><p>execute permission: other</p></item> + <tag><c>16#800</c></tag> + <item><p>set user id on execution</p></item> + <tag><c>16#400</c></tag> + <item><p>set group id on execution</p></item> </taglist> - <p>On Unix platforms, other bits than those listed above - may be set.</p> + <p>On Unix platforms, the following bits + can also be set:</p> </item> <tag><c>links = integer() >= 0</c></tag> <item> - <p>Number of links to the file (this will always be 1 for - file systems which have no concept of links).</p> + <p>Number of links to the file (this is always 1 for + file systems that have no concept of links).</p> </item> <tag><c>major_device = integer() >= 0</c></tag> <item> @@ -1373,17 +1500,17 @@ <tag><c>inode = integer() >= 0</c></tag> <item> <p>Gives the <c>inode</c> number. On non-Unix file systems, - this field will be zero.</p> + this field is zero.</p> </item> <tag><c>uid = integer() >= 0</c></tag> <item> - <p>Indicates the owner of the file. Will be zero for - non-Unix file systems.</p> + <p>Indicates the owner of the file. On non-Unix file systems, + this field is zero.</p> </item> <tag><c>gid = integer() >= 0</c></tag> <item> <p>Gives the group that the owner of the file belongs to. - Will be zero for non-Unix file systems.</p> + On non-Unix file systems, this field is zero.</p> </item> </taglist> <p>Typical error reasons:</p> @@ -1399,7 +1526,7 @@ </item> <tag><c>enotdir</c></tag> <item> - <p>A component of the file name is not a directory. On some + <p>A component of the filename is not a directory. On some platforms, <c>enoent</c> is returned instead.</p> </item> </taglist> @@ -1407,18 +1534,34 @@ </func> <func> <name name="read_line" arity="1"/> - <fsummary>Read a line from a file</fsummary> + <fsummary>Read a line from a file.</fsummary> <desc> <p>Reads a line of bytes/characters from the file referenced by - <c><anno>IoDevice</anno></c>. Lines are defined to be delimited by the linefeed (LF, <c>\n</c>) character, but any carriage return (CR, <c>\r</c>) followed by a newline is also treated as a single LF character (the carriage return is silently ignored). The line is returned <em>including</em> the LF, but excluding any CR immediately followed by a LF. This behaviour is consistent with the behaviour of <seealso marker="stdlib:io#get_line/2">io:get_line/2</seealso>. If end of file is reached without any LF ending the last line, a line with no trailing LF is returned.</p> - <p>The function can be used on files opened in <c>raw</c> mode. It is however inefficient to use it on <c>raw</c> files if the file is not opened with the option <c>{read_ahead, Size}</c> specified, why combining <c>raw</c> and <c>{read_ahead, Size}</c> is highly recommended when opening a text file for raw line oriented reading.</p> - <p>If <c>encoding</c> is set to something else than <c>latin1</c>, the <c>read_line/1</c> call will fail if the data contains characters larger than 255, why the <seealso marker="stdlib:io">io(3)</seealso> module is to be preferred when reading such a file.</p> + <c><anno>IoDevice</anno></c>. Lines are defined to be delimited by the + linefeed (LF, <c>\n</c>) character, but any carriage return (CR, <c>\r</c>) + followed by a newline is also treated as a single LF character (the carriage + return is silently ignored). The line is returned <em>including</em> the LF, + but excluding any CR immediately followed by an LF. This behaviour is + consistent with the behaviour of + <seealso marker="stdlib:io#get_line/2"><c>io:get_line/2</c></seealso>. + If end of file is reached without any LF ending the last line, a line with no + trailing LF is returned.</p> + <p>The function can be used on files opened in <c>raw</c> mode. However, it is + inefficient to use it on <c>raw</c> files if the file is not opened with + option <c>{read_ahead, Size}</c> specified. Thus, combining <c>raw</c> and + <c>{read_ahead, Size}</c> is highly recommended when opening a text file for + raw line-oriented reading.</p> + <p>If <c>encoding</c> is set to something else than <c>latin1</c>, the + <c>read_line/1</c> call fails if the data contains characters larger than 255, + why module <seealso marker="stdlib:io"><c>stdlib:io(3)</c></seealso> is to be + preferred when reading such a file.</p> <p>The function returns:</p> <taglist> <tag><c>{ok, <anno>Data</anno>}</c></tag> <item> - <p>One line from the file is returned, including the trailing LF, but with CRLF sequences replaced by a single LF (see above).</p> - <p>If the file was opened in binary mode, the read bytes are + <p>One line from the file is returned, including the trailing LF, + but with CRLF sequences replaced by a single LF (see above).</p> + <p>If the file is opened in binary mode, the read bytes are returned in a binary, otherwise in a list.</p> </item> <tag><c>eof</c></tag> @@ -1439,22 +1582,24 @@ </item> <tag><c>{no_translation, unicode, latin1}</c></tag> <item> - <p>The file is was opened with another <c>encoding</c> than <c>latin1</c> and the data on the file can not be translated to the byte-oriented data that this function returns.</p> + <p>The file is opened with another <c>encoding</c> than <c>latin1</c> and + the data on the file cannot be translated to the byte-oriented data that + this function returns.</p> </item> </taglist> </desc> </func> <func> <name name="read_link" arity="1"/> - <fsummary>See what a link is pointing to</fsummary> + <fsummary>See what a link is pointing to.</fsummary> <desc> - <p><marker id="read_link_all"/>This function returns + <p><marker id="read_link_all"/>Returns <c>{ok, <anno>Filename</anno>}</c> if <c><anno>Name</anno></c> refers to a symbolic link that is - not a "raw" file name, or <c>{error, <anno>Reason</anno>}</c> + not a raw filename, or <c>{error, <anno>Reason</anno>}</c> otherwise. On platforms that do not support symbolic links, the return - value will be <c>{error,enotsup}</c>.</p> + value is <c>{error,enotsup}</c>.</p> <p>Typical error reasons:</p> <taglist> <tag><c>einval</c></tag> @@ -1476,14 +1621,14 @@ </func> <func> <name name="read_link_all" arity="1"/> - <fsummary>See what a link is pointing to</fsummary> + <fsummary>See what a link is pointing to.</fsummary> <desc> - <p>This function returns <c>{ok, <anno>Filename</anno>}</c> if + <p>Returns <c>{ok, <anno>Filename</anno>}</c> if <c><anno>Name</anno></c> refers to a symbolic link or <c>{error, <anno>Reason</anno>}</c> otherwise. On platforms that do not support symbolic links, the return - value will be <c>{error,enotsup}</c>.</p> - <p>Note that <c><anno>Filename</anno></c> can be either a list + value is <c>{error,enotsup}</c>.</p> + <p>Notice that <c><anno>Filename</anno></c> can be either a list or a binary.</p> <p>Typical error reasons:</p> <taglist> @@ -1505,31 +1650,30 @@ <func> <name name="read_link_info" arity="1"/> <name name="read_link_info" arity="2"/> - <fsummary>Get information about a link or file</fsummary> + <fsummary>Retrieve information about a link or file.</fsummary> <desc> - <p>This function works like - <seealso marker="#read_file_info/2">read_file_info/1,2</seealso> except that - if <c><anno>Name</anno></c> is a symbolic link, information about - the link will be returned in the <c>file_info</c> record and - the <c>type</c> field of the record will be set to - <c>symlink</c>.</p> - <p>If the <c>raw</c> option is set, the file server will not be called - and only informations about local files will be returned.</p> + <p>Works like + <seealso marker="#read_file_info/2"><c>read_file_info/1,2</c></seealso> + except that if <c><anno>Name</anno></c> is a symbolic link, information + about the link is returned in the <c>file_info</c> record and + the <c>type</c> field of the record is set to <c>symlink</c>.</p> + <p>If the option <c>raw</c> is set, the file server is not called + and only information about local files is returned.</p> <p>If <c><anno>Name</anno></c> is not a symbolic link, this function returns - exactly the same result as <c>read_file_info/1</c>. + the same result as <c>read_file_info/1</c>. On platforms that do not support symbolic links, this function is always equivalent to <c>read_file_info/1</c>.</p> </desc> </func> <func> <name name="rename" arity="2"/> - <fsummary>Rename a file</fsummary> + <fsummary>Rename a file.</fsummary> <desc> <p>Tries to rename the file <c><anno>Source</anno></c> to <c><anno>Destination</anno></c>. It can be used to move files (and directories) between directories, but it is not sufficient to specify - the destination only. The destination file name must also be + the destination only. The destination filename must also be specified. For example, if <c>bar</c> is a normal file and <c>foo</c> and <c>baz</c> are directories, <c>rename("foo/bar", "baz")</c> returns an error, but @@ -1560,7 +1704,7 @@ <item> <p><c><anno>Source</anno></c> is a root directory, or <c><anno>Destination</anno></c> - is a sub-directory of <c><anno>Source</anno></c>.</p> + is a subdirectory of <c><anno>Source</anno></c>.</p> </item> <tag><c>eisdir</c></tag> <item> @@ -1586,58 +1730,105 @@ </func> <func> <name name="script" arity="1"/> - <fsummary>Evaluate and return the value of Erlang expressions in a file</fsummary> + <fsummary>Evaluate and return the value of Erlang expressions in a file.</fsummary> <desc> <p>Reads and evaluates Erlang expressions, separated by '.' (or ',', a sequence of expressions is also an expression), from - the file. Returns one of the following:</p> + the file.</p> + <p>Returns one of the following:</p> <taglist> <tag><c>{ok, <anno>Value</anno>}</c></tag> <item> - <p>The file was read and evaluated. <c><anno>Value</anno></c> is + <p>The file is read and evaluated. <c><anno>Value</anno></c> is the value of the last expression.</p> </item> <tag><c>{error, atom()}</c></tag> <item> <p>An error occurred when opening the file or reading it. - See <seealso marker="#open/2">open/2</seealso> for a list - of typical error codes.</p> + For a list of typical error codes, see + <seealso marker="#open/2"><c>open/2</c></seealso>.</p> </item> <tag><c>{error, {<anno>Line</anno>, <anno>Mod</anno>, <anno>Term</anno>}}</c></tag> <item> <p>An error occurred when interpreting the Erlang - expressions in the file. Use <c>format_error/1</c> to - convert the three-element tuple to an English description + expressions in the file. Use + <seealso marker="#format_error/1"><c>format_error/1</c></seealso> + to convert the three-element tuple to an English description of the error.</p> </item> </taglist> - <p>The encoding of of <c><anno>Filename</anno></c> can be set - by a comment as described in <seealso - marker="stdlib:epp#encoding">epp(3)</seealso>.</p> + <p>The encoding of <c><anno>Filename</anno></c> can be set + by a comment as described in + <seealso marker="stdlib:epp#encoding"><c>epp(3)</c></seealso>.</p> </desc> </func> <func> <name name="script" arity="2"/> - <fsummary>Evaluate and return the value of Erlang expressions in a file</fsummary> + <fsummary>Evaluate and return the value of Erlang expressions in a file.</fsummary> <desc> <p>The same as <c>script/1</c> but the variable bindings <c><anno>Bindings</anno></c> are used in the evaluation. See - <seealso marker="stdlib:erl_eval">erl_eval(3)</seealso> about + <seealso marker="stdlib:erl_eval"><c>erl_eval(3)</c></seealso> about variable bindings.</p> </desc> </func> <func> + <name name="sendfile" arity="2"/> + <fsummary>Send a file to a socket.</fsummary> + <desc> + <p>Sends the file <c>Filename</c> to <c>Socket</c>. + Returns <c>{ok, BytesSent}</c> if successful, + otherwise <c>{error, Reason}</c>.</p> + </desc> + </func> + <func> + <name name="sendfile" arity="5"/> + <fsummary>Send a file to a socket.</fsummary> + <type name="sendfile_option"/> + <desc> + <p>Sends <c>Bytes</c> from the file + referenced by <c>RawFile</c> beginning at <c>Offset</c> to + <c>Socket</c>. + Returns <c>{ok, BytesSent}</c> if successful, + otherwise <c>{error, Reason}</c>. If <c>Bytes</c> is set to + <c>0</c> all data after the specified <c>Offset</c> is sent.</p> + <p>The file used must be opened using the <c>raw</c> flag, and the process + calling <c>sendfile</c> must be the controlling process of the socket. + See <seealso marker="gen_tcp#controlling_process-2"><c>gen_tcp:controlling_process/2</c></seealso>.</p> + <p>If the OS used does not support <c>sendfile</c>, an Erlang fallback + using + <seealso marker="#read/2"><c>read/2</c></seealso> and + <seealso marker="gen_tcp#send/2"><c>gen_tcp:send/2</c></seealso> is used.</p> + <p>The option list can contain the following options:</p> + <taglist> + <tag><c>chunk_size</c></tag> + <item><p>The chunk size used by the Erlang fallback to send + data. If using the fallback, set this to a value + that comfortably fits in the systems memory. Default is 20 MB.</p></item> + <tag><c>use_threads</c></tag> + <item><p>Instructs the emulator to use the <c>async</c> thread pool for the + <c>sendfile</c> system call. This can be useful if the OS you are running + on does not properly support non-blocking <c>sendfile</c> calls. Notice that + using <c>async</c> threads potentially makes your system vulnerable to slow + client attacks. If set to <c>true</c> and no <c>async</c> threads are available, + the <c>sendfile</c> call returns <c>{error,einval}</c>. + Introduced in Erlang/OTP 17.0. Default is <c>false</c>.</p></item> + </taglist> + </desc> + </func> + <func> <name name="set_cwd" arity="1"/> - <fsummary>Set the current working directory</fsummary> + <fsummary>Set the current working directory.</fsummary> <desc> <p>Sets the current working directory of the file server to <c><anno>Dir</anno></c>. Returns <c>ok</c> if successful.</p> - <p>The functions in the <c>file</c> module usually treat binaries - as raw filenames, i.e. they are passed as is even when the encoding - of the binary does not agree with <c>file:native_name_encoding()</c>. - This function however expects binaries to be encoded according to the - value returned by <c>file:native_name_encoding()</c>.</p> + <p>The functions in the module <c>file</c> usually treat binaries + as raw filenames, that is, they are passed "as is" even when the + encoding of the binary does not agree with + <seealso marker="#native_name_encoding"><c>native_name_encoding()</c></seealso>. + However, this function expects binaries to be encoded according to the + value returned by <c>native_name_encoding()</c>.</p> <p>Typical error reasons are:</p> <taglist> <tag><c>enoent</c></tag> @@ -1656,31 +1847,31 @@ </item> <tag><c>badarg</c></tag> <item> - <p><c><anno>Dir</anno></c> had an improper type, + <p><c><anno>Dir</anno></c> has an improper type, such as tuple.</p> </item> <tag><c>no_translation</c></tag> <item> <p><c><anno>Dir</anno></c> is a <c>binary()</c> with characters coded in ISO-latin-1 and the VM is operating - with unicode file name encoding.</p> + with unicode filename encoding.</p> </item> </taglist> <warning> - <p>In a future release, a bad type for the + <p>In a future release, a bad type for argument <c><anno>Dir</anno></c> - argument will probably generate an exception.</p> + will probably generate an exception.</p> </warning> </desc> </func> <func> <name name="sync" arity="1"/> - <fsummary>Synchronizes the in-memory state of a file with that on the physical medium</fsummary> + <fsummary>Synchronize the in-memory state of a file with that on the physical medium.</fsummary> <desc> - <p>Makes sure that any buffers kept by the operating system + <p>Ensures that any buffers kept by the operating system (not by the Erlang runtime system) are written to disk. On some platforms, this function might have no effect.</p> - <p>Typical error reasons are:</p> + <p>A typical error reason is:</p> <taglist> <tag><c>enospc</c></tag> <item> @@ -1690,90 +1881,28 @@ </desc> </func> <func> - <name name="datasync" arity="1"/> - <fsummary>Synchronizes the in-memory data of a file, ignoring most of its metadata, with that on the physical medium</fsummary> - <desc> - <p>Makes sure that any buffers kept by the operating system - (not by the Erlang runtime system) are written to disk. In - many ways it resembles fsync but it does not update - some of the file's metadata such as the access time. On - some platforms this function has no effect.</p> - <p>Applications that access databases or log files often write - a tiny data fragment (e.g., one line in a log file) and then - call fsync() immediately in order to ensure that the written - data is physically stored on the harddisk. Unfortunately, fsync() - will always initiate two write operations: one for the newly - written data and another one in order to update the modification - time stored in the inode. If the modification time is not a part - of the transaction concept, fdatasync() can be used to avoid - unnecessary inode disk write operations.</p> - <p>Available only in some POSIX systems, this call results in a - call to fsync(), or has no effect in systems not implementing - the fdatasync() syscall.</p> - </desc> - </func> - <func> <name name="truncate" arity="1"/> - <fsummary>Truncate a file</fsummary> + <fsummary>Truncate a file.</fsummary> <desc> <p>Truncates the file referenced by <c><anno>IoDevice</anno></c> at - the current position. Returns <c>ok</c> if successful, + the current position. Returns <c>ok</c> if successful, otherwise <c>{error, <anno>Reason</anno>}</c>.</p> </desc> </func> <func> - <name name="sendfile" arity="2"/> - <fsummary>send a file to a socket</fsummary> - <desc> - <p>Sends the file <c>Filename</c> to <c>Socket</c>. - Returns <c>{ok, BytesSent}</c> if successful, - otherwise <c>{error, Reason}</c>.</p> - </desc> - </func> - <func> - <name name="sendfile" arity="5"/> - <fsummary>send a file to a socket</fsummary> - <type name="sendfile_option"/> - <desc> - <p>Sends <c>Bytes</c> from the file - referenced by <c>RawFile</c> beginning at <c>Offset</c> to - <c>Socket</c>. - Returns <c>{ok, BytesSent}</c> if successful, - otherwise <c>{error, Reason}</c>. If <c>Bytes</c> is set to - 0 all data after the given <c>Offset</c> is sent.</p> - <p>The file used must be opened using the raw flag, and the process - calling sendfile must be the controlling process of the socket. - See <seealso marker="gen_tcp#controlling_process-2">gen_tcp:controlling_process/2</seealso></p> - <p>If the OS used does not support sendfile, an Erlang fallback - using file:read and gen_tcp:send is used.</p> - <p>The option list can contain the following options:</p> - <taglist> - <tag><c>chunk_size</c></tag> - <item>The chunk size used by the erlang fallback to send - data. If using the fallback, this should be set to a value - which comfortably fits in the systems memory. Default is 20 MB.</item> - <tag><c>use_threads</c></tag> - <item>Instruct the emulator to use the async thread pool for the - sendfile system call. This could be usefull if the OS you are running - on does not properly support non-blocking sendfile calls. Do note that - using async threads potentially makes your system volnerable to slow - client attacks. If set to true and no async threads are available, - the sendfile call will return <c>{error,einval}</c>. - Introduced in Erlang/OTP 17.0. Default is false.</item> - </taglist> - </desc> - </func> - <func> <name name="write" arity="2"/> - <fsummary>Write to a file</fsummary> + <fsummary>Write to a file.</fsummary> <desc> <p>Writes <c><anno>Bytes</anno></c> to the file referenced by <c><anno>IoDevice</anno></c>. This function is the only way to write to a - file opened in raw mode (although it works for normally - opened files, too). Returns <c>ok</c> if successful, and + file opened in <c>raw</c> mode (although it works for normally opened + files too). Returns <c>ok</c> if successful, and <c>{error, <anno>Reason</anno>}</c> otherwise.</p> - <p>If the file is opened with <c>encoding</c> set to something else than <c>latin1</c>, each byte written might result in several bytes actually being written to the file, as the byte range 0..255 might represent anything between one and four bytes depending on value and UTF encoding type.</p> - <p>Typical error reasons are:</p> + <p>If the file is opened with <c>encoding</c> set to something else than + <c>latin1</c>, each byte written can result in many bytes being written to + the file, as the byte range 0..255 can represent anything between one and + four bytes depending on value and UTF encoding type.</p> + <p>Typical error reasons:</p> <taglist> <tag><c>ebadf</c></tag> <item> @@ -1781,34 +1910,35 @@ </item> <tag><c>enospc</c></tag> <item> - <p>There is a no space left on the device.</p> + <p>No space is left on the device.</p> </item> </taglist> </desc> </func> <func> <name name="write_file" arity="2"/> - <fsummary>Write a file</fsummary> + <fsummary>Write a file.</fsummary> <desc> - <p>Writes the contents of the iodata term <c><anno>Bytes</anno></c> - to the file <c><anno>Filename</anno></c>. - The file is created if it does not - exist. If it exists, the previous contents are - overwritten. Returns <c>ok</c>, or <c>{error, <anno>Reason</anno>}</c>.</p> - <p>Typical error reasons are:</p> + <p>Writes the contents of the <c>iodata</c> term <c><anno>Bytes</anno></c> + to file <c><anno>Filename</anno></c>. + The file is created if it does not exist. + If it exists, the previous contents are overwritten. + Returns <c>ok</c> if successful, otherwise + <c>{error, <anno>Reason</anno>}</c>.</p> + <p>Typical error reasons:</p> <taglist> <tag><c>enoent</c></tag> <item> - <p>A component of the file name does not exist.</p> + <p>A component of the filename does not exist.</p> </item> <tag><c>enotdir</c></tag> <item> - <p>A component of the file name is not a directory. On some + <p>A component of the filename is not a directory. On some platforms, <c>enoent</c> is returned instead.</p> </item> <tag><c>enospc</c></tag> <item> - <p>There is a no space left on the device.</p> + <p>No space is left on the device.</p> </item> <tag><c>eacces</c></tag> <item> @@ -1824,51 +1954,64 @@ </func> <func> <name name="write_file" arity="3"/> - <fsummary>Write a file</fsummary> + <fsummary>Write a file.</fsummary> <desc> <p>Same as <c>write_file/2</c>, but takes a third argument <c><anno>Modes</anno></c>, a list of possible modes, see - <seealso marker="#open/2">open/2</seealso>. The mode flags - <c>binary</c> and <c>write</c> are implicit, so they should - not be used.</p> + <seealso marker="#open/2"><c>open/2</c></seealso>. The mode flags + <c>binary</c> and <c>write</c> are implicit, so they are + not to be used.</p> </desc> </func> <func> <name name="write_file_info" arity="2"/> <name name="write_file_info" arity="3"/> - <fsummary>Change information about a file</fsummary> + <fsummary>Change file information.</fsummary> <desc> - <p>Change file information. Returns <c>ok</c> if successful, + <p>Changes file information. Returns <c>ok</c> if successful, otherwise <c>{error, <anno>Reason</anno>}</c>. <c><anno>FileInfo</anno></c> is a record - <c>file_info</c>, defined in the Kernel include file + <c>file_info</c>, defined in the <c>Kernel</c> include file <c>file.hrl</c>. Include the following directive in the module from which the function is called:</p> <code type="none"> --include_lib("kernel/include/file.hrl").</code> - <p>The time type set in <c>atime</c>, <c>mtime</c> and <c>ctime</c> - is dependent on the time type set in <c>Opts :: {time, Type}</c>. - Type <c>local</c> will interpret the time set as local, <c>universal</c> will - interpret it as universal time and <c>posix</c> must be seconds since - or before unix time epoch which is 1970-01-01 00:00 UTC. - Default is <c>{time, local}</c>.</p> - <p>If the <c>raw</c> option is set, the file server will not be called - and only informations about local files will be returned.</p> + -include_lib("kernel/include/file.hrl").</code> + <p>The time type set in <c>atime</c>, <c>mtime</c>, and <c>ctime</c> + depends on the time type set in <c>Opts :: {time, Type}</c> as + follows:</p> + <taglist> + <tag><c>local</c></tag> + <item><p>Interprets the time set as local.</p></item> + <tag><c>universal</c></tag> + <item><p>Interprets it as universal time.</p></item> + <tag><c>posix</c></tag> + <item><p>Must be seconds since or before Unix time epoch, + which is 1970-01-01 00:00 UTC.</p></item> + </taglist> + <p>Default is <c>{time, local}</c>.</p> + <p>If the option <c>raw</c> is set, the file server is not called + and only information about local files is returned.</p> <p>The following fields are used from the record, if they are - given.</p> + specified:</p> <taglist> - <tag><c>atime = </c><seealso marker="#type-date_time">date_time()</seealso><c> | integer() >= 0</c></tag> + <tag><c>atime = </c> + <seealso marker="#type-date_time"><c>date_time()</c></seealso><c> | + integer() >= 0</c></tag> <item> <p>The last time the file was read.</p> </item> - <tag><c>mtime = </c><seealso marker="#type-date_time">date_time()</seealso><c> | integer() >= 0</c></tag> + <tag><c>mtime = </c> + <seealso marker="#type-date_time"><c>date_time()</c></seealso><c> | + integer() >= 0</c></tag> <item> <p>The last time the file was written.</p> </item> - <tag><c>ctime = </c><seealso marker="#type-date_time">date_time()</seealso><c> | integer() >= 0</c></tag> + <tag><c>ctime = </c> + <seealso marker="#type-date_time"><c>date_time()</c></seealso><c> | + integer() >= 0</c></tag> <item> - <p>On Unix, any value give for this field will be ignored - (the "ctime" for the file will be set to the current + <p>On Unix, any value specified for this field is ignored + (the "ctime" for the file is set to the current time). On Windows, this field is the new creation time to set for the file.</p> </item> @@ -1877,40 +2020,40 @@ <p>The file permissions as the sum of the following bit values:</p> <taglist> - <tag>8#00400</tag> - <item>read permission: owner</item> - <tag>8#00200</tag> - <item>write permission: owner</item> - <tag>8#00100</tag> - <item>execute permission: owner</item> - <tag>8#00040</tag> - <item>read permission: group</item> - <tag>8#00020</tag> - <item>write permission: group</item> - <tag>8#00010</tag> - <item>execute permission: group</item> - <tag>8#00004</tag> - <item>read permission: other</item> - <tag>8#00002</tag> - <item>write permission: other</item> - <tag>8#00001</tag> - <item>execute permission: other</item> - <tag>16#800</tag> - <item>set user id on execution</item> - <tag>16#400</tag> - <item>set group id on execution</item> + <tag><c>8#00400</c></tag> + <item><p>Read permission: owner</p></item> + <tag><c>8#00200</c></tag> + <item><p>Write permission: owner</p></item> + <tag><c>8#00100</c></tag> + <item><p>Execute permission: owner</p></item> + <tag><c>8#00040</c></tag> + <item><p>Read permission: group</p></item> + <tag><c>8#00020</c></tag> + <item><p>Write permission: group</p></item> + <tag><c>8#00010</c></tag> + <item><p>Execute permission: group</p></item> + <tag><c>8#00004</c></tag> + <item><p>Read permission: other</p></item> + <tag><c>8#00002</c></tag> + <item><p>Write permission: other</p></item> + <tag><c>8#00001</c></tag> + <item><p>Execute permission: other</p></item> + <tag><c>16#800</c></tag> + <item><p>Set user id on execution</p></item> + <tag><c>16#400</c></tag> + <item><p>Set group id on execution</p></item> </taglist> - <p>On Unix platforms, other bits than those listed above - may be set.</p> + <p>On Unix platforms, the following bits + can also be set.</p> </item> <tag><c>uid = integer() >= 0</c></tag> <item> - <p>Indicates the owner of the file. Ignored for non-Unix + <p>Indicates the file owner. Ignored for non-Unix file systems.</p> </item> <tag><c>gid = integer() >= 0</c></tag> <item> - <p>Gives the group that the owner of the file belongs to. + <p>Gives the group that the file owner belongs to. Ignored for non-Unix file systems.</p> </item> </taglist> @@ -1927,7 +2070,7 @@ </item> <tag><c>enotdir</c></tag> <item> - <p>A component of the file name is not a directory. On some + <p>A component of the filename is not a directory. On some platforms, <c>enoent</c> is returned instead.</p> </item> </taglist> @@ -1938,77 +2081,82 @@ <section> <title>POSIX Error Codes</title> <list type="bulleted"> - <item><c>eacces</c> - permission denied</item> - <item><c>eagain</c> - resource temporarily unavailable</item> - <item><c>ebadf</c> - bad file number</item> - <item><c>ebusy</c> - file busy</item> - <item><c>edquot</c> - disk quota exceeded</item> - <item><c>eexist</c> - file already exists</item> - <item><c>efault</c> - bad address in system call argument</item> - <item><c>efbig</c> - file too large</item> - <item><c>eintr</c> - interrupted system call</item> - <item><c>einval</c> - invalid argument</item> - <item><c>eio</c> - IO error</item> - <item><c>eisdir</c> - illegal operation on a directory</item> - <item><c>eloop</c> - too many levels of symbolic links</item> - <item><c>emfile</c> - too many open files</item> - <item><c>emlink</c> - too many links</item> - <item><c>enametoolong</c> - file name too long</item> - <item><c>enfile</c> - file table overflow</item> - <item><c>enodev</c> - no such device</item> - <item><c>enoent</c> - no such file or directory</item> - <item><c>enomem</c> - not enough memory</item> - <item><c>enospc</c> - no space left on device</item> - <item><c>enotblk</c> - block device required</item> - <item><c>enotdir</c> - not a directory</item> - <item><c>enotsup</c> - operation not supported</item> - <item><c>enxio</c> - no such device or address</item> - <item><c>eperm</c> - not owner</item> - <item><c>epipe</c> - broken pipe</item> - <item><c>erofs</c> - read-only file system</item> - <item><c>espipe</c> - invalid seek</item> - <item><c>esrch</c> - no such process</item> - <item><c>estale</c> - stale remote file handle</item> - <item><c>exdev</c> - cross-domain link</item> + <item><c>eacces</c> - Permission denied</item> + <item><c>eagain</c> - Resource temporarily unavailable</item> + <item><c>ebadf</c> - Bad file number</item> + <item><c>ebusy</c> - File busy</item> + <item><c>edquot</c> - Disk quota exceeded</item> + <item><c>eexist</c> - File already exists</item> + <item><c>efault</c> - Bad address in system call argument</item> + <item><c>efbig</c> - File too large</item> + <item><c>eintr</c> - Interrupted system call</item> + <item><c>einval</c> - Invalid argument</item> + <item><c>eio</c> - I/O error</item> + <item><c>eisdir</c> - Illegal operation on a directory</item> + <item><c>eloop</c> - Too many levels of symbolic links</item> + <item><c>emfile</c> - Too many open files</item> + <item><c>emlink</c> - Too many links</item> + <item><c>enametoolong</c> - Filename too long</item> + <item><c>enfile</c> - File table overflow</item> + <item><c>enodev</c> - No such device</item> + <item><c>enoent</c> - No such file or directory</item> + <item><c>enomem</c> - Not enough memory</item> + <item><c>enospc</c> - No space left on device</item> + <item><c>enotblk</c> - Block device required</item> + <item><c>enotdir</c> - Not a directory</item> + <item><c>enotsup</c> - Operation not supported</item> + <item><c>enxio</c> - No such device or address</item> + <item><c>eperm</c> - Not owner</item> + <item><c>epipe</c> - Broken pipe</item> + <item><c>erofs</c> - Read-only file system</item> + <item><c>espipe</c> - Invalid seek</item> + <item><c>esrch</c> - No such process</item> + <item><c>estale</c> - Stale remote file handle</item> + <item><c>exdev</c> - Cross-domain link</item> </list> </section> <section> <title>Performance</title> - <p>Some operating system file operations, for example a - <c>sync/1</c> or <c>close/1</c> on a huge file, may block their - calling thread for seconds. If this befalls the emulator main + <p>Some operating system file operations, for example, a + <c>sync/1</c> or <c>close/1</c> on a huge file, can block their + calling thread for seconds. If this affects the emulator main thread, the response time is no longer in the order of milliseconds, depending on the definition of "soft" in soft real-time system.</p> <p>If the device driver thread pool is active, file operations are done through those threads instead, so the emulator can go on executing Erlang processes. Unfortunately, the time for serving a - file operation increases due to the extra scheduling required + file operation increases because of the extra scheduling required from the operating system.</p> <p>If the device driver thread pool is disabled or of size 0, large - file reads and writes are segmented into several smaller, which - enables the emulator so server other processes during the file - operation. This gives the same effect as when using the thread + file reads and writes are segmented into many smaller, which + enable the emulator to serve other processes during the file + operation. This has the same effect as when using the thread pool, but with larger overhead. Other file operations, for - example <c>sync/1</c> or <c>close/1</c> on a huge file, still are + example, <c>sync/1</c> or <c>close/1</c> on a huge file, still are a problem.</p> <p>For increased performance, raw files are recommended. Raw files - uses the file system of the node's host machine. For normal files - (non-raw), the file server is used to find the files, and if - the node is running its file server as slave to another node's, - and the other node runs on some other host machine, they may have - different file systems. This is seldom a problem, but you have - now been warned.</p> - <p>A normal file is really a process so it can be used as an IO - device (see <c>io</c>). Therefore when data is written to a + use the file system of the host machine of the node.</p> + <note> + <p> + For normal files (non-raw), the file server is used to find the files, + and if the node is running its file server as slave to the file server + of another node, and the other node runs on some other host machine, + they can have different file systems. + However, this is seldom a problem.</p> + </note> + <p>A normal file is really a process so it can be used as an I/O + device (see + <seealso marker="stdlib:io"><c>io</c></seealso>). + Therefore, when data is written to a normal file, the sending of the data to the file process, copies all data that are not binaries. Opening the file in binary mode and writing binaries is therefore recommended. If the file is opened on another node, or if the file server runs as slave to - another node's, also binaries are copied.</p> + the file server of another node, also binaries are copied.</p> <p>Caching data to reduce the number of file operations, or rather - the number of calls to the file driver, will generally increase + the number of calls to the file driver, generally increases performance. The following function writes 4 MBytes in 23 seconds when tested:</p> <code type="none"><![CDATA[ @@ -2023,10 +2171,12 @@ create_file_slow(FD, M, M) -> create_file_slow(FD, M, N) -> ok = file:write(FD, <<M:32/unsigned>>), create_file_slow(FD, M+1, N).]]></code> + <p>The following, functionally equivalent, function collects 1024 entries into a list of 128 32-byte binaries before each call to - <c>file:write/2</c> and so does the same work in 0.52 seconds, - which is 44 times faster.</p> + <seealso marker="#write/2"><c>write/2</c></seealso> and so + does the same work in 0.52 seconds, + which is 44 times faster:</p> <code type="none"><![CDATA[ create_file(Name, N) when integer(N), N >= 0 -> {ok, FD} = file:open(Name, [raw, write, delayed_write, binary]), @@ -2055,61 +2205,62 @@ create_file(FD, M, N0, R) when M + 8 =< N0 -> create_file(FD, M, N0, R) -> N1 = N0-1, create_file(FD, M, N1, [<<N1:32/unsigned>> | R]).]]></code> + <note> <p>Trust only your own benchmarks. If the list length in - <c>create_file/2</c> above is increased, it will run slightly - faster, but consume more memory and cause more memory + <c>create_file/2</c> above is increased, it runs slightly + faster, but consumes more memory and causes more memory fragmentation. How much this affects your application is - something that this simple benchmark can not predict.</p> - <p>If the size of each binary is increased to 64 bytes, it will - also run slightly faster, but the code will be twice as clumsy. - In the current implementation are binaries larger than 64 bytes + something that this simple benchmark cannot predict.</p> + <p>If the size of each binary is increased to 64 bytes, it + also runs slightly faster, but the code is then twice as clumsy. + In the current implementation, binaries larger than 64 bytes are stored in memory common to all processes and not copied when sent between processes, while these smaller binaries are stored on the process heap and copied when sent like any other term.</p> - <p>So, with a binary size of 68 bytes <c>create_file/2</c> runs - 30 percent slower then with 64 bytes, and will cause much more - memory fragmentation. Note that if the binaries were to be sent - between processes (for example a non-raw file) the results + <p>So, with a binary size of 68 bytes, <c>create_file/2</c> runs + 30 percent slower than with 64 bytes, and causes much more + memory fragmentation. Notice that if the binaries were to be sent + between processes (for example, a non-raw file), the results would probably be completely different.</p> </note> <p>A raw file is really a port. When writing data to a port, it is - efficient to write a list of binaries. There is no need to + efficient to write a list of binaries. It is not needed to flatten a deep list before writing. On Unix hosts, scatter output, which writes a set of buffers in one operation, is used when - possible. In this way <c>file:write(FD, [Bin1, Bin2 | Bin3])</c> - will write the contents of the binaries without copying the data - at all except for perhaps deep down in the operating system + possible. In this way <c>write(FD, [Bin1, Bin2 | Bin3])</c> + writes the contents of the binaries without copying the data + at all, except for perhaps deep down in the operating system kernel.</p> <p>For raw files, <c>pwrite/2</c> and <c>pread/2</c> are efficiently implemented. The file driver is called only once for the whole operation, and the list iteration is done in the file driver.</p> <p>The options <c>delayed_write</c> and <c>read_ahead</c> to - <c>file:open/2</c> makes the file driver cache data to reduce + <seealso marker="#open/2"><c>open/2</c></seealso> + make the file driver cache data to reduce the number of operating system calls. The function - <c>create_file/2</c> in the example above takes 60 seconds - seconds without the <c>delayed_write</c> option, which is 2.6 + <c>create_file/2</c> in the recent example takes 60 seconds + without option <c>delayed_write</c>, which is 2.6 times slower.</p> - <p>And, as a really bad example, <c>create_file_slow/2</c> above - without the <c>raw</c>, <c>binary</c> and <c>delayed_write</c> - options, that is it calls <c>file:open(Name, [write])</c>, needs + <p>As a bad example, <c>create_file_slow/2</c> + without options <c>raw</c>, <c>binary</c>, and <c>delayed_write</c>, + meaning it calls <c>open(Name, [write])</c>, needs 1 min 20 seconds for the job, which is 3.5 times slower than the first example, and 150 times slower than the optimized - <c>create_file/2</c>. </p> - </section> - - <section> - <title>Warnings</title> - <p>If an error occurs when accessing an open file with the <c>io</c> - module, the process which handles the file will exit. The dead - file process might hang if a process tries to access it later. + <c>create_file/2</c>.</p> + <warning> + <p>If an error occurs when accessing an open file with module + <seealso marker="stdlib:io"><c>io</c></seealso>, + the process handling the file exits. The dead + file process can hang if a process tries to access it later. This will be fixed in a future release.</p> + </warning> </section> <section> - <title>SEE ALSO</title> - <p><seealso marker="stdlib:filename">filename(3)</seealso></p> + <title>See Also</title> + <p><seealso marker="stdlib:filename"><c>filename(3)</c></seealso></p> </section> </erlref> diff --git a/lib/kernel/doc/src/gen_sctp.xml b/lib/kernel/doc/src/gen_sctp.xml index 3a50d4cbe0..57307cd594 100644 --- a/lib/kernel/doc/src/gen_sctp.xml +++ b/lib/kernel/doc/src/gen_sctp.xml @@ -30,68 +30,60 @@ <checked></checked> <date>2007-03-21</date> <rev>A</rev> - <file>gen_sctp.sgml</file> + <file>gen_sctp.xml</file> </header> <module>gen_sctp</module> - <modulesummary>The gen_sctp module provides functions for communicating with sockets using the SCTP protocol.</modulesummary> + <modulesummary>Functions for communicating with sockets using the SCTP + protocol.</modulesummary> <description> - <p>The <c>gen_sctp</c> module provides functions for communicating with + <p>This module provides functions for communicating with sockets using the SCTP protocol. The implementation assumes that the OS kernel supports SCTP - <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">(RFC2960)</url> through the user-level - <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions.</url> - During development this implementation was tested on - Linux Fedora Core 5.0 (kernel 2.6.15-2054 or later is needed), - and on Solaris 10, 11. During OTP adaptation it was tested on - SUSE Linux Enterprise Server 10 (x86_64) kernel 2.6.16.27-0.6-smp, - with lksctp-tools-1.0.6, briefly on Solaris 10, and later on - SUSE Linux Enterprise Server 10 Service Pack 1 (x86_64) - kernel 2.6.16.54-0.2.3-smp with lksctp-tools-1.0.7, - and later also on FreeBSD 8.2. - </p> - <p> - This module was written for one-to-many style sockets + <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">(RFC 2960)</url> + through the user-level + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions</url>.</p> + <p>During development, this implementation was tested on:</p> + <list type="bulleted"> + <item>Linux Fedora Core 5.0 (kernel 2.6.15-2054 or later is needed)</item> + <item>Solaris 10, 11</item> + </list> + <p>During OTP adaptation it was tested on:</p> + <list type="bulleted"> + <item>SUSE Linux Enterprise Server 10 (x86_64) kernel 2.6.16.27-0.6-smp, + with lksctp-tools-1.0.6</item> + <item>Briefly on Solaris 10</item> + <item>SUSE Linux Enterprise Server 10 Service Pack 1 (x86_64) + kernel 2.6.16.54-0.2.3-smp with lksctp-tools-1.0.7</item> + <item>FreeBSD 8.2</item> + </list> + <p>This module was written for one-to-many style sockets (type <c>seqpacket</c>). With the addition of - <seealso marker="#peeloff/2">peeloff/2</seealso>, one-to-one style - sockets (type <c>stream</c>) were introduced. - </p> - <p>Record definitions for the <c>gen_sctp</c> module can be found using:</p> -<pre> -include_lib("kernel/include/inet_sctp.hrl"). </pre> + <seealso marker="#peeloff/2"><c>peeloff/2</c></seealso>, + one-to-one style sockets (type <c>stream</c>) were introduced.</p> + <p>Record definitions for this module can be found using:</p> + <pre> +-include_lib("kernel/include/inet_sctp.hrl").</pre> <p>These record definitions use the "new" spelling 'adaptation', not the deprecated 'adaption', regardless of which spelling the underlying C API uses.</p> </description> - <section> - <marker id="contents"></marker> - <title>CONTENTS</title> - <list type="bulleted"> - <item><seealso marker="#types">DATA TYPES</seealso></item> - <item><seealso marker="#exports">EXPORTS</seealso></item> - <item><seealso marker="#options">SCTP SOCKET OPTIONS</seealso></item> - <item><seealso marker="#examples">SCTP EXAMPLES</seealso></item> - <item><seealso marker="#seealso">SEE ALSO</seealso></item> - </list> - <marker id="types"></marker> - </section> - <datatypes> <datatype> <name>assoc_id()</name> <desc> <p><marker id="type-assoc_id"/> - An opaque term returned in for example #sctp_paddr_change{} - that identifies an association for an SCTP socket. The term - is opaque except for the special value <c>0</c> that has a - meaning such as "the whole endpoint" or "all future associations". - </p> + An opaque term returned in, for example, <c>#sctp_paddr_change{}</c>, + which identifies an association for an SCTP socket. The term + is opaque except for the special value <c>0</c>, which has a + meaning such as "the whole endpoint" or "all future associations".</p> </desc> </datatype> <datatype> <name name="option"/> <desc> - <p>One of the - <seealso marker="#options">SCTP Socket Options.</seealso></p> + <p>One of the + <seealso marker="#options">SCTP Socket Options</seealso>.</p> </desc> </datatype> <datatype> @@ -101,8 +93,8 @@ <datatype> <name>sctp_socket()</name> <desc> - <p><marker id="type-sctp_socket"/> - Socket identifier returned from <c>open/*</c>.</p> + <p><marker id="type-sctp_socket"/>Socket identifier returned from + <seealso marker="#open/0"><c>open/*</c></seealso>.</p> <marker id="exports"></marker> </desc> </datatype> @@ -111,405 +103,438 @@ <funcs> <func> <name name="abort" arity="2"/> - <fsummary>Abnormally terminate the association given by Assoc, without flushing of unsent data</fsummary> + <fsummary>Abnormally terminate the association specified by + <c>Assoc</c>, without flushing of unsent data.</fsummary> <desc> - <p>Abnormally terminates the association given by <c><anno>Assoc</anno></c>, without + <p>Abnormally terminates the association specified by + <c><anno>Assoc</anno></c>, without flushing of unsent data. The socket itself remains open. Other - associations opened on this socket are still valid, and it can be - used in new associations.</p> + associations opened on this socket are still valid, and the socket + can be used in new associations.</p> </desc> </func> + <func> <name name="close" arity="1"/> - <fsummary>Completely close the socket and all associations on it</fsummary> + <fsummary>Close the socket and all associations on it.</fsummary> <desc> - <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 + <p>Closes the socket and all associations on it. The unsent + data is flushed as in <seealso marker="#eof/2"><c>eof/2</c></seealso>. + The <c>close/1</c> call is blocking or otherwise depending of the value of - 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 <seealso marker="inet#option-linger"><c>linger</c></seealso> + socket <seealso marker="#options">option</seealso>. + If <c>close</c> does not linger or linger time-out expires, the call returns and the data is flushed in the background.</p> </desc> </func> + <func> <name name="connect" arity="4"/> <fsummary>Same as <c>connect(Socket, Addr, Port, Opts, infinity)</c>.</fsummary> <desc> - <p>Same as <c>connect(<anno>Socket</anno>, <anno>Addr</anno>, <anno>Port</anno>, <anno>Opts</anno>, infinity)</c>.</p> + <p>Same as <c>connect(<anno>Socket</anno>, <anno>Addr</anno>, + <anno>Port</anno>, <anno>Opts</anno>, infinity)</c>.</p> </desc> </func> + <func> <name name="connect" arity="5"/> - <fsummary>Establish a new association for the socket <c>Socket</c>, with a peer (SCTP server socket)</fsummary> + <fsummary>Establish a new association for socket <c>Socket</c>, with a + peer (SCTP server socket).</fsummary> <desc> - <p>Establishes a new association for the socket <c><anno>Socket</anno></c>, - with the peer (SCTP server socket) given by - <c><anno>Addr</anno></c> and <c><anno>Port</anno></c>. The <c><anno>Timeout</anno></c>, - is expressed in milliseconds. A socket can be associated with multiple peers.</p> - - <p><em>WARNING:</em>Using a value of <c><anno>Timeout</anno></c> less than - the maximum time taken by the OS to establish an association (around 4.5 minutes - if the default values from RFC 4960 are used) can result in - inconsistent or incorrect return values. This is especially - relevant for associations sharing the same <c><anno>Socket</anno></c> - (i.e. source address and port) since the controlling process - blocks until <c>connect/*</c> returns. - <seealso marker="#connect_init/4">connect_init/*</seealso> - provides an alternative not subject to this limitation.</p> - + <p>Establishes a new association for socket <c><anno>Socket</anno></c>, + with the peer (SCTP server socket) specified by + <c><anno>Addr</anno></c> and <c><anno>Port</anno></c>. + <c><anno>Timeout</anno></c>, is expressed in milliseconds. + A socket can be associated with multiple peers.</p> + <warning><p>Using a value of <c><anno>Timeout</anno></c> less than + the maximum time taken by the OS to establish an association (around + 4.5 minutes if the default values from + <url href="https://tools.ietf.org/html/rfc4960">RFC 4960</url> + are used), can result + in inconsistent or incorrect return values. This is especially + relevant for associations sharing the same <c><anno>Socket</anno></c> + (that is, source address and port), as the controlling process + blocks until <c>connect/*</c> returns. + <seealso marker="#connect_init/4"><c>connect_init/*</c></seealso> + provides an alternative without this limitation.</p> + </warning> <p><marker id="record-sctp_assoc_change"></marker> The result of <c>connect/*</c> is an <c>#sctp_assoc_change{}</c> - event which contains, in particular, the new - <seealso marker="#type-assoc_id">Association ID</seealso>.</p> -<pre> #sctp_assoc_change{ - state = atom(), - error = atom(), - outbound_streams = integer(), - inbound_streams = integer(), - assoc_id = assoc_id() - } </pre> + event that contains, in particular, the new + <seealso marker="#type-assoc_id">Association ID</seealso>:</p> + <pre> +#sctp_assoc_change{ + state = atom(), + error = atom(), + outbound_streams = integer(), + inbound_streams = integer(), + assoc_id = assoc_id() +}</pre> <p>The number of outbound and inbound streams can be set by - giving an <c>sctp_initmsg</c> option to <c>connect</c> - as in:</p> -<pre> connect(Socket, Ip, Port>, - [{sctp_initmsg,#sctp_initmsg{num_ostreams=OutStreams, - max_instreams=MaxInStreams}}]) </pre> + giving an <c>sctp_initmsg</c> option to <c>connect</c> as in:</p> + <pre> +connect(Socket, Ip, Port>, + [{sctp_initmsg,#sctp_initmsg{num_ostreams=OutStreams, + max_instreams=MaxInStreams}}])</pre> <p>All options <c><anno>Opt</anno></c> are set on the socket before the - association is attempted. If an option record has got undefined + association is attempted. If an option record has undefined field values, the options record is first read from the socket - for those values. In effect, <c><anno>Opt</anno></c> option records only - define field values to change before connecting.</p> + for those values. In effect, <c><anno>Opt</anno></c> option records + only define field values to change before connecting.</p> <p>The returned <c>outbound_streams</c> and <c>inbound_streams</c> - are the actual stream numbers on the socket, which may be different - from the requested values (<c>OutStreams</c> and <c>MaxInStreams</c> + are the stream numbers on the socket. These can be different + from the requested values (<c>OutStreams</c> and <c>MaxInStreams</c>, respectively) if the peer requires lower values.</p> - <p>The following values of <c>state</c> are possible:</p> - <list type="bulleted"> - <item> - <p><c>comm_up</c>: association successfully established. This - indicates a successful completion of <c>connect</c>.</p> - </item> - <item> - <p><c>cant_assoc</c>: association cannot be established - (<c>connect/*</c> failure).</p> - </item> - </list> - <p>All other states do not normally occur in the output from - <c>connect/*</c>. Rather, they may occur in + <p><c>state</c> can have the following values:</p> + <taglist> + <tag><c>comm_up</c></tag> + <item><p>Association is successfully established. This + indicates a successful completion of <c>connect</c>.</p></item> + <tag><c>cant_assoc</c></tag> + <item><p>The association cannot be established + (<c>connect/*</c> failure).</p></item> + </taglist> + <p>Other states do not normally occur in the output from + <c>connect/*</c>. Rather, they can occur in <c>#sctp_assoc_change{}</c> events received instead of data in - <seealso marker="#recv/1">recv/*</seealso> calls. - All of them indicate losing the association due to various - error conditions, and are listed here for the sake of completeness. - The <c>error</c> field may provide more detailed diagnostics.</p> - <list type="bulleted"> - <item> - <p><c>comm_lost</c>;</p> - </item> - <item> - <p><c>restart</c>;</p> - </item> - <item> - <p><c>shutdown_comp</c>.</p> - </item> - </list> + <seealso marker="#recv/1"><c>recv/*</c></seealso> calls. + All of them indicate losing the association because of various error + conditions, and are listed here for the sake of completeness:</p> + <taglist> + <tag><c>comm_lost</c></tag> + <item></item> + <tag><c>restart</c></tag> + <item></item> + <tag><c>shutdown_comp</c></tag> + <item></item> + </taglist> + <p>Field <c>error</c> can provide more detailed diagnostics.</p> </desc> </func> + <func> <name name="connect_init" arity="4"/> - <fsummary>Same as <c>connect_init(Socket, Addr, Port, Opts, infinity)</c>.</fsummary> + <fsummary>Same as <c>connect_init(Socket, Addr, Port, Opts, infinity)</c>..</fsummary> <desc> - <p>Same as <c>connect_init(<anno>Socket</anno>, <anno>Addr</anno>, <anno>Port</anno>, <anno>Opts</anno>, infinity)</c>.</p> + <p>Same as <c>connect_init(<anno>Socket</anno>, <anno>Addr</anno>, + <anno>Port</anno>, <anno>Opts</anno>, infinity)</c>.</p> </desc> </func> + <func> <name name="connect_init" arity="5"/> - <fsummary>Initiate a new association for the socket <c>Socket</c>, with a peer (SCTP server socket)</fsummary> + <fsummary>Initiate a new association for socket <c>Socket</c>, with a + peer (SCTP server socket).</fsummary> <desc> - <p>Initiates a new association for the socket <c><anno>Socket</anno></c>, - with the peer (SCTP server socket) given by + <p>Initiates a new association for socket <c><anno>Socket</anno></c>, + with the peer (SCTP server socket) specified by <c><anno>Addr</anno></c> and <c><anno>Port</anno></c>.</p> - <p>The fundamental difference between this API - and <c>connect/*</c> is that the return value is that of the - underlying OS connect(2) system call. If <c>ok</c> is returned - then the result of the association establishement is received - by the calling process as - an <seealso marker="#record-sctp_assoc_change"> - #sctp_assoc_change{}</seealso> - event. The calling process must be prepared to receive this, or - poll for it using <c>recv/*</c> depending on the value of the - active option.</p> - <p>The parameters are as described - in <seealso marker="#connect/5">connect/*</seealso>, with the - exception of the <c><anno>Timeout</anno></c> value.</p> - <p>The timer associated with <c><anno>Timeout</anno></c> only supervises - IP resolution of <c><anno>Addr</anno></c></p> + <p>The fundamental difference between this API + and <c>connect/*</c> is that the return value is that of the + underlying OS <c>connect(2)</c> system call. If <c>ok</c> is returned, + the result of the association establishment is received + by the calling process as an + <seealso marker="#record-sctp_assoc_change"><c>#sctp_assoc_change{}</c></seealso> + event. The calling process must be prepared to receive this, or + poll for it using + <seealso marker="#recv/1"><c>recv/*</c></seealso>, + depending on the value of the active option.</p> + <p>The parameters are as described in + <seealso marker="#connect/5"><c>connect/*</c></seealso>, + except the <c><anno>Timeout</anno></c> value.</p> + <p>The timer associated with <c><anno>Timeout</anno></c> only supervises + IP resolution of <c><anno>Addr</anno></c>.</p> </desc> </func> + <func> <name name="controlling_process" arity="2"/> - <fsummary>Assign a new controlling process pid to the socket</fsummary> + <fsummary>Assign a new controlling process pid to the socket.</fsummary> <desc> - <p>Assigns a new controlling process <c><anno>Pid</anno></c> to <c><anno>Socket</anno></c>. Same implementation - as <c>gen_udp:controlling_process/2</c>.</p> + <p>Assigns a new controlling process <c><anno>Pid</anno></c> to + <c><anno>Socket</anno></c>. Same implementation as + <seealso marker="gen_udp:controlling_process/2"><c>gen_udp:controlling_process/2</c></seealso>. + </p> </desc> </func> + <func> <name name="eof" arity="2"/> - <fsummary>Gracefully terminate the association given by Assoc, with flushing of all unsent data</fsummary> + <fsummary>Gracefully terminate the association specified by <c>Assoc</c>, + with flushing of all unsent data.</fsummary> <desc> - <p>Gracefully terminates the association given by <c><anno>Assoc</anno></c>, with + <p>Gracefully terminates the association specified by + <c><anno>Assoc</anno></c>, with flushing of all unsent data. The socket itself remains open. Other - associations opened on this socket are still valid, and it can be - used in new associations.</p> + associations opened on this socket are still valid. The socket can + be used in new associations.</p> + </desc> + </func> + + <func> + <name name="error_string" arity="1"/> + <fsummary>Translate an SCTP error number into a string.</fsummary> + <desc> + <p>Translates an SCTP error number from, for example, + <c>#sctp_remote_error{}</c> or <c>#sctp_send_failed{}</c> into + an explanatory string, or one of the atoms <c>ok</c> for no + error or <c>undefined</c> for an unrecognized error.</p> </desc> </func> + <func> <name name="listen" arity="2" clause_i="1"/> <name name="listen" arity="2" clause_i="2"/> <fsummary>Set up a socket to listen.</fsummary> <desc> <p>Sets up a socket to listen on the IP address and port number - it is bound to.</p> - <p>For type <c>seqpacket</c> sockets (the default) - <c><anno>IsServer</anno></c> must be <c>true</c> or <c>false</c>. - In contrast to TCP, in SCTP there is no listening queue length. - If <c><anno>IsServer</anno></c> is <c>true</c> the socket accepts new associations, i.e. - it will become an SCTP server socket.</p> - <p>For type <c>stream</c> sockets <anno>Backlog</anno> defines - the backlog queue length just like in TCP.</p> + it is bound to.</p> + <p>For type <c>seqpacket</c>, sockets (the default) + <c><anno>IsServer</anno></c> must be <c>true</c> or <c>false</c>. + In contrast to TCP, there is no listening queue length in SCTP. + If <c><anno>IsServer</anno></c> is <c>true</c>, the socket accepts + new associations, that is, it becomes an SCTP server socket.</p> + <p>For type <c>stream</c>, sockets <anno>Backlog</anno> define + the backlog queue length just like in TCP.</p> </desc> </func> + <func> <name name="open" arity="0"/> <name name="open" arity="1" clause_i="1"/> <name name="open" arity="1" clause_i="2"/> <name name="open" arity="2"/> - <fsummary>Create an SCTP socket and bind it to local addresses</fsummary> + <fsummary>Create an SCTP socket and binds it to local addresses.</fsummary> <desc> - <p>Creates an SCTP socket and binds it to the local addresses - specified by all <c>{ip,<anno>IP</anno>}</c> (or synonymously <c>{ifaddr,<anno>IP</anno>}</c>) - options (this feature is called SCTP multi-homing). - The default <c><anno>IP</anno></c> and <c><anno>Port</anno></c> are <c>any</c> + <p>Creates an SCTP socket and binds it to the local addresses + specified by all <c>{ip,<anno>IP</anno>}</c> (or synonymously + <c>{ifaddr,<anno>IP</anno>}</c>) + options (this feature is called SCTP multi-homing). The default + <c><anno>IP</anno></c> and <c><anno>Port</anno></c> are <c>any</c> and <c>0</c>, meaning bind to all local addresses on any - one free port.</p> - - <p>Other options are:</p> + free port.</p> + <p>Other options:</p> <taglist> <tag><c>inet6</c></tag> <item> - <p>Set up the socket for IPv6.</p> + <p>Sets up the socket for IPv6.</p> </item> <tag><c>inet</c></tag> <item> - <p>Set up the socket for IPv4. This is the default.</p> + <p>Sets up the socket for IPv4. This is the default.</p> </item> </taglist> - <p>A default set of socket <seealso marker="#options">options</seealso> - is used. In particular, the socket is opened in + is used. In particular, the socket is opened in <seealso marker="#option-binary">binary</seealso> and <seealso marker="#option-active">passive</seealso> mode, - with <anno>SockType</anno> <c>seqpacket</c>, - and with reasonably large + with <anno>SockType</anno> <c>seqpacket</c>, and with reasonably large <seealso marker="inet#option-sndbuf">kernel</seealso> and driver - <seealso marker="inet#option-buffer">buffers.</seealso></p> + <seealso marker="inet#option-buffer">buffers</seealso>.</p> </desc> </func> + <func> <name name="peeloff" arity="2"/> - <fsummary> - Peel off a type <c>stream</c> socket from a type <c>seqpacket</c> one - </fsummary> + <fsummary>Peel off a type <c>stream</c> socket from a type + <c>seqpacket</c> one.</fsummary> <desc> - <p> - Branch off an existing association <anno>Assoc</anno> - in a socket <anno>Socket</anno> of type <c>seqpacket</c> - (one-to-many style) into - a new socket <anno>NewSocket</anno> of type <c>stream</c> - (one-to-one style). - </p> - <p> - The existing association argument <anno>Assoc</anno> - can be either a - <seealso marker="#record-sctp_assoc_change"> - #sctp_assoc_change{} - </seealso> - record as returned from e.g - <seealso marker="#recv-2">recv/*</seealso>, - <seealso marker="#connect-5">connect/*</seealso> or - from a listening socket in active mode. Or it can be just - the field <c>assoc_id</c> integer from such a record. - </p> + <p>Branches off an existing association <c><anno>Assoc</anno></c> + in a socket <c><anno>Socket</anno></c> of type <c>seqpacket</c> + (one-to-many style) into + a new socket <c><anno>NewSocket</anno></c> of type <c>stream</c> + (one-to-one style).</p> + <p>The existing association argument <c><anno>Assoc</anno></c> + can be either a + <seealso marker="#record-sctp_assoc_change"><c>#sctp_assoc_change{}</c></seealso> + record as returned from, for example, + <seealso marker="#recv-2"><c>recv/*</c></seealso>, + <seealso marker="#connect-5"><c>connect/*</c></seealso>, or + from a listening socket in active mode. It can also be just + the field <c>assoc_id</c> integer from such a record.</p> </desc> </func> + <func> <name name="recv" arity="1"/> <name name="recv" arity="2"/> - <fsummary>Receive a message from a socket</fsummary> + <fsummary>Receive a message from a socket.</fsummary> <desc> - <p>Receives the <c><anno>Data</anno></c> message from any association of the socket. - If the receive times out <c>{error,timeout</c> is returned. - The default timeout is <c>infinity</c>. - <c><anno>FromIP</anno></c> and <c><anno>FromPort</anno></c> indicate the sender's address.</p> - <p><c><anno>AncData</anno></c> is a list of Ancillary Data items which - may be received along with the main <c><anno>Data</anno></c>. + <p>Receives the <c><anno>Data</anno></c> message from any association + of the socket. + If the receive times out, <c>{error,timeout}</c> is returned. + The default time-out is <c>infinity</c>. <c><anno>FromIP</anno></c> + and <c><anno>FromPort</anno></c> indicate the address of the + sender.</p> + <p><c><anno>AncData</anno></c> is a list of ancillary data items that + can be received along with the main <c><anno>Data</anno></c>. This list can be empty, or contain a single - <seealso marker="#record-sctp_sndrcvinfo">#sctp_sndrcvinfo{}</seealso> - record, if receiving of such ancillary data is enabled - (see option - <seealso marker="#option-sctp_events">sctp_events</seealso>). - It is enabled by default, since such ancillary data - provide an easy way of determining the association and stream - over which the message has been received. - (An alternative way would be to get the Association ID from the - <c><anno>FromIP</anno></c> and <c><anno>FromPort</anno></c> using the - <seealso marker="#option-sctp_get_peer_addr_info">sctp_get_peer_addr_info</seealso> socket option, - but this would still not produce the Stream number).</p> - <p>The actual <c><anno>Data</anno></c> received may be a <c>binary()</c>, - or <c>list()</c> of bytes (integers in the range 0 through 255) - depending on the socket mode, or an SCTP Event. - <marker id="sctp_events"></marker> - - The following SCTP Events are possible:</p> + <seealso marker="#record-sctp_sndrcvinfo"><c>#sctp_sndrcvinfo{}</c></seealso> + record if receiving of such ancillary data is enabled (see option + <seealso marker="#option-sctp_events"><c>sctp_events</c></seealso>). + It is enabled by default, as such ancillary data + provides an easy way of determining the association and stream + over which the message is received. + (An alternative way is to get the association ID from + <c><anno>FromIP</anno></c> and <c><anno>FromPort</anno></c> using + socket option + <seealso marker="#option-sctp_get_peer_addr_info"><c>sctp_get_peer_addr_info</c></seealso>, + but this does still not produce the stream number).</p> + <p>The <c><anno>Data</anno></c> received can be a <c>binary()</c> + or a <c>list()</c> of bytes (integers in the range 0 through 255) + depending on the socket mode, or an SCTP event.</p> + <marker id="sctp_events"></marker> + <p>Possible SCTP events:</p> <list type="bulleted"> <item> - <p><seealso marker="#record-sctp_sndrcvinfo">#sctp_sndrcvinfo{}</seealso></p> + <seealso marker="#record-sctp_sndrcvinfo"><c>#sctp_sndrcvinfo{}</c></seealso> </item> <item> - <p><seealso marker="#record-sctp_assoc_change">#sctp_assoc_change{}</seealso>;</p> + <seealso marker="#record-sctp_assoc_change"><c>#sctp_assoc_change{}</c></seealso> </item> <item> -<pre> #sctp_paddr_change{ - addr = {ip_address(),port()}, - state = atom(), - error = integer(), - assoc_id = assoc_id() - } </pre> - <p>Indicates change of the status of the peer's IP address given by - <c>addr</c> within the association <c>assoc_id</c>. - Possible values of <c>state</c> (mostly self-explanatory) include:</p> - <list type="bulleted"> - <item> - <p><c>addr_unreachable</c>;</p> - </item> - <item> - <p><c>addr_available</c>;</p> - </item> - <item> - <p><c>addr_removed</c>;</p> - </item> - <item> - <p><c>addr_added</c>;</p> + <pre> +#sctp_paddr_change{ + addr = {ip_address(),port()}, + state = atom(), + error = integer(), + assoc_id = assoc_id() +}</pre> + <p>Indicates change of the status of the IP address of the peer + specified by + <c>addr</c> within association <c>assoc_id</c>. Possible + values of <c>state</c> (mostly self-explanatory) include:</p> + <taglist> + <tag><c>addr_unreachable</c></tag> + <item></item> + <tag><c>addr_available</c></tag> + <item></item> + <tag><c>addr_removed</c></tag> + <item></item> + <tag><c>addr_added</c></tag> + <item></item> + <tag><c>addr_made_prim</c></tag> + <item></item> + <tag><c>addr_confirmed</c></tag> + <item></item> + </taglist> + <p>In case of an error (for example, <c>addr_unreachable</c>), + field <c>error</c> provides more diagnostics. In such cases, + event <c>#sctp_paddr_change{}</c> is automatically + converted into an <c>error</c> term returned by + <seealso marker="#recv/1"><c>recv</c></seealso>. + The <c>error</c> field value can be converted into a string using + <seealso marker="#error_string/1"><c>error_string/1</c></seealso>. + </p> + </item> + <item> + <pre> +#sctp_send_failed{ + flags = true | false, + error = integer(), + info = #sctp_sndrcvinfo{}, + assoc_id = assoc_id() + data = binary() +}</pre> + <p>The sender can receive this event if a send operation fails.</p> + <taglist> + <tag><c>flags</c></tag> + <item><p>A Boolean specifying if the data has been transmitted + over the wire.</p></item> + <tag><c>error</c></tag> + <item><p>Provides extended diagnostics, use + <seealso marker="#error_string/1"><c>error_string/1</c>.</seealso></p> </item> - <item> - <p><c>addr_made_prim</c>.</p> + <tag><c>info</c></tag> + <item><p>The original + <seealso marker="#record-sctp_sndrcvinfo"><c>#sctp_sndrcvinfo{}</c></seealso> + record used in the failed + <seealso marker="#send/3"><c>send/*</c>.</seealso></p> </item> - <item> - <p><c>addr_confirmed</c>.</p> + <tag><c>data</c></tag> + <item><p>The whole original data chunk attempted to be sent.</p> </item> - </list> - <p>In case of an error (e.g. <c>addr_unreachable</c>), the - <c>error</c> field provides additional diagnostics. In such cases, - the <c>#sctp_paddr_change{}</c> Event is automatically - converted into an <c>error</c> term returned by - <c>gen_sctp:recv</c>. The <c>error</c> field value can be - converted into a string using <c>error_string/1</c>.</p> - </item> - <item> -<pre> #sctp_send_failed{ - flags = true | false, - error = integer(), - info = #sctp_sndrcvinfo{}, - assoc_id = assoc_id() - data = binary() - } </pre> - <p>The sender may receive this event if a send operation fails. - The <c>flags</c> is a Boolean specifying whether the data have - actually been transmitted over the wire; <c>error</c> provides - extended diagnostics, use <c>error_string/1</c>; - <c>info</c> is the original - <seealso marker="#record-sctp_sndrcvinfo">#sctp_sndrcvinfo{}</seealso> record used in the failed - <seealso marker="#send/3">send/*,</seealso> and <c>data</c> - is the whole original data chunk attempted to be sent.</p> + </taglist> <p>In the current implementation of the Erlang/SCTP binding, - this Event is internally converted into an <c>error</c> term - returned by <c>recv/*</c>.</p> + this event is internally converted into an <c>error</c> term + returned by + <seealso marker="#recv/1"><c>recv/*</c></seealso>.</p> </item> <item> -<pre> #sctp_adaptation_event{ - adaptation_ind = integer(), - assoc_id = assoc_id() - } </pre> - <p>Delivered when a peer sends an Adaptation Layer Indication - parameter (configured through the option - <seealso marker="#option-sctp_adaptation_layer">sctp_adaptation_layer</seealso>). - Note that with the current implementation of + <pre> +#sctp_adaptation_event{ + adaptation_ind = integer(), + assoc_id = assoc_id() +}</pre> + <p>Delivered when a peer sends an adaptation layer indication + parameter (configured through option + <seealso marker="#option-sctp_adaptation_layer"><c>sctp_adaptation_layer</c></seealso>). + Notice that with the current implementation of the Erlang/SCTP binding, this event is disabled by default.</p> </item> <item> -<pre> #sctp_pdapi_event{ - indication = sctp_partial_delivery_aborted, - assoc_id = assoc_id() - } </pre> + <pre> +#sctp_pdapi_event{ + indication = sctp_partial_delivery_aborted, + assoc_id = assoc_id() +}</pre> <p>A partial delivery failure. In the current implementation of - the Erlang/SCTP binding, this Event is internally converted - into an <c>error</c> term returned by <c>recv/*</c>.</p> + the Erlang/SCTP binding, this event is internally converted + into an <c>error</c> term returned by + <seealso marker="#recv/1"><c>recv/*</c></seealso>.</p> </item> </list> </desc> </func> + <func> <name name="send" arity="3"/> - <fsummary>Send a message using an <c>#sctp_sndrcvinfo{}</c>record</fsummary> + <fsummary>Send a message using an <c>#sctp_sndrcvinfo{}</c>record.</fsummary> <desc> - <p>Sends the <c><anno>Data</anno></c> message with all sending parameters from a - <seealso marker="#record-sctp_sndrcvinfo">#sctp_sndrcvinfo{}</seealso> record. - This way, the user can specify the PPID (passed to the remote end) - and Context (passed to the local SCTP layer) which can be used - for example for error identification. + <p>Sends the <c><anno>Data</anno></c> message with all sending + parameters from a + <seealso marker="#record-sctp_sndrcvinfo"><c>#sctp_sndrcvinfo{}</c></seealso> + record. This way, the user can specify the PPID (passed to the remote + end) and context (passed to the local SCTP layer), which can be used, + for example, for error identification. However, such a fine level of user control is rarely required. - The send/4 function is sufficient for most applications.</p> + The function <c>send/4</c> is sufficient for most applications.</p> </desc> </func> + <func> <name name="send" arity="4"/> - <fsummary>Send a message over an existing association and given stream</fsummary> + <fsummary>Send a message over an existing association and specified + stream.</fsummary> <desc> - <p>Sends <c><anno>Data</anno></c> message over an existing association and given - stream.</p> - </desc> - </func> - <func> - <name name="error_string" arity="1"/> - <fsummary>Translate an SCTP error number into a string</fsummary> - <desc> - <p>Translates an SCTP error number from for example - <c>#sctp_remote_error{}</c> or <c>#sctp_send_failed{}</c> into - an explanatory string, or one of the atoms <c>ok</c> for no - error and <c>undefined</c> for an unrecognized error.</p> + <p>Sends a <c><anno>Data</anno></c> message over an existing association + and specified stream.</p> </desc> </func> </funcs> <section> <marker id="options"></marker> - <title>SCTP SOCKET OPTIONS</title> + <title>SCTP Socket Options</title> <p>The set of admissible SCTP socket options is by construction - orthogonal to the sets of TCP, UDP and generic INET options: - only those options which are explicitly listed below are allowed + orthogonal to the sets of TCP, UDP, and generic <c>inet</c> options. + Only options listed here are allowed for SCTP sockets. Options can be set on the socket using - <seealso marker="#open/1"><c>gen_sctp:open/1,2</c></seealso> - or <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>, - retrieved using <seealso marker="inet#getopts/2"><c>inet:getopts/2</c></seealso>, - and when calling <seealso marker="#connect/4"><c>gen_sctp:connect/4,5</c></seealso> - options can be changed.</p> + <seealso marker="#open/1"><c>open/1,2</c></seealso> or + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>, + retrieved using + <seealso marker="inet#getopts/2"><c>inet:getopts/2</c></seealso>. + Options can be changed when calling + <seealso marker="#connect/4"><c>connect/4,5</c></seealso>.</p> <marker id="option-binary"></marker> <marker id="option-list"></marker> <taglist> <tag><c>{mode, list|binary}</c> or just <c>list</c> or <c>binary</c></tag> <item> - <p>Determines the type of data returned from <c>gen_sctp:recv/1,2</c>.</p> + <p>Determines the type of data returned from + <seealso marker="#recv/1"><c>recv/1,2</c></seealso>.</p> <marker id="option-active"></marker> </item> <tag><c>{active, true|false|once|N}</c></tag> @@ -517,176 +542,177 @@ <list type="bulleted"> <item> <p>If <c>false</c> (passive mode, the default), - the caller needs to do an explicit <c>gen_sctp:recv</c> call - in order to retrieve the available data from the socket.</p> + the caller must do an explicit + <seealso marker="#recv/1"><c>recv</c></seealso> call + to retrieve the available data from the socket.</p> </item> <item> <p>If <c>true</c> (full active mode), the pending data or events are sent to the owning process.</p> - <p><em>NB:</em> This can cause the message queue to overflow, + <p>Notice that this can cause the message queue to overflow, as there is no way to throttle the sender in this case - (no flow control!).</p> + (no flow control).</p> </item> <item> <p>If <c>once</c>, only one message is automatically placed in the message queue, and after that the mode is automatically - reset to passive. This provides flow control as well as + reset to passive. This provides flow control and the possibility for the receiver to listen for its incoming SCTP data interleaved with other inter-process messages.</p> </item> <item> <p>If <c>active</c> is specified as an integer <c>N</c> in the - range -32768 to 32767 (inclusive), then that number is added to - the socket's count of the number of data messages to be + range -32768 to 32767 (inclusive), that number is added to + the socket's counting of data messages to be delivered to the controlling process. If the result of the - addition would be negative, the count is set to 0. Once the - count reaches 0, either through the delivery of messages or by - being explicitly set with <seealso - marker="inet#setopts/2">inet:setopts/2</seealso>, the socket's - mode is automatically reset to passive (<c>{active, - false}</c>) mode. When a socket in this active mode transitions to + addition is negative, the count is set to <c>0</c>. Once the + count reaches <c>0</c>, either through the delivery of messages + or by being explicitly set with + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>, + the socket mode is automatically reset to passive (<c>{active, + false}</c>). When a socket in this active mode transitions to passive mode, the message <c>{sctp_passive, Socket}</c> is sent to the controlling process to notify it that if it wants to receive more data messages from the socket, it must call - <seealso marker="inet#setopts/2">inet:setopts/2</seealso> to set - the socket back into an active mode.</p> + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso> + to set the socket back into an active mode.</p> </item> </list> </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 + <p>Sets the Type-Of-Service field on the IP datagrams that are sent, + to the specified value. This effectively determines a prioritization policy for the outbound packets. The acceptable values - are system-dependent. TODO: we do not provide - symbolic names for these values yet.</p> + are system-dependent.</p> </item> <tag><c>{priority, integer()}</c></tag> <item> <p>A protocol-independent equivalent of <c>tos</c> above. Setting - priority implies setting tos as well.</p> + priority implies setting <c>tos</c> as well.</p> </item> <tag><c>{dontroute, true|false}</c></tag> <item> - <p>By default <c>false</c>. If <c>true</c>, the kernel does not - send packets via any gateway, only sends them to directly + <p>Defaults to <c>false</c>. If <c>true</c>, the kernel does not + send packets through any gateway, only sends them to directly connected hosts.</p> </item> <tag><c>{reuseaddr, true|false}</c></tag> <item> - <p>By default <c>false</c>. If true, the local binding address - <c>{IP,Port}</c> of the socket can be re-used immediately: - no waiting in the CLOSE_WAIT state is performed (may be + <p>Defaults to <c>false</c>. If true, the local binding address + <c>{IP,Port}</c> of the socket can be reused immediately. + No waiting in state <c>CLOSE_WAIT</c> is performed (can be required for high-throughput servers).</p> </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. + <p>The size, in bytes, of the OS kernel send buffer for this socket. Sending errors would occur for datagrams larger than <c>val(sndbuf)</c>. Setting this option also adjusts the size of the driver buffer (see <c>buffer</c> above).</p> </item> <tag><c>{recbuf, integer()}</c></tag> <item> - <p>The size, in bytes, of the *kernel* recv buffer for this socket. + <p>The size, in bytes, of the OS kernel receive buffer for this socket. Sending errors would occur for datagrams larger than - <c>val(sndbuf)</c>. Setting this option also adjusts + <c>val(recbuf)</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_module, module()}</c></tag> + <item> + <p>Overrides 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{ - assoc_id = assoc_id(), - initial = integer(), - max = integer(), - min = integer() - } </pre> - <p>Determines re-transmission time-out parameters, in milliseconds, - for the association(s) given by <c>assoc_id</c>. - If <c>assoc_id = 0</c> (default) indicates the whole endpoint. See - <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC2960</url> and - <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP</url> for the exact semantics of the fields values.</p> + <pre> +#sctp_rtoinfo{ + assoc_id = assoc_id(), + initial = integer(), + max = integer(), + min = integer() +}</pre> + <p>Determines retransmission time-out parameters, in milliseconds, + for the association(s) specified by <c>assoc_id</c>.</p> + <p><c>assoc_id = 0</c> (default) indicates the whole endpoint. See + <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC + 2960</url> and + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets + API Extensions for SCTP</url> + for the exact semantics of the field values.</p> </item> <tag><c>{sctp_associnfo, #sctp_assocparams{}}</c></tag> <item> -<pre> #sctp_assocparams{ - assoc_id = assoc_id(), - asocmaxrxt = integer(), - number_peer_destinations = integer(), - peer_rwnd = integer(), - local_rwnd = integer(), - cookie_life = integer() - } </pre> - <p>Determines association parameters for the association(s) given by - <c>assoc_id</c>. <c>assoc_id = 0</c> (default) indicates - the whole endpoint. See - <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP</url> for the discussion of their semantics. Rarely used.</p> + <pre> +#sctp_assocparams{ + assoc_id = assoc_id(), + asocmaxrxt = integer(), + number_peer_destinations = integer(), + peer_rwnd = integer(), + local_rwnd = integer(), + cookie_life = integer() +}</pre> + <p>Determines association parameters for the association(s) specified by + <c>assoc_id</c>.</p> + <p><c>assoc_id = 0</c> (default) indicates the whole endpoint. See + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP</url> + for the discussion of their semantics. Rarely used.</p> </item> <tag><c>{sctp_initmsg, #sctp_initmsg{}}</c></tag> <item> -<pre> #sctp_initmsg{ - num_ostreams = integer(), - max_instreams = integer(), - max_attempts = integer(), - max_init_timeo = integer() - } </pre> - <p>Determines the default parameters which this socket attempts + <pre> +#sctp_initmsg{ + num_ostreams = integer(), + max_instreams = integer(), + max_attempts = integer(), + max_init_timeo = integer() +}</pre> + <p>Determines the default parameters that this socket tries to negotiate with its peer while establishing an association with it. - Should be set after <c>open/*</c> but before the first - <c>connect/*</c>. <c>#sctp_initmsg{}</c> can also be used - as ancillary data with the first call of <c>send/*</c> to + Is to be set after + <seealso marker="#open/1"><c>open/*</c></seealso> + but before the first + <seealso marker="#connect/4"><c>connect/*</c></seealso>. + <c>#sctp_initmsg{}</c> can also be used + as ancillary data with the first call of + <seealso marker="#send/3"><c>send/*</c></seealso> to a new peer (when a new association is created).</p> - <list type="bulleted"> - <item> - <p><c>num_ostreams</c>: number of outbound streams;</p> - </item> - <item> - <p><c>max_instreams</c>: max number of in-bound streams;</p> - </item> - <item> - <p><c>max_attempts</c>: max re-transmissions while - establishing an association;</p> - </item> - <item> - <p><c>max_init_timeo</c>: time-out in milliseconds - for establishing an association.</p> - </item> - </list> + <taglist> + <tag><c>num_ostreams</c></tag> + <item>Number of outbound streams</item> + <tag><c>max_instreams</c></tag> + <item>Maximum number of inbound streams</item> + <tag><c>max_attempts</c></tag> + <item>Maximum retransmissions while establishing an association</item> + <tag><c>max_init_timeo</c></tag> + <item>Time-out, in milliseconds, for establishing an association</item> + </taglist> </item> <tag><c>{sctp_autoclose, integer() >= 0}</c></tag> <item> - <p>Determines the time (in seconds) after which an idle association is + <p>Determines the time, in seconds, after which an idle association is automatically closed. <c>0</c> means that the association is never automatically closed.</p> </item> <tag><c>{sctp_nodelay, true|false}</c></tag> <item> <p>Turns on|off the Nagle algorithm for merging small packets - into larger ones (which improves throughput at the expense - of latency).</p> + into larger ones. This improves throughput at the expense + of latency.</p> </item> <tag><c>{sctp_disable_fragments, true|false}</c></tag> <item> <p>If <c>true</c>, induces an error on an attempt to send - a message which is larger than the current PMTU size - (which would require fragmentation/re-assembling). - Note that message fragmentation does not affect + a message larger than the current PMTU size + (which would require fragmentation/reassembling). + Notice that message fragmentation does not affect the logical atomicity of its delivery; this option is provided for performance reasons only.</p> </item> <tag><c>{sctp_i_want_mapped_v4_addr, true|false}</c></tag> <item> <p>Turns on|off automatic mapping of IPv4 addresses into IPv6 ones - (if the socket address family is AF_INET6).</p> + (if the socket address family is <c>AF_INET6</c>).</p> </item> <tag><c>{sctp_maxseg, integer()}</c></tag> <item> @@ -695,176 +721,171 @@ </item> <tag><c>{sctp_primary_addr, #sctp_prim{}}</c></tag> <item> -<pre> #sctp_prim{ - assoc_id = assoc_id(), - addr = {IP, Port} - } - IP = ip_address() - Port = port_number() </pre> - <p>For the association given by <c>assoc_id</c>, - <c>{IP,Port}</c> must be one of the peer's addresses. - This option determines that the given address is - treated by the local SCTP stack as the peer's primary address.</p> + <pre> +#sctp_prim{ + assoc_id = assoc_id(), + addr = {IP, Port} +} + IP = ip_address() + Port = port_number()</pre> + <p>For the association specified by <c>assoc_id</c>, + <c>{IP,Port}</c> must be one of the peer addresses. + This option determines that the specified address is treated by + the local SCTP stack as the primary address of the peer.</p> </item> <tag><c>{sctp_set_peer_primary_addr, #sctp_setpeerprim{}}</c></tag> <item> -<pre> #sctp_setpeerprim{ - assoc_id = assoc_id(), - addr = {IP, Port} - } - IP = ip_address() - Port = port_number() </pre> - <p>When set, informs the peer that it should use <c>{IP, Port}</c> + <pre> +#sctp_setpeerprim{ + assoc_id = assoc_id(), + addr = {IP, Port} +} + IP = ip_address() + Port = port_number()</pre> + <p>When set, informs the peer to use <c>{IP, Port}</c> as the primary address of the local endpoint for the association - given by <c>assoc_id</c>.</p> + specified by <c>assoc_id</c>.</p> <marker id="option-sctp_adaptation_layer"></marker> </item> <tag><c>{sctp_adaptation_layer, #sctp_setadaptation{}}</c></tag> <item> <marker id="record-sctp_setadaptation"></marker> -<pre> #sctp_setadaptation{ - adaptation_ind = integer() - } </pre> - <p>When set, requests that the local endpoint uses the value given by - <c>adaptation_ind</c> as the Adaptation Indication parameter for - establishing new associations. See - <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC2960</url> and - <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extenstions for SCTP</url> for more details.</p> + <pre> +#sctp_setadaptation{ + adaptation_ind = integer() +}</pre> + <p>When set, requests that the local endpoint uses the value specified + by <c>adaptation_ind</c> as the Adaptation Indication parameter for + establishing new associations. For details, see + <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC 2960</url> + and + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets + API Extenstions for SCTP</url>.</p> </item> <tag><c>{sctp_peer_addr_params, #sctp_paddrparams{}}</c></tag> <item> -<pre> #sctp_paddrparams{ - assoc_id = assoc_id(), - address = {IP, Port}, - hbinterval = integer(), - pathmaxrxt = integer(), - pathmtu = integer(), - sackdelay = integer(), - flags = list() - } - IP = ip_address() - Port = port_number() </pre> - <p>This option determines various per-address parameters for - the association given by <c>assoc_id</c> and the peer address - <c>address</c> (the SCTP protocol supports multi-homing, - so more than 1 address can correspond to a given association).</p> - <list type="bulleted"> - <item> - <p><c>hbinterval</c>: heartbeat interval, in milliseconds;</p> - </item> - <item> - <p><c>pathmaxrxt</c>: max number of retransmissions - before this address is considered unreachable (and an - alternative address is selected);</p> - </item> - <item> - <p><c>pathmtu</c>: fixed Path MTU, if automatic discovery is - disabled (see <c>flags</c> below);</p> - </item> - <item> - <p><c>sackdelay</c>: delay in milliseconds for SAC messages - (if the delay is enabled, see <c>flags</c> below);</p> - </item> - <item> - <p><c>flags</c>: the following flags are available:</p> - <list type="bulleted"> - <item> - <p><c>hb_enable</c>: enable heartbeat; </p> - </item> - <item> - <p><c>hb_disable</c>: disable heartbeat;</p> - </item> - <item> - <p><c>hb_demand</c>: initiate heartbeat immediately;</p> - </item> - <item> - <p><c>pmtud_enable</c>: enable automatic Path MTU discovery;</p> - </item> - <item> - <p><c>pmtud_disable</c>: disable automatic Path MTU discovery;</p> - </item> - <item> - <p><c>sackdelay_enable</c>: enable SAC delay;</p> - </item> - <item> - <p><c>sackdelay_disable</c>: disable SAC delay.</p> - </item> - </list> + <pre> +#sctp_paddrparams{ + assoc_id = assoc_id(), + address = {IP, Port}, + hbinterval = integer(), + pathmaxrxt = integer(), + pathmtu = integer(), + sackdelay = integer(), + flags = list() +} +IP = ip_address() +Port = port_number()</pre> + <p>Determines various per-address parameters for + the association specified by <c>assoc_id</c> and the peer address + <c>address</c> (the SCTP protocol supports multi-homing, so + more than one address can correspond to a specified association).</p> + <taglist> + <tag><c>hbinterval</c></tag> + <item><p>Heartbeat interval, in milliseconds</p></item> + <tag><c>pathmaxrxt</c></tag> + <item><p>Maximum number of retransmissions before this address is + considered unreachable (and an alternative address is selected)</p> </item> - </list> + <tag><c>pathmtu</c></tag> + <item><p>Fixed Path MTU, if automatic discovery is disabled (see + <c>flags</c> below)</p></item> + <tag><c>sackdelay</c></tag> + <item><p>Delay, in milliseconds, for SAC messages (if the delay is + enabled, see <c>flags</c> below)</p></item> + <tag><c>flags</c></tag> + <item><p>The following flags are available:</p> + <taglist> + <tag><c>hb_enable</c></tag> + <item>Enables heartbeat</item> + <tag><c>hb_disable</c></tag> + <item>Disables heartbeat</item> + <tag><c>hb_demand</c></tag> + <item>Initiates heartbeat immediately</item> + <tag><c>pmtud_enable</c></tag> + <item>Enables automatic Path MTU discovery</item> + <tag><c>pmtud_disable</c></tag> + <item>Disables automatic Path MTU discovery</item> + <tag><c>sackdelay_enable</c></tag> + <item>Enables SAC delay</item> + <tag><c>sackdelay_disable</c></tag> + <item>Disables SAC delay</item> + </taglist></item> + </taglist> </item> <tag><c>{sctp_default_send_param, #sctp_sndrcvinfo{}}</c></tag> <item> <marker id="record-sctp_sndrcvinfo"></marker> -<pre> #sctp_sndrcvinfo{ - stream = integer(), - ssn = integer(), - flags = list(), - ppid = integer(), - context = integer(), - timetolive = integer(), - tsn = integer(), - cumtsn = integer(), - assoc_id = assoc_id() - } </pre> + <pre> +#sctp_sndrcvinfo{ + stream = integer(), + ssn = integer(), + flags = list(), + ppid = integer(), + context = integer(), + timetolive = integer(), + tsn = integer(), + cumtsn = integer(), + assoc_id = assoc_id() +}</pre> <p><c>#sctp_sndrcvinfo{}</c> is used both in this socket option, and as ancillary data while sending or receiving SCTP messages. When - set as an option, it provides a default values for subsequent - <c>gen_sctp:send</c>calls on the association given by - <c>assoc_id</c>. <c>assoc_id = 0</c> (default) indicates - the whole endpoint. The following fields typically need - to be specified by the sender:</p> - <list type="bulleted"> - <item> - <p><c>sinfo_stream</c>: stream number (0-base) within the association - to send the messages through;</p> - </item> - <item> - <p><c>sinfo_flags</c>: the following flags are recognised:</p> - <list type="bulleted"> - <item> - <p><c>unordered</c>: the message is to be sent unordered;</p> - </item> - <item> - <p><c>addr_over</c>: the address specified in - <c>gen_sctp:send</c> overwrites the primary peer address;</p> - </item> - <item> - <p><c>abort</c>: abort the current association without - flushing any unsent data;</p> - </item> - <item> - <p><c>eof</c>: gracefully shut down the current - association, with flushing of unsent data.</p> - </item> - </list> - <p>Other fields are rarely used. See - <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC2960</url> and - <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP</url> for full information.</p> - </item> - </list> + set as an option, it provides default values for subsequent + <seealso marker="#send/3"><c>send</c></seealso> + calls on the association specified by + <c>assoc_id</c>.</p> + <p><c>assoc_id = 0</c> (default) indicates + the whole endpoint.</p> + <p>The following fields typically must be specified by the sender:</p> + <taglist> + <tag><c>sinfo_stream</c></tag> + <item><p>Stream number (0-base) within the association + to send the messages through;</p></item> + <tag><c>sinfo_flags</c></tag> + <item><p>The following flags are recognised:</p> + <taglist> + <tag><c>unordered</c></tag> + <item>The message is to be sent unordered</item> + <tag><c>addr_over</c></tag> + <item>The address specified in + <seealso marker="#send/3"><c>send</c></seealso> + overwrites the primary peer address</item> + <tag><c>abort</c></tag> + <item>Aborts the current association without flushing any unsent + data</item> + <tag><c>eof</c></tag> + <item>Gracefully shuts down the current association, with + flushing of unsent data</item> + </taglist> + <p>Other fields are rarely used. For complete information, see + <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC 2960</url> + and + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets + API Extensions for SCTP</url>.</p></item> + </taglist> <marker id="option-sctp_events"></marker> </item> <tag><c>{sctp_events, #sctp_event_subscribe{}}</c></tag> <item> <marker id="record-sctp_event_subscribe"></marker> -<pre> #sctp_event_subscribe{ - data_io_event = true | false, - association_event = true | false, - address_event = true | false, - send_failure_event = true | false, - peer_error_event = true | false, - shutdown_event = true | false, - partial_delivery_event = true | false, - adaptation_layer_event = true | false - } </pre> + <pre> +#sctp_event_subscribe{ + data_io_event = true | false, + association_event = true | false, + address_event = true | false, + send_failure_event = true | false, + peer_error_event = true | false, + shutdown_event = true | false, + partial_delivery_event = true | false, + adaptation_layer_event = true | false +}</pre> <p>This option determines which <seealso marker="#sctp_events">SCTP Events</seealso> are to be - received (via <seealso marker="#recv/1">recv/*</seealso>) - along with the data. The only - exception is <c>data_io_event</c> which enables or disables - receiving of - <seealso marker="#record-sctp_sndrcvinfo">#sctp_sndrcvinfo{}</seealso> + received (through + <seealso marker="#recv/1"><c>recv/*</c></seealso>) + along with the data. The only exception is <c>data_io_event</c>, + which enables or disables receiving of + <seealso marker="#record-sctp_sndrcvinfo"><c>#sctp_sndrcvinfo{}</c></seealso> ancillary data, not events. By default, all flags except <c>adaptation_layer_event</c> are enabled, although <c>sctp_data_io_event</c> and @@ -873,201 +894,185 @@ </item> <tag><c>{sctp_delayed_ack_time, #sctp_assoc_value{}}</c></tag> <item> -<pre> #sctp_assoc_value{ - assoc_id = assoc_id(), - assoc_value = integer() - } </pre> + <pre> +#sctp_assoc_value{ + assoc_id = assoc_id(), + assoc_value = integer() +}</pre> <p>Rarely used. Determines the ACK time - (given by <c>assoc_value</c> in milliseconds) for - the given association or the whole endpoint + (specified by <c>assoc_value</c>, in milliseconds) for + the specified association or the whole endpoint if <c>assoc_value = 0</c> (default).</p> </item> <tag><c>{sctp_status, #sctp_status{}}</c></tag> <item> -<pre> #sctp_status{ - assoc_id = assoc_id(), - state = atom(), - rwnd = integer(), - unackdata = integer(), - penddata = integer(), - instrms = integer(), - outstrms = integer(), - fragmentation_point = integer(), - primary = #sctp_paddrinfo{} - } </pre> + <pre> +#sctp_status{ + assoc_id = assoc_id(), + state = atom(), + rwnd = integer(), + unackdata = integer(), + penddata = integer(), + instrms = integer(), + outstrms = integer(), + fragmentation_point = integer(), + primary = #sctp_paddrinfo{} +}</pre> <p>This option is read-only. It determines the status of - the SCTP association given by <c>assoc_id</c>. Possible values of - <c>state</c> follows. The state designations are mostly - self-explanatory. <c>state_empty</c> is the default which means - that no other state is active:</p> - <list type="bulleted"> - <item> - <p><c>sctp_state_empty</c></p> - </item> - <item> - <p><c>sctp_state_closed</c></p> - </item> - <item> - <p><c>sctp_state_cookie_wait</c></p> - </item> - <item> - <p><c>sctp_state_cookie_echoed</c></p> - </item> - <item> - <p><c>sctp_state_established</c></p> - </item> - <item> - <p><c>sctp_state_shutdown_pending</c></p> - </item> - <item> - <p><c>sctp_state_shutdown_sent</c></p> - </item> - <item> - <p><c>sctp_state_shutdown_received</c></p> - </item> - <item> - <p><c>sctp_state_shutdown_ack_sent</c></p> - </item> - </list> - <p>The semantics of other fields is the following:</p> - <list type="bulleted"> - <item> - <p><c>sstat_rwnd</c>: the association peer's current receiver - window size;</p> - </item> - <item> - <p><c>sstat_unackdata</c>: number of unacked data chunks;</p> - </item> - <item> - <p><c>sstat_penddata</c>: number of data chunks pending receipt;</p> - </item> - <item> - <p><c>sstat_instrms</c>: number of inbound streams;</p> - </item> - <item> - <p><c>sstat_outstrms</c>: number of outbound streams;</p> - </item> - <item> - <p><c>sstat_fragmentation_point</c>: message size at which SCTP - fragmentation will occur;</p> - </item> - <item> - <p><c>sstat_primary</c>: information on the current primary peer - address (see below for the format of <c>#sctp_paddrinfo{}</c>).</p> - </item> - </list> + the SCTP association specified by <c>assoc_id</c>. + The following are the + possible values of <c>state</c> (the state designations are mostly + self-explanatory):</p> + <taglist> + <tag><c>sctp_state_empty</c></tag> + <item>Default. Means that no other state is active.</item> + <tag><c>sctp_state_closed</c></tag> + <item></item> + <tag><c>sctp_state_cookie_wait</c></tag> + <item></item> + <tag><c>sctp_state_cookie_echoed</c></tag> + <item></item> + <tag><c>sctp_state_established</c></tag> + <item></item> + <tag><c>sctp_state_shutdown_pending</c></tag> + <item></item> + <tag><c>sctp_state_shutdown_sent</c></tag> + <item></item> + <tag><c>sctp_state_shutdown_received</c></tag> + <item></item> + <tag><c>sctp_state_shutdown_ack_sent</c></tag> + <item></item> + </taglist> + <p>Semantics of the other fields:</p> + <taglist> + <tag><c>sstat_rwnd</c></tag> + <item>Current receiver window size of the association</item> + <tag><c>sstat_unackdata</c></tag> + <item>Number of unacked data chunks</item> + <tag><c>sstat_penddata</c></tag> + <item>Number of data chunks pending receipt</item> + <tag><c>sstat_instrms</c></tag> + <item>Number of inbound streams</item> + <tag><c>sstat_outstrms</c></tag> + <item>Number of outbound streams</item> + <tag><c>sstat_fragmentation_point</c></tag> + <item>Message size at which SCTP fragmentation occurs</item> + <tag><c>sstat_primary</c></tag> + <item>Information on the current primary peer address (see below for + the format of <c>#sctp_paddrinfo{}</c>)</item> + </taglist> <marker id="option-sctp_get_peer_addr_info"></marker> </item> <tag><c>{sctp_get_peer_addr_info, #sctp_paddrinfo{}}</c></tag> <item> <marker id="record-sctp_paddrinfo"></marker> -<pre> #sctp_paddrinfo{ - assoc_id = assoc_id(), - address = {IP, Port}, - state = inactive | active | unconfirmed, - cwnd = integer(), - srtt = integer(), - rto = integer(), - mtu = integer() - } - IP = ip_address() - Port = port_number() </pre> + <pre> +#sctp_paddrinfo{ + assoc_id = assoc_id(), + address = {IP, Port}, + state = inactive | active | unconfirmed, + cwnd = integer(), + srtt = integer(), + rto = integer(), + mtu = integer() +} +IP = ip_address() +Port = port_number()</pre> <p>This option is read-only. It determines the parameters specific to - the peer's address given by <c>address</c> within the association - given by <c>assoc_id</c>. The <c>address</c> field must be set by the + the peer address specified by <c>address</c> within the association + specified by <c>assoc_id</c>. Field <c>address</c> fmust be set by the caller; all other fields are filled in on return. If <c>assoc_id = 0</c> (default), the <c>address</c> is automatically translated into the corresponding - association ID. This option is rarely used; see - <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC2960</url> and - <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP</url> for the semantics of all fields.</p> + association ID. This option is rarely used. + For the semantics of all fields, see + <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC 2960</url> + and + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets + API Extensions for SCTP</url>.</p> </item> </taglist> </section> <section> <marker id="examples"></marker> - <title>SCTP EXAMPLES</title> - <list type="bulleted"> - <item> - <p>Example of an Erlang SCTP Server which receives SCTP messages and - prints them on the standard output:</p> -<pre> -module(sctp_server). - - -export([server/0,server/1,server/2]). - -include_lib("kernel/include/inet.hrl"). - -include_lib("kernel/include/inet_sctp.hrl"). - - server() -> - server(any, 2006). - - server([Host,Port]) when is_list(Host), is_list(Port) -> - {ok, #hostent{h_addr_list = [IP|_]}} = inet:gethostbyname(Host), - io:format("~w -> ~w~n", [Host, IP]), - server([IP, list_to_integer(Port)]). - - server(IP, Port) when is_tuple(IP) orelse IP == any orelse IP == loopback, - is_integer(Port) -> - {ok,S} = gen_sctp:open(Port, [{recbuf,65536}, {ip,IP}]), - io:format("Listening on ~w:~w. ~w~n", [IP,Port,S]), - ok = gen_sctp:listen(S, true), - server_loop(S). - - server_loop(S) -> - case gen_sctp:recv(S) of - {error, Error} -> - io:format("SCTP RECV ERROR: ~p~n", [Error]); - Data -> - io:format("Received: ~p~n", [Data]) - end, - server_loop(S). </pre> - </item> - <item> - <p>Example of an Erlang SCTP Client which interacts with the above Server. - Note that in this example, the Client creates an association with - the Server with 5 outbound streams. For this reason, sending of - "Test 0" over Stream 0 succeeds, but sending of "Test 5" - over Stream 5 fails. The client then <c>abort</c>s the association, - which results in the corresponding Event being received on - the Server side.</p> -<pre> -module(sctp_client). - - -export([client/0, client/1, client/2]). - -include_lib("kernel/include/inet.hrl"). - -include_lib("kernel/include/inet_sctp.hrl"). + <title>SCTP Examples</title> + <p>Example of an Erlang SCTP server that receives SCTP messages and + prints them on the standard output:</p> + <pre> +-module(sctp_server). + +-export([server/0,server/1,server/2]). +-include_lib("kernel/include/inet.hrl"). +-include_lib("kernel/include/inet_sctp.hrl"). + +server() -> + server(any, 2006). + +server([Host,Port]) when is_list(Host), is_list(Port) -> + {ok, #hostent{h_addr_list = [IP|_]}} = inet:gethostbyname(Host), + io:format("~w -> ~w~n", [Host, IP]), + server([IP, list_to_integer(Port)]). + +server(IP, Port) when is_tuple(IP) orelse IP == any orelse IP == loopback, + is_integer(Port) -> + {ok,S} = gen_sctp:open(Port, [{recbuf,65536}, {ip,IP}]), + io:format("Listening on ~w:~w. ~w~n", [IP,Port,S]), + ok = gen_sctp:listen(S, true), + server_loop(S). + +server_loop(S) -> + case gen_sctp:recv(S) of + {error, Error} -> + io:format("SCTP RECV ERROR: ~p~n", [Error]); + Data -> + io:format("Received: ~p~n", [Data]) + end, + server_loop(S).</pre> + <p>Example of an Erlang SCTP client interacting with the above server. + Notice that in this example the client creates an association with + the server with 5 outbound streams. Therefore, sending of + <c>"Test 0"</c> over stream 0 succeeds, but sending of <c>"Test 5"</c> + over stream 5 fails. The client then <c>abort</c>s the association, + which results in that the corresponding event is received on + the server side.</p> + <pre> +-module(sctp_client). + +-export([client/0, client/1, client/2]). +-include_lib("kernel/include/inet.hrl"). +-include_lib("kernel/include/inet_sctp.hrl"). + +client() -> + client([localhost]). + +client([Host]) -> + client(Host, 2006); - client() -> - client([localhost]). - - client([Host]) -> - client(Host, 2006); - - client([Host, Port]) when is_list(Host), is_list(Port) -> - client(Host,list_to_integer(Port)), - init:stop(). - - client(Host, Port) when is_integer(Port) -> - {ok,S} = gen_sctp:open(), - {ok,Assoc} = gen_sctp:connect - (S, Host, Port, [{sctp_initmsg,#sctp_initmsg{num_ostreams=5}}]), - io:format("Connection Successful, Assoc=~p~n", [Assoc]), - - io:write(gen_sctp:send(S, Assoc, 0, <<"Test 0">>)), - io:nl(), - timer:sleep(10000), - io:write(gen_sctp:send(S, Assoc, 5, <<"Test 5">>)), - io:nl(), - timer:sleep(10000), - io:write(gen_sctp:abort(S, Assoc)), - io:nl(), - - timer:sleep(1000), - gen_sctp:close(S). </pre> - </item> - <item> - <p>A very simple Erlang SCTP Client which uses the - connect_init API.</p> -<pre>-module(ex3). +client([Host, Port]) when is_list(Host), is_list(Port) -> + client(Host,list_to_integer(Port)), + init:stop(). + +client(Host, Port) when is_integer(Port) -> + {ok,S} = gen_sctp:open(), + {ok,Assoc} = gen_sctp:connect + (S, Host, Port, [{sctp_initmsg,#sctp_initmsg{num_ostreams=5}}]), + io:format("Connection Successful, Assoc=~p~n", [Assoc]), + + io:write(gen_sctp:send(S, Assoc, 0, <<"Test 0">>)), + io:nl(), + timer:sleep(10000), + io:write(gen_sctp:send(S, Assoc, 5, <<"Test 5">>)), + io:nl(), + timer:sleep(10000), + io:write(gen_sctp:abort(S, Assoc)), + io:nl(), + + timer:sleep(1000), + gen_sctp:close(S).</pre> + <p>A simple Erlang SCTP client that uses the <c>connect_init</c> API:</p> + <pre> +-module(ex3). -export([client/4]). -include_lib("kernel/include/inet.hrl"). @@ -1099,7 +1104,7 @@ client_loop(S, Peer1, Port1, AssocId1, Peer2, Port2, AssocId2) -> io:format("Association 2 connect result: ~p. AssocId: ~p~n", [SAC#sctp_assoc_change.state, SAC#sctp_assoc_change.assoc_id]), client_loop(S, Peer1, Port1, AssocId1, Peer2, Port2, - SAC#sctp_assoc_change.assoc_id); + SAC#sctp_assoc_change.assoc_id); {sctp, S, Peer1, Port1, Data} -> io:format("Association 1: received ~p~n", [Data]), @@ -1118,20 +1123,19 @@ client_loop(S, Peer1, Port1, AssocId1, Peer2, Port2, AssocId2) -> after 5000 -> ok - end. -</pre> - </item> - </list> + end.</pre> </section> <section> <marker id="seealso"></marker> - <title>SEE ALSO</title> - <p><seealso marker="inet">inet(3)</seealso>, - <seealso marker="gen_tcp">gen_tcp(3)</seealso>, - <seealso marker="gen_udp">gen_udp(3)</seealso>, - <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC2960</url> (Stream Control Transmission Protocol), - <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets API Extensions for SCTP.</url></p> + <title>See Also</title> + <p><seealso marker="gen_tcp"><c>gen_tcp(3)</c></seealso>, + <seealso marker="gen_udp"><c>gen_udp(3)</c></seealso>, + <seealso marker="inet"><c>inet(3)</c></seealso>, + <url href="http://www.rfc-archive.org/getrfc.php?rfc=2960">RFC 2960</url> + (Stream Control Transmission Protocol), + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">Sockets + API Extensions for SCTP</url></p> </section> </erlref> diff --git a/lib/kernel/doc/src/gen_tcp.xml b/lib/kernel/doc/src/gen_tcp.xml index c0fed31f43..919178195f 100644 --- a/lib/kernel/doc/src/gen_tcp.xml +++ b/lib/kernel/doc/src/gen_tcp.xml @@ -21,7 +21,6 @@ limitations under the License. </legalnotice> - <title>gen_tcp</title> <prepared>[email protected]</prepared> <docno></docno> @@ -29,13 +28,13 @@ <rev>A</rev> </header> <module>gen_tcp</module> - <modulesummary>Interface to TCP/IP sockets</modulesummary> + <modulesummary>Interface to TCP/IP sockets.</modulesummary> <description> - <p>The <c>gen_tcp</c> module provides functions for communicating + <p>This module provides functions for communicating with sockets using the TCP/IP protocol.</p> - <p>The following code fragment provides a simple example of + <p>The following code fragment is a simple example of a client connecting to a server at port 5678, transferring a - binary and closing the connection:</p> + binary, and closing the connection:</p> <code type="none"> client() -> SomeHostInNet = "localhost", % to make it runnable on one machine @@ -43,8 +42,8 @@ client() -> [binary, {packet, 0}]), ok = gen_tcp:send(Sock, "Some Data"), ok = gen_tcp:close(Sock).</code> - <p>At the other end a server is listening on port 5678, accepts - the connection and receives the binary:</p> + <p>At the other end, a server is listening on port 5678, accepts + the connection, and receives the binary:</p> <code type="none"> server() -> {ok, LSock} = gen_tcp:listen(5678, [binary, {packet, 0}, @@ -61,7 +60,8 @@ do_recv(Sock, Bs) -> {error, closed} -> {ok, list_to_binary(Bs)} end.</code> - <p>For more examples, see the <seealso marker="#examples">examples</seealso> section.</p> + <p>For more examples, see section + <seealso marker="#examples">Examples</seealso>.</p> </description> <datatypes> @@ -79,9 +79,10 @@ do_recv(Sock, Bs) -> </datatype> <datatype> <name>socket()</name> - <desc> - <p><marker id="type-socket"/> - As returned by accept/1,2 and connect/3,4.</p> + <desc><p><marker id="type-socket"/> + As returned by + <seealso marker="#accept/1"><c>accept/1,2</c></seealso> and + <seealso marker="#connect/3"><c>connect/3,4</c></seealso>.</p> <marker id="connect"></marker> </desc> </datatype> @@ -89,285 +90,256 @@ do_recv(Sock, Bs) -> <funcs> <func> + <name name="accept" arity="1"/> + <name name="accept" arity="2"/> + <fsummary>Accept an incoming connection request on a listening socket.</fsummary> + <type_desc variable="ListenSocket">Returned by + <seealso marker="#listen/2"><c>listen/2</c></seealso>. + </type_desc> + <desc> + <p>Accepts an incoming connection request on a listening socket. + <c><anno>Socket</anno></c> must be a socket returned from + <seealso marker="#listen/2"><c>listen/2</c></seealso>. + <c><anno>Timeout</anno></c> specifies a time-out value in + milliseconds. Defaults to <c>infinity</c>.</p> + <p>Returns:</p> + <list type="bulleted"> + <item><p><c>{ok, <anno>Socket</anno>}</c> if a connection is + established</p></item> + <item><p><c>{error, closed}</c> if <c><anno>ListenSocket</anno></c> + is closed</p></item> + <item><p><c>{error, timeout}</c> if no connection is established + within the specified time</p></item> + <item><p><c>{error, system_limit}</c> if all available ports in the + Erlang emulator are in use</p></item> + <item><p>A POSIX error value if something else goes wrong, see + <seealso marker="inet"><c>inet(3)</c></seealso> for possible + error values</p></item> + </list> + <p>Packets can be sent to the returned socket <c><anno>Socket</anno></c> + using + <seealso marker="#send/2"><c>send/2</c></seealso>. + Packets sent from the peer are delivered as messages (unless + <c>{active, false}</c> is specified in the option list for the + listening socket, in which case packets are retrieved by calling + <seealso marker="#recv/2"><c>recv/2</c></seealso>):</p> + <code type="none"> +{tcp, Socket, Data}</code> + <note> + <p>The <c>accept</c> call does + <em>not</em> have to be issued from the socket owner + process. Using version 5.5.3 and higher of the emulator, + multiple simultaneous accept calls can be issued from + different processes, which allows for a pool of acceptor + processes handling incoming connections.</p> + </note> + </desc> + </func> + + <func> + <name name="close" arity="1"/> + <fsummary>Close a TCP socket.</fsummary> + <desc> + <p>Closes a TCP socket.</p> + </desc> + </func> + + <func> <name name="connect" arity="3"/> <name name="connect" arity="4"/> - <fsummary>Connect to a TCP port</fsummary> + <fsummary>Connect to a TCP port.</fsummary> <desc> <p>Connects to a server on TCP port <c><anno>Port</anno></c> on the host - with IP address <c><anno>Address</anno></c>. The <c><anno>Address</anno></c> argument - can be either a hostname, or an IP address.</p> - <p>The available options are:</p> + with IP address <c><anno>Address</anno></c>. Argument + <c><anno>Address</anno></c> can be a hostname or an IP address.</p> + <p>The following options are available:</p> <taglist> - <tag><c>{ip, ip_address()}</c></tag> - <item> - <p>If the host has several network interfaces, this option - specifies which one to use.</p> + <tag><c>{ip, ip_address()}</c></tag> + <item><p>If the host has many 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 many + 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 connected without using - <c>gen_tcp</c>, use this option to pass the file - descriptor for it. If <c>{ip, ip_address()}</c> - and/or <c>{port, port_number()}</c> is combined with - this option the fd will be bound to the given interface - and port before connecting. If these options are not given - it is assumed that the fd is already bound appropriately. - </p> - </item> - + <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. If <c>{ip, ip_address()}</c> and/or + <c>{port, port_number()}</c> is combined with this option, the + <c>fd</c> is bound to the specified interface and port before + connecting. If these options are not specified, it is assumed that + the <c>fd</c> is already bound appropriately.</p></item> <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> - + <item><p>Sets up the socket for IPv4.</p></item> + <tag><c>inet6</c></tag> + <item><p>Sets up the socket for IPv6.</p></item> <tag><c>{port, Port}</c></tag> - <item> - <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> - + <item><p>Specifies which local port number to use.</p></item> + <tag><c>{tcp_module, module()}</c></tag> + <item><p>Overrides 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 - <seealso marker="inet#setopts/2">inet:setopts/2</seealso>.</p> + <item><p>See + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>.</p> </item> </taglist> <p>Packets can be sent to the returned socket <c><anno>Socket</anno></c> - using <c>send/2</c>. Packets sent from the peer are delivered - as messages:</p> + using <seealso marker="#send/2"><c>send/2</c></seealso>. + Packets sent from the peer are delivered as messages:</p> <code type="none"> {tcp, Socket, Data}</code> - <p>If the socket is in <c>{active, N}</c> mode (see <seealso marker="inet#setopts/2"> - inet:setopts/2</seealso> for details) and its message counter - drops to 0, the following message is delivered to indicate that the + <p>If the socket is in <c>{active, N}</c> mode (see + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso> + for details) and its message counter drops to <c>0</c>, the following + message is delivered to indicate that the socket has transitioned to passive (<c>{active, false}</c>) mode:</p> <code type="none"> {tcp_passive, Socket}</code> <p>If the socket is closed, the following message is delivered:</p> <code type="none"> {tcp_closed, Socket}</code> - <p>If an error occurs on the socket, the following message is - delivered:</p> + <p>If an error occurs on the socket, the following message is delivered + (unless <c>{active, false}</c> is specified in the option list for + the socket, in which case packets are retrieved by calling + <seealso marker="#recv/2"><c>recv/2</c></seealso>):</p> <code type="none"> {tcp_error, Socket, Reason}</code> - <p>unless <c>{active, false}</c> is specified in the option list - for the socket, in which case packets are retrieved by - calling <c>recv/2</c>.</p> - <p>The optional <c><anno>Timeout</anno></c> parameter specifies a timeout in - milliseconds. The default value is <c>infinity</c>.</p> + <p>The optional <c><anno>Timeout</anno></c> parameter specifies a + time-out in milliseconds. Defaults to <c>infinity</c>.</p> <note> - <p>The default values for options given to <c>connect</c> can - be affected by the Kernel configuration parameter - <c>inet_default_connect_options</c>. See - <seealso marker="inet">inet(3)</seealso> for details.</p> + <p>The default values for options specified to <c>connect</c> can + be affected by the <c>Kernel</c> configuration parameter + <c>inet_default_connect_options</c>. For details, see + <seealso marker="inet"><c>inet(3)</c></seealso>.</p> </note> </desc> </func> + + <func> + <name name="controlling_process" arity="2"/> + <fsummary>Change controlling process of a socket.</fsummary> + <desc> + <p>Assigns a new controlling process <c><anno>Pid</anno></c> to + <c><anno>Socket</anno></c>. The controlling process is the process + that 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> <name name="listen" arity="2"/> - <fsummary>Set up a socket to listen on a port</fsummary> + <fsummary>Set up a socket to listen on a port.</fsummary> <desc> - <p>Sets up a socket to listen on the port <c><anno>Port</anno></c> on + <p>Sets up a socket to listen on port <c><anno>Port</anno></c> on the local host.</p> - <p>If <c><anno>Port</anno> == 0</c>, the underlying OS assigns an available - port number, use <c>inet:port/1</c> to retrieve it.</p> - <p>The available options are:</p> + <p>If <c><anno>Port</anno> == 0</c>, the underlying OS assigns an + available port number, use + <seealso marker="inet#port/1"><c>inet:port/1</c></seealso> + to retrieve it.</p> + <p>The following options are available:</p> <taglist> <tag><c>list</c></tag> - <item> - <p>Received <c>Packet</c> is delivered as a list.</p> - </item> + <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> + <item><p>Received <c>Packet</c> is delivered as a binary.</p></item> <tag><c>{backlog, B}</c></tag> - <item> - <p><c>B</c> is an integer >= 0. The backlog value defaults - to 5. The backlog value defines the maximum length that - the queue of pending connections may grow to.</p> - </item> + <item><p><c>B</c> is an integer >= <c>0</c>. The backlog value + defines the maximum length that the queue of pending connections + can grow to. Defaults to <c>5</c>.</p></item> <tag><c>{ip, ip_address()}</c></tag> - <item> - <p>If the host has several network interfaces, this option - specifies which one to listen on.</p> - </item> + <item><p>If the host has many network interfaces, this option + specifies which one to listen on.</p></item> <tag><c>{port, Port}</c></tag> - <item> - <p>Specify which local port number to use.</p> - </item> + <item><p>Specifies which local port number to use.</p></item> <tag><c>{fd, Fd}</c></tag> - <item> - <p>If a socket has somehow been connected without using - <c>gen_tcp</c>, use this option to pass the file - descriptor for it.</p> + <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>{ifaddr, ip_address()}</c></tag> + <item><p>Same as <c>{ip, ip_address()}</c>. If the host has many + 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>inet6</c></tag> - <item> - <p>Set up the socket for IPv6.</p> - </item> + <item><p>Sets up the socket for IPv6.</p></item> <tag><c>inet</c></tag> - <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> - + <item><p>Sets up the socket for IPv4.</p></item> + <tag><c>{tcp_module, module()}</c></tag> + <item><p>Overrides 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 - <seealso marker="inet#setopts/2">inet:setopts/2</seealso>.</p> - </item> + <item><p>See + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>. + </p></item> </taglist> - <p>The returned socket <c><anno>ListenSocket</anno></c> can only be used in - calls to <c>accept/1,2</c>.</p> + <p>The returned socket <c><anno>ListenSocket</anno></c> can only be + used in calls to + <seealso marker="#accept/1"><c>accept/1,2</c></seealso>.</p> <note> - <p>The default values for options given to <c>listen</c> can - be affected by the Kernel configuration parameter - <c>inet_default_listen_options</c>. See - <seealso marker="inet">inet(3)</seealso> for details.</p> + <p>The default values for options specified to <c>listen</c> can + be affected by the <c>Kernel</c> configuration parameter + <c>inet_default_listen_options</c>. For details, see + <seealso marker="inet"><c>inet(3)</c></seealso>.</p> </note> </desc> </func> - <func> - <name name="accept" arity="1"/> - <name name="accept" arity="2"/> - <fsummary>Accept an incoming connection request on a listen socket</fsummary> - <type_desc variable="ListenSocket">Returned by <c>listen/2</c>. - </type_desc> - <desc> - <p>Accepts an incoming connection request on a listen socket. - <c><anno>Socket</anno></c> must be a socket returned from <c>listen/2</c>. - <c><anno>Timeout</anno></c> specifies a timeout value in ms, defaults to - <c>infinity</c>.</p> - <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, - 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> - using <c>send/2</c>. Packets sent from the peer are delivered - as messages:</p> - <code type="none"> -{tcp, Socket, Data}</code> - <p>unless <c>{active, false}</c> was specified in the option - list for the listen socket, in which case packets are - retrieved by calling <c>recv/2</c>.</p> - <note> - <p>It is worth noting that the <c>accept</c> call does - <em>not</em> have to be issued from the socket owner - process. Using version 5.5.3 and higher of the emulator, - multiple simultaneous accept calls can be issued from - different processes, which allows for a pool of acceptor - processes handling incoming connections.</p> - </note> - </desc> - </func> - <func> - <name name="send" arity="2"/> - <fsummary>Send a packet</fsummary> - <desc> - <p>Sends a packet on a socket. </p> - <p>There is no <c>send</c> call with timeout option, you use the - <c>send_timeout</c> socket option if timeouts are - desired. See the <seealso marker="#examples">examples</seealso> section.</p> - </desc> - </func> + <func> <name name="recv" arity="2"/> <name name="recv" arity="3"/> - <fsummary>Receive a packet from a passive socket</fsummary> + <fsummary>Receive a packet from a passive socket.</fsummary> <type_desc variable="HttpPacket">See the description of - <c>HttpPacket</c> in <seealso marker="erts:erlang#decode_packet/3"> - erlang:decode_packet/3</seealso>. + <c>HttpPacket</c> in + <seealso marker="erts:erlang#decode_packet/3"><c>erlang:decode_packet/3</c></seealso> + in <c>ERTS</c>. </type_desc> <desc> - <p>This function receives a packet from a socket in passive - mode. A closed socket is indicated by a return value + <p>Receives a packet from a socket in passive + mode. A closed socket is indicated by return value <c>{error, closed}</c>.</p> - <p>The <c><anno>Length</anno></c> argument is only meaningful when + <p>Argument <c><anno>Length</anno></c> is only meaningful when the socket is in <c>raw</c> mode and denotes the number of - bytes to read. If <c><anno>Length</anno></c> = 0, all available bytes are - returned. If <c><anno>Length</anno></c> > 0, exactly <c><anno>Length</anno></c> - bytes are returned, or an error; possibly discarding less - than <c><anno>Length</anno></c> bytes of data when the socket gets closed - from the other side.</p> - <p>The optional <c><anno>Timeout</anno></c> parameter specifies a timeout in - milliseconds. The default value is <c>infinity</c>.</p> - </desc> - </func> - <func> - <name name="controlling_process" arity="2"/> - <fsummary>Change controlling process of a socket</fsummary> - <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. If called by any other - process than the current controlling process, - <c>{error, not_owner}</c> is returned.</p> + bytes to read. If <c><anno>Length</anno></c> is <c>0</c>, all + available bytes are returned. + If <c><anno>Length</anno></c> > <c>0</c>, exactly + <c><anno>Length</anno></c> bytes are returned, or an error; + possibly discarding less than <c><anno>Length</anno></c> bytes of + data when the socket is closed from the other side.</p> + <p>The optional <c><anno>Timeout</anno></c> parameter specifies a + time-out in milliseconds. Defaults to <c>infinity</c>.</p> </desc> </func> + <func> - <name name="close" arity="1"/> - <fsummary>Close a TCP socket</fsummary> + <name name="send" arity="2"/> + <fsummary>Send a packet.</fsummary> <desc> - <p>Closes a TCP socket.</p> + <p>Sends a packet on a socket.</p> + <p>There is no <c>send</c> call with a time-out option, use socket + option <c>send_timeout</c> if time-outs are desired. See section + <seealso marker="#examples">Examples</seealso>.</p> </desc> </func> + <func> <name name="shutdown" arity="2"/> - <fsummary>Asynchronously close a socket</fsummary> + <fsummary>Asynchronously close a socket.</fsummary> <desc> - <p>Close a socket in one or two directions.</p> - <p><c><anno>How</anno> == write</c> means closing the socket for writing, - reading from it is still possible.</p> - <p>If <c><anno>How</anno> == read</c>, or there is no outgoing + <p>Closes a socket in one or two directions.</p> + <p><c><anno>How</anno> == write</c> means closing the socket for + writing, reading from it is still possible.</p> + <p>If <c><anno>How</anno> == read</c> or there is no outgoing data buffered in the <c><anno>Socket</anno></c> port, - then the socket is shutdown immediately and any error encountered + the socket is shut down immediately and any error encountered is returned in <c><anno>Reason</anno></c>.</p> - <p>If there is data buffered in the socket port, then the attempt + <p>If there is data buffered in the socket port, the attempt to shutdown the socket is postponed until that data is written to the - kernel socket send buffer. Any errors encountered will result - in the socket being closed and <c>{error, closed}</c> being returned - on the next - <seealso marker="gen_tcp#recv/2">recv/2</seealso> or - <seealso marker="gen_tcp#send/2">send/2</seealso>.</p> - <p>To be able to handle that the peer has done a shutdown on - the write side, the <c>{exit_on_close, false}</c> option - is useful.</p> + kernel socket send buffer. If any errors are encountered, the socket + is closed and <c>{error, closed}</c> is returned on the next + <seealso marker="#recv/2"><c>recv/2</c></seealso> or + <seealso marker="#send/2"><c>send/2</c></seealso>.</p> + <p>Option <c>{exit_on_close, false}</c> is useful if the peer has done + a shutdown on the write side.</p> </desc> </func> </funcs> @@ -375,14 +347,14 @@ do_recv(Sock, Bs) -> <section> <title>Examples</title> <marker id="examples"></marker> - <p>The following example illustrates usage of the {active,once} - option and multiple accepts by implementing a server as a - number of worker processes doing accept on one single listen - socket. The start/2 function takes the number of worker - processes as well as a port number to listen for incoming - connections on. If <c>LPort</c> is specified as <c>0</c>, an - ephemeral portnumber is used, why the start function returns - the actual portnumber allocated:</p> + <p>The following example illustrates use of option + <c>{active,once}</c> and multiple accepts by implementing a server + as a number of worker processes doing accept on a single listening + socket. Function <c>start/2</c> takes the number of worker + processes and the port number on which to listen for incoming + connections. If <c>LPort</c> is specified as <c>0</c>, an + ephemeral port number is used, which is why the start function + returns the actual port number allocated:</p> <code type="none"> start(Num,LPort) -> case gen_tcp:listen(LPort,[{active, false},{packet,2}]) of @@ -421,7 +393,7 @@ loop(S) -> io:format("Socket ~w closed [~w]~n",[S,self()]), ok end.</code> - <p>A simple client could look like this:</p> + <p>Example of a simple client:</p> <code type="none"> client(PortNo,Message) -> {ok,Sock} = gen_tcp:connect("localhost",PortNo,[{active,false}, @@ -430,30 +402,29 @@ client(PortNo,Message) -> A = gen_tcp:recv(Sock,0), gen_tcp:close(Sock), A.</code> - <p>The fact that the <c>send</c> call does not accept a timeout - option, is because timeouts on send is handled through the socket + <p>The <c>send</c> call does not accept a time-out + option because time-outs on send is handled through socket option <c>send_timeout</c>. The behavior of a send operation with - no receiver is in a very high degree defined by the underlying TCP - stack, as well as the network infrastructure. If one wants to write - code that handles a hanging receiver that might eventually cause - the sender to hang on a <c>send</c> call, one writes code like - the following.</p> - <p>Consider a process that receives data from a client process that - is to be forwarded to a server on the network. The process has - connected to the server via TCP/IP and does not get any acknowledge - for each message it sends, but has to rely on the send timeout - option to detect that the other end is unresponsive. We could use - the <c>send_timeout</c> option when connecting:</p> + no receiver is mainly defined by the underlying TCP + stack and the network infrastructure. To write + code that handles a hanging receiver that can eventually cause + the sender to hang on a <c>send</c> do like the following.</p> + <p>Consider a process that receives data from a client process + to be forwarded to a server on the network. The process is + connected to the server through TCP/IP and does not get any acknowledge + for each message it sends, but has to rely on the send time-out + option to detect that the other end is unresponsive. Option + <c>send_timeout</c> can be used when connecting:</p> <code type="none"> - ... - {ok,Sock} = gen_tcp:connect(HostAddress, Port, - [{active,false}, - {send_timeout, 5000}, - {packet,2}]), - loop(Sock), % See below - ... </code> - <p>In the loop where requests are handled, we can now detect send - timeouts:</p> +... +{ok,Sock} = gen_tcp:connect(HostAddress, Port, + [{active,false}, + {send_timeout, 5000}, + {packet,2}]), + loop(Sock), % See below +...</code> + <p>In the loop where requests are handled, send time-outs can now be + detected:</p> <code type="none"> loop(Sock) -> receive @@ -477,11 +448,11 @@ loop(Sock) -> Client ! {self(), data_sent}, loop(Sock) end - end. </code> - <p>Usually it would suffice to detect timeouts on receive, as most + end.</code> + <p>Usually it suffices to detect time-outs on receive, as most protocols include some sort of acknowledgment from the server, - but if the protocol is strictly one way, the <c>send_timeout</c> - option comes in handy!</p> + but if the protocol is strictly one way, option <c>send_timeout</c> + comes in handy.</p> </section> </erlref> diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index 1f428ce16f..906883ed34 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -29,9 +29,9 @@ <rev>A</rev> </header> <module>gen_udp</module> - <modulesummary>Interface to UDP sockets</modulesummary> + <modulesummary>Interface to UDP sockets.</modulesummary> <description> - <p>The <c>gen_udp</c> module provides functions for communicating + <p>This module provides functions for communicating with sockets using the UDP protocol.</p> </description> @@ -45,175 +45,140 @@ <datatype> <name>socket()</name> <desc> - <p><marker id="type-socket"/>As returned by open/1,2.</p> + <marker id="type-socket"/> + <p>As returned by + <seealso marker="#open/1"><c>open/1,2</c></seealso>.</p> </desc> </datatype> </datatypes> <funcs> <func> + <name name="close" arity="1"/> + <fsummary>Close a UDP socket.</fsummary> + <desc> + <p>Closes a UDP socket.</p> + </desc> + </func> + + <func> + <name name="controlling_process" arity="2"/> + <fsummary>Change controlling process of a socket.</fsummary> + <desc> + <p>Assigns a new controlling process <c><anno>Pid</anno></c> to + <c><anno>Socket</anno></c>. The controlling process is the process + that 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> <name name="open" arity="1"/> <name name="open" arity="2"/> - <fsummary>Associate a UDP port number with the process calling it</fsummary> + <fsummary>Associate a UDP port number with the process calling it.</fsummary> <desc> - <p>Associates a UDP port number (<c><anno>Port</anno></c>) with the calling - process.</p> - <p>The available options are:</p> + <p>Associates a UDP port number (<c><anno>Port</anno></c>) with the + calling process.</p> + <p>The following options are available:</p> <taglist> <tag><c>list</c></tag> - <item> - <p>Received <c>Packet</c> is delivered as a list.</p> - </item> + <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> + <item><p>Received <c>Packet</c> is delivered as a binary.</p></item> <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>{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> - - + <item><p>If the host has many 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 many + 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 - <c>gen_udp</c>, use this option to pass the file - descriptor for it. If <c><anno>Port</anno></c> is not set to 0 - and/or <c>{ip, ip_address()}</c> is combined with this option - the fd will be bound to the given interface and port after being - opened. If these options are not given it is assumed that the fd - is already bound appropriately. - </p> - </item> + <item><p>If a socket has somehow been opened without using + <c>gen_udp</c>, use this option to pass the file descriptor + for it. If <c><anno>Port</anno></c> is not set to <c>0</c> and/or + <c>{ip, ip_address()}</c> is combined with this option, the + <c>fd</c> is bound to the specified interface and port after it is + being opened. If these options are not specified, it is assumed that + the <c>fd</c> is already bound appropriately.</p></item> <tag><c>inet6</c></tag> - <item> - <p>Set up the socket for IPv6.</p> - </item> + <item><p>Sets up the socket for IPv6.</p></item> <tag><c>inet</c></tag> - <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> - + <item><p>Sets up the socket for IPv4.</p></item> + <tag><c>{udp_module, module()}</c></tag> + <item><p>Overrides 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> - + <item><p>Sets 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> - + <item><p>When <c>true</c>, sent multicast packets are 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> - + <item><p>Option <c>multicast_ttl</c> changes the time-to-live (TTL) + for outgoing multicast datagrams to control the scope of the + multicasts.</p> + <p>Datagrams with a TTL of 1 are not forwarded beyond the local + network. Defaults to <c>1</c>.</p></item> + <tag><c>{add_membership, {MultiAddress, InterfaceAddress}}</c></tag> + <item><p>Joins a multicast group.</p></item> + <tag><c>{drop_membership, {MultiAddress, InterfaceAddress}}</c></tag> + <item><p>Leaves a multicast group.</p></item> <tag><c>Opt</c></tag> - <item> - <p>See - <seealso marker="inet#setopts/2">inet:setopts/2</seealso>.</p> - </item> + <item><p>See + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso>. + </p></item> </taglist> <p>The returned socket <c><anno>Socket</anno></c> is used to send - packets from this port with <c>send/4</c>. When UDP packets arrive - at the opened port, if the socket is in an active mode the packets + packets from this port with + <seealso marker="#send/4"><c>send/4</c></seealso>. + When UDP packets arrive + at the opened port, if the socket is in an active mode, the packets are delivered as messages to the controlling process:</p> <code type="none"> {udp, Socket, IP, InPortNo, Packet}</code> <p>If the socket is not in an active mode, data can be - retrieved via the <seealso marker="#recv/2">recv/2,3</seealso> calls. - Note that arriving UDP packets that are longer than - the receive buffer option specifies, might be truncated + retrieved through the + <seealso marker="#recv/2"><c>recv/2,3</c></seealso> calls. + Notice that arriving UDP packets that are longer than + the receive buffer option specifies can be truncated without warning.</p> - <p>When a socket in <c>{active, N}</c> mode (see <seealso marker="inet#setopts/2"> - inet:setopts/2</seealso> for details) transitions to passive - (<c>{active, false}</c>) mode, the controlling process is notified by a - message of the following form:</p> + <p>When a socket in <c>{active, N}</c> mode (see + <seealso marker="inet#setopts/2"><c>inet:setopts/2</c></seealso> + for details), transitions to passive (<c>{active, false}</c>) mode, + the controlling process is notified by a message of the following + form:</p> <code type="none"> {udp_passive, Socket}</code> <p><c>IP</c> and <c>InPortNo</c> define the address from which - <c>Packet</c> came. <c>Packet</c> is a list of bytes if - the option <c>list</c> was specified. <c>Packet</c> is a - binary if the option <c>binary</c> was specified.</p> + <c>Packet</c> comes. <c>Packet</c> is a list of bytes if + option <c>list</c> is specified. <c>Packet</c> is a + binary if option <c>binary</c> is specified.</p> <p>Default value for the receive buffer option is <c>{recbuf, 8192}</c>.</p> - <p>If <c><anno>Port</anno> == 0</c>, the underlying OS assigns a free UDP - port, use <c>inet:port/1</c> to retrieve it.</p> - </desc> - </func> - <func> - <name name="send" arity="4"/> - <fsummary>Send a packet</fsummary> - <desc> - <p>Sends a packet to the specified address and port. - The <c><anno>Address</anno></c> argument can be either a hostname, or an - IP address.</p> + <p>If <c><anno>Port</anno> == 0</c>, the underlying OS assigns a free + UDP port, use + <seealso marker="inet#port/1"><c>inet:port/1</c></seealso> + to retrieve it.</p> </desc> </func> + <func> <name name="recv" arity="2"/> <name name="recv" arity="3"/> - <fsummary>Receive a packet from a passive socket</fsummary> - <desc> - <p>This function receives a packet from a socket in passive - mode.</p> - <p>The optional <c><anno>Timeout</anno></c> parameter specifies a timeout in - milliseconds. The default value is <c>infinity</c>.</p> - </desc> - </func> - <func> - <name name="controlling_process" arity="2"/> - <fsummary>Change controlling process of a socket</fsummary> + <fsummary>Receive a packet from a passive socket.</fsummary> <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. If called by any other - process than the current controlling process, - <c>{error, not_owner}</c> is returned.</p> + <p>Receives a packet from a socket in passive mode. Optional parameter + <c><anno>Timeout</anno></c> specifies a time-out in milliseconds. + Defaults to <c>infinity</c>.</p> </desc> </func> + <func> - <name name="close" arity="1"/> - <fsummary>Close a UDP socket</fsummary> + <name name="send" arity="4"/> + <fsummary>Send a packet.</fsummary> <desc> - <p>Closes a UDP socket.</p> + <p>Sends a packet to the specified address and port. Argument + <c><anno>Address</anno></c> can be a hostname or an IP address.</p> </desc> </func> </funcs> diff --git a/lib/kernel/doc/src/global.xml b/lib/kernel/doc/src/global.xml index 9ce02a99ed..4442741f54 100644 --- a/lib/kernel/doc/src/global.xml +++ b/lib/kernel/doc/src/global.xml @@ -29,82 +29,67 @@ <rev></rev> </header> <module>global</module> - <modulesummary>A Global Name Registration Facility</modulesummary> + <modulesummary>A global name registration facility.</modulesummary> <description> - <p>This documentation describes the Global module which consists - of the following functionalities:</p> - + <p>This module consists of the following services:</p> <list type="bulleted"> - <item>registration of global names;</item> - <item>global locks;</item> - <item>maintenance of the fully connected network.</item> + <item>Registration of global names</item> + <item>Global locks</item> + <item>Maintenance of the fully connected network</item> </list> - - <p>These services are controlled via the process - <c>global_name_server</c> which exists on every node. The global - name server is started automatically when a node is started. + <p>These services are controlled through the process + <c>global_name_server</c> that exists on every node. The global + name server starts automatically when a node is started. With the term <em>global</em> is meant over a system consisting - of several Erlang nodes.</p> - + of many Erlang nodes.</p> <p>The ability to globally register names is a central concept in the programming of distributed Erlang systems. In this module, the equivalent of the <c>register/2</c> and <c>whereis/1</c> - BIFs (for local name registration) are implemented, but for a + BIFs (for local name registration) are provided, but for a network of Erlang nodes. A registered name is an alias for a process identifier (pid). The global name server monitors - globally registered pids. If a process terminates, the name will - also be globally unregistered.</p> - + globally registered pids. If a process terminates, the name is + also globally unregistered.</p> <p>The registered names are stored in replica global name tables on every node. There is no central storage point. Thus, the translation of a name to a pid is fast, as it is always done - locally. When any action in taken which results in a change to - the global name table, all tables on other nodes are automatically - updated.</p> - + locally. For any action resulting in a change to the global name table, + all tables on other nodes are automatically updated.</p> <p>Global locks have lock identities and are set on a specific - resource. For instance, the specified resource could be a pid. + resource. For example, the specified resource can be a pid. When a global lock is set, access to the locked resource is - denied for all other resources other than the lock requester.</p> - - <p>Both the registration and lock functionalities are atomic. All - nodes involved in these actions will have the same view of + denied for all resources other than the lock requester.</p> + <p>Both the registration and lock services are atomic. + All nodes involved in these actions have the same view of the information.</p> - <p>The global name server also performs the critical task of - continuously monitoring changes in node configuration: if a node - which runs a globally registered process goes down, the name - will be globally unregistered. To this end the global name + continuously monitoring changes in node configuration. If a node + that runs a globally registered process goes down, the name + is globally unregistered. To this end, the global name server subscribes to <c>nodeup</c> and <c>nodedown</c> messages - sent from the <c>net_kernel</c> module. Relevant Kernel + sent from module <c>net_kernel</c>. Relevant Kernel application variables in this context are <c>net_setuptime</c>, <c>net_ticktime</c>, and <c>dist_auto_connect</c>. See also - <seealso marker="kernel_app#net_setuptime">kernel(6)</seealso>.</p> - - <p>The name server will also maintain a fully connected network. For + <seealso marker="kernel_app#net_setuptime"><c>kernel(6)</c></seealso>.</p> + <p>The name server also maintains a fully connected network. For example, if node <c>N1</c> connects to node <c>N2</c> (which is already connected to <c>N3</c>), the global name servers on the - nodes <c>N1</c> and <c>N3</c> will make sure that also <c>N1</c> - and <c>N3</c> are connected. If this is not desired, the command - line flag <c>-connect_all false</c> can be used (see also - <seealso marker="erts:erl#connect_all">erl(1)</seealso>). In - this case the name registration facility cannot be used, but the - lock mechanism will still work.</p> - + nodes <c>N1</c> and <c>N3</c> ensure that also <c>N1</c> + and <c>N3</c> are connected. If this is not desired, + command-line flag <c>-connect_all false</c> can be used (see also + <seealso marker="erts:erl#connect_all"><c>erl(1)</c></seealso>). + In this case, the name registration service cannot be used, but the + lock mechanism still works.</p> <p>If the global name server fails to connect nodes (<c>N1</c> and - <c>N3</c> in the example above) a warning event is sent to the + <c>N3</c> in the example), a warning event is sent to the error logger. The presence of such an event does not exclude the - possibility that the nodes will later connect--one can for - example try the command <c>rpc:call(N1, net_adm, ping, [N2])</c> in - the Erlang shell--but it indicates some kind of problem with - the network.</p> - + nodes to connect later (you can, for + example, try command <c>rpc:call(N1, net_adm, ping, [N2])</c> in + the Erlang shell), but it indicates a network problem.</p> <note> - <p>If the fully connected network is not set up properly, the - first thing to try is to increase the value of - <c>net_setuptime</c>.</p> + <p>If the fully connected network is not set up properly, try + first to increase the value of <c>net_setuptime</c>.</p> </note> - </description> <datatypes> @@ -117,7 +102,7 @@ <func> <name name="del_lock" arity="1"/> <name name="del_lock" arity="2"/> - <fsummary>Delete a lock</fsummary> + <fsummary>Delete a lock.</fsummary> <desc> <p>Deletes the lock <c><anno>Id</anno></c> synchronously.</p> </desc> @@ -125,11 +110,13 @@ <func> <name name="notify_all_name" arity="3"/> - <fsummary>Name resolving function that notifies both pids</fsummary> + <fsummary>Name resolving function that notifies both pids.</fsummary> <desc> - <p>This function can be used as a name resolving function for - <c>register_name/3</c> and <c>re_register_name/3</c>. It - unregisters both pids, and sends the message + <p>Can be used as a name resolving function for + <seealso marker="#register_name/3"><c>register_name/3</c></seealso> + and + <seealso marker="#re_register_name/3"><c>re_register_name/3</c></seealso>.</p> + <p>The function unregisters both pids and sends the message <c>{global_name_conflict, <anno>Name</anno>, OtherPid}</c> to both processes.</p> </desc> @@ -137,85 +124,97 @@ <func> <name name="random_exit_name" arity="3"/> - <fsummary>Name resolving function that kills one pid</fsummary> + <fsummary>Name resolving function that kills one pid.</fsummary> <desc> - <p>This function can be used as a name resolving function for - <c>register_name/3</c> and <c>re_register_name/3</c>. It - randomly chooses one of the pids for registration and kills - the other one.</p> + <p>Can be used as a name resolving function for + <seealso marker="#register_name/3"><c>register_name/3</c></seealso> + and + <seealso marker="#re_register_name/3"><c>re_register_name/3</c></seealso>.</p> + <p>The function randomly selects one of the pids for registration and + kills the other one.</p> </desc> </func> <func> <name name="random_notify_name" arity="3"/> - <fsummary>Name resolving function that notifies one pid</fsummary> + <fsummary>Name resolving function that notifies one pid.</fsummary> <desc> - <p>This function can be used as a name resolving function for - <c>register_name/3</c> and <c>re_register_name/3</c>. It - randomly chooses one of the pids for registration, and sends - the message <c>{global_name_conflict, <anno>Name</anno>}</c> to the other - pid.</p> + <p>Can be used as a name resolving function for + <seealso marker="#register_name/3"><c>register_name/3</c></seealso> + and + <seealso marker="#re_register_name/3"><c>re_register_name/3</c></seealso>.</p> + <p>The function randomly selects one of the pids for registration, and + sends the message <c>{global_name_conflict, <anno>Name</anno>}</c> to + the other pid.</p> + </desc> + </func> + + <func> + <name name="re_register_name" arity="2"/> + <name name="re_register_name" arity="3"/> + <fsummary>Atomically re-register a name.</fsummary> + <type name="method"/> + <type_desc name="method">{<c>Module</c>, <c>Function</c>} + is also allowed. + </type_desc> + <desc> + <p>Atomically changes the registered name <c><anno>Name</anno></c> on + all nodes to refer to <c><anno>Pid</anno></c>.</p> + <p>Function <c><anno>Resolve</anno></c> has the same behavior as in + <seealso marker="#register_name/2"><c>register_name/2,3</c></seealso>. + </p> </desc> </func> <func> <name name="register_name" arity="2"/> <name name="register_name" arity="3"/> - <fsummary>Globally register a name for a pid</fsummary> + <fsummary>Globally register a name for a pid.</fsummary> <type name="method"/> - <type_desc name="method">{<c>Module</c>, <c>Function</c>} - is currently also allowed for backward compatibility, but its use is - deprecated + <type_desc name="method">{<c>Module</c>, <c>Function</c>} is also + allowed for backward compatibility, but its use is deprecated. </type_desc> <desc> - <p>Globally associates the name <c><anno>Name</anno></c> with a pid, that is, - Globally notifies all nodes of a new global name in a network + <p>Globally associates name <c><anno>Name</anno></c> with a pid, that + is, globally notifies all nodes of a new global name in a network of Erlang nodes.</p> - <p>When new nodes are added to the network, they are informed of the globally registered names that already exist. The network is also informed of any global names in newly connected nodes. If any name clashes are discovered, - the <c><anno>Resolve</anno></c> function is called. Its purpose is to + function <c><anno>Resolve</anno></c> is called. Its purpose is to decide which pid is correct. If the function crashes, or returns anything other than one of the pids, the name is unregistered. This function is called once for each name clash.</p> - <warning> <p>If you plan to change code without restarting your system, you must use an external fun (<c>fun Module:Function/Arity</c>) - as the <c><anno>Resolve</anno></c> function; if you use a - local fun you can never replace the code for the module that - the fun belongs to. - </p> + as function <c><anno>Resolve</anno></c>. If you use a + local fun, you can never replace the code for the module that + the fun belongs to.</p> </warning> - - <p>There are three pre-defined resolve functions: + <p>Three predefined resolve functions exist: <c>random_exit_name/3</c>, <c>random_notify_name/3</c>, and - <c>notify_all_name/3</c>. If no <c><anno>Resolve</anno></c> function is - defined, <c>random_exit_name</c> is used. This means that one - of the two registered processes will be selected as correct + <c>notify_all_name/3</c>. If no <c><anno>Resolve</anno></c> function + is defined, <c>random_exit_name</c> is used. This means that one + of the two registered processes is selected as correct while the other is killed.</p> - - <p>This function is completely synchronous. This means that + <p>This function is completely synchronous, that is, when this function returns, the name is either registered on all nodes or none.</p> - <p>The function returns <c>yes</c> if successful, <c>no</c> if it fails. For example, <c>no</c> is returned if an attempt is made to register an already registered process or to register a process with a name that is already in use.</p> - <note> - <p>Releases up to and including OTP R10 did not check if the - process was already registered. As a consequence the - global name table could become inconsistent. The old + <p>Releases up to and including Erlang/OTP R10 did not check if the + process was already registered. The global name table could + therefore become inconsistent. The old (buggy) behavior can be chosen by giving the Kernel application variable <c>global_multi_name_action</c> the value <c>allow</c>.</p> </note> - <p>If a process with a registered name dies, or the node goes down, the name is unregistered on all nodes.</p> </desc> @@ -223,38 +222,20 @@ <func> <name name="registered_names" arity="0"/> - <fsummary>All globally registered names</fsummary> - <desc> - <p>Returns a lists of all globally registered names.</p> - </desc> - </func> - - <func> - <name name="re_register_name" arity="2"/> - <name name="re_register_name" arity="3"/> - <fsummary>Atomically re-register a name</fsummary> - <type name="method"/> - <type_desc name="method">{<c>Module</c>, <c>Function</c>} - is also allowed - </type_desc> + <fsummary>All globally registered names.</fsummary> <desc> - <p>Atomically changes the registered name <c><anno>Name</anno></c> on all - nodes to refer to <c><anno>Pid</anno></c>.</p> - - <p>The <c><anno>Resolve</anno></c> function has the same behavior as in - <c>register_name/2,3</c>.</p> + <p>Returns a list of all globally registered names.</p> </desc> </func> <func> <name name="send" arity="2"/> - <fsummary>Send a message to a globally registered pid</fsummary> + <fsummary>Send a message to a globally registered pid.</fsummary> <desc> - <p>Sends the message <c><anno>Msg</anno></c> to the pid globally registered + <p>Sends message <c><anno>Msg</anno></c> to the pid globally registered as <c><anno>Name</anno></c>.</p> - - <p>Failure: If <c><anno>Name</anno></c> is not a globally registered - name, the calling function will exit with reason + <p>If <c><anno>Name</anno></c> is not a globally registered + name, the calling function exits with reason <c>{badarg, {<anno>Name</anno>, <anno>Msg</anno>}}</c>.</p> </desc> </func> @@ -263,7 +244,7 @@ <name name="set_lock" arity="1"/> <name name="set_lock" arity="2"/> <name name="set_lock" arity="3"/> - <fsummary>Set a lock on the specified nodes</fsummary> + <fsummary>Set a lock on the specified nodes.</fsummary> <type name="id"/> <type name="retries"/> <desc> @@ -271,50 +252,48 @@ are specified) on <c><anno>ResourceId</anno></c> for <c><anno>LockRequesterId</anno></c>. If a lock already exists on <c><anno>ResourceId</anno></c> for another requester than - <c><anno>LockRequesterId</anno></c>, and <c><anno>Retries</anno></c> is not equal to 0, - the process sleeps for a while and will try to execute - the action later. When <c><anno>Retries</anno></c> attempts have been made, - <c>false</c> is returned, otherwise <c>true</c>. If - <c><anno>Retries</anno></c> is <c>infinity</c>, <c>true</c> is eventually - returned (unless the lock is never released).</p> - - <p>If no value for <c><anno>Retries</anno></c> is given, <c>infinity</c> is - used.</p> - + <c><anno>LockRequesterId</anno></c>, and <c><anno>Retries</anno></c> + is not equal to <c>0</c>, the process sleeps for a while and tries + to execute the action later. When <c><anno>Retries</anno></c> + attempts have been made, <c>false</c> is returned, otherwise + <c>true</c>. If <c><anno>Retries</anno></c> is <c>infinity</c>, + <c>true</c> is eventually returned (unless the lock is never + released).</p> + <p>If no value for <c><anno>Retries</anno></c> is specified, + <c>infinity</c> is used.</p> <p>This function is completely synchronous.</p> - - <p>If a process which holds a lock dies, or the node goes + <p>If a process that holds a lock dies, or the node goes down, the locks held by the process are deleted.</p> - <p>The global name server keeps track of all processes sharing the same lock, that is, if two processes set the same lock, both processes must delete the lock.</p> - <p>This function does not address the problem of a deadlock. A deadlock can never occur as long as processes only lock one - resource at a time. But if some processes try to lock two or - more resources, a deadlock may occur. It is up to the + resource at a time. A deadlock can occur if some processes + try to lock two or more resources. It is up to the application to detect and rectify a deadlock.</p> - <note> - <p>Some values of <c><anno>ResourceId</anno></c> should be avoided or - Erlang/OTP will not work properly. A list of resources to - avoid: <c>global</c>, <c>dist_ac</c>, - <c>mnesia_table_lock</c>, <c>mnesia_adjust_log_writes</c>, - <c>pg2</c>.</p> + <p>Avoid the following values of <c><anno>ResourceId</anno></c>, + otherwise Erlang/OTP does not work properly:</p> + <list type="bulleted"> + <item><c>dist_ac</c></item> + <item><c>global</c></item> + <item><c>mnesia_adjust_log_writes</c></item> + <item><c>mnesia_table_lock</c></item> + <item><c>pg2</c></item> + </list> </note> - </desc> </func> <func> <name name="sync" arity="0"/> - <fsummary>Synchronize the global name server</fsummary> + <fsummary>Synchronize the global name server.</fsummary> <desc> <p>Synchronizes the global name server with all nodes known to - this node. These are the nodes which are returned from + this node. These are the nodes that are returned from <c>erlang:nodes()</c>. When this function returns, - the global name server will receive global information from + the global name server receives global information from all nodes. This function can be called when new nodes are added to the network.</p> <p>The only possible error reason <c>Reason</c> is @@ -326,24 +305,25 @@ <name name="trans" arity="2"/> <name name="trans" arity="3"/> <name name="trans" arity="4"/> - <fsummary>Micro transaction facility</fsummary> + <fsummary>Micro transaction facility.</fsummary> <type name="retries"/> <type name="trans_fun"/> <desc> - <p>Sets a lock on <c><anno>Id</anno></c> (using <c>set_lock/3</c>). If this - succeeds, <c><anno>Fun</anno>()</c> is evaluated and the result <c><anno>Res</anno></c> - is returned. Returns <c>aborted</c> if the lock attempt - failed. If <c><anno>Retries</anno></c> is set to <c>infinity</c>, - the transaction will not abort.</p> - - <p><c>infinity</c> is the default setting and will be used if - no value is given for <c><anno>Retries</anno></c>.</p> + <p>Sets a lock on <c><anno>Id</anno></c> (using + <seealso marker="#set_lock/3"><c>set_lock/3</c></seealso>). + If this succeeds, <c><anno>Fun</anno>()</c> is evaluated and the + result <c><anno>Res</anno></c> + is returned. Returns <c>aborted</c> if the lock attempt fails. + If <c><anno>Retries</anno></c> is set to <c>infinity</c>, + the transaction does not abort.</p> + <p><c>infinity</c> is the default setting and is used if + no value is specified for <c><anno>Retries</anno></c>.</p> </desc> </func> <func> <name name="unregister_name" arity="1"/> - <fsummary>Remove a globally registered name for a pid</fsummary> + <fsummary>Remove a globally registered name for a pid.</fsummary> <desc> <p>Removes the globally registered name <c><anno>Name</anno></c> from the network of Erlang nodes.</p> @@ -352,7 +332,7 @@ <func> <name name="whereis_name" arity="1"/> - <fsummary>Get the pid with a given globally registered name</fsummary> + <fsummary>Get the pid with a specified globally registered name.</fsummary> <desc> <p>Returns the pid with the globally registered name <c><anno>Name</anno></c>. Returns <c>undefined</c> if the name is not @@ -363,8 +343,8 @@ <section> <title>See Also</title> - <p><seealso marker="global_group">global_group(3)</seealso>, - <seealso marker="net_kernel">net_kernel(3)</seealso></p> + <p><seealso marker="global_group"><c>global_group(3)</c></seealso>, + <seealso marker="net_kernel"><c>net_kernel(3)</c></seealso></p> </section> </erlref> diff --git a/lib/kernel/doc/src/global_group.xml b/lib/kernel/doc/src/global_group.xml index 3a993fc9e6..8f947b9adf 100644 --- a/lib/kernel/doc/src/global_group.xml +++ b/lib/kernel/doc/src/global_group.xml @@ -26,22 +26,22 @@ <prepared>Esko Vierumäki</prepared> <docno></docno> <date>1998-12-18</date> - <rev>b</rev> + <rev>B</rev> </header> <module>global_group</module> - <modulesummary>Grouping Nodes to Global Name Registration Groups</modulesummary> + <modulesummary>Grouping nodes to global name registration groups.</modulesummary> <description> - <p>The global group function makes it possible to group the nodes - in a system into partitions, each partition having its own global - name space, refer to <c>global(3)</c>. These partitions are - called global groups.</p> - <p>The main advantage of dividing systems to global groups is that + <p>This module makes it possible to partition the nodes of a + system into <em>global groups</em>. Each global group has its own + global namespace, see <seealso marker="global"> + <c>global(3)</c></seealso>.</p> + <p>The main advantage of dividing systems into global groups is that the background load decreases while the number of nodes to be updated is reduced when manipulating globally registered names.</p> <p>The Kernel configuration parameter <c>global_groups</c> defines the global groups (see also - <seealso marker="kernel_app">kernel(6)</seealso>, - <seealso marker="config">config(4)</seealso>:</p> + <seealso marker="kernel_app#global_groups"><c>kernel(6)</c></seealso> + and <seealso marker="config"><c>config(4)</c></seealso>):</p> <code type="none"> {global_groups, [GroupTuple :: group_tuple()]}</code> <p>For the processes and nodes to run smoothly using the global @@ -54,22 +54,24 @@ </item> <item> <p>All involved nodes must agree on the global group definition, - or the behavior of the system is undefined.</p> + otherwise the behavior of the system is undefined.</p> </item> <item> - <p><em>All</em> nodes in the system should belong to exactly + <p><em>All</em> nodes in the system must belong to exactly one global group.</p> </item> </list> - <p>In the following description, a <em>group node</em> is a node + <p>In the following descriptions, a <em>group node</em> is a node belonging to the same global group as the local node.</p> </description> - <datatypes> + + <datatypes> <datatype> <name name="group_tuple"/> <desc> <p>A <c>GroupTuple</c> without <c>PublishType</c> is the same as a - <c>GroupTuple</c> with <c>PublishType == normal</c>.</p> + <c>GroupTuple</c> with <c>PublishType</c> equal to <c>normal</c>. + </p> </desc> </datatype> <datatype> @@ -78,52 +80,57 @@ <datatype> <name name="publish_type"/> <desc> - <p>A node started with the command line flag <c>-hidden</c>, see - <seealso marker="erts:erl">erl(1)</seealso>, is said to be a - <em>hidden</em> node. A hidden node will establish hidden + <p>A node started with command-line flag <c>-hidden</c> (see + <seealso marker="erts:erl"><c>erl(1)</c></seealso>) is said + to be a <em>hidden</em> node. A hidden node establishes hidden connections to nodes not part of the same global group, but normal (visible) connections to nodes part of the same global group.</p> - <p>A global group defined with <c>PublishType == hidden</c>, is - said to be a hidden global group. All nodes in a hidden global - group are hidden nodes, regardless if they are started with - the <c>-hidden</c> command line flag or not.</p> + <p>A global group defined with <c>PublishType</c> equal to + <c>hidden</c> is said to be a hidden global group. + All nodes in a hidden global + group are hidden nodes, whether they are started with + command-line flag <c>-hidden</c> or not.</p> </desc> </datatype> <datatype> <name name="name"/> <desc><p>A registered name.</p></desc> </datatype> + <datatype> <name name="where"/> </datatype> </datatypes> + <funcs> <func> <name name="global_groups" arity="0"/> - <fsummary>Return the global group names</fsummary> + <fsummary>Return the global group names.</fsummary> <desc> - <p>Returns a tuple containing the name of the global group + <p>Returns a tuple containing the name of the global group that the local node belongs to, and the list of all other known group names. Returns <c>undefined</c> if no global groups are defined.</p> </desc> </func> + <func> <name name="info" arity="0"/> - <fsummary>Information about global groups</fsummary> + <fsummary>Information about global groups.</fsummary> <type name="info_item"/> <type name="sync_state"/> <desc> <p>Returns a list containing information about the global - groups. Each element of the list is a tuple. The order of - the tuples is not defined.</p> + groups. Each list element is a tuple. The order of + the tuples is undefined.</p> <taglist> <tag><c>{state, <anno>State</anno>}</c></tag> <item> <p>If the local node is part of a global group, - <c><anno>State</anno> == synced</c>. If no global groups are defined, - <c><anno>State</anno> == no_conf</c>.</p> + <c><anno>State</anno></c> is equal to <c>synced</c>. + If no global groups are defined, + <c><anno>State</anno></c> is equal to <c>no_conf</c>.</p> </item> <tag><c>{own_group_name, <anno>GroupName</anno>}</c></tag> <item> @@ -152,117 +159,131 @@ <tag><c>{other_groups, <anno>Groups</anno>}</c></tag> <item> <p><c><anno>Groups</anno></c> is a list of tuples - <c>{<anno>GroupName</anno>, <anno>Nodes</anno>}</c>, specifying the name and nodes + <c>{<anno>GroupName</anno>, <anno>Nodes</anno>}</c>, + specifying the name and nodes of the other global groups.</p> </item> <tag><c>{monitoring, <anno>Pids</anno>}</c></tag> <item> - <p>A list of pids, specifying the processes which have + <p>A list of pids, specifying the processes that have subscribed to <c>nodeup</c> and <c>nodedown</c> messages.</p> </item> </taglist> </desc> </func> + <func> <name name="monitor_nodes" arity="1"/> - <fsummary>Subscribe to node status changes</fsummary> + <fsummary>Subscribe to node status changes.</fsummary> <desc> - <p>Depending on <c><anno>Flag</anno></c>, the calling process starts - subscribing (<c><anno>Flag</anno> == true</c>) or stops subscribing - (<c><anno>Flag</anno> == false</c>) to node status change messages.</p> - <p>A process which has subscribed will receive the messages + <p>Depending on <c><anno>Flag</anno></c>, the calling process + starts subscribing (<c><anno>Flag</anno></c> equal to + <c>true</c>) or stops subscribing (<c><anno>Flag</anno></c> + equal to <c>false</c>) to node status change messages.</p> + <p>A process that has subscribed receives the messages <c>{nodeup, Node}</c> and <c>{nodedown, Node}</c> when a group node connects or disconnects, respectively.</p> </desc> </func> + <func> <name name="own_nodes" arity="0"/> - <fsummary>Return the group nodes</fsummary> + <fsummary>Return the group nodes.</fsummary> <desc> <p>Returns the names of all group nodes, regardless of their current status.</p> </desc> </func> + <func> <name name="registered_names" arity="1"/> - <fsummary>Return globally registered names</fsummary> + <fsummary>Return globally registered names.</fsummary> <desc> - <p>Returns a list of all names which are globally registered + <p>Returns a list of all names that are globally registered on the specified node or in the specified global group.</p> </desc> </func> + <func> <name name="send" arity="2"/> <name name="send" arity="3"/> - <fsummary>Send a message to a globally registered pid</fsummary> + <fsummary>Send a message to a globally registered pid.</fsummary> <desc> <p>Searches for <c><anno>Name</anno></c>, globally registered on - the specified node or in the specified global group, or -- - if the <c><anno>Where</anno></c> argument is not provided -- in any global - group. The global groups are searched in the order in which - they appear in the value of the <c>global_groups</c> - configuration parameter.</p> - <p>If <c><anno>Name</anno></c> is found, the message <c><anno>Msg</anno></c> is sent to + the specified node or in the specified global group, or + (if argument <c><anno>Where</anno></c> is not provided) in any + global group. The global groups are searched in the order that + they appear in the value of configuration parameter + <c>global_groups</c>.</p> + <p>If <c><anno>Name</anno></c> is found, message + <c><anno>Msg</anno></c> is sent to the corresponding pid. The pid is also the return value of the function. If the name is not found, the function returns <c>{badarg, {<anno>Name</anno>, <anno>Msg</anno>}}</c>.</p> </desc> </func> + <func> <name name="sync" arity="0"/> - <fsummary>Synchronize the group nodes</fsummary> + <fsummary>Synchronize the group nodes.</fsummary> <desc> <p>Synchronizes the group nodes, that is, the global name - servers on the group nodes. Also check the names globally + servers on the group nodes. Also checks the names globally registered in the current global group and unregisters them on any known node not part of the group.</p> <p>If synchronization is not possible, an error report is sent - to the error logger (see also <c>error_logger(3)</c>).</p> - <p>Failure: - <c>{error, {'invalid global_groups definition', Bad}}</c> if - the <c>global_groups</c> configuration parameter has an + to the error logger (see also + <seealso marker="error_logger"><c>error_logger(3)</c></seealso>. + </p> + <p>Returns <c>{error, {'invalid global_groups definition', Bad}}</c> + if configuration parameter <c>global_groups</c> has an invalid value <c>Bad</c>.</p> </desc> </func> + <func> <name name="whereis_name" arity="1"/> <name name="whereis_name" arity="2"/> - <fsummary>Get the pid with a given globally registered name</fsummary> + <fsummary>Get the pid with a specified globally registered name.</fsummary> <desc> <p>Searches for <c><anno>Name</anno></c>, globally registered on - the specified node or in the specified global group, or -- if - the <c><anno>Where</anno></c> argument is not provided -- in any global - group. The global groups are searched in the order in which - they appear in the value of the <c>global_groups</c> - configuration parameter.</p> - <p>If <c><anno>Name</anno></c> is found, the corresponding pid is returned. - If the name is not found, the function returns + the specified node or in the specified global group, or + (if argument <c><anno>Where</anno></c> is not provided) in any global + group. The global groups are searched in the order that + they appear in the value of configuration parameter + <c>global_groups</c>.</p> + <p>If <c><anno>Name</anno></c> is found, the corresponding pid is + returned. If the name is not found, the function returns <c>undefined</c>.</p> </desc> </func> </funcs> <section> - <title>NOTE</title> - <p>In the situation where a node has lost its connections to other - nodes in its global group, but has connections to nodes in other - global groups, a request from another global group may produce an - incorrect or misleading result. For example, the isolated node may - not have accurate information about registered names in its - global group.</p> - <p>Note also that the <c>send/2,3</c> function is not secure.</p> - <p>Distribution of applications is highly dependent of the global - group definitions. It is not recommended that an application is - distributed over several global groups of the obvious reason that - the registered names may be moved to another global group at - failover/takeover. There is nothing preventing doing this, but - the application code must in such case handle the situation.</p> + <title>Notes</title> + <list type="bulleted"> + <item><p>In the situation where a node has lost its connections to other + nodes in its global group, but has connections to nodes in other + global groups, a request from another global group can produce an + incorrect or misleading result. For example, the isolated node can + have inaccurate information about registered names in its + global group.</p></item> + <item><p>Function + <seealso marker="#send/2"><c>send/2,3</c></seealso> + is not secure.</p></item> + <item><p>Distribution of applications is highly dependent of the global + group definitions. It is not recommended that an application is + distributed over many global groups, as + the registered names can be moved to another global group at + failover/takeover. Nothing prevents this to be done, but + the application code must then handle the situation.</p></item> + </list> </section> <section> - <title>SEE ALSO</title> - <p><seealso marker="erts:erl">erl(1)</seealso>, - <seealso marker="global">global(3)</seealso></p> + <title>See Also</title> + <p><seealso marker="global"><c>global(3)</c></seealso>, + <seealso marker="erts:erl"><c>erl(1)</c></seealso></p> </section> </erlref> diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml index 2bd8fef0df..c587e39345 100644 --- a/lib/kernel/doc/src/heart.xml +++ b/lib/kernel/doc/src/heart.xml @@ -29,93 +29,80 @@ <rev>A</rev> </header> <module>heart</module> - <modulesummary>Heartbeat Monitoring of an Erlang Runtime System</modulesummary> + <modulesummary>Heartbeat monitoring of an Erlang runtime system.</modulesummary> <description> <p>This modules contains the interface to the <c>heart</c> process. <c>heart</c> sends periodic heartbeats to an external port program, which is also named <c>heart</c>. The purpose of - the heart port program is to check that the Erlang runtime system + the <c>heart</c> port program is to check that the Erlang runtime system it is supervising is still running. If the port program has not received any heartbeats within <c>HEART_BEAT_TIMEOUT</c> seconds - (default is 60 seconds), the system can be rebooted. Also, if + (defaults to 60 seconds), the system can be rebooted. Also, if the system is equipped with a hardware watchdog timer and is running Solaris, the watchdog can be used to supervise the entire system.</p> - <p>An Erlang runtime system to be monitored by a heart program, - should be started with the command line flag <c>-heart</c> (see - also <seealso marker="erts:erl">erl(1)</seealso>). The <c>heart</c> - process is then started automatically:</p> + <p>An Erlang runtime system to be monitored by a heart program + is to be started with command-line flag <c>-heart</c> (see + also <seealso marker="erts:erl"><c>erl(1)</c></seealso>). + The <c>heart</c> process is then started automatically:</p> <pre> % <input>erl -heart ...</input></pre> - <p>If the system should be rebooted because of missing heart-beats, - or a terminated Erlang runtime system, the environment variable - <c>HEART_COMMAND</c> has to be set before the system is started. - If this variable is not set, a warning text will be printed but - the system will not reboot. However, if the hardware watchdog is - used, it will trigger a reboot <c>HEART_BEAT_BOOT_DELAY</c> - seconds later nevertheless (default is 60).</p> - <p>To reboot on the WINDOWS platform <c>HEART_COMMAND</c> can be + <p>If the system is to be rebooted because of missing heartbeats, + or a terminated Erlang runtime system, environment variable + <c>HEART_COMMAND</c> must be set before the system is started. + If this variable is not set, a warning text is printed but + the system does not reboot. However, if the hardware watchdog is + used, it still triggers a reboot <c>HEART_BEAT_BOOT_DELAY</c> + seconds later (defaults to 60 seconds).</p> + <p>To reboot on Windows, <c>HEART_COMMAND</c> can be set to <c>heart -shutdown</c> (included in the Erlang delivery) - or of course to any other suitable program which can activate a - reboot.</p> - <p>The hardware watchdog will not be started under Solaris if - the environment variable <c>HW_WD_DISABLE</c> is set.</p> - <p>The <c>HEART_BEAT_TIMEOUT</c> and <c>HEART_BEAT_BOOT_DELAY</c> - environment variables can be used to configure the heart timeouts, - they can be set in the operating system shell before Erlang is - started or be specified at the command line:</p> + or to any other suitable program that can activate a reboot.</p> + <p>The hardware watchdog is not started under Solaris if + environment variable <c>HW_WD_DISABLE</c> is set.</p> + <p>The environment variables <c>HEART_BEAT_TIMEOUT</c> and + <c>HEART_BEAT_BOOT_DELAY</c> can be used to configure the heart + time-outs; they can be set in the operating system shell before Erlang + is started or be specified at the command line:</p> <pre> % <input>erl -heart -env HEART_BEAT_TIMEOUT 30 ...</input></pre> <p>The value (in seconds) must be in the range 10 < X <= 65535.</p> - <p>It should be noted that if the system clock is adjusted with - more than <c>HEART_BEAT_TIMEOUT</c> seconds, <c>heart</c> will - timeout and try to reboot the system. This can happen, for - example, if the system clock is adjusted automatically by use of - NTP (Network Time Protocol).</p> - - <p> If a crash occurs, an <c><![CDATA[erl_crash.dump]]></c> will <em>not</em> be written - unless the environment variable <c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c> is set. - </p> - + <p>Notice that if the system clock is adjusted with + more than <c>HEART_BEAT_TIMEOUT</c> seconds, <c>heart</c> + times out and tries to reboot the system. This can occur, for + example, if the system clock is adjusted automatically by use of the + Network Time Protocol (NTP).</p> + <p>If a crash occurs, an <c><![CDATA[erl_crash.dump]]></c> is <em>not</em> + written unless environment variable + <c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c> is set:</p> <pre> % <input>erl -heart -env ERL_CRASH_DUMP_SECONDS 10 ...</input></pre> - - <p> If a regular core dump is wanted, let heart know by setting the kill signal to abort - using the environment variable <c><![CDATA[HEART_KILL_SIGNAL=SIGABRT]]></c>. - If unset, or not set to <c><![CDATA[SIGABRT]]></c>, the default behaviour will be a kill - signal using <c><![CDATA[SIGKILL]]></c>. - </p> - + <p>If a regular core dump is wanted, let <c>heart</c> know by setting + the kill signal to abort using environment variable + <c><![CDATA[HEART_KILL_SIGNAL=SIGABRT]]></c>. If unset, or not set to + <c><![CDATA[SIGABRT]]></c>, the default behavior is a kill signal using + <c><![CDATA[SIGKILL]]></c>:</p> <pre> % <input>erl -heart -env HEART_KILL_SIGNAL SIGABRT ...</input></pre> - - <p> - Furthermore, <c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c> has the following behaviour on - <c>heart</c>: - </p> - <taglist> - <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag> - <item><p> - Suppresses the writing a crash dump file entirely, - thus rebooting the runtime system immediately. - This is the same as not setting the environment variable. - </p> - </item> - <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag> - <item><p> Setting the environment variable to a negative value will not reboot - the runtime system until the crash dump file has been completly written. - </p> - </item> - <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag> - <item><p> - Heart will wait for <c>S</c> seconds to let the crash dump file be written. - After <c>S</c> seconds <c>heart</c> will reboot the runtime system regardless of - the crash dump file has been written or not. - </p> - </item> - </taglist> - - <p>In the following descriptions, all function fails with reason + <p>Furthermore, <c><![CDATA[ERL_CRASH_DUMP_SECONDS]]></c> has the + following behavior on <c>heart</c>:</p> + <taglist> + <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=0]]></c></tag> + <item><p>Suppresses the writing of a crash dump file entirely, + thus rebooting the runtime system immediately. + This is the same as not setting the environment variable.</p> + </item> + <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=-1]]></c></tag> + <item><p>Setting the environment variable to a negative value does not + reboot the runtime system until the crash dump file is completly + written.</p> + </item> + <tag><c><![CDATA[ERL_CRASH_DUMP_SECONDS=S]]></c></tag> + <item><p><c>heart</c> waits for <c>S</c> seconds to let the crash dump + file be written. After <c>S</c> seconds, <c>heart</c> reboots the + runtime system, whether the crash dump file is written or not.</p> + </item> + </taglist> + <p>In the following descriptions, all functions fail with reason <c>badarg</c> if <c>heart</c> is not started.</p> </description> @@ -128,37 +115,36 @@ <funcs> <func> <name name="set_cmd" arity="1"/> - <fsummary>Set a temporary reboot command</fsummary> + <fsummary>Set a temporary reboot command.</fsummary> <desc> - <p>Sets a temporary reboot command. This command is used if + <p>Sets a temporary reboot command. This command is used if a <c>HEART_COMMAND</c> other than the one specified with - the environment variable should be used in order to reboot - the system. The new Erlang runtime system will (if it - misbehaves) use the environment variable - <c>HEART_COMMAND</c> to reboot.</p> - - <p>Limitations: The <c><anno>Cmd</anno></c> command string - will be sent to the heart program as a ISO-latin-1 or UTF-8 - encoded binary depending on the file name encoding mode of the - emulator (see - <seealso marker="kernel:file#native_name_encoding/0"><c>file:native_name_encoding/0</c></seealso>). - The size of the encoded binary must be less than 2047 bytes.</p> + the environment variable is to be used to reboot + the system. The new Erlang runtime system uses (if it misbehaves) + environment variable <c>HEART_COMMAND</c> to reboot.</p> + <p>Limitations: Command string <c><anno>Cmd</anno></c> is sent to the + <c>heart</c> program as an ISO Latin-1 or UTF-8 encoded binary, + depending on the filename encoding mode of the emulator (see + <seealso marker="kernel:file#native_name_encoding/0"><c>file:native_name_encoding/0</c></seealso>). + The size of the encoded binary must be less than 2047 bytes.</p> </desc> </func> + <func> <name name="clear_cmd" arity="0"/> - <fsummary>Clear the temporary boot command</fsummary> + <fsummary>Clear the temporary boot command.</fsummary> <desc> <p>Clears the temporary boot command. If the system terminates, the normal <c>HEART_COMMAND</c> is used to reboot.</p> </desc> </func> + <func> <name name="get_cmd" arity="0"/> - <fsummary>Get the temporary reboot command</fsummary> + <fsummary>Get the temporary reboot command.</fsummary> <desc> - <p>Get the temporary reboot command. If the command is cleared, - the empty string will be returned.</p> + <p>Gets the temporary reboot command. If the command is cleared, + the empty string is returned.</p> </desc> </func> @@ -166,12 +152,12 @@ <name name="set_callback" arity="2"/> <fsummary>Set a validation callback</fsummary> <desc> - <p> This validation callback will be executed before any heartbeat sent - to the port program. For the validation to succeed it needs to return - with the value <c>ok</c>. + <p> This validation callback will be executed before any + heartbeat is sent to the port program. For the validation to + succeed it needs to return with the value <c>ok</c>. </p> - <p> An exception within the callback will be treated as a validation failure. </p> - <p> The callback will be removed if the system reboots. </p> + <p>An exception within the callback will be treated as a validation failure.</p> + <p>The callback will be removed if the system reboots.</p> </desc> </func> <func> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index 088d78c1d6..cfff393b8c 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -29,43 +29,47 @@ <rev>A</rev> </header> <module>inet</module> - <modulesummary>Access to TCP/IP Protocols</modulesummary> + <modulesummary>Access to TCP/IP protocols.</modulesummary> <description> - <p>Provides access to TCP/IP protocols.</p> - <p>See also <em>ERTS User's Guide, Inet configuration</em> for more - information on how to configure an Erlang runtime system for IP - communication.</p> - <p>Two Kernel configuration parameters affect the behaviour of all - sockets opened on an Erlang node: - <c>inet_default_connect_options</c> can contain a list of default - options used for all sockets returned when doing <c>connect</c>, - and <c>inet_default_listen_options</c> can contain a list of - default options used when issuing a <c>listen</c> call. When - <c>accept</c> is issued, the values of the listensocket options - are inherited, why no such application variable is needed for + <p>This module provides access to TCP/IP protocols.</p> + <p>See also + <seealso marker="erts:inet_cfg">ERTS User's Guide: + Inet Configuration</seealso> for more information about how to + configure an Erlang runtime system for IP communication.</p> + <p>The following two <c>Kernel</c> configuration parameters affect the + behavior of all sockets opened on an Erlang node:</p> + <list type="bulleted"> + <item><p><c>inet_default_connect_options</c> can contain a list of + default options used for all sockets returned when doing + <c>connect</c>.</p></item> + <item><p><c>inet_default_listen_options</c> can contain a list of + default options used when issuing a <c>listen</c> call.</p></item> + </list> + <p>When <c>accept</c> is issued, the values of the listening socket options + are inherited. No such application variable is therefore needed for <c>accept</c>.</p> - <p>Using the Kernel configuration parameters mentioned above, one - can set default options for all TCP sockets on a node. This should - be used with care, but options like <c>{delay_send,true}</c> - might be specified in this way. An example of starting an Erlang - node with all sockets using delayed send could look like this:</p> + <p>Using the <c>Kernel</c> configuration parameters above, one + can set default options for all TCP sockets on a node, but use this + with care. Options such as <c>{delay_send,true}</c> can be + specified in this way. The following is an example of starting an Erlang + node with all sockets using delayed send:</p> <pre> $ <input>erl -sname test -kernel \</input> <input>inet_default_connect_options '[{delay_send,true}]' \</input> <input>inet_default_listen_options '[{delay_send,true}]'</input></pre> - <p>Note that the default option <c>{active, true}</c> currently + <p>Notice that default option <c>{active, true}</c> cannot be changed, for internal reasons.</p> <p>Addresses as inputs to functions can be either a string or a - tuple. For instance, the IP address 150.236.20.73 can be passed to - <c>gethostbyaddr/1</c> either as the string "150.236.20.73" - or as the tuple <c>{150, 236, 20, 73}</c>.</p> - <p>IPv4 address examples:</p> + tuple. For example, the IP address 150.236.20.73 can be passed to + <c>gethostbyaddr/1</c>, either as string <c>"150.236.20.73"</c> + or as tuple <c>{150, 236, 20, 73}</c>.</p> + <p><em>IPv4 address examples:</em></p> <code type="none"> Address ip_address() ------- ------------ 127.0.0.1 {127,0,0,1} 192.168.42.2 {192,168,42,2}</code> - <p>IPv6 address examples:</p> + <p><em>IPv6 address examples:</em></p> <code type="none"> Address ip_address() ------- ------------ @@ -77,7 +81,9 @@ FFFF::192.168.42.2 {16#3ffe,16#b80,16#1f8d,16#2,16#204,16#acff,16#fe17,16#bf38} fe80::204:acff:fe17:bf38 {16#fe80,0,0,0,0,16#204,16#acff,16#fe17,16#bf38}</code> - <p>A function that may be useful is <seealso marker="#parse_address/1">parse_address/1</seealso>:</p> + <p>Function + <seealso marker="#parse_address/1"><c>parse_address/1</c></seealso> + can be useful:</p> <pre> 1> <input>inet:parse_address("192.168.42.2").</input> {ok,{192,168,42,2}} @@ -89,9 +95,12 @@ fe80::204:acff:fe17:bf38 <datatype> <name name="hostent"/> <desc> - <p>The record is defined in the Kernel include file "inet.hrl". - Add the following directive to the module:</p> -<code>-include_lib("kernel/include/inet.hrl").</code></desc> + <p>The record is defined in the <c>Kernel</c> include file + <c>"inet.hrl"</c>.</p> + <p>Add the following directive to the module:</p> + <code> +-include_lib("kernel/include/inet.hrl").</code> + </desc> </datatype> <datatype> <name name="hostname"/> @@ -110,17 +119,20 @@ fe80::204:acff:fe17:bf38 </datatype> <datatype> <name name="posix"/> - <desc><p>An atom which is named from the Posix error codes - used in Unix, and in the runtime libraries of most - C compilers. See + <desc> + <p>An atom that is named from the POSIX error codes used in Unix, + and in the runtime libraries of most C compilers. See section <seealso marker="#error_codes">POSIX Error Codes</seealso>.</p> </desc> </datatype> <datatype> <name>socket()</name> - <desc><p><marker id="type-socket"></marker> - See <seealso marker="gen_tcp#type-socket">gen_tcp(3)</seealso> - and <seealso marker="gen_udp#type-socket">gen_udp(3)</seealso>.</p> + <desc> + <p><marker id="type-socket"></marker>See + <seealso marker="gen_tcp#type-socket"><c>gen_tcp:type-socket</c></seealso> + and + <seealso marker="gen_udp#type-socket"><c>gen_udp:type-socket</c></seealso>. + </p> </desc> </datatype> <datatype> @@ -131,443 +143,415 @@ fe80::204:acff:fe17:bf38 <funcs> <func> <name name="close" arity="1"/> - <fsummary>Close a socket of any type</fsummary> + <fsummary>Close a socket of any type.</fsummary> <desc> <p>Closes a socket of any type.</p> </desc> </func> + <func> - <name name="get_rc" arity="0"/> - <fsummary>Return a list of IP configuration parameters</fsummary> + <name name="format_error" arity="1"/> + <fsummary>Return a descriptive string for an error reason.</fsummary> <desc> - <p>Returns the state of the Inet configuration database in - form of a list of recorded configuration parameters. (See the - ERTS User's Guide, Inet configuration, for more information). - Only parameters with other than default values are returned.</p> + <p>Returns a diagnostic error string. For possible POSIX values and + corresponding strings, see section + <seealso marker="#error_codes">POSIX Error Codes</seealso>.</p> </desc> </func> + <func> - <name name="format_error" arity="1"/> - <fsummary>Return a descriptive string for an error reason</fsummary> + <name name="get_rc" arity="0"/> + <fsummary>Return a list of IP configuration parameters.</fsummary> <desc> - <p>Returns a diagnostic error string. See the section below - for possible Posix values and the corresponding - strings.</p> + <p>Returns the state of the <c>Inet</c> configuration database in + form of a list of recorded configuration parameters. For more + information, see <seealso marker="erts:inet_cfg">ERTS User's Guide: + Inet Configuration</seealso>. + Only parameters with other than default values are returned.</p> </desc> </func> + <func> <name name="getaddr" arity="2"/> - <fsummary>Return the IP-address for a host</fsummary> + <fsummary>Return the IP address for a host.</fsummary> <desc> - <p>Returns the IP-address for <c><anno>Host</anno></c> as a tuple of - integers. <c><anno>Host</anno></c> can be an IP-address, a single hostname - or a fully qualified hostname.</p> + <p>Returns the IP address for <c><anno>Host</anno></c> as a tuple of + integers. <c><anno>Host</anno></c> can be an IP address, a single + hostname, or a fully qualified hostname.</p> </desc> </func> + <func> <name name="getaddrs" arity="2"/> - <fsummary>Return the IP-addresses for a host</fsummary> + <fsummary>Return the IP addresses for a host.</fsummary> <desc> - <p>Returns a list of all IP-addresses for <c><anno>Host</anno></c>. - <c><anno>Host</anno></c> can be an IP-address, a single hostname or a fully - qualified hostname.</p> + <p>Returns a list of all IP addresses for <c><anno>Host</anno></c>. + <c><anno>Host</anno></c> can be an IP address, a single hostname, or + a fully qualified hostname.</p> </desc> </func> + <func> <name name="gethostbyaddr" arity="1"/> - <fsummary>Return a hostent record for the host with the given address</fsummary> + <fsummary>Return a hostent record for the host with the specified + address.</fsummary> <desc> - <p>Returns a <c>hostent</c> record given an address.</p> - </desc> + <p>Returns a <c>hostent</c> record for the host with the specified + address.</p></desc> </func> + <func> <name name="gethostbyname" arity="1"/> - <fsummary>Return a hostent record for the host with the given name</fsummary> + <fsummary>Return a hostent record for the host with the specified name. + </fsummary> <desc> - <p>Returns a <c>hostent</c> record given a hostname.</p> + <p>Returns a <c>hostent</c> record for the host with the specified + hostname.</p> </desc> </func> + <func> <name name="gethostbyname" arity="2"/> - <fsummary>Return a hostent record for the host with the given name</fsummary> + <fsummary>Return a hostent record for the host with the specified name. + </fsummary> <desc> - <p>Returns a <c>hostent</c> record given a hostname, restricted - to the given address family.</p> + <p>Returns a <c>hostent</c> record for the host with the specified + name, restricted to the specified address family.</p> </desc> </func> + <func> <name name="gethostname" arity="0"/> - <fsummary>Return the local hostname</fsummary> + <fsummary>Return the local hostname.</fsummary> <desc> - <p>Returns the local hostname. Will never fail.</p> + <p>Returns the local hostname. Never fails.</p> </desc> </func> <func> <name name="getifaddrs" arity="0"/> - <fsummary>Return a list of interfaces and their addresses</fsummary> - <desc> - <p> - Returns a list of 2-tuples containing interface names and the - interface's addresses. <c><anno>Ifname</anno></c> is a Unicode string. - <c><anno>Hwaddr</anno></c> is hardware dependent, e.g on Ethernet interfaces - it is the 6-byte Ethernet address (MAC address (EUI-48 address)). - </p> - <p> - The <c>{addr,<anno>Addr</anno>}</c>, <c>{netmask,_}</c> and <c>{broadaddr,_}</c> - tuples are repeated in the result list iff the interface has multiple - addresses. If you come across an interface that has - multiple <c>{flag,_}</c> or <c>{hwaddr,_}</c> tuples you have - a really strange interface or possibly a bug in this function. - The <c>{flag,_}</c> tuple is mandatory, all other optional. - </p> - <p> - Do not rely too much on the order of <c><anno>Flag</anno></c> atoms or - <c><anno>Ifopt</anno></c> tuples. There are some rules, though:</p> - <list> - <item> - Immediately after <c>{addr,_}</c> follows <c>{netmask,_}</c> - </item> - <item> - Immediately thereafter follows <c>{broadaddr,_}</c> if - the <c>broadcast</c> flag is <em>not</em> set and the - <c>pointtopoint</c> flag <em>is</em> set. - </item> - <item> - Any <c>{netmask,_}</c>, <c>{broadaddr,_}</c> or - <c>{dstaddr,_}</c> tuples that follow an <c>{addr,_}</c> - tuple concerns that address. - </item> - </list> - <p> - The <c>{hwaddr,_}</c> tuple is not returned on Solaris since the - hardware address historically belongs to the link layer and only - the superuser can read such addresses. - </p> - <p> - On Windows, the data is fetched from quite different OS API - functions, so the <c><anno>Netmask</anno></c> and <c><anno>Broadaddr</anno></c> - values may be calculated, just as some <c><anno>Flag</anno></c> values. - You have been warned. Report flagrant bugs. - </p> - </desc> + <fsummary>Return a list of interfaces and their addresses.</fsummary> + <desc> + <p>Returns a list of 2-tuples containing interface names and the + interface addresses. <c><anno>Ifname</anno></c> is a Unicode string. + <c><anno>Hwaddr</anno></c> is hardware dependent, for example, on + Ethernet interfaces + it is the 6-byte Ethernet address (MAC address (EUI-48 address)).</p> + <p>The tuples <c>{addr,<anno>Addr</anno>}</c>, <c>{netmask,_}</c>, and + <c>{broadaddr,_}</c> are repeated in the result list if the interface + has multiple addresses. If you come across an interface with + multiple <c>{flag,_}</c> or <c>{hwaddr,_}</c> tuples, you have + a strange interface or possibly a bug in this function. The tuple + <c>{flag,_}</c> is mandatory, all others are optional.</p> + <p>Do not rely too much on the order of <c><anno>Flag</anno></c> atoms + or <c><anno>Ifopt</anno></c> tuples. There are however some rules:</p> + <list type="bulleted"> + <item><p>Immediately after + <c>{addr,_}</c> follows <c>{netmask,_}</c>.</p></item> + <item><p>Immediately thereafter follows <c>{broadaddr,_}</c> if flag + <c>broadcast</c> is <em>not</em> set and flag + <c>pointtopoint</c> <em>is</em> set.</p></item> + <item><p>Any <c>{netmask,_}</c>, <c>{broadaddr,_}</c>, or + <c>{dstaddr,_}</c> tuples that follow an <c>{addr,_}</c> + tuple concerns that address.</p></item> + </list> + <p>The tuple <c>{hwaddr,_}</c> is not returned on Solaris, as the + hardware address historically belongs to the link layer and only + the superuser can read such addresses.</p> + <warning> + <p>On Windows, the data is fetched from different OS API functions, + so the <c><anno>Netmask</anno></c> and <c><anno>Broadaddr</anno></c> + values can be calculated, just as some <c><anno>Flag</anno></c> + values. Report flagrant bugs.</p> + </warning> + </desc> </func> <func> <name name="getopts" arity="2"/> - <fsummary>Get one or more options for a socket</fsummary> + <fsummary>Get one or more options for a socket.</fsummary> <type name="socket_getopt"/> <type name="socket_setopt"/> <desc> - <p>Gets one or more options for a socket. - See <seealso marker="#setopts/2">setopts/2</seealso> - for a list of available options.</p> - <p>The number of elements in the returned <c><anno>OptionValues</anno></c> + <p>Gets one or more options for a socket. For a list of available + options, see + <seealso marker="#setopts/2"><c>setopts/2</c></seealso>.</p> + <p>The number of elements in the returned + <c><anno>OptionValues</anno></c> list does not necessarily correspond to the number of options asked for. If the operating system fails to support an option, - it is simply left out in the returned list. An error tuple is only - returned when getting options for the socket is impossible - (i.e. the socket is closed or the buffer size in a raw request + it is left out in the returned list. An error tuple is returned + only when getting options for the socket is impossible (that is, + the socket is closed or the buffer size in a raw request is too large). This behavior is kept for backward compatibility reasons.</p> - <p>A raw option request <c>RawOptReq = {raw, Protocol, OptionNum, ValueSpec}</c> can be used to get information about + <p>A raw option request + <c>RawOptReq = {raw, Protocol, OptionNum, ValueSpec}</c> + can be used to get information about socket options not (explicitly) supported by the emulator. The - use of raw socket options makes the code non portable, but + use of raw socket options makes the code non-portable, but allows the Erlang programmer to take advantage of unusual features present on the current platform.</p> - <p>The <c>RawOptReq</c> consists of the tag <c>raw</c> followed - by the protocol level, the option number and either a binary + <p><c>RawOptReq</c> consists of tag <c>raw</c> followed + by the protocol level, the option number, and either a binary or the size, in bytes, of the - buffer in which the option value is to be stored. A binary - should be used when the underlying <c>getsockopt</c> requires - <em>input</em> - in the argument field, in which case the size of the binary - should correspond to the required buffer + buffer in which the option value is to be stored. A binary is to be + used when the underlying <c>getsockopt</c> requires <em>input</em> + in the argument field. In this case, the binary size + is to correspond to the required buffer size of the return value. The supplied values in a <c>RawOptReq</c> - correspond to the second, third and fourth/fifth parameters to the + correspond to the second, third, and fourth/fifth parameters to the <c>getsockopt</c> call in the C socket API. The value stored - in the buffer is returned as a binary <c>ValueBin</c> + in the buffer is returned as a binary <c>ValueBin</c>, where all values are coded in the native endianess.</p> - <p>Asking for and inspecting raw socket options require low - level information about the current operating system and TCP - stack.</p> - <p>As an example, consider a Linux machine where the - <c>TCP_INFO</c> option could be used to collect TCP statistics - for a socket. Lets say we're interested in the - <c>tcpi_sacked</c> field of the <c>struct tcp_info</c> - filled in when asking for <c>TCP_INFO</c>. To - be able to access this information, we need to know both the - numeric value of the protocol level <c>IPPROTO_TCP</c>, the - numeric value of the option <c>TCP_INFO</c>, the size of the - <c>struct tcp_info</c> and the size and offset of - the specific field. By inspecting the headers or writing a small C - program, we found <c>IPPROTO_TCP</c> to be 6, - <c>TCP_INFO</c> to be 11, the structure size to be 92 (bytes), - the offset of <c>tcpi_sacked</c> to be 28 bytes and the actual - value to be a 32 bit integer. We could use the following - code to retrieve the value:</p> + <p>Asking for and inspecting raw socket options require low-level + information about the current operating system and TCP stack.</p> + <p><em>Example:</em></p> + <p>Consider a Linux machine where option + <c>TCP_INFO</c> can be used to collect TCP statistics + for a socket. Assume you are interested in field + <c>tcpi_sacked</c> of <c>struct tcp_info</c> + filled in when asking for <c>TCP_INFO</c>. To be able to access + this information, you need to know the following:</p> + <list type="bulleted"> + <item>The numeric value of protocol level <c>IPPROTO_TCP</c></item> + <item>The numeric value of option <c>TCP_INFO</c></item> + <item>The size of <c>struct tcp_info</c></item> + <item>The size and offset of the specific field</item> + </list> + <p>By inspecting the headers or writing a small C program, it is found + that <c>IPPROTO_TCP</c> is 6, <c>TCP_INFO</c> is 11, the structure + size is 92 (bytes), the offset of <c>tcpi_sacked</c> is 28 bytes, + and the value is a 32-bit integer. The following code can be used + to retrieve the value:</p> <code type="none"><![CDATA[ - get_tcpi_sacked(Sock) -> - {ok,[{raw,_,_,Info}]} = inet:getopts(Sock,[{raw,6,11,92}]), - <<_:28/binary,TcpiSacked:32/native,_/binary>> = Info, - TcpiSacked.]]></code> - <p>Preferably, you would check the machine type, the OS - and the kernel version prior to executing anything similar to the - code above.</p> +get_tcpi_sacked(Sock) -> + {ok,[{raw,_,_,Info}]} = inet:getopts(Sock,[{raw,6,11,92}]), + <<_:28/binary,TcpiSacked:32/native,_/binary>> = Info, + TcpiSacked.]]></code> + <p>Preferably, you would check the machine type, the operating system, + and the <c>Kernel</c> version before executing anything similar to + this code.</p> </desc> </func> <func> <name name="getstat" arity="1"/> <name name="getstat" arity="2"/> - <fsummary>Get one or more statistic options for a socket</fsummary> + <fsummary>Get one or more statistic options for a socket.</fsummary> <type name="stat_option"/> <desc> <p>Gets one or more statistic options for a socket.</p> - - <p><c>getstat(<anno>Socket</anno>)</c> is equivalent to - <c>getstat(<anno>Socket</anno>, [recv_avg, recv_cnt, recv_dvi, - recv_max, recv_oct, send_avg, send_cnt, send_dvi, send_max, - send_oct])</c>.</p> - <p>The following options are available:</p> + <p><c>getstat(<anno>Socket</anno>)</c> is equivalent to + <c>getstat(<anno>Socket</anno>, [recv_avg, recv_cnt, recv_dvi, + recv_max, recv_oct, send_avg, send_cnt, send_dvi, send_max, + send_oct])</c>.</p> + <p>The following options are available:</p> <taglist> - <tag><c>recv_avg</c></tag> - <item> - <p>Average size of packets in bytes received by the socket.</p> - </item> - <tag><c>recv_cnt</c></tag> - <item> + <tag><c>recv_avg</c></tag> + <item> + <p>Average size of packets, in bytes, received by the socket.</p> + </item> + <tag><c>recv_cnt</c></tag> + <item> <p>Number of packets received by the socket.</p> - </item> - <tag><c>recv_dvi</c></tag> - <item> - <p>Average packet size deviation in bytes received by the socket.</p> - </item> - <tag><c>recv_max</c></tag> - <item> - <p>The size of the largest packet in bytes received by the socket.</p> - </item> - <tag><c>recv_oct</c></tag> - <item> + </item> + <tag><c>recv_dvi</c></tag> + <item> + <p>Average packet size deviation, in bytes, received by the socket.</p> + </item> + <tag><c>recv_max</c></tag> + <item> + <p>Size of the largest packet, in bytes, received by the socket.</p> + </item> + <tag><c>recv_oct</c></tag> + <item> <p>Number of bytes received by the socket.</p> - </item> - - <tag><c>send_avg</c></tag> - <item> - <p>Average size of packets in bytes sent from the socket.</p> - </item> - <tag><c>send_cnt</c></tag> - <item> + </item> + <tag><c>send_avg</c></tag> + <item> + <p>Average size of packets, in bytes, sent from the socket.</p> + </item> + <tag><c>send_cnt</c></tag> + <item> <p>Number of packets sent from the socket.</p> - </item> - <tag><c>send_dvi</c></tag> - <item> - <p>Average packet size deviation in bytes sent from the socket.</p> - </item> - <tag><c>send_max</c></tag> - <item> - <p>The size of the largest packet in bytes sent from the socket.</p> - </item> - <tag><c>send_oct</c></tag> - <item> + </item> + <tag><c>send_dvi</c></tag> + <item> + <p>Average packet size deviation, in bytes, sent from the socket.</p> + </item> + <tag><c>send_max</c></tag> + <item> + <p>Size of the largest packet, in bytes, sent from the socket.</p> + </item> + <tag><c>send_oct</c></tag> + <item> <p>Number of bytes sent from the socket.</p> - </item> + </item> </taglist> </desc> </func> + <func> <name name="ntoa" arity="1" /> - <fsummary>Convert IPv6 / IPV4 adress to ascii</fsummary> + <fsummary>Convert IPv6/IPV4 address to ASCII.</fsummary> + <desc> + <p>Parses an + <seealso marker="#type-ip_address"><c>ip_address()</c></seealso> + and returns an IPv4 or IPv6 address string.</p> + </desc> + </func> + + <func> + <name name="parse_address" arity="1" /> + <fsummary>Parse an IPv4 or IPv6 address.</fsummary> <desc> - <p>Parses an <seealso marker="#type-ip_address">ip_address()</seealso> and returns an IPv4 or IPv6 address string.</p> + <p>Parses an IPv4 or IPv6 address string and returns an + <seealso marker="#type-ip4_address"><c>ip4_address()</c></seealso> or + <seealso marker="#type-ip6_address"><c>ip6_address()</c></seealso>. + Accepts a shortened IPv4 address string.</p> </desc> </func> + <func> <name name="parse_ipv4_address" arity="1" /> - <fsummary>Parse an IPv4 address</fsummary> + <fsummary>Parse an IPv4 address.</fsummary> <desc> - <p>Parses an IPv4 address string and returns an <seealso marker="#type-ip4_address">ip4_address()</seealso>. - Accepts a shortened IPv4 shortened address string.</p> + <p>Parses an IPv4 address string and returns an + <seealso marker="#type-ip4_address"><c>ip4_address()</c></seealso>. + Accepts a shortened IPv4 address string.</p> </desc> </func> + <func> <name name="parse_ipv4strict_address" arity="1" /> <fsummary>Parse an IPv4 address strict.</fsummary> <desc> - <p>Parses an IPv4 address string containing four fields, i.e <em>not</em> shortened, and returns an <seealso marker="#type-ip4_address">ip4_address()</seealso>.</p> + <p>Parses an IPv4 address string containing four fields, that is, + <em>not</em> shortened, and returns an + <seealso marker="#type-ip4_address"><c>ip4_address()</c></seealso>. + </p> </desc> </func> + <func> <name name="parse_ipv6_address" arity="1" /> - <fsummary>Parse an IPv6 address</fsummary> + <fsummary>Parse an IPv6 address.</fsummary> <desc> - <p>Parses an IPv6 address string and returns an <seealso marker="#type-ip6_address">ip6_address()</seealso>. - If an IPv4 address string is passed, an IPv4-mapped IPv6 address is returned.</p> + <p>Parses an IPv6 address string and returns an + <seealso marker="#type-ip6_address"><c>ip6_address()</c></seealso>. + If an IPv4 address string is specified, an IPv4-mapped IPv6 address + is returned.</p> </desc> </func> + <func> <name name="parse_ipv6strict_address" arity="1" /> <fsummary>Parse an IPv6 address strict.</fsummary> <desc> - <p>Parses an IPv6 address string and returns an <seealso marker="#type-ip6_address">ip6_address()</seealso>. - Does <em>not</em> accept IPv4 adresses.</p> - </desc> - </func> - <func> - <name name="parse_address" arity="1" /> - <fsummary>Parse an IPv4 or IPv6 address.</fsummary> - <desc> - <p>Parses an IPv4 or IPv6 address string and returns an <seealso marker="#type-ip4_address">ip4_address()</seealso> or <seealso marker="#type-ip6_address">ip6_address()</seealso>. Accepts a shortened IPv4 address string.</p> + <p>Parses an IPv6 address string and returns an + <seealso marker="#type-ip6_address"><c>ip6_address()</c></seealso>. + Does <em>not</em> accept IPv4 addresses.</p> </desc> </func> + <func> <name name="parse_strict_address" arity="1" /> <fsummary>Parse an IPv4 or IPv6 address strict.</fsummary> <desc> - <p>Parses an IPv4 or IPv6 address string and returns an <seealso marker="#type-ip4_address">ip4_address()</seealso> or <seealso marker="#type-ip6_address">ip6_address()</seealso>. Does <em>not</em> accept a shortened IPv4 address string.</p> + <p>Parses an IPv4 or IPv6 address string and returns an + <seealso marker="#type-ip4_address"><c>ip4_address()</c></seealso> or + <seealso marker="#type-ip6_address"><c>ip6_address()</c></seealso>. + Does <em>not</em> accept a shortened IPv4 address string.</p> </desc> </func> + <func> <name name="peername" arity="1"/> - <fsummary>Return the address and port for the other end of a connection</fsummary> + <fsummary>Return the address and port for the other end of a connection. + </fsummary> <desc> - <p> - Returns the address and port for the other end of a - connection. - </p> - <p> - Note that for SCTP sockets this function only returns - one of the socket's peer addresses. The function - <seealso marker="#peernames/1">peernames/1,2</seealso> - returns all. - </p> + <p>Returns the address and port for the other end of a connection.</p> + <p>Notice that for SCTP sockets, this function returns only + one of the peer addresses of the socket. Function + <seealso marker="#peernames/1"><c>peernames/1,2</c></seealso> + returns all.</p> </desc> </func> + <func> <name name="peernames" arity="1"/> - <fsummary> - Return all address/port numbers for the other end of a connection - </fsummary> + <fsummary>Return all address/port numbers for the other end of a + connection.</fsummary> <desc> - <p> - Equivalent to + <p>Equivalent to <seealso marker="#peernames/2"><c>peernames(<anno>Socket</anno>, 0)</c></seealso>. - Note that this function's behaviour for an SCTP + </p> + <p>Notice that the behavior of this function for an SCTP one-to-many style socket is not defined by the - <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url>. - </p> + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url>.</p> </desc> </func> + <func> <name name="peernames" arity="2"/> - <fsummary> - Return all address/port numbers for the other end of a connection - </fsummary> + <fsummary>Return all address/port numbers for the other end of a + connection.</fsummary> <desc> - <p> - Returns a list of all address/port number pairs for the other end - of a socket's association <c><anno>Assoc</anno></c>. - </p> - <p> - This function can return multiple addresses for multihomed - sockets such as SCTP sockets. For other sockets it - returns a one element list. - </p> - <p> - Note that the <c><anno>Assoc</anno></c> parameter is by the + <p>Returns a list of all address/port number pairs for the other end + of an association <c><anno>Assoc</anno></c> of a socket.</p> + <p>This function can return multiple addresses for multihomed + sockets, such as SCTP sockets. For other sockets it + returns a one-element list.</p> + <p>Notice that parameter <c><anno>Assoc</anno></c> is by the <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url> defined to be ignored for - one-to-one style sockets. What the special value <c>0</c> - means hence its behaviour for one-to-many style sockets - is unfortunately not defined. - </p> + one-to-one style sockets. What the special value <c>0</c> + means, hence its behavior for one-to-many style sockets, + is unfortunately undefined.</p> </desc> </func> + <func> <name name="port" arity="1"/> - <fsummary>Return the local port number for a socket</fsummary> + <fsummary>Return the local port number for a socket.</fsummary> <desc> <p>Returns the local port number for a socket.</p> </desc> </func> - <func> - <name name="sockname" arity="1"/> - <fsummary>Return the local address and port number for a socket</fsummary> - <desc> - <p>Returns the local address and port number for a socket.</p> - <p> - Note that for SCTP sockets this function only returns - one of the socket addresses. The function - <seealso marker="#socknames/1">socknames/1,2</seealso> - returns all. - </p> - </desc> - </func> - <func> - <name name="socknames" arity="1"/> - <fsummary>Return all local address/port numbers for a socket</fsummary> - <desc> - <p> - Equivalent to - <seealso marker="#socknames/2"><c>socknames(<anno>Socket</anno>, 0)</c></seealso>. - </p> - </desc> - </func> - <func> - <name name="socknames" arity="2"/> - <fsummary>Return all local address/port numbers for a socket</fsummary> - <desc> - <p> - Returns a list of all local address/port number pairs for a socket - for the given association <c><anno>Assoc</anno></c>. - </p> - <p> - This function can return multiple addresses for multihomed - sockets such as SCTP sockets. For other sockets it - returns a one element list. - </p> - <p> - Note that the <c><anno>Assoc</anno></c> parameter is by the - <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url> - defined to be ignored for one-to-one style sockets. - For one-to-many style sockets the special value <c>0</c> - is defined to mean that the returned addresses shall be - without regard to any particular association. - How different SCTP implementations interprets this varies somewhat. - </p> - </desc> - </func> + <func> <name name="setopts" arity="2"/> - <fsummary>Set one or more options for a socket</fsummary> + <fsummary>Set one or more options for a socket.</fsummary> <type name="socket_setopt"/> <desc> - <p>Sets one or more options for a socket. The following options - are available:</p> + <p>Sets one or more options for a socket.</p> + <p>The following options are available:</p> <taglist> <tag><c>{active, true | false | once | N}</c></tag> <item> <p>If the value is <c>true</c>, which is the default, - everything received from the socket will be sent as - messages to the receiving process. If the value is - <c>false</c> (passive mode), the process must explicitly - receive incoming data by calling + everything received from the socket is sent as + messages to the receiving process.</p> + <p>If the value is <c>false</c> (passive mode), the process must + explicitly receive incoming data by calling <seealso marker="gen_tcp#recv/2"><c>gen_tcp:recv/2,3</c></seealso>, - <seealso marker="gen_udp#recv/2"><c>gen_udp:recv/2,3</c></seealso> + <seealso marker="gen_udp#recv/2"><c>gen_udp:recv/2,3</c></seealso>, or <seealso marker="gen_sctp#recv/1"><c>gen_sctp:recv/1,2</c></seealso> (depending on the type of socket).</p> <p>If the value is <c>once</c> (<c>{active, once}</c>), - <em>one</em> data message from the socket will be sent - to the process. To receive one more message, - <c>setopts/2</c> must be called again with the - <c>{active, once}</c> option.</p> + <em>one</em> data message from the socket is sent + to the process. To receive one more message, + <c>setopts/2</c> must be called again with option + <c>{active, once}</c>.</p> <p>If the value is an integer <c>N</c> in the range -32768 to 32767 (inclusive), the value is added to the socket's count of data messages sent to the controlling process. A socket's default - message count is 0. If a negative value is specified and its - magnitude is equal to or greater than the socket's current - message count, the socket's message count is set to 0. Once - the socket's message count reaches 0, either due to sending + message count is <c>0</c>. If a negative value is specified, and + its magnitude is equal to or greater than the socket's current + message count, the socket's message count is set to <c>0</c>. + Once the socket's message count reaches <c>0</c>, either because + of sending received data messages to the process or by being explicitly set, the process is then notified by a special message, specific to the type of socket, that the socket has entered passive @@ -575,339 +559,298 @@ fe80::204:acff:fe17:bf38 messages <c>setopts/2</c> must be called again to set the socket back into an active mode.</p> <p>When using <c>{active, once}</c> or <c>{active, N}</c>, the - socket changes behaviour automatically when data is received. - This can sometimes be confusing in combination with - connection-oriented sockets (i.e. <c>gen_tcp</c>) as a socket - with <c>{active, false}</c> behaviour reports closing + socket changes behavior automatically when data is received. + This can be confusing in combination with connection-oriented + sockets (that is, <c>gen_tcp</c>), as a socket + with <c>{active, false}</c> behavior reports closing differently than a socket with <c>{active, true}</c> - behaviour. To make programming easier, a socket where - the peer closed and this was detected while in - <c>{active, false}</c> mode, will still generate the - message + behavior. To simplify programming, a socket where + the peer closed, and this is detected while in + <c>{active, false}</c> mode, still generates message <c>{tcp_closed,Socket}</c> when set to <c>{active, once}</c>, - <c>{active, true}</c> or <c>{active, N}</c> mode. It is therefore - safe to assume that the message - <c>{tcp_closed,Socket}</c>, possibly followed by socket - port termination (depending on the <c>exit_on_close</c> - option) will eventually appear when a socket changes + <c>{active, true}</c>, or <c>{active, N}</c> mode. + It is therefore safe to assume that message + <c>{tcp_closed,Socket}</c>, possibly followed by socket port + termination (depending on option <c>exit_on_close</c>) + eventually appears when a socket changes back and forth between <c>{active, true}</c> and <c>{active, false}</c> mode. However, - <em>when</em> peer closing is detected is all up to the + <em>when</em> peer closing is detected it is all up to the underlying TCP/IP stack and protocol.</p> - <p>Note that <c>{active, true}</c> mode provides no flow - control; a fast sender could easily overflow the - receiver with incoming messages. The same is true of - <c>{active, N}</c> mode while the message count is greater - than zero. Use active mode only if + <p>Notice that <c>{active, true}</c> mode provides no flow + control; a fast sender can easily overflow the + receiver with incoming messages. The same is true for + <c>{active, N}</c> mode, while the message count is greater + than zero.</p> + <p>Use active mode only if your high-level protocol provides its own flow control - (for instance, acknowledging received messages) or the + (for example, acknowledging received messages) or the amount of data exchanged is small. <c>{active, false}</c> - mode, use of the <c>{active, once}</c> mode or <c>{active, N}</c> + mode, use of the <c>{active, once}</c> mode, or <c>{active, N}</c> mode with values of <c>N</c> appropriate for the application - provides flow control; the other side will not be able send + provides flow control. The other side cannot send faster than the receiver can read.</p> </item> - - <tag><c>{broadcast, Boolean}</c>(UDP sockets)</tag> + <tag><c>{broadcast, Boolean}</c> (UDP sockets)</tag> <item> - <p>Enable/disable permission to send broadcasts.</p> - <marker id="option-buffer"></marker> + <p>Enables/disables permission to send broadcasts.</p> + <marker id="option-buffer"></marker> + </item> + <tag><c>{buffer, Size}</c></tag> + <item> + <p>The size of the user-level software buffer used by + the driver. Not to be confused with options <c>sndbuf</c> + and <c>recbuf</c>, which correspond to the + <c>Kernel</c> socket buffers. It is recommended + to have <c>val(buffer) >= max(val(sndbuf),val(recbuf))</c> to + avoid performance issues because of unnecessary copying. + <c>val(buffer)</c> is automatically set to the above + maximum when values <c>sndbuf</c> or <c>recbuf</c> are set. + However, as the sizes set for <c>sndbuf</c> and <c>recbuf</c> + usually become larger, you are encouraged to use + <seealso marker="#getopts/2"><c>getopts/2</c></seealso> + to analyze the behavior of your operating system.</p> </item> - - <tag><c>{buffer, Size}</c></tag> - <item> - <p>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> to - avoid performance issues due to unnecessary copying. - 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. - However, since the actual sizes set for <c>sndbuf</c> and <c>recbuf</c> - usually becomes larger, you are encouraged to use - <seealso marker="inet#getopts/2"><c>inet:getopts/2</c></seealso> - to analyze the behavior of your operating system.</p> - </item> - <tag><c>{delay_send, Boolean}</c></tag> <item> <p>Normally, when an Erlang process sends to a socket, - the driver will try to immediately send the data. If that - fails, the driver will use any means available to queue + the driver tries to send the data immediately. If that + fails, the driver uses any means available to queue up the message to be sent whenever the operating system says it can handle it. Setting <c>{delay_send, true}</c> - will make <em>all</em> messages queue up. This makes - the messages actually sent onto the network be larger but - fewer. The option actually affects the scheduling of send + makes <em>all</em> messages queue up. The messages sent + to the network are then larger but fewer. + The option affects the scheduling of send requests versus Erlang processes instead of changing any - real property of the socket. Needless to say it is an - implementation specific option. Default is <c>false</c>.</p> + real property of the socket. The option is + implementation-specific. Defaults to <c>false</c>.</p> + </item> + <tag><c>{deliver, port | term}</c></tag> + <item> + <p>When <c>{active, true}</c>, data is delivered on the form + <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>{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> + <p>Enables/disables 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> + <p>This option is set to <c>true</c> by default.</p> <p>The only reason to set it to <c>false</c> is if you want - to continue sending data to the socket after a close has - been detected, for instance if the peer has used - <seealso marker="gen_tcp#shutdown/2">gen_tcp:shutdown/2</seealso> - to shutdown the write side.</p> + to continue sending data to the socket after a close is + detected, for example, if the peer uses + <seealso marker="gen_tcp#shutdown/2"><c>gen_tcp:shutdown/2</c></seealso> + to shut down the write side.</p> </item> - <tag><c>{header, Size}</c></tag> <item> - <p>This option is only meaningful if the <c>binary</c> - option was specified when the socket was created. If - the <c>header</c> option is specified, the first + <p>This option is only meaningful if option <c>binary</c> + was specified when the socket was created. If option + <c>header</c> is specified, the first <c>Size</c> number bytes of data received from the socket - will be elements of a list, and the rest of the data will - be a binary given as the tail of the same list. If for - example <c>Size == 2</c>, the data received will match + are elements of a list, and the remaining data is + a binary specified as the tail of the same list. For example, + if <c>Size == 2</c>, the data received matches <c>[Byte1,Byte2|Binary]</c>.</p> </item> - <tag><c>{high_msgq_watermark, Size}</c></tag> <item> - <p>The socket message queue will be set into a busy - state when the amount of data queued on the message - queue reaches this limit. Note that this limit only - concerns data that have not yet reached the ERTS internal - socket implementation. Default value used is 8 kB.</p> - <p>Senders of data to the socket will be suspended if - either the socket message queue is busy, or the socket - itself is busy.</p> - <p>For more information see the <c>low_msgq_watermark</c>, - <c>high_watermark</c>, and <c>low_watermark</c> options.</p> - <p>Note that distribution sockets will disable the use of - <c>high_msgq_watermark</c> and <c>low_msgq_watermark</c>, - and will instead use the - <seealso marker="erts:erlang#system_info_dist_buf_busy_limit">distribution - buffer busy limit</seealso> which is a similar feature.</p> + <p>The socket message queue is set to a busy + state when the amount of data on the message + queue reaches this limit. Notice that this limit only + concerns data that has not yet reached the <c>ERTS</c> internal + socket implementation. Defaults to 8 kB.</p> + <p>Senders of data to the socket are suspended if + either the socket message queue is busy or the socket + itself is busy.</p> + <p>For more information, see options <c>low_msgq_watermark</c>, + <c>high_watermark</c>, and <c>low_watermark</c>.</p> + <p>Notice that distribution sockets disable the use of + <c>high_msgq_watermark</c> and <c>low_msgq_watermark</c>. + Instead use the + <seealso marker="erts:erlang#system_info_dist_buf_busy_limit">distribution buffer busy limit</seealso>, + which is a similar feature.</p> </item> - <tag><c>{high_watermark, Size}</c> (TCP/IP sockets)</tag> <item> - <p>The socket will be set into a busy state when the amount - of data queued internally by the ERTS socket implementation - reaches this limit. Default value used is 8 kB.</p> - <p>Senders of data to the socket will be suspended if - either the socket message queue is busy, or the socket - itself is busy.</p> - <p>For more information see the <c>low_watermark</c>, - <c>high_msgq_watermark</c>, and <c>low_msqg_watermark</c> - options.</p> + <p>The socket is set to a busy state when the amount + of data queued internally by the <c>ERTS</c> socket implementation + reaches this limit. Defaults to 8 kB.</p> + <p>Senders of data to the socket are suspended if + either the socket message queue is busy or the socket + itself is busy.</p> + <p>For more information, see options <c>low_watermark</c>, + <c>high_msgq_watermark</c>, and <c>low_msqg_watermark</c>.</p> </item> - <tag><c>{ipv6_v6only, Boolean}</c></tag> <item> - <p> - Restricts the socket to only use IPv6, prohibiting any + <p>Restricts the socket to use only IPv6, prohibiting any IPv4 connections. This is only applicable for - IPv6 sockets (option <c>inet6</c>). - </p> - <p> - On most platforms this option has to be set on the socket - before associating it to an address. Therefore it is only - reasonable to give it when creating the socket and not - to use it when calling the function - (<seealso marker="#setopts/2">setopts/2</seealso>) - containing this description. - </p> - <p> - The behaviour of a socket with this socket option set to - <c>true</c> is becoming the only portable one. The original + IPv6 sockets (option <c>inet6</c>).</p> + <p>On most platforms this option must be set on the socket + before associating it to an address. It is therefore only + reasonable to specify it when creating the socket and not + to use it when calling function + (<seealso marker="#setopts/2"><c>setopts/2</c></seealso>) + containing this description.</p> + <p>The behavior of a socket with this option set to + <c>true</c> is the only portable one. The original idea when IPv6 was new of using IPv6 for all traffic is now not recommended by FreeBSD (you can use <c>{ipv6_v6only,false}</c> to override the recommended system default value), - forbidden by OpenBSD (the supported GENERIC kernel) - and impossible on Windows (that has separate + forbidden by OpenBSD (the supported GENERIC kernel), + and impossible on Windows (which has separate IPv4 and IPv6 protocol stacks). Most Linux distros still have a system default value of <c>false</c>. - This policy shift among operating systems towards - separating IPv6 from IPv4 traffic has evolved since + This policy shift among operating systems to + separate IPv6 from IPv4 traffic has evolved, as it gradually proved hard and complicated to get - a dual stack implementation correct and secure. - </p> - <p> - On some platforms the only allowed value for this option - is <c>true</c>, e.g. OpenBSD and Windows. Trying to set - this option to <c>false</c> when creating the socket - will in this case fail. - </p> - <p> - Setting this option on platforms where it does not exist - is ignored and getting this option with - <seealso marker="#getopts/2">getopts/2</seealso> - returns no value i.e the returned list will not contain an - <c>{ipv6_v6only,_}</c> tuple. On Windows the option acually - does not exist, but it is emulated as being a - read-only option with the value <c>true</c>. - </p> - <p> - So it boils down to that setting this option to <c>true</c> - when creating a socket will never fail except possibly - (at the time of this writing) on a platform where you + a dual stack implementation correct and secure.</p> + <p>On some platforms, the only allowed value for this option + is <c>true</c>, for example, OpenBSD and Windows. Trying to set + this option to <c>false</c>, when creating the socket, fails + in this case.</p> + <p>Setting this option on platforms where it does not exist + is ignored. Getting this option with + <seealso marker="#getopts/2"><c>getopts/2</c></seealso> + returns no value, that is, the returned list does not contain an + <c>{ipv6_v6only,_}</c> tuple. On Windows, the option + does not exist, but it is emulated as a + read-only option with value <c>true</c>.</p> + <p>Therefore, setting this option to <c>true</c> + when creating a socket never fails, except possibly on a + platform where you have customized the kernel to only allow <c>false</c>, - which might be doable (but weird) on e.g. OpenBSD. - </p> - <p> - If you read back the option value using - <seealso marker="#getopts/2">getopts/2</seealso> - and get no value the option does not exist in the host OS - and all bets are off regarding the behaviour of both - an IPv6 and an IPv4 socket listening on the same port - as well as for an IPv6 socket getting IPv4 traffic. - </p> + which can be doable (but awkward) on, for example, OpenBSD.</p> + <p>If you read back the option value using + <seealso marker="#getopts/2"><c>getopts/2</c></seealso> + and get no value, the option does not exist in the host + operating system. The behavior of both an IPv6 and an IPv4 + socket listening on the same port, and for an IPv6 socket + getting IPv4 traffic is then no longer predictable.</p> </item> - <tag><c>{keepalive, Boolean}</c>(TCP/IP sockets)</tag> <item> <p>Enables/disables periodic transmission on a connected - socket, when no other data is being exchanged. If + socket when no other data is exchanged. If 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> + considered broken and an error message is sent to + the controlling process. Defaults to <c>disabled</c>.</p> + <marker id="option-linger"></marker> + </item> + <tag><c>{linger, {true|false, Seconds}}</c></tag> + <item> + <p>Determines the time-out, in seconds, for flushing unsent data + in the <c>close/1</c> socket call. If the first component of + the value tuple is <c>false</c>, the second is ignored. This + means that <c>close/1</c> returns immediately, not waiting + for data to be flushed. Otherwise, the second component is + the flushing time-out, in seconds.</p> </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_msgq_watermark, Size}</c></tag> <item> <p>If the socket message queue is in a busy state, the - socket message queue will be set in a not busy state when - the amount of data queued in the message queue falls - below this limit. Note that this limit only concerns data - that have not yet reached the ERTS internal socket - implementation. Default value used is 4 kB.</p> - <p>Senders that have been suspended due to either a - busy message queue or a busy socket, will be resumed - when neither the socket message queue, nor the socket - are busy.</p> - <p>For more information see the <c>high_msgq_watermark</c>, - <c>high_watermark</c>, and <c>low_watermark</c> options.</p> - <p>Note that distribution sockets will disable the use of - <c>high_msgq_watermark</c> and <c>low_msgq_watermark</c>, - and will instead use the - <seealso marker="erts:erlang#system_info_dist_buf_busy_limit">distribution - buffer busy limit</seealso> which is a similar feature.</p> + socket message queue is set in a not busy state when + the amount of data queued in the message queue falls + below this limit. Notice that this limit only concerns data + that has not yet reached the <c>ERTS</c> internal socket + implementation. Defaults to 4 kB.</p> + <p>Senders that are suspended because of either a + busy message queue or a busy socket are resumed + when the socket message queue and the socket + are not busy.</p> + <p>For more information, see options <c>high_msgq_watermark</c>, + <c>high_watermark</c>, and <c>low_watermark</c>.</p> + <p>Notice that distribution sockets disable the use of + <c>high_msgq_watermark</c> and <c>low_msgq_watermark</c>. + Instead they use the + <seealso marker="erts:erlang#system_info_dist_buf_busy_limit">distribution + buffer busy limit</seealso>, which is a similar feature.</p> </item> - <tag><c>{low_watermark, Size}</c> (TCP/IP sockets)</tag> <item> - <p>If the socket is in a busy state, the socket will - be set in a not busy state when the amount of data - queued internally by the ERTS socket implementation - falls below this limit. Default value used is 4 kB.</p> - <p>Senders that have been suspended due to either a - busy message queue or a busy socket, will be resumed - when neither the socket message queue, nor the socket - are busy.</p> - <p>For more information see the <c>high_watermark</c>, - <c>high_msgq_watermark</c>, and <c>low_msgq_watermark</c> - options.</p> + <p>If the socket is in a busy state, the socket is + set in a not busy state when the amount of data + queued internally by the <c>ERTS</c> socket implementation + falls below this limit. Defaults to 4 kB.</p> + <p>Senders that are suspended because of a + busy message queue or a busy socket are resumed + when the socket message queue and the socket are not busy.</p> + <p>For more information, see options <c>high_watermark</c>, + <c>high_msgq_watermark</c>, and <c>low_msgq_watermark</c>.</p> </item> - - <tag><c>{mode, Mode :: binary | list}</c></tag> + <tag><c>{mode, Mode :: binary | list}</c></tag> <item> - <p>Received <c>Packet</c> is delivered as defined by Mode.</p> - </item> - + <p>Received <c>Packet</c> is delivered as defined by <c>Mode</c>. + </p> + </item> <tag><c>{netns, Namespace :: file:filename_all()}</c></tag> <item> - <p>Set a network namespace for the socket. The <c>Namespace</c> - parameter is a filename defining the namespace for example - <c>"/var/run/netns/example"</c> typically created by the command - <c>ip netns add example</c>. This option must be used in a - function call that creates a socket i.e - <seealso marker="gen_tcp#connect/3"> - gen_tcp:connect/3,4</seealso>, - <seealso marker="gen_tcp#listen/2"> - gen_tcp:listen/2</seealso>, - <seealso marker="gen_udp#open/1"> - gen_udp:open/1,2</seealso> or - <seealso marker="gen_sctp#open/0"> - gen_sctp:open/0-2</seealso>. - </p> - <p>This option uses the Linux specific syscall - <c>setns()</c> such as in Linux kernel 3.0 or later - and therefore only exists when the runtime system - has been compiled for such an operating system. - </p> - <p> - The virtual machine also needs elevated privileges either - running as superuser or (for Linux) having the capability - <c>CAP_SYS_ADMIN</c> according to the documentation for setns(2). - However, during testing also <c>CAP_SYS_PTRACE</c> - and <c>CAP_DAC_READ_SEARCH</c> has proven to be necessary. - Example:</p><code> -setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp -</code> - <p>Note also that the filesystem containing the virtual machine - executable (<c>beam.smp</c> in the example above) has to be local, - mounted without the <c>nosetuid</c> flag, - support extended attributes and that - the kernel has to support file capabilities. - All this runs out of the box on at least Ubuntu 12.04 LTS, - except that SCTP sockets appears to not support - network namespaces. - </p> - <p>The <c>Namespace</c> is a file name and is encoded - and decoded as discussed in - <seealso marker="file">file</seealso> - except that the emulator flag <c>+fnu</c> is ignored and - <seealso marker="#getopts/2">getopts/2</seealso> - for this option will return a binary for the filename - if the stored filename can not be decoded, - which should only happen if you set the option using a binary - that can not be decoded with the emulator's filename encoding: - <seealso marker="file#native_name_encoding/0"> - file:native_name_encoding/0</seealso>. - </p> - </item> - - <tag><c>list</c></tag> + <p>Sets a network namespace for the socket. Parameter + <c>Namespace</c> is a filename defining the namespace, for + example, <c>"/var/run/netns/example"</c>, typically created by + command <c>ip netns add example</c>. This option must be used in + a function call that creates a socket, that is, + <seealso marker="gen_tcp#connect/3"><c>gen_tcp:connect/3,4</c></seealso>, + <seealso marker="gen_tcp#listen/2"><c>gen_tcp:listen/2</c></seealso>, + <seealso marker="gen_udp#open/1"><c>gen_udp:open/1,2</c></seealso>, or + <seealso marker="gen_sctp#open/0"><c>gen_sctp:open/0,1,2</c></seealso>.</p> + <p>This option uses the Linux-specific syscall + <c>setns()</c>, such as in Linux kernel 3.0 or later, + and therefore only exists when the runtime system + is compiled for such an operating system.</p> + <p>The virtual machine also needs elevated privileges, either + running as superuser or (for Linux) having capability + <c>CAP_SYS_ADMIN</c> according to the documentation for + <c>setns(2)</c>. + However, during testing also <c>CAP_SYS_PTRACE</c> + and <c>CAP_DAC_READ_SEARCH</c> have proven to be necessary.</p> + <p><em>Example:</em></p> + <code> +setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp</code> + <p>Notice that the filesystem containing the virtual machine + executable (<c>beam.smp</c> in the example) must be local, + mounted without flag <c>nosetuid</c>, + support extended attributes, and + the kernel must support file capabilities. + All this runs out of the box on at least Ubuntu 12.04 LTS, + except that SCTP sockets appear to not support + network namespaces.</p> + <p><c>Namespace</c> is a filename and is encoded + and decoded as discussed in module + <seealso marker="file">file</seealso>, with the + following exceptions:</p> + <list type="bulleted"> + <item><p>Emulator flag <c>+fnu</c> is ignored.</p></item> + <item><p><seealso marker="#getopts/2"><c>getopts/2</c></seealso> + for this option returns a binary for the filename if the stored + filename cannot be decoded. This is only to occur if you set the + option using a binary that cannot be decoded with the emulator's + filename encoding: + <seealso marker="file#native_name_encoding/0"><c>file:native_name_encoding/0</c></seealso>.</p></item> + </list> + </item> + <tag><c>list</c></tag> <item> - <p>Received <c>Packet</c> is delivered as a list.</p> - </item> - - <tag><c>binary</c></tag> + <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> - + <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 - is turned on for the socket, which means that even small - amounts of data will be sent immediately.</p> + <p>If <c>Boolean == true</c>, option <c>TCP_NODELAY</c> + is turned on for the socket, which means that also small + amounts of data are sent immediately.</p> </item> <tag><c>{packet, PacketType}</c>(TCP/IP sockets)</tag> <item> <p>Defines the type of packets to use for a socket. - The following values are valid:</p> + Possible values:</p> <taglist> <tag><c>raw | 0</c></tag> <item> @@ -917,104 +860,99 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp <item> <p>Packets consist of a header specifying the number of bytes in the packet, followed by that number of bytes. - The length of header can be one, two, or four bytes; + The header length can be one, two, or four bytes, and containing an unsigned integer in big-endian byte order. - Each send operation will generate the header, and the header - will be stripped off on each receive operation.</p> - <p>In current implementation the 4-byte header is limited to 2Gb.</p> + Each send operation generates the header, and the header + is stripped off on each receive operation.</p> + <p>The 4-byte header is limited to 2Gb.</p> </item> <tag><c>asn1 | cdr | sunrm | fcgi | tpkt | line</c></tag> <item> <p>These packet types only have effect on receiving. When sending a packet, it is the responsibility of the application to supply a correct header. On - receiving, however, there will be one message sent to + receiving, however, one message is sent to the controlling process for each complete packet received, and, similarly, each call to <c>gen_tcp:recv/2,3</c> returns one complete packet. The header is <em>not</em> stripped off.</p> - <p>The meanings of the packet types are as follows: - <br></br> -<c>asn1</c> - ASN.1 BER, - <br></br> -<c>sunrm</c> - Sun's RPC encoding, - <br></br> -<c>cdr</c> - CORBA (GIOP 1.1), - <br></br> -<c>fcgi</c> - Fast CGI, - <br></br> -<c>tpkt</c> - TPKT format [RFC1006], - <br></br> -<c>line</c> - Line mode, a packet is a line - terminated with newline, lines longer than - the receive buffer are truncated.</p> - </item> + <p>The meanings of the packet types are as follows:</p> + <list type="bulleted"> + <item><c>asn1</c> - ASN.1 BER</item> + <item><c>sunrm</c> - Sun's RPC encoding</item> + <item><c>cdr</c> - CORBA (GIOP 1.1)</item> + <item><c>fcgi</c> - Fast CGI</item> + <item><c>tpkt</c> - TPKT format [RFC1006]</item> + <item><c>line</c> - Line mode, a packet is a line-terminated + with newline, lines longer than the receive buffer are + truncated</item> + </list> + </item> <tag><c>http | http_bin</c></tag> <item> <p>The Hypertext Transfer Protocol. The packets are returned with the format according to <c>HttpPacket</c> - described in <seealso marker="erts:erlang#decode_packet/3"> - erlang:decode_packet/3</seealso>. A socket in passive - mode will return <c>{ok, HttpPacket}</c> from <c>gen_tcp:recv</c> - while an active socket will send messages like <c>{http, - Socket, HttpPacket}</c>.</p> + described in + <seealso marker="erts:erlang#decode_packet/3"> + <c>erlang:decode_packet/3</c></seealso> in <c>ERTS</c>. + A socket in passive + mode returns <c>{ok, HttpPacket}</c> from <c>gen_tcp:recv</c> + while an active socket sends messages like + <c>{http, Socket, HttpPacket}</c>.</p> </item> <tag><c>httph | httph_bin</c></tag> <item> - <p>These two types are often not needed as the socket will - automatically switch from <c>http</c>/<c>http_bin</c> to + <p>These two types are often not needed, as the socket + automatically switches from <c>http</c>/<c>http_bin</c> to <c>httph</c>/<c>httph_bin</c> internally after the first line - has been read. There might be occasions however when they are + is read. However, there can be occasions when they are useful, such as parsing trailers from chunked encoding.</p> </item> </taglist> </item> <tag><c>{packet_size, Integer}</c>(TCP/IP sockets)</tag> <item> - <p>Sets the max allowed length of the packet body. If + <p>Sets the maximum allowed length of the packet body. If the packet header indicates that the length of the packet - is longer than the max allowed length, the packet is - considered invalid. The same happens if the packet header - is too big for the socket receive buffer.</p> - <p>For line oriented protocols (<c>line</c>,<c>http*</c>), - option <c>packet_size</c> also guarantees that lines up to the - indicated length are accepted and not considered invalid due - to internal buffer limitations.</p> + is longer than the maximum allowed length, the packet is + considered invalid. The same occurs if the packet header + is too large for the socket receive buffer.</p> + <p>For line-oriented protocols (<c>line</c>, <c>http*</c>), + option <c>packet_size</c> also guarantees that lines up to the + indicated length are accepted and not considered invalid + because of internal buffer limitations.</p> </item> <tag><c>{line_delimiter, Char}</c>(TCP/IP sockets)</tag> <item> - <p>Sets the line delimiting character for line oriented protocols - (<c>line</c>). Default value is <c>$\n</c>.</p> + <p>Sets the line delimiting character for line-oriented protocols + (<c>line</c>). Defaults to <c>$\n</c>.</p> + </item> + <tag><c>{priority, Priority}</c></tag> + <item> + <p>Sets 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>{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 + <p>Sets the maximum number of UDP packets to read without intervention from the socket when data is available. When this many packets have been read and delivered to the destination process, new packets are not read until a new notification of available data has arrived. - The default is 5, and if this parameter is set too - high the system can become unresponsive due to + Defaults to <c>5</c>. If this parameter is set too + high, the system can become unresponsive because of UDP packet flooding.</p> </item> <tag><c>{recbuf, Size}</c></tag> <item> <p>The minimum size of the receive buffer to use for the socket. You are encouraged to use - <seealso marker="inet#getopts/2"><c>inet:getopts/2</c></seealso>, - to retrieve the actual size set by your operating system. - - </p> + <seealso marker="#getopts/2"><c>getopts/2</c></seealso> + to retrieve the size set by your operating system.</p> </item> <tag><c>{reuseaddr, Boolean}</c></tag> <item> @@ -1023,118 +961,160 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp </item> <tag><c>{send_timeout, Integer}</c></tag> <item> - <p>Only allowed for connection oriented sockets.</p> + <p>Only allowed for connection-oriented sockets.</p> <p>Specifies a longest time to wait for a send operation to be accepted by the underlying TCP stack. When the limit is - exceeded, the send operation will return - <c>{error,timeout}</c>. How much of a packet that actually - got sent is unknown, why the socket should be closed - whenever a timeout has occurred (see <c>send_timeout_close</c>). - Default is <c>infinity</c>.</p> + exceeded, the send operation returns + <c>{error,timeout}</c>. How much of a packet that + got sent is unknown; the socket is therefore to be closed + whenever a time-out has occurred (see <c>send_timeout_close</c> + below). Defaults to <c>infinity</c>.</p> </item> <tag><c>{send_timeout_close, Boolean}</c></tag> <item> - <p>Only allowed for connection oriented sockets.</p> + <p>Only allowed for connection-oriented sockets.</p> <p>Used together with <c>send_timeout</c> to specify whether - the socket will be automatically closed when the send operation + the socket is to be automatically closed when the send operation 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> + <c>true</c>, which automatically closes the socket. + Defaults to <c>false</c> because of backward compatibility.</p> + <marker id="option-sndbuf"></marker> </item> - <tag><c>{show_econnreset, Boolean}</c>(TCP/IP sockets)</tag> <item> - <p>When this option is set to <c>false</c>, as it is by - default, an RST that is received from the TCP peer is treated - as a normal close (as though a FIN was sent). A caller - to <seealso marker="gen_tcp#recv/2">gen_tcp:recv/2</seealso> - will get <c>{error, closed}</c>. In active - mode the controlling process will receive a + <p>When this option is set to <c>false</c>, which is + default, an RST received from the TCP peer is treated + as a normal close (as though an FIN was sent). A caller to + <seealso marker="gen_tcp#recv/2"><c>gen_tcp:recv/2</c></seealso> + gets <c>{error, closed}</c>. In active + mode, the controlling process receives a <c>{tcp_close, Socket}</c> message, indicating that the peer has closed the connection.</p> - <p>Setting this option to <c>true</c> will allow you to + <p>Setting this option to <c>true</c> allows you to distinguish between a connection that was closed normally, - and one which was aborted (intentionally or unintentionally) + and one that was aborted (intentionally or unintentionally) by the TCP peer. A call to - <seealso marker="gen_tcp#recv/2">gen_tcp:recv/2</seealso> - will return <c>{error, econnreset}</c>. In - active mode, the controlling process will receive a + <seealso marker="gen_tcp#recv/2"><c>gen_tcp:recv/2</c></seealso> + returns <c>{error, econnreset}</c>. In + active mode, the controlling process receives a <c>{tcp_error, Socket, econnreset}</c> message before the usual <c>{tcp_closed, Socket}</c>, as is the case for any other socket error. Calls to - <seealso marker="gen_tcp#send/2">gen_tcp:send/2</seealso> - will also return <c>{error, econnreset}</c> when it + <seealso marker="gen_tcp#send/2"><c>gen_tcp:send/2</c></seealso> + also returns <c>{error, econnreset}</c> when it is detected that a TCP peer has sent an RST.</p> <p>A connected socket returned from - <seealso marker="gen_tcp#accept/1">gen_tcp:accept/1</seealso> - will inherit the <c>show_econnreset</c> setting from the + <seealso marker="gen_tcp#accept/1"><c>gen_tcp:accept/1</c></seealso> + inherits the <c>show_econnreset</c> setting from the listening socket.</p> - <marker id="option-show_econnreset"></marker> + <marker id="option-show_econnreset"></marker> </item> - <tag><c>{sndbuf, Size}</c></tag> <item> <p>The minimum size of the send buffer to use for the socket. - You are encouraged to use - <seealso marker="inet#getopts/2"><c>inet:getopts/2</c></seealso>, - to retrieve the actual size set by your operating system. - </p> + You are encouraged to use + <seealso marker="#getopts/2"><c>getopts/2</c></seealso>, + to retrieve the size set by your operating system.</p> </item> <tag><c>{priority, Integer}</c></tag> <item> - <p>Sets the SO_PRIORITY socket level option on platforms where - this is implemented. The behaviour and allowed range varies on - different systems. The option is ignored on platforms where the - option is not implemented. Use with caution.</p> + <p>Sets the <c>SO_PRIORITY</c> socket level option on platforms + where this is implemented. The behavior and allowed range varies + between different systems. + The option is ignored on platforms where it + is not implemented. Use with caution.</p> </item> <tag><c>{tos, Integer}</c></tag> <item> - <p>Sets IP_TOS IP level options on platforms where this is - implemented. The behaviour and allowed range varies on different - systems. The option is ignored on platforms where the option is - not implemented. Use with caution.</p> + <p>Sets <c>IP_TOS IP</c> level options on platforms where this is + implemented. The behavior and allowed range varies between + different systems. + The option is ignored on platforms where it is not + implemented. Use with caution.</p> </item> </taglist> - - <p>In addition to the options mentioned above, <em>raw</em> + <p>In addition to these options, <em>raw</em> option specifications can be used. The raw options are - specified as a tuple of arity four, beginning with the tag - <c>raw</c>, followed by the protocol level, the option number - and the actual option value specified as a binary. This - corresponds to the second, third and fourth argument to the + specified as a tuple of arity four, beginning with tag + <c>raw</c>, followed by the protocol level, the option number, + and the option value specified as a binary. This + corresponds to the second, third, and fourth arguments to the <c>setsockopt</c> call in the C socket API. The option value - needs to be coded in the native endianess of the platform and, - if a structure is required, needs to follow the struct + must be coded in the native endianess of the platform and, + if a structure is required, must follow the structure alignment conventions on the specific platform.</p> - <p>Using raw socket options require detailed knowledge about + <p>Using raw socket options requires detailed knowledge about the current operating system and TCP stack.</p> - <p>As an example of the usage of raw options, consider a Linux - system where you want to set the <c>TCP_LINGER2</c> option on - the <c>IPPROTO_TCP</c> protocol level in the stack. You know + <p><em>Example:</em></p> + <p>This example concerns the use of raw options. Consider a Linux + system where you want to set option <c>TCP_LINGER2</c> on + protocol level <c>IPPROTO_TCP</c> in the stack. You know that on this particular system it defaults to 60 (seconds), - but you would like to lower it to 30 for a particular - socket. The <c>TCP_LINGER2</c> option is not explicitly - supported by inet, but you know that the protocol level - translates to the number 6, the option number to the number 8 - and the value is to be given as a 32 bit integer. You can use - this line of code to set the option for the socket named + but you want to lower it to 30 for a particular + socket. Option <c>TCP_LINGER2</c> is not explicitly + supported by <c>inet</c>, but you know that the protocol level + translates to number 6, the option number to number 8, + and the value is to be specified as a 32-bit integer. You can use + this code line to set the option for the socket named <c>Sock</c>:</p> <code type="none"><![CDATA[ - inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code> +inet:setopts(Sock,[{raw,6,8,<<30:32/native>>}]),]]></code> <p>As many options are silently discarded by the stack if they - are given out of range, it could be a good idea to check that - a raw option really got accepted. This code places the value - in the variable TcpLinger2:</p> + are specified out of range; it can be a good idea to check that + a raw option is accepted. The following code places the value + in variable <c>TcpLinger2:</c></p> <code type="none"><![CDATA[ - {ok,[{raw,6,8,<<TcpLinger2:32/native>>}]}=inet:getopts(Sock,[{raw,6,8,4}]),]]></code> - <p>Code such as the examples above is inherently non portable, - even different versions of the same OS on the same platform - may respond differently to this kind of option +{ok,[{raw,6,8,<<TcpLinger2:32/native>>}]}=inet:getopts(Sock,[{raw,6,8,4}]),]]></code> + <p>Code such as these examples is inherently non-portable, + even different versions of the same OS on the same platform + can respond differently to this kind of option manipulation. Use with care.</p> - <p>Note that the default options for TCP/IP sockets can be - changed with the Kernel configuration parameters mentioned in - the beginning of this document.</p> + <p>Notice that the default options for TCP/IP sockets can be + changed with the <c>Kernel</c> configuration parameters mentioned in + the beginning of this manual page.</p> + </desc> + </func> + + <func> + <name name="sockname" arity="1"/> + <fsummary>Return the local address and port number for a socket. + </fsummary> + <desc> + <p>Returns the local address and port number for a socket.</p> + <p>Notice that for SCTP sockets this function returns only + one of the socket addresses. Function + <seealso marker="#socknames/1"><c>socknames/1,2</c></seealso> + returns all.</p> + </desc> + </func> + + <func> + <name name="socknames" arity="1"/> + <fsummary>Return all local address/port numbers for a socket.</fsummary> + <desc> + <p>Equivalent to + <seealso marker="#socknames/2"><c>socknames(<anno>Socket</anno>, 0)</c></seealso>. + </p> + </desc> + </func> + + <func> + <name name="socknames" arity="2"/> + <fsummary>Return all local address/port numbers for a socket.</fsummary> + <desc> + <p>Returns a list of all local address/port number pairs for a socket + for the specified association <c><anno>Assoc</anno></c>.</p> + <p>This function can return multiple addresses for multihomed + sockets, such as SCTP sockets. For other sockets it + returns a one-element list.</p> + <p>Notice that parameter <c><anno>Assoc</anno></c> is by the + <url href="http://tools.ietf.org/html/draft-ietf-tsvwg-sctpsocket-13">SCTP Sockets API Extensions</url> + defined to be ignored for one-to-one style sockets. + For one-to-many style sockets, the special value <c>0</c> + is defined to mean that the returned addresses must be + without any particular association. + How different SCTP implementations interprets this varies somewhat. + </p> </desc> </func> </funcs> @@ -1143,148 +1123,147 @@ setcap cap_sys_admin,cap_sys_ptrace,cap_dac_read_search+epi beam.smp <marker id="error_codes"></marker> <title>POSIX Error Codes</title> <list type="bulleted"> - <item><c>e2big</c> - argument list too long</item> - <item><c>eacces</c> - permission denied</item> - <item><c>eaddrinuse</c> - address already in use</item> - <item><c>eaddrnotavail</c> - cannot assign requested address</item> - <item><c>eadv</c> - advertise error</item> - <item><c>eafnosupport</c> - address family not supported by - protocol family</item> - <item><c>eagain</c> - resource temporarily unavailable</item> + <item><c>e2big</c> - Too long argument list</item> + <item><c>eacces</c> - Permission denied</item> + <item><c>eaddrinuse</c> - Address already in use</item> + <item><c>eaddrnotavail</c> - Cannot assign requested address</item> + <item><c>eadv</c> - Advertise error</item> + <item><c>eafnosupport</c> - Address family not supported by + protocol family</item> + <item><c>eagain</c> - Resource temporarily unavailable</item> <item><c>ealign</c> - EALIGN</item> - <item><c>ealready</c> - operation already in progress</item> - <item><c>ebade</c> - bad exchange descriptor</item> - <item><c>ebadf</c> - bad file number</item> - <item><c>ebadfd</c> - file descriptor in bad state</item> - <item><c>ebadmsg</c> - not a data message</item> - <item><c>ebadr</c> - bad request descriptor</item> - <item><c>ebadrpc</c> - RPC structure is bad</item> - <item><c>ebadrqc</c> - bad request code</item> - <item><c>ebadslt</c> - invalid slot</item> - <item><c>ebfont</c> - bad font file format</item> - <item><c>ebusy</c> - file busy</item> - <item><c>echild</c> - no children</item> - <item><c>echrng</c> - channel number out of range</item> - <item><c>ecomm</c> - communication error on send</item> - <item><c>econnaborted</c> - software caused connection abort</item> - <item><c>econnrefused</c> - connection refused</item> - <item><c>econnreset</c> - connection reset by peer</item> - <item><c>edeadlk</c> - resource deadlock avoided</item> - <item><c>edeadlock</c> - resource deadlock avoided</item> - <item><c>edestaddrreq</c> - destination address required</item> - <item><c>edirty</c> - mounting a dirty fs w/o force</item> - <item><c>edom</c> - math argument out of range</item> - <item><c>edotdot</c> - cross mount point</item> - <item><c>edquot</c> - disk quota exceeded</item> - <item><c>eduppkg</c> - duplicate package name</item> - <item><c>eexist</c> - file already exists</item> - <item><c>efault</c> - bad address in system call argument</item> - <item><c>efbig</c> - file too large</item> - <item><c>ehostdown</c> - host is down</item> - <item><c>ehostunreach</c> - host is unreachable</item> - <item><c>eidrm</c> - identifier removed</item> - <item><c>einit</c> - initialization error</item> - <item><c>einprogress</c> - operation now in progress</item> - <item><c>eintr</c> - interrupted system call</item> - <item><c>einval</c> - invalid argument</item> + <item><c>ealready</c> - Operation already in progress</item> + <item><c>ebade</c> - Bad exchange descriptor</item> + <item><c>ebadf</c> - Bad file number</item> + <item><c>ebadfd</c> - File descriptor in bad state</item> + <item><c>ebadmsg</c> - Not a data message</item> + <item><c>ebadr</c> - Bad request descriptor</item> + <item><c>ebadrpc</c> - Bad RPC structure</item> + <item><c>ebadrqc</c> - Bad request code</item> + <item><c>ebadslt</c> - Invalid slot</item> + <item><c>ebfont</c> - Bad font file format</item> + <item><c>ebusy</c> - File busy</item> + <item><c>echild</c> - No children</item> + <item><c>echrng</c> - Channel number out of range</item> + <item><c>ecomm</c> - Communication error on send</item> + <item><c>econnaborted</c> - Software caused connection abort</item> + <item><c>econnrefused</c> - Connection refused</item> + <item><c>econnreset</c> - Connection reset by peer</item> + <item><c>edeadlk</c> - Resource deadlock avoided</item> + <item><c>edeadlock</c> - Resource deadlock avoided</item> + <item><c>edestaddrreq</c> - Destination address required</item> + <item><c>edirty</c> - Mounting a dirty fs without force</item> + <item><c>edom</c> - Math argument out of range</item> + <item><c>edotdot</c> - Cross mount point</item> + <item><c>edquot</c> - Disk quota exceeded</item> + <item><c>eduppkg</c> - Duplicate package name</item> + <item><c>eexist</c> - File already exists</item> + <item><c>efault</c> - Bad address in system call argument</item> + <item><c>efbig</c> - File too large</item> + <item><c>ehostdown</c> - Host is down</item> + <item><c>ehostunreach</c> - Host is unreachable</item> + <item><c>eidrm</c> - Identifier removed</item> + <item><c>einit</c> - Initialization error</item> + <item><c>einprogress</c> - Operation now in progress</item> + <item><c>eintr</c> - Interrupted system call</item> + <item><c>einval</c> - Invalid argument</item> <item><c>eio</c> - I/O error</item> - <item><c>eisconn</c> - socket is already connected</item> - <item><c>eisdir</c> - illegal operation on a directory</item> - <item><c>eisnam</c> - is a named file</item> - <item><c>el2hlt</c> - level 2 halted</item> - <item><c>el2nsync</c> - level 2 not synchronized</item> - <item><c>el3hlt</c> - level 3 halted</item> - <item><c>el3rst</c> - level 3 reset</item> + <item><c>eisconn</c> - Socket is already connected</item> + <item><c>eisdir</c> - Illegal operation on a directory</item> + <item><c>eisnam</c> - Is a named file</item> + <item><c>el2hlt</c> - Level 2 halted</item> + <item><c>el2nsync</c> - Level 2 not synchronized</item> + <item><c>el3hlt</c> - Level 3 halted</item> + <item><c>el3rst</c> - Level 3 reset</item> <item><c>elbin</c> - ELBIN</item> - <item><c>elibacc</c> - cannot access a needed shared library</item> - <item><c>elibbad</c> - accessing a corrupted shared library</item> - <item><c>elibexec</c> - cannot exec a shared library directly</item> - <item><c>elibmax</c> - attempting to link in more shared - libraries than system limit</item> - <item><c>elibscn</c> - .lib section in a.out corrupted</item> - <item><c>elnrng</c> - link number out of range</item> - <item><c>eloop</c> - too many levels of symbolic links</item> - <item><c>emfile</c> - too many open files</item> - <item><c>emlink</c> - too many links</item> - <item><c>emsgsize</c> - message too long</item> - <item><c>emultihop</c> - multihop attempted</item> - <item><c>enametoolong</c> - file name too long</item> - <item><c>enavail</c> - not available</item> + <item><c>elibacc</c> - Cannot access a needed shared library</item> + <item><c>elibbad</c> - Accessing a corrupted shared library</item> + <item><c>elibexec</c> - Cannot exec a shared library directly</item> + <item><c>elibmax</c> - Attempting to link in more shared + libraries than system limit</item> + <item><c>elibscn</c> - <c>.lib</c> section in <c>a.out</c> + corrupted</item> + <item><c>elnrng</c> - Link number out of range</item> + <item><c>eloop</c> - Too many levels of symbolic links</item> + <item><c>emfile</c> - Too many open files</item> + <item><c>emlink</c> - Too many links</item> + <item><c>emsgsize</c> - Message too long</item> + <item><c>emultihop</c> - Multihop attempted</item> + <item><c>enametoolong</c> - Filename too long</item> + <item><c>enavail</c> - Unavailable</item> <item><c>enet</c> - ENET</item> - <item><c>enetdown</c> - network is down</item> - <item><c>enetreset</c> - network dropped connection on reset</item> - <item><c>enetunreach</c> - network is unreachable</item> - <item><c>enfile</c> - file table overflow</item> - <item><c>enoano</c> - anode table overflow</item> - <item><c>enobufs</c> - no buffer space available</item> - <item><c>enocsi</c> - no CSI structure available</item> - <item><c>enodata</c> - no data available</item> - <item><c>enodev</c> - no such device</item> - <item><c>enoent</c> - no such file or directory</item> - <item><c>enoexec</c> - exec format error</item> - <item><c>enolck</c> - no locks available</item> - <item><c>enolink</c> - link has be severed</item> - <item><c>enomem</c> - not enough memory</item> - <item><c>enomsg</c> - no message of desired type</item> - <item><c>enonet</c> - machine is not on the network</item> - <item><c>enopkg</c> - package not installed</item> - <item><c>enoprotoopt</c> - bad protocol option</item> - <item><c>enospc</c> - no space left on device</item> - <item><c>enosr</c> - out of stream resources or not a stream - device</item> - <item><c>enosym</c> - unresolved symbol name</item> - <item><c>enosys</c> - function not implemented</item> - <item><c>enotblk</c> - block device required</item> - <item><c>enotconn</c> - socket is not connected</item> - <item><c>enotdir</c> - not a directory</item> - <item><c>enotempty</c> - directory not empty</item> - <item><c>enotnam</c> - not a named file</item> - <item><c>enotsock</c> - socket operation on non-socket</item> - <item><c>enotsup</c> - operation not supported</item> - <item><c>enotty</c> - inappropriate device for ioctl</item> - <item><c>enotuniq</c> - name not unique on network</item> - <item><c>enxio</c> - no such device or address</item> - <item><c>eopnotsupp</c> - operation not supported on socket</item> - <item><c>eperm</c> - not owner</item> - <item><c>epfnosupport</c> - protocol family not supported</item> - <item><c>epipe</c> - broken pipe</item> - <item><c>eproclim</c> - too many processes</item> - <item><c>eprocunavail</c> - bad procedure for program</item> - <item><c>eprogmismatch</c> - program version wrong</item> - <item><c>eprogunavail</c> - RPC program not available</item> - <item><c>eproto</c> - protocol error</item> - <item><c>eprotonosupport</c> - protocol not supported</item> - <item><c>eprototype</c> - protocol wrong type for socket</item> - <item><c>erange</c> - math result unrepresentable</item> + <item><c>enetdown</c> - Network is down</item> + <item><c>enetreset</c> - Network dropped connection on reset</item> + <item><c>enetunreach</c> - Network is unreachable</item> + <item><c>enfile</c> - File table overflow</item> + <item><c>enoano</c> - Anode table overflow</item> + <item><c>enobufs</c> - No buffer space available</item> + <item><c>enocsi</c> - No CSI structure available</item> + <item><c>enodata</c> - No data available</item> + <item><c>enodev</c> - No such device</item> + <item><c>enoent</c> - No such file or directory</item> + <item><c>enoexec</c> - Exec format error</item> + <item><c>enolck</c> - No locks available</item> + <item><c>enolink</c> - Link has been severed</item> + <item><c>enomem</c> - Not enough memory</item> + <item><c>enomsg</c> - No message of desired type</item> + <item><c>enonet</c> - Machine is not on the network</item> + <item><c>enopkg</c> - Package not installed</item> + <item><c>enoprotoopt</c> - Bad protocol option</item> + <item><c>enospc</c> - No space left on device</item> + <item><c>enosr</c> - Out of stream resources or not a stream device</item> + <item><c>enosym</c> - Unresolved symbol name</item> + <item><c>enosys</c> - Function not implemented</item> + <item><c>enotblk</c> - Block device required</item> + <item><c>enotconn</c> - Socket is not connected</item> + <item><c>enotdir</c> - Not a directory</item> + <item><c>enotempty</c> - Directory not empty</item> + <item><c>enotnam</c> - Not a named file</item> + <item><c>enotsock</c> - Socket operation on non-socket</item> + <item><c>enotsup</c> - Operation not supported</item> + <item><c>enotty</c> - Inappropriate device for <c>ioctl</c></item> + <item><c>enotuniq</c> - Name not unique on network</item> + <item><c>enxio</c> - No such device or address</item> + <item><c>eopnotsupp</c> - Operation not supported on socket</item> + <item><c>eperm</c> - Not owner</item> + <item><c>epfnosupport</c> - Protocol family not supported</item> + <item><c>epipe</c> - Broken pipe</item> + <item><c>eproclim</c> - Too many processes</item> + <item><c>eprocunavail</c> - Bad procedure for program</item> + <item><c>eprogmismatch</c> - Wrong program version</item> + <item><c>eprogunavail</c> - RPC program unavailable</item> + <item><c>eproto</c> - Protocol error</item> + <item><c>eprotonosupport</c> - Protocol not supported</item> + <item><c>eprototype</c> - Wrong protocol type for socket</item> + <item><c>erange</c> - Math result unrepresentable</item> <item><c>erefused</c> - EREFUSED</item> - <item><c>eremchg</c> - remote address changed</item> - <item><c>eremdev</c> - remote device</item> - <item><c>eremote</c> - pathname hit remote file system</item> - <item><c>eremoteio</c> - remote i/o error</item> + <item><c>eremchg</c> - Remote address changed</item> + <item><c>eremdev</c> - Remote device</item> + <item><c>eremote</c> - Pathname hit remote filesystem</item> + <item><c>eremoteio</c> - Remote I/O error</item> <item><c>eremoterelease</c> - EREMOTERELEASE</item> - <item><c>erofs</c> - read-only file system</item> - <item><c>erpcmismatch</c> - RPC version is wrong</item> - <item><c>erremote</c> - object is remote</item> - <item><c>eshutdown</c> - cannot send after socket shutdown</item> - <item><c>esocktnosupport</c> - socket type not supported</item> - <item><c>espipe</c> - invalid seek</item> - <item><c>esrch</c> - no such process</item> - <item><c>esrmnt</c> - srmount error</item> - <item><c>estale</c> - stale remote file handle</item> + <item><c>erofs</c> - Read-only filesystem</item> + <item><c>erpcmismatch</c> - Wrong RPC version</item> + <item><c>erremote</c> - Object is remote</item> + <item><c>eshutdown</c> - Cannot send after socket shutdown</item> + <item><c>esocktnosupport</c> - Socket type not supported</item> + <item><c>espipe</c> - Invalid seek</item> + <item><c>esrch</c> - No such process</item> + <item><c>esrmnt</c> - Srmount error</item> + <item><c>estale</c> - Stale remote file handle</item> <item><c>esuccess</c> - Error 0</item> - <item><c>etime</c> - timer expired</item> - <item><c>etimedout</c> - connection timed out</item> - <item><c>etoomanyrefs</c> - too many references</item> - <item><c>etxtbsy</c> - text file or pseudo-device busy</item> - <item><c>euclean</c> - structure needs cleaning</item> - <item><c>eunatch</c> - protocol driver not attached</item> - <item><c>eusers</c> - too many users</item> - <item><c>eversion</c> - version mismatch</item> - <item><c>ewouldblock</c> - operation would block</item> - <item><c>exdev</c> - cross-domain link</item> - <item><c>exfull</c> - message tables full</item> - <item><c>nxdomain</c> - the hostname or domain name could not be - found</item> + <item><c>etime</c> - Timer expired</item> + <item><c>etimedout</c> - Connection timed out</item> + <item><c>etoomanyrefs</c> - Too many references</item> + <item><c>etxtbsy</c> - Text file or pseudo-device busy</item> + <item><c>euclean</c> - Structure needs cleaning</item> + <item><c>eunatch</c> - Protocol driver not attached</item> + <item><c>eusers</c> - Too many users</item> + <item><c>eversion</c> - Version mismatch</item> + <item><c>ewouldblock</c> - Operation would block</item> + <item><c>exdev</c> - Cross-domain link</item> + <item><c>exfull</c> - Message tables full</item> + <item><c>nxdomain</c> - Hostname or domain name cannot be found</item> </list> </section> </erlref> diff --git a/lib/kernel/doc/src/inet_res.xml b/lib/kernel/doc/src/inet_res.xml index 851a36aba9..4ada4203c0 100644 --- a/lib/kernel/doc/src/inet_res.xml +++ b/lib/kernel/doc/src/inet_res.xml @@ -11,7 +11,7 @@ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at - + http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software @@ -19,7 +19,7 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - + </legalnotice> <title>inet_res</title> @@ -29,52 +29,50 @@ <rev>A</rev> </header> <module>inet_res</module> - <modulesummary>A Rudimentary DNS Client</modulesummary> + <modulesummary>A rudimentary DNS client.</modulesummary> <description> - <p>Performs DNS name resolving towards recursive name servers</p> - <p>See also - <seealso marker="erts:inet_cfg"> - ERTS User's Guide: Inet configuration - </seealso> for more - information on how to configure an Erlang runtime system for IP - communication and how to enable this DNS client by defining - <c><![CDATA['dns']]></c> as a lookup method. It then acts - as a backend for the resolving functions in - <seealso marker="kernel:inet">inet</seealso>.</p> + <p>This module performs DNS name resolving to recursive name servers.</p> + <p>See also + <seealso marker="erts:inet_cfg">ERTS User's Guide: Inet Configuration</seealso> + for more information about how to configure an Erlang runtime system + for IP communication, and how to enable this DNS client by defining + <c><![CDATA['dns']]></c> as a lookup method. + The DNS client then acts as a backend for the resolving functions in + <seealso marker="kernel:inet"><c>inet</c></seealso>.</p> <p>This DNS client can resolve DNS records even if it is not used for normal name resolving in the node.</p> - <p>This is not a full-fledged resolver. It is just a - DNS client that relies on asking trusted recursive nameservers.</p> + <p>This is not a full-fledged resolver, only a + DNS client that relies on asking trusted recursive name servers.</p> </description> <section> <title>Name Resolving</title> <p>UDP queries are used unless resolver option <c>usevc</c> is <c>true</c>, which forces TCP queries. - If the query is to large for UDP, TCP is used instead. - For regular DNS queries 512 bytes is the size limit. - When EDNS is enabled (resolver option - <c>edns</c> is set to the EDNS version i.e <c>0</c> + If the query is too large for UDP, TCP is used instead. + For regular DNS queries, 512 bytes is the size limit.</p> + <p>When EDNS is enabled (resolver option + <c>edns</c> is set to the EDNS version (that is, <c>0</c> instead of <c>false</c>), resolver option - <c>udp_payload_size</c> sets the limit. If a nameserver - replies with the TC bit set (truncation), indicating + <c>udp_payload_size</c> sets the limit. If a name server + replies with the TC bit set (truncation), indicating that the answer is incomplete, the query is retried - to that nameserver using TCP. The resolver option + to that name server using TCP. Resolver option <c>udp_payload_size</c> also sets the advertised - size for the max allowed reply size, if EDNS is - enabled, otherwise the nameserver uses the limit - 512 byte. If the reply is larger it gets truncated, - forcing a TCP re-query.</p> - <p>For UDP queries, the resolver options <c>timeout</c> + size for the maximum allowed reply size, if EDNS is + enabled, otherwise the name server uses the limit + 512 bytes. If the reply is larger, it gets truncated, + forcing a TCP requery.</p> + <p>For UDP queries, resolver options <c>timeout</c> and <c>retry</c> control retransmission. - Each nameserver in the <c>nameservers</c> list is - tried with a timeout of <c>timeout</c> / <c>retry</c>. - Then all nameservers are tried again doubling the - timeout, for a total of <c>retry</c> times.</p> - <p>For queries that not use the <c>search</c> list, + Each name server in the <c>nameservers</c> list is + tried with a time-out of <c>timeout</c>/<c>retry</c>. + Then all name servers are tried again, doubling the + time-out, for a total of <c>retry</c> times.</p> + <p>For queries not using the <c>search</c> list, if the query to all <c>nameservers</c> results in - <c>{error,nxdomain}</c>or an empty answer, the same - query is tried for the <c>alt_nameservers</c>.</p> + <c>{error,nxdomain}</c> or an empty answer, the same + query is tried for <c>alt_nameservers</c>.</p> </section> <section> @@ -92,11 +90,13 @@ <name name="res_error"/> </datatype> </datatypes> + <section> <title>DNS Types</title> <p><marker id="dns_types"/> The following data types concern the DNS client:</p> </section> + <datatypes> <datatype> <name name="dns_name"/> @@ -112,10 +112,10 @@ <name name="dns_msg"/> <desc> <p>This is the start of a hiearchy of opaque data structures - that can be examined with access functions in inet_dns that - return lists of {Field,Value} tuples. The arity 2 functions - just return the value for a given field.</p> -<pre> + that can be examined with access functions in <c>inet_dns</c>, which + return lists of <c>{Field,Value}</c> tuples. The arity 2 functions + only return the value for a specified field.</p> + <pre> dns_msg() = DnsMsg inet_dns:msg(DnsMsg) -> [ {header, dns_header()} @@ -163,63 +163,55 @@ dns_rr() = DnsRr | {z, integer()} | {data, dns_data()} ] inet_dns:rr(DnsRr, Field) -> Value</pre> - -<p>There is an info function for the types above:</p> - -<pre> + <p>There is an information function for the types above:</p> + <pre> inet_dns:record_type(dns_msg()) -> msg; inet_dns:record_type(dns_header()) -> header; inet_dns:record_type(dns_query()) -> dns_query; inet_dns:record_type(dns_rr()) -> rr; inet_dns:record_type(_) -> undefined.</pre> - -<p>So; inet_dns:(inet_dns:record_type(X))(X) will convert -any of these data structures into a {Field,Value} list.</p> + <p>So, <c>inet_dns:(inet_dns:record_type(X))(X)</c> converts + any of these data structures into a <c>{Field,Value}</c> list.</p> </desc> </datatype> <datatype> <name name="dns_data"/> - <desc><p><c><anno>Regexp</anno></c> is a string with characters encoded in the - UTF-8 coding standard.</p> + <desc> + <p><c><anno>Regexp</anno></c> is a string with characters encoded + in the UTF-8 coding standard.</p> </desc> </datatype> </datatypes> - <funcs> - <func> <name name="getbyname" arity="2"/> <name name="getbyname" arity="3"/> - <fsummary>Resolve a DNS record of the given type for the given host - </fsummary> + <fsummary>Resolve a DNS record of the specified type for the specified + host.</fsummary> <desc> - <p>Resolve a DNS record of the given type for the given host, - of class <c>in</c>. On success returns a <c>hostent()</c> record with - <c>dns_data()</c> elements in the address list field. - </p><p> - This function uses the resolver option <c>search</c> that + <p>Resolves a DNS record of the specified type for the specified host, + of class <c>in</c>. Returns, on success, a <c>hostent()</c> record + with <c>dns_data()</c> elements in the address list field.</p> + <p>This function uses resolver option <c>search</c> that is a list of domain names. If the name to resolve contains no dots, it is prepended to each domain name in the search list, and they are tried in order. If the name contains dots, it is first tried as an absolute name - and if that fails the search list is used. If the name - has a trailing dot it is simply supposed to be - an absolute name and the search list is not used. - </p> + and if that fails, the search list is used. If the name + has a trailing dot, it is supposed to be + an absolute name and the search list is not used.</p> </desc> </func> <func> <name name="gethostbyaddr" arity="1"/> <name name="gethostbyaddr" arity="2"/> - <fsummary>Return a hostent record for the host with the given address - </fsummary> + <fsummary>Return a hostent record for the host with the specified + address.</fsummary> <desc> <p>Backend functions used by - <seealso marker="kernel:inet#gethostbyaddr/1"> - inet:gethostbyaddr/1 - </seealso>. + <seealso marker="kernel:inet#gethostbyaddr/1"><c>inet:gethostbyaddr/1</c></seealso>. </p> </desc> </func> @@ -228,22 +220,19 @@ any of these data structures into a {Field,Value} list.</p> <name name="gethostbyname" arity="1"/> <name name="gethostbyname" arity="2"/> <name name="gethostbyname" arity="3"/> - <fsummary>Return a hostent record for the host with the given name + <fsummary>Return a hostent record for the host with the specified name. </fsummary> <desc> <p>Backend functions used by - <seealso marker="kernel:inet#gethostbyname/1"> - inet:gethostbyname/1,2 - </seealso>. - </p><p> - This function uses the resolver option <c>search</c> just like - <seealso marker="#getbyname/2">getbyname/2,3</seealso>. - </p><p> - If the resolver option <c>inet6</c> is <c>true</c>, - an IPv6 address is looked up, and if that fails - the IPv4 address is looked up and returned on - IPv6 mapped IPv4 format. + <seealso marker="kernel:inet#gethostbyname/1"><c>inet:gethostbyname/1,2</c></seealso>. </p> + <p>This function uses resolver option <c>search</c> just like + <seealso marker="#getbyname/2"><c>getbyname/2,3</c></seealso>. + </p> + <p>If resolver option <c>inet6</c> is <c>true</c>, + an IPv6 address is looked up. If that fails, + the IPv4 address is looked up and returned on + IPv6-mapped IPv4 format.</p> </desc> </func> @@ -251,22 +240,21 @@ any of these data structures into a {Field,Value} list.</p> <name name="lookup" arity="3"/> <name name="lookup" arity="4"/> <name name="lookup" arity="5"/> - <fsummary>Resolve the DNS data for the record of the given type and class - for the given name - </fsummary> + <fsummary>Resolve the DNS data for the record of the specified type + and class for the specified name.</fsummary> <desc> - <p>Resolve the DNS data for the record of the given type and class - for the given name. On success filters out the answer records - with the correct <c><anno>Class</anno></c> and <c><anno>Type</anno></c> and returns - a list of their data fields. So a lookup for type <c>any</c> - will give an empty answer since the answer records have + <p>Resolves the DNS data for the record of the specified type and class + for the specified name. On success, filters out the answer records + with the correct <c><anno>Class</anno></c> and + <c><anno>Type</anno></c>, and returns + a list of their data fields. So, a lookup for type <c>any</c> + gives an empty answer, as the answer records have specific types that are not <c>any</c>. An empty answer - as well as a failed lookup returns an empty list. - </p><p> - Calls <seealso marker="#resolve/3">resolve/2..4</seealso> + or a failed lookup returns an empty list.</p> + <p>Calls + <seealso marker="#resolve/3"><c>resolve/*</c></seealso> with the same arguments and filters the result, so - <c><anno>Opts</anno></c> is explained there. - </p> + <c><anno>Opts</anno></c> is described for those functions.</p> </desc> </func> @@ -274,90 +262,77 @@ any of these data structures into a {Field,Value} list.</p> <name name="resolve" arity="3"/> <name name="resolve" arity="4"/> <name name="resolve" arity="5"/> - <fsummary>Resolve a DNS record of the given type and class - for the given name - </fsummary> + <fsummary>Resolve a DNS record of the specified type and class + for the specified name.</fsummary> <desc> - <p>Resolve a DNS record of the given type and class for the given name. - The returned <c>dns_msg()</c> can be examined using - access functions in <c>inet_db</c> as described - in <seealso marker="#dns_types">DNS Types</seealso>. - </p><p> - If <c><anno>Name</anno></c> is an <c>ip_address()</c>, the domain name - to query for is generated as the standard reverse - ".IN-ADDR.ARPA." name for an IPv4 address, or the - ".IP6.ARPA." name for an IPv6 address. - In this case you most probably want to use - <c><anno>Class</anno> = in</c> and <c><anno>Type</anno> = ptr</c> but it - is not done automatically. - </p><p> - <c><anno>Opts</anno></c> override the corresponding resolver options. - If the option <c>nameservers</c> is given, it is - also assumed that it is the complete list of nameserves, - so the resolver option <c>alt_nameserves</c> is ignored. - Of course, if that option is also given to this function, - it is used. - </p><p> - The <c>verbose</c> option (or rather <c>{verbose,true}</c>), + <p>Resolves a DNS record of the specified type and class for the + specified name. The returned <c>dns_msg()</c> can be examined using + access functions in <c>inet_db</c>, as described in section + in <seealso marker="#dns_types">DNS Types</seealso>.</p> + <p>If <c><anno>Name</anno></c> is an <c>ip_address()</c>, the domain + name to query for is generated as the standard reverse + <c>".IN-ADDR.ARPA."</c> name for an IPv4 address, or the + <c>".IP6.ARPA."</c> name for an IPv6 address. + In this case, you most probably want to use + <c><anno>Class</anno> = in</c> and <c><anno>Type</anno> = ptr</c>, + but it is not done automatically.</p> + <p><c><anno>Opts</anno></c> overrides the corresponding resolver + options. If option <c>nameservers</c> is specified, it is + assumed that it is the complete list of name serves, + so resolver option <c>alt_nameserves</c> is ignored. + However, if option <c>alt_nameserves</c> is also specified to this + function, it is used.</p> + <p>Option <c>verbose</c> (or rather <c>{verbose,true}</c>) causes diagnostics printout through - <seealso marker="stdlib:io#format/3">io:format/2</seealso> - of queries, replies retransmissions, etc, similar - to from utilities like <c>dig</c>, <c>nslookup</c> et.al. - </p><p> - If <c><anno>Opt</anno></c> is an arbitrary atom it is interpreted + <seealso marker="stdlib:io#format/3"><c>io:format/2</c></seealso> + of queries, replies retransmissions, and so on, similar + to from utilities, such as <c>dig</c> and <c>nslookup</c>.</p> + <p>If <c><anno>Opt</anno></c> is any atom, it is interpreted as <c>{<anno>Opt</anno>,true}</c> unless the atom string starts with - <c>"no"</c> making the interpretation <c>{<anno>Opt</anno>,false}</c>. - For example: <c>usevc</c> is an alias for <c>{usevc,true}</c>, - and <c>nousevc</c> an alias for <c>{usevc,false}</c>. - </p><p> - The <c>inet6</c> option currently has no effect on this function. - You probably want to use <c><anno>Type</anno> = a | aaaa</c> instead. - </p> + <c>"no"</c>, making the + interpretation <c>{<anno>Opt</anno>,false}</c>. + For example, <c>usevc</c> is an alias for <c>{usevc,true}</c> + and <c>nousevc</c> is an alias for <c>{usevc,false}</c>.</p> + <p>Option <c>inet6</c> has no effect on this function. You + probably want to use <c><anno>Type</anno> = a | aaaa</c> instead.</p> </desc> </func> - </funcs> - - <section> - <title>Examples</title> - <p>Access functions example: how - <seealso marker="#lookup/3">lookup/3</seealso> - could have been implemented using - <seealso marker="#resolve/3">resolve/3</seealso> - from outside the module. - </p><code type="none"> - example_lookup(Name, Class, Type) -> - case inet_res:resolve(Name, Class, Type) of - {ok,Msg} -> - [inet_dns:rr(RR, data) - || RR <- inet_dns:msg(Msg, anlist), - inet_dns:rr(RR, type) =:= Type, - inet_dns:rr(RR, class) =:= Class]; - {error,_} -> - [] - end.</code> + <title>Example</title> + <p>This access functions example shows how + <seealso marker="#lookup/3"><c>lookup/3</c></seealso> + can be implemented using + <seealso marker="#resolve/3"><c>resolve/3</c></seealso> + from outside the module:</p> + <code type="none"> +example_lookup(Name, Class, Type) -> + case inet_res:resolve(Name, Class, Type) of + {ok,Msg} -> + [inet_dns:rr(RR, data) + || RR <- inet_dns:msg(Msg, anlist), + inet_dns:rr(RR, type) =:= Type, + inet_dns:rr(RR, class) =:= Class]; + {error,_} -> + [] + end.</code> </section> - - <section> <title>Legacy Functions</title> - <p>These have been deprecated due to the annoying double - meaning of the nameservers/timeout argument, and - because they had no decent place for a resolver options list.</p> + <p>These are deprecated because the annoying double + meaning of the name servers/time-out argument, and + because they have no decent place for a resolver options list.</p> </section> <funcs> - <func> <name name="nslookup" arity="3"/> <name name="nslookup" arity="4" clause_i="1"/> <name name="nslookup" arity="4" clause_i="2"/> - <fsummary>Resolve a DNS record of the given type and class - for the given name - </fsummary> + <fsummary>Resolve a DNS record of the specified type and class for the + specified name.</fsummary> <type variable="Name"/> <type variable="Class"/> <type variable="Type"/> @@ -365,23 +340,20 @@ any of these data structures into a {Field,Value} list.</p> <type variable="Nameservers"/> <type variable="Reason"/> <desc> - <p>Resolve a DNS record of the given type and class for the given name. - </p> + <p>Resolves a DNS record of the specified type and class for the + specified name.</p> </desc> </func> <func> <name name="nnslookup" arity="4"/> <name name="nnslookup" arity="5"/> - <fsummary>Resolve a DNS record of the given type and class - for the given name - </fsummary> + <fsummary>Resolve a DNS record of the specified type and class + for the specified name.</fsummary> <desc> - <p>Resolve a DNS record of the given type and class for the given name. - </p> + <p>Resolves a DNS record of the specified type and class for the + specified name.</p> </desc> </func> - </funcs> - </erlref> diff --git a/lib/kernel/doc/src/init_stub.xml b/lib/kernel/doc/src/init_stub.xml index 0fdf541037..df89b174ca 100644 --- a/lib/kernel/doc/src/init_stub.xml +++ b/lib/kernel/doc/src/init_stub.xml @@ -31,13 +31,9 @@ <rev>A</rev> </header> <module>init</module> - <modulesummary>Coordination of System Startup</modulesummary> - <description><p> - - The module init is moved to the runtime system - application. Please see <seealso - marker="erts:init">init(3)</seealso> in the - erts reference manual instead. - - </p></description> + <modulesummary>Coordination of system startup.</modulesummary> + <description> + <p>This module is moved to the + <seealso marker="erts:init"><c>ERTS</c></seealso> application.</p> + </description> </erlref> diff --git a/lib/kernel/doc/src/kernel_app.xml b/lib/kernel/doc/src/kernel_app.xml index 956c57f7c1..9e6fb60bb7 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>2015</year> + <year>1996</year><year>2016</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -29,66 +29,59 @@ <rev></rev> </header> <app>kernel</app> - <appsummary>The Kernel Application</appsummary> + <appsummary>The Kernel application.</appsummary> <description> - <p>The Kernel application is the first application started. It is + <p>The <c>Kernel</c> application has all the code necessary to run + the Erlang runtime system: file servers, code servers, + and so on.</p> + <p>The <c>Kernel</c> application is the first application started. It is mandatory in the sense that the minimal system based on - Erlang/OTP consists of Kernel and STDLIB. The Kernel application - contains the following services:</p> + Erlang/OTP consists of <c>Kernel</c> and <c>STDLIB</c>. <c>Kernel</c> + contains the following functional areas:</p> <list type="bulleted"> - <item>application controller, see <seealso marker="application">application(3)</seealso></item> - <item><c>code</c></item> - <item><c>disk_log</c></item> - <item><c>dist_ac</c>, distributed application controller</item> - <item><c>erl_boot_server</c></item> - <item><c>erl_ddll</c></item> - <item><c>error_logger</c></item> - <item><c>error_logger_format_depth</c></item> - <item><c>file</c></item> - <item><c>global</c></item> - <item><c>global_group</c></item> - <item><c>heart</c></item> - <item><c>inet</c></item> - <item><c>net_kernel</c></item> - <item><c>os</c></item> - <item><c>pg2</c></item> - <item><c>rpc</c></item> - <item><c>seq_trace</c></item> - <item><c>user</c></item> + <item>Start, stop, supervision, configuration, and distribution of applications</item> + <item>Code loading</item> + <item>Logging</item> + <item>Error logging</item> + <item>Global name service</item> + <item>Supervision of Erlang/OTP</item> + <item>Communication with sockets</item> + <item>Operating system interface</item> </list> </description> <section> <title>Error Logger Event Handlers</title> <p>Two standard error logger event handlers are defined in - the Kernel application. These are described in - <seealso marker="error_logger">error_logger(3)</seealso>.</p> + the <c>Kernel</c> application. These are described in + <seealso marker="error_logger"><c>error_logger(3)</c></seealso>.</p> </section> <section> <title>Configuration</title> - <p>The following configuration parameters are defined for the Kernel - application. See <seealso marker="app">app(4)</seealso> for more - information about configuration parameters.</p> + <p>The following configuration parameters are defined for the <c>Kernel</c> + application. For more information about configuration parameters, + see file <seealso marker="app"><c>app(4)</c></seealso>.</p> <taglist> <tag><c>browser_cmd = string() | {M,F,A}</c></tag> <item> - <p>When pressing the Help button in a tool such as Debugger or - TV, the help text (an HTML file <c>File</c>) is by default - displayed in a Netscape browser which is required to be up and - running. This parameter can be used to change the command for + <p>When pressing the <em>Help</em> button in a tool such as Debugger, + the help text (an HTML file <c>File</c>) is by default + displayed in a Netscape browser, which is required to be + operational. This parameter can be used to change the command for how to display the help text if another browser than Netscape - is preferred, or another platform than Unix or Windows is + is preferred, or if another platform than Unix or Windows is used.</p> <p>If set to a string <c>Command</c>, the command - <c>"Command File"</c> will be evaluated using <c>os:cmd/1</c>.</p> - <p>If set to a module-function-args tuple <c>{M,F,A}</c>, - the call <c>apply(M,F,[File|A])</c> will be evaluated.</p> + <c>"Command File"</c> is evaluated using + <seealso marker="os#cmd/1"><c>os:cmd/1</c></seealso>.</p> + <p>If set to a module-function-args tuple, <c>{M,F,A}</c>, + the call <c>apply(M,F,[File|A])</c> is evaluated.</p> </item> <tag><c>distributed = [Distrib]</c></tag> <item> - <p>Specifies which applications are distributed and on which - nodes they may execute. In this parameter:</p> + <p>Specifies which applications that are distributed and on which + nodes they are allowed to execute. In this parameter:</p> <list type="bulleted"> <item><c>Distrib = {App,Nodes} | {App,Time,Nodes}</c></item> <item><c>App = atom()</c></item> @@ -96,25 +89,24 @@ <item><c>Nodes = [node() | {node(),...,node()}]</c></item> </list> <p>The parameter is described in - <seealso marker="application">application(3)</seealso>, function - <c>load/2</c>.</p> + <seealso marker="application#load/2"><c>application:load/2</c></seealso>.</p> </item> <tag><c>dist_auto_connect = Value</c></tag> <item> - <p>Specifies when nodes will be automatically connected. If + <p>Specifies when nodes are automatically connected. If this parameter is not specified, a node is always - automatically connected, e.g when a message is to be sent to + automatically connected, for example, when a message is to be sent to that node. <c>Value</c> is one of:</p> <taglist> <tag><c>never</c></tag> - <item>Connections are never automatically established, they + <item><p>Connections are never automatically established, they must be explicitly connected. See - <seealso marker="net_kernel">net_kernel(3)</seealso>.</item> + <seealso marker="net_kernel"><c>net_kernel(3)</c></seealso>.</p></item> <tag><c>once</c></tag> - <item>Connections will be established automatically, but only + <item><p>Connections are established automatically, but only once per node. If a node goes down, it must thereafter be explicitly connected. See - <seealso marker="net_kernel">net_kernel(3)</seealso>.</item> + <seealso marker="net_kernel"><c>net_kernel(3)</c></seealso>.</p></item> </taglist> </item> <tag><c>permissions = [Perm]</c></tag> @@ -127,25 +119,24 @@ <item><c>Bool = boolean()</c></item> </list> <p>Permissions are described in - <seealso marker="application">application(3)</seealso>, function - <c>permit/2</c>.</p> + <seealso marker="application#permit/2"><c>application:permit/2</c></seealso>.</p> </item> <tag><c>error_logger = Value</c></tag> <item> <p><c>Value</c> is one of:</p> <taglist> <tag><c>tty</c></tag> - <item>Installs the standard event handler which prints error - reports to <c>stdio</c>. This is the default option.</item> + <item><p>Installs the standard event handler, which prints error + reports to <c>stdio</c>. This is the default option.</p></item> <tag><c>{file, FileName}</c></tag> - <item>Installs the standard event handler which prints error - reports to the file <c>FileName</c>, where <c>FileName</c> - is a string.</item> + <item><p>Installs the standard event handler, which prints error + reports to file <c>FileName</c>, where <c>FileName</c> + is a string.</p></item> <tag><c>false</c></tag> <item> <p>No standard event handler is installed, but the initial, primitive event handler is kept, printing - raw event messages to tty.</p> + raw event messages to <c>tty</c>.</p> </item> <tag><c>silent</c></tag> <item> @@ -156,111 +147,110 @@ <tag><c>error_logger_format_depth = Depth</c></tag> <item> <marker id="error_logger_format_depth"></marker> - <p>This parameter can be used to limit the size of the + <p>Can be used to limit the size of the formatted output from the error logger event handlers.</p> - <note><p>This configuration parameter was introduced in OTP 18.1. - It is currently experimental. Based on user feedback it - may be changed or improved in future releases, for example + <note><p>This configuration parameter was introduced in OTP 18.1 + and is experimental. Based on user feedback, it + can be changed or improved in future releases, for example, to gain better control over how to limit the size of the - formatted output. We have no plans to entirely remove this - new feature, unless it turns out to be completely - useless. In OTP 19, the default may be changed to limit the - formatted output.</p></note> + formatted output. We have no plans to remove this + new feature entirely, unless it turns out to be + useless.</p></note> - <p><c>Depth</c> is a positive integer that is the maximum + <p><c>Depth</c> is a positive integer representing the maximum depth to which terms are printed by the error logger event - handlers included in OTP. Specifically, the two event handlers + handlers included in OTP. This + configuration parameter is used by the two event handlers defined by the <c>Kernel</c> application and the two event - handlers in the <c>SASL</c> application will use this - configuration parameter. (If you have implemented you own - error handlers, this configuration parameter will have no - effect on them.)</p> + handlers in the <c>SASL</c> application. + (If you have implemented your own error handlers, this configuration + parameter has no effect on them.)</p> - <p>The way <c>Depth</c> is used, is that format strings - string passed to the event handlers will be rewritten. - The "~p" and "~w" format controls will be replaced with - "~P" and "~W", respectively, and <c>Depth</c> will be - used as the depth parameter. See - <seealso marker="stdlib:io#format/2">io:format/2</seealso>.</p> + <p><c>Depth</c> is used as follows: Format strings + passed to the event handlers are rewritten. + The format controls <c>~p</c> and <c>~w</c> are replaced with + <c>~P</c> and <c>~W</c>, respectively, and <c>Depth</c> is + used as the depth parameter. For details, see + <seealso marker="stdlib:io#format/2"><c>io:format/2</c></seealso> + in <c>STDLIB</c>.</p> <note><p>A reasonable starting value for <c>Depth</c> is - <c>30</c>. You should test crashing various processes in your - application and examine the logs from the crashes, and then - either increase or decrease the value.</p></note> + <c>30</c>. We recommend to test crashing various processes in your + application, examine the logs from the crashes, and then + increase or decrease the value.</p></note> </item> <tag><c>global_groups = [GroupTuple]</c></tag> <item> + <marker id="global_groups"></marker> <p>Defines global groups, see - <seealso marker="global_group">global_group(3)</seealso>.</p> + <seealso marker="global_group"><c>global_group(3)</c></seealso>. + In this parameter:</p> <list type="bulleted"> - <item><c>GroupTuple = {GroupName, [Node]} | {GroupName, PublishType, [Node]}</c></item> - <item><c>GroupName = atom()</c></item> - <item><c>PublishType = normal | hidden</c></item> - <item><c>Node = node()</c></item> + <item><p><c>GroupTuple = {GroupName, [Node]} | {GroupName, PublishType, [Node]}</c></p></item> + <item><p><c>GroupName = atom()</c></p></item> + <item><p><c>PublishType = normal | hidden</c></p></item> + <item><p><c>Node = node()</c></p></item> </list> </item> <tag><c>inet_default_connect_options = [{Opt, Val}]</c></tag> <item> <p>Specifies default options for <c>connect</c> sockets, - see <seealso marker="inet">inet(3)</seealso>.</p> + see <seealso marker="inet"><c>inet(3)</c></seealso>.</p> </item> <tag><c>inet_default_listen_options = [{Opt, Val}]</c></tag> <item> <p>Specifies default options for <c>listen</c> (and - <c>accept</c>) sockets, see <seealso marker="inet">inet(3)</seealso>.</p> + <c>accept</c>) sockets, see <seealso marker="inet"><c>inet(3)</c></seealso>.</p> </item> <tag><c>{inet_dist_use_interface, ip_address()}</c></tag> <item> - <p>If the host of an Erlang node has several network interfaces, - this parameter specifies which one to listen on. See - <seealso marker="inet">inet(3)</seealso> for the type definition - of <c>ip_address()</c>.</p> + <p>If the host of an Erlang node has many network interfaces, + this parameter specifies which one to listen on. For the type definition + of <c>ip_address()</c>, + see <seealso marker="inet"><c>inet(3)</c></seealso>.</p> </item> - <tag><c>{inet_dist_listen_min, First}</c></tag> + <tag><c>{inet_dist_listen_min, First}</c> and <c>{inet_dist_listen_max, Last}</c></tag> <item> - <p>See below.</p> - </item> - <tag><c>{inet_dist_listen_max, Last}</c></tag> - <item> - <p>Define the <c>First..Last</c> port range for the listener + <p>Defines the <c>First..Last</c> port range for the listener socket of a distributed Erlang node.</p> </item> <tag><c>{inet_dist_listen_options, Opts}</c></tag> <item> - <p>Define a list of extra socket options to be used when opening the + <p>Defines a list of extra socket options to be used when opening the listening socket for a distributed Erlang node. - See <seealso marker="gen_tcp#listen/2">gen_tcp:listen/2</seealso></p> + See <seealso marker="gen_tcp#listen/2"><c>gen_tcp:listen/2</c></seealso>.</p> </item> <tag><c>{inet_dist_connect_options, Opts}</c></tag> <item> - <p>Define a list of extra socket options to be used when connecting to + <p>Defines a list of extra socket options to be used when connecting to other distributed Erlang nodes. - See <seealso marker="gen_tcp#connect/4">gen_tcp:connect/4</seealso></p> + See <seealso marker="gen_tcp#connect/4"><c>gen_tcp:connect/4</c></seealso>.</p> </item> <tag><c>inet_parse_error_log = silent</c></tag> <item> - <p>If this configuration parameter is set, no + <p>If set, no <c>error_logger</c> messages are generated when erroneous lines are found and skipped in the various Inet configuration files.</p> </item> <tag><c>inetrc = Filename</c></tag> <item> - <p>The name (string) of an Inet user configuration file. See - ERTS User's Guide, Inet configuration.</p> + <p>The name (string) of an Inet user configuration file. For details, + see section + <seealso marker="erts:inet_cfg"><c>Inet Configuration</c></seealso> + in the <c>ERTS</c> User's Guide.</p> </item> <tag><c>net_setuptime = SetupTime</c></tag> <item> <marker id="net_setuptime"></marker> <p><c>SetupTime</c> must be a positive integer or floating point - number, and will be interpreted as the maximally allowed time + number, and is interpreted as the maximum allowed time for each network operation during connection setup to another - Erlang node. The maximum allowed value is 120; if higher values - are given, 120 will be used. The default value if the variable - is not given, or if the value is incorrect (e.g. not a number), - is 7 seconds.</p> - <p>Note that this value does not limit the total connection + Erlang node. The maximum allowed value is <c>120</c>. If higher values + are specified, <c>120</c> is used. Default is 7 seconds if the variable + is not specified, or if the value is incorrect (for example, not a number).</p> + <p>Notice that this value does not limit the total connection setup time, but rather each individual network operation during the connection setup and handshake.</p> </item> @@ -268,45 +258,44 @@ <item> <marker id="net_ticktime"></marker> <p>Specifies the <c>net_kernel</c> tick time. <c>TickTime</c> - is given in seconds. Once every <c>TickTime/4</c> second, all - connected nodes are ticked (if anything else has been written - to a node) and if nothing has been received from another node - within the last four (4) tick times that node is considered - to be down. This ensures that nodes which are not responding, + is specified in seconds. Once every <c>TickTime/4</c> second, all + connected nodes are ticked (if anything else is written + to a node). If nothing is received from another node + within the last four tick times, that node is considered + to be down. This ensures that nodes that are not responding, for reasons such as hardware errors, are considered to be down.</p> <p>The time <c>T</c>, in which a node that is not responding is - detected, is calculated as: <c><![CDATA[MinT < T < MaxT]]></c> where:</p> + detected, is calculated as <c><![CDATA[MinT < T < MaxT]]></c>, where:</p> <code type="none"> MinT = TickTime - TickTime / 4 MaxT = TickTime + TickTime / 4</code> - <p><c>TickTime</c> is by default 60 (seconds). Thus, + <p><c>TickTime</c> defaults to <c>60</c> (seconds). Thus, <c><![CDATA[45 < T < 75]]></c> seconds.</p> - <p><em>Note:</em> All communicating nodes should have the same + <p>Notice that <em>all</em> communicating nodes are to have the <em>same</em> <c>TickTime</c> value specified.</p> - <p><em>Note:</em> Normally, a terminating node is detected - immediately.</p> + <p>Normally, a terminating node is detected immediately.</p> </item> <tag><c>shutdown_timeout = integer() | infinity</c></tag> <item> - <p>Specifies the time <c>application_controller</c> will wait + <p>Specifies the time <c>application_controller</c> waits for an application to terminate during node shutdown. If the - timer expires, <c>application_controller</c> will brutally - kill <c>application_master</c> of the hanging + timer expires, <c>application_controller</c> brutally + kills <c>application_master</c> of the hanging application. If this parameter is undefined, it defaults to <c>infinity</c>.</p> </item> <tag><c>sync_nodes_mandatory = [NodeName]</c></tag> <item> - <p>Specifies which other nodes <em>must</em> be alive in order + <p>Specifies which other nodes that <em>must</em> be alive for this node to start properly. If some node in the list - does not start within the specified time, this node will not + does not start within the specified time, this node does not start either. If this parameter is undefined, it defaults to - [].</p> + <c>[]</c>.</p> </item> <tag><c>sync_nodes_optional = [NodeName]</c></tag> <item> - <p>Specifies which other nodes <em>can</em> be alive in order + <p>Specifies which other nodes that <em>can</em> be alive for this node to start properly. If some node in this list does not start within the specified time, this node starts anyway. If this parameter is undefined, it defaults to @@ -314,66 +303,65 @@ MaxT = TickTime + TickTime / 4</code> </item> <tag><c>sync_nodes_timeout = integer() | infinity</c></tag> <item> - <p>Specifies the amount of time (in milliseconds) this node - will wait for the mandatory and optional nodes to start. If + <p>Specifies the time (in milliseconds) that this node + waits for the mandatory and optional nodes to start. If this parameter is undefined, no node synchronization is - performed. This option also makes sure that <c>global</c> is + performed. This option ensures that <c>global</c> is synchronized.</p> </item> <tag><c>start_dist_ac = true | false</c></tag> <item> <p>Starts the <c>dist_ac</c> server if the parameter is - <c>true</c>. This parameter should be set to <c>true</c> for - systems that use distributed applications.</p> - <p>The default value is <c>false</c>. If this parameter is - undefined, the server is started if the parameter + <c>true</c>. This parameter is to be set to <c>true</c> for + systems using distributed applications.</p> + <p>Defaults to <c>false</c>. If this parameter is + undefined, the server is started if parameter <c>distributed</c> is set.</p> </item> <tag><c>start_boot_server = true | false</c></tag> <item> <p>Starts the <c>boot_server</c> if the parameter is <c>true</c> - (see <seealso marker="erl_boot_server">erl_boot_server(3)</seealso>). - This parameter should be - set to <c>true</c> in an embedded system which uses this - service.</p> - <p>The default value is <c>false</c>.</p> + (see <seealso marker="erl_boot_server"><c>erl_boot_server(3)</c></seealso>). + This parameter is to be set to <c>true</c> in an embedded system + using this service.</p> + <p>Defaults to <c>false</c>.</p> </item> <tag><c>boot_server_slaves = [SlaveIP]</c></tag> <item> - <p>If the <c>start_boot_server</c> configuration parameter is + <p>If configuration parameter <c>start_boot_server</c> is <c>true</c>, this parameter can be used to initialize - <c>boot_server</c> with a list of slave IP addresses. - <c>SlaveIP = string() | atom | {integer(),integer(),integer(),integer()}</c></p> + <c>boot_server</c> with a list of slave IP addresses:</p> + <p> + <c>SlaveIP = string() | atom | {integer(),integer(),integer(),integer()}</c>,</p> <p>where <c><![CDATA[0 <= integer() <=255]]></c>.</p> - <p>Examples of <c>SlaveIP</c> in atom, string and tuple form - are: <br></br> -<c>'150.236.16.70', "150,236,16,70", {150,236,16,70}</c>.</p> - <p>The default value is <c>[]</c>.</p> + <p>Examples of <c>SlaveIP</c> in atom, string, and tuple form:</p> + <p><c>'150.236.16.70', "150,236,16,70", {150,236,16,70}</c>.</p> + <p>Defaults to <c>[]</c>.</p> </item> <tag><c>start_disk_log = true | false</c></tag> <item> <p>Starts the <c>disk_log_server</c> if the parameter is - <c>true</c> (see <seealso marker="disk_log">disk_log(3)</seealso>). - This parameter should be - set to true in an embedded system which uses this service.</p> - <p>The default value is <c>false</c>.</p> + <c>true</c> (see <seealso marker="disk_log"><c>disk_log(3)</c></seealso>). + This parameter is to be set to <c>true</c> in an embedded system + using this service.</p> + <p>Defaults to <c>false</c>.</p> </item> <tag><c>start_pg2 = true | false</c></tag> <item> + <marker id="start_pg2"></marker> <p>Starts the <c>pg2</c> server (see - <seealso marker="pg2">pg2(3)</seealso>) if - the parameter is <c>true</c>. This parameter should be set to - <c>true</c> in an embedded system which uses this service.</p> - <p>The default value is <c>false</c>.</p> + <seealso marker="pg2"><c>pg2(3)</c></seealso>) if + the parameter is <c>true</c>. This parameter is to be set to + <c>true</c> in an embedded system that uses this service.</p> + <p>Defaults to <c>false</c>.</p> </item> <tag><c>start_timer = true | false</c></tag> <item> <p>Starts the <c>timer_server</c> if the parameter is - <c>true</c> (see <seealso marker="stdlib:timer">timer(3)</seealso>). - This parameter should be - set to <c>true</c> in an embedded system which uses this - service.</p> - <p>The default value is <c>false</c>.</p> + <c>true</c> (see <seealso marker="stdlib:timer"><c>stdlib:timer(3)</c></seealso>). + This parameter is to be set to <c>true</c> in an embedded system + using this service.</p> + <p>Defaults to <c>false</c>.</p> </item> <tag><c>shutdown_func = {Mod, Func}</c></tag> <item> @@ -383,7 +371,7 @@ MaxT = TickTime + TickTime / 4</code> <item><c>Func = atom()</c></item> </list> <p>Sets a function that <c>application_controller</c> calls - when it starts to terminate. The function is called as: + when it starts to terminate. The function is called as <c>Mod:Func(Reason)</c>, where <c>Reason</c> is the terminate reason for <c>application_controller</c>, and it must return as soon as possible for <c>application_controller</c> @@ -394,25 +382,25 @@ MaxT = TickTime + TickTime / 4</code> <section> <title>See Also</title> - <p><seealso marker="app">app(4)</seealso>, - <seealso marker="application">application(3)</seealso>, - <seealso marker="code">code(3)</seealso>, - <seealso marker="disk_log">disk_log(3)</seealso>, - <seealso marker="erl_boot_server">erl_boot_server(3)</seealso>, - <seealso marker="erl_ddll">erl_ddll(3)</seealso>, - <seealso marker="error_logger">error_logger(3)</seealso>, - <seealso marker="file">file(3)</seealso>, - <seealso marker="global">global(3)</seealso>, - <seealso marker="global_group">global_group(3)</seealso>, - <seealso marker="heart">heart(3)</seealso>, - <seealso marker="inet">inet(3)</seealso>, - <seealso marker="net_kernel">net_kernel(3)</seealso>, - <seealso marker="os">os(3)</seealso>, - <seealso marker="pg2">pg2(3)</seealso>, - <seealso marker="rpc">rpc(3)</seealso>, - <seealso marker="seq_trace">seq_trace(3)</seealso>, - <seealso marker="stdlib:timer">timer(3)</seealso>, - <seealso marker="user">user(3)</seealso></p> + <p><seealso marker="app"><c>app(4)</c></seealso>, + <seealso marker="application"><c>application(3)</c></seealso>, + <seealso marker="code"><c>code(3)</c></seealso>, + <seealso marker="disk_log"><c>disk_log(3)</c></seealso>, + <seealso marker="erl_boot_server"><c>erl_boot_server(3)</c></seealso>, + <seealso marker="erl_ddll"><c>erl_ddll(3)</c></seealso>, + <seealso marker="error_logger"><c>error_logger(3)</c></seealso>, + <seealso marker="file"><c>file(3)</c></seealso>, + <seealso marker="global"><c>global(3)</c></seealso>, + <seealso marker="global_group"><c>global_group(3)</c></seealso>, + <seealso marker="heart"><c>heart(3)</c></seealso>, + <seealso marker="inet"><c>inet(3)</c></seealso>, + <seealso marker="net_kernel"><c>net_kernel(3)</c></seealso>, + <seealso marker="os"><c>os(3)</c></seealso>, + <seealso marker="pg2"><c>pg2(3)</c></seealso>, + <seealso marker="rpc"><c>rpc(3)</c></seealso>, + <seealso marker="seq_trace"><c>seq_trace(3)</c></seealso>, + <seealso marker="user"><c>user(3)</c></seealso>, + <seealso marker="stdlib:timer"><c>timer(3)</c></seealso></p> </section> </appref> diff --git a/lib/kernel/doc/src/net_adm.xml b/lib/kernel/doc/src/net_adm.xml index 11712f4b42..6957a3b5e4 100644 --- a/lib/kernel/doc/src/net_adm.xml +++ b/lib/kernel/doc/src/net_adm.xml @@ -25,95 +25,105 @@ <title>net_adm</title> <prepared>Claes Wikstrom</prepared> <docno>1</docno> - <date>96-09-10</date> + <date>1996-09-10</date> <rev>A</rev> </header> <module>net_adm</module> - <modulesummary>Various Erlang Net Administration Routines</modulesummary> + <modulesummary>Various Erlang net administration routines.</modulesummary> <description> <p>This module contains various network utility functions.</p> </description> + <funcs> <func> <name name="dns_hostname" arity="1"/> - <fsummary>Official name of a host</fsummary> + <fsummary>Official name of a host.</fsummary> <desc> <p>Returns the official name of <c><anno>Host</anno></c>, or <c>{error, <anno>Host</anno>}</c> if no such name is found. See also - <c>inet(3)</c>.</p> + <seealso marker="inet"><c>inet(3)</c></seealso>.</p> </desc> </func> + <func> <name name="host_file" arity="0"/> - <fsummary>Read the <c>.hosts.erlang</c>file</fsummary> + <fsummary>Read file <c>.hosts.erlang</c>.</fsummary> <desc> - <p>Reads the <c>.hosts.erlang</c> file, see the section - <em>Files</em> below. Returns the hosts in this file as a - list, or returns <c>{error, <anno>Reason</anno>}</c> if the file could not - be read or the Erlang terms on the file could not be interpreted.</p> + <p>Reads file <c>.hosts.erlang</c>, see section + <seealso marker="#files">Files</seealso>. Returns the hosts in this + file as a list. Returns <c>{error, <anno>Reason</anno>}</c> if the + file cannot be read or the Erlang terms on the file cannot be + interpreted.</p> </desc> </func> + <func> <name name="localhost" arity="0"/> - <fsummary>Name of the local host</fsummary> + <fsummary>Name of the local host.</fsummary> <desc> <p>Returns the name of the local host. If Erlang was started - with the <c>-name</c> command line flag, <c><anno>Name</anno></c> is + with command-line flag <c>-name</c>, <c><anno>Name</anno></c> is the fully qualified name.</p> </desc> </func> + <func> <name name="names" arity="0"/> <name name="names" arity="1"/> - <fsummary>Names of Erlang nodes at a host</fsummary> + <fsummary>Names of Erlang nodes at a host.</fsummary> <desc> - <p>Similar to <c>epmd -names</c>, see <c>epmd(1)</c>. - <c><anno>Host</anno></c> defaults to the local host. Returns the names and - associated port numbers of the Erlang nodes that <c>epmd</c> - at the specified host has registered.</p> - <p>Returns <c>{error, address}</c> if <c>epmd</c> is not - running.</p> + <p>Similar to <c>epmd -names</c>, see + <seealso marker="erts:epmd"><c>erts:epmd(1)</c></seealso>. + <c><anno>Host</anno></c> defaults to the local host. Returns the + names and associated port numbers of the Erlang nodes that + <c>epmd</c> registered at the specified host. Returns + <c>{error, address}</c> if <c>epmd</c> is not operational.</p> + <p><em>Example:</em></p> <pre> (arne@dunn)1> <input>net_adm:names().</input> {ok,[{"arne",40262}]}</pre> </desc> </func> + <func> <name name="ping" arity="1"/> - <fsummary>Set up a connection to a node</fsummary> + <fsummary>Set up a connection to a node.</fsummary> <desc> - <p>Tries to set up a connection to <c><anno>Node</anno></c>. Returns - <c>pang</c> if it fails, or <c>pong</c> if it is successful.</p> + <p>Sets up a connection to <c><anno>Node</anno></c>. Returns + <c>pong</c> if it is successful, otherwise <c>pang</c>.</p> </desc> </func> + <func> <name name="world" arity="0"/> <name name="world" arity="1"/> - <fsummary>Lookup and connect to all nodes at all hosts in <c>.hosts.erlang</c></fsummary> + <fsummary>Lookup and connect to all nodes at all hosts in + <c>.hosts.erlang</c>.</fsummary> <type name="verbosity"/> <desc> - <p>This function calls <c>names(Host)</c> for all hosts which + <p>Calls <c>names(Host)</c> for all hosts that are specified in the Erlang host file <c>.hosts.erlang</c>, - collects the replies and then evaluates <c>ping(Node)</c> on - all those nodes. Returns the list of all nodes that were, - successfully pinged.</p> + collects the replies, and then evaluates <c>ping(Node)</c> on + all those nodes. Returns the list of all nodes that are + successfully pinged.</p> <p><c><anno>Arg</anno></c> defaults to <c>silent</c>. - If <c><anno>Arg</anno> == verbose</c>, the function writes information about which - nodes it is pinging to stdout.</p> + If <c><anno>Arg</anno> == verbose</c>, the function writes + information about which nodes it is pinging to <c>stdout</c>.</p> <p>This function can be useful when a node is started, and - the names of the other nodes in the network are not initially - known.</p> - <p>Failure: <c>{error, Reason}</c> if <c>host_file()</c> + the names of the other network nodes are not initially known.</p> + <p>Returns <c>{error, Reason}</c> if <c>host_file()</c> returns <c>{error, Reason}</c>.</p> </desc> </func> + <func> <name name="world_list" arity="1"/> <name name="world_list" arity="2"/> - <fsummary>Lookup and connect to all nodes at specified hosts</fsummary> + <fsummary>Lookup and connect to all nodes at specified hosts.</fsummary> <type name="verbosity"/> <desc> - <p>As <c>world/0,1</c>, but the hosts are given as argument + <p>Same as <seealso marker="#world/1"><c>world/0,1</c></seealso>, + but the hosts are specified as argument instead of being read from <c>.hosts.erlang</c>.</p> </desc> </func> @@ -121,13 +131,14 @@ <section> <title>Files</title> - <p>The <c>.hosts.erlang</c> file consists of a number of host names + <marker id="files"/> + <p>File <c>.hosts.erlang</c> consists of a number of host names written as Erlang terms. It is looked for in the current work directory, the user's home directory, and <c>$OTP_ROOT</c> (the root directory of Erlang/OTP), in that order.</p> - <p>The format of the <c>.hosts.erlang</c> file must be one host - name per line. The host names must be within quotes as shown in - the following example:</p> + <p>The format of file <c>.hosts.erlang</c> must be one host + name per line. The host names must be within quotes.</p> + <p><em>Example:</em></p> <pre> 'super.eua.ericsson.se'. 'renat.eua.ericsson.se'. diff --git a/lib/kernel/doc/src/net_kernel.xml b/lib/kernel/doc/src/net_kernel.xml index 97b20fe94f..f48a534d4f 100644 --- a/lib/kernel/doc/src/net_kernel.xml +++ b/lib/kernel/doc/src/net_kernel.xml @@ -25,41 +25,47 @@ <title>net_kernel</title> <prepared>Claes Wikstrom</prepared> <docno>1</docno> - <date>96-09-10</date> + <date>1996-09-10</date> <rev>A</rev> </header> <module>net_kernel</module> - <modulesummary>Erlang Networking Kernel</modulesummary> + <modulesummary>Erlang networking kernel.</modulesummary> <description> <p>The net kernel is a system process, registered as - <c>net_kernel</c>, which must be running for distributed Erlang + <c>net_kernel</c>, which must be operational for distributed Erlang to work. The purpose of this process is to implement parts of the BIFs <c>spawn/4</c> and <c>spawn_link/4</c>, and to provide monitoring of the network.</p> - <p>An Erlang node is started using the command line flag + <p>An Erlang node is started using command-line flag <c>-name</c> or <c>-sname</c>:</p> -<pre>$ <input>erl -sname foobar</input></pre> + <pre> +$ <input>erl -sname foobar</input></pre> <p>It is also possible to call <c>net_kernel:start([foobar])</c> directly from the normal Erlang shell prompt:</p> -<pre>1> <input>net_kernel:start([foobar, shortnames]).</input> + <pre> +1> <input>net_kernel:start([foobar, shortnames]).</input> {ok,<0.64.0>} (foobar@gringotts)2></pre> - <p>If the node is started with the command line flag <c>-sname</c>, - the node name will be <c>foobar@Host</c>, where <c>Host</c> is + <p>If the node is started with command-line flag <c>-sname</c>, + the node name is <c>foobar@Host</c>, where <c>Host</c> is the short name of the host (not the fully qualified domain name). - If started with the <c>-name</c> flag, <c>Host</c> is the fully - qualified domain name. See <c>erl(1)</c>.</p> + If started with flag <c>-name</c>, the node name is <c>foobar@Host</c>, + where <c>Host</c> is the fully qualified domain name. + For more information, see + <seealso marker="erts:erl"><c>erl</c></seealso>.</p> <p>Normally, connections are established automatically when another node is referenced. This functionality can be disabled - by setting the Kernel configuration parameter + by setting <c>Kernel</c> configuration parameter <c>dist_auto_connect</c> to <c>false</c>, see - <seealso marker="kernel_app">kernel(6)</seealso>. In this case, + <seealso marker="kernel_app"><c>kernel(6)</c></seealso>. In this case, connections must be established explicitly by calling - <c>net_kernel:connect_node/1</c>.</p> - <p>Which nodes are allowed to communicate with each other is handled - by the magic cookie system, see - <seealso marker="doc/reference_manual:distributed">Distributed Erlang</seealso> in the Erlang Reference Manual.</p> + <seealso marker="#connect_node/1"><c>connect_node/1</c></seealso>.</p> + <p>Which nodes that are allowed to communicate with each other is handled + by the magic cookie system, see section + <seealso marker="doc/reference_manual:distributed">Distributed Erlang</seealso> + in the Erlang Reference Manual.</p> </description> + <funcs> <func> <name name="allow" arity="1"/> @@ -77,237 +83,240 @@ an atom.</p> </desc> </func> + <func> <name name="connect_node" arity="1"/> - <fsummary>Establish a connection to a node</fsummary> + <fsummary>Establish a connection to a node.</fsummary> <desc> - <p>Establishes a connection to <c><anno>Node</anno></c>. Returns <c>true</c> - if successful, <c>false</c> if not, and <c>ignored</c> if - the local node is not alive.</p> + <p>Establishes a connection to <c><anno>Node</anno></c>. Returns + <c>true</c> if successful, <c>false</c> if not, and <c>ignored</c> + if the local node is not alive.</p> + </desc> + </func> + + <func> + <name name="get_net_ticktime" arity="0"/> + <fsummary>Get <c>net_ticktime</c>.</fsummary> + <desc> + <p>Gets <c>net_ticktime</c> (see + <seealso marker="kernel_app"><c>kernel(6)</c></seealso>).</p> + <p>Defined return values (<c><anno>Res</anno></c>):</p> + <taglist> + <tag><c><anno>NetTicktime</anno></c></tag> + <item><p><c>net_ticktime</c> is <c><anno>NetTicktime</anno></c> + seconds.</p></item> + <tag><c>{ongoing_change_to, <anno>NetTicktime</anno>}</c></tag> + <item><p><c>net_kernel</c> is currently changing + <c>net_ticktime</c> to <c><anno>NetTicktime</anno></c> + seconds.</p></item> + <tag><c>ignored</c></tag> + <item><p>The local node is not alive.</p></item> + </taglist> </desc> </func> + <func> <name name="monitor_nodes" arity="1"/> <name name="monitor_nodes" arity="2"/> - <fsummary>Subscribe to node status change messages</fsummary> + <fsummary>Subscribe to node status change messages.</fsummary> <desc> <p>The calling process subscribes or unsubscribes to node status change messages. A <c>nodeup</c> message is delivered - to all subscribing process when a new node is connected, and + to all subscribing processes when a new node is connected, and a <c>nodedown</c> message is delivered when a node is disconnected.</p> - <p>If <c><anno>Flag</anno></c> is <c>true</c>, a new subscription is started. - If <c><anno>Flag</anno></c> is <c>false</c>, all previous subscriptions -- - started with the same <c><anno>Options</anno></c> -- are stopped. Two + <p>If <c><anno>Flag</anno></c> is <c>true</c>, a new subscription is + started. If <c><anno>Flag</anno></c> is <c>false</c>, all previous + subscriptions started with the same <c><anno>Options</anno></c> + are stopped. Two option lists are considered the same if they contain the same set of options.</p> - <p>As of <c>kernel</c> version 2.11.4, and <c>erts</c> version + <p>As from <c>Kernel</c> version 2.11.4, and <c>ERTS</c> version 5.5.4, the following is guaranteed:</p> <list type="bulleted"> - <item><c>nodeup</c> messages will be delivered before delivery - of any message from the remote node passed through the - newly established connection.</item> - <item><c>nodedown</c> messages will not be delivered until all - messages from the remote node that have been passed - through the connection have been delivered.</item> + <item><p><c>nodeup</c> messages are delivered before delivery + of any message from the remote node passed through the + newly established connection.</p></item> + <item><p><c>nodedown</c> messages are not delivered until all + messages from the remote node that have been passed + through the connection have been delivered.</p></item> </list> - <p>Note, that this is <em>not</em> guaranteed for <c>kernel</c> + <p>Notice that this is <em>not</em> guaranteed for <c>Kernel</c> versions before 2.11.4.</p> - <p>As of <c>kernel</c> version 2.11.4 subscriptions can also be - made before the <c>net_kernel</c> server has been started, - i.e., <c>net_kernel:monitor_nodes/[1,2]</c> does not return + <p>As from <c>Kernel</c> version 2.11.4, subscriptions can also be + made before the <c>net_kernel</c> server is started, that is, + <c>net_kernel:monitor_nodes/[1,2]</c> does not return <c>ignored</c>.</p> - <p>As of <c>kernel</c> version 2.13, and <c>erts</c> version + <p>As from <c>Kernel</c> version 2.13, and <c>ERTS</c> version 5.7, the following is guaranteed:</p> <list type="bulleted"> - <item><c>nodeup</c> messages will be delivered after the - corresponding node appears in results from - <c>erlang:nodes/X</c>.</item> - <item><c>nodedown</c> messages will be delivered after the - corresponding node has disappeared in results from - <c>erlang:nodes/X</c>.</item> + <item><p><c>nodeup</c> messages are delivered after the + corresponding node appears in results from + <c>erlang:nodes/X</c>.</p></item> + <item><p><c>nodedown</c> messages are delivered after the + corresponding node has disappeared in results from + <c>erlang:nodes/X</c>.</p></item> </list> - <p>Note, that this is <em>not</em> guaranteed for <c>kernel</c> + <p>Notice that this is <em>not</em> guaranteed for <c>Kernel</c> versions before 2.13.</p> <p>The format of the node status change messages depends on - <c><anno>Options</anno></c>. If <c><anno>Options</anno></c> is [], which is the default, - the format is:</p> + <c><anno>Options</anno></c>. If <c><anno>Options</anno></c> is + <c>[]</c>, which is the default, the format is as follows:</p> <code type="none"> {nodeup, Node} | {nodedown, Node} Node = node()</code> - <p>If <c><anno>Options</anno> /= []</c>, the format is:</p> + <p>If <c><anno>Options</anno></c> is not <c>[]</c>, the format is + as follows:</p> <code type="none"> {nodeup, Node, InfoList} | {nodedown, Node, InfoList} Node = node() InfoList = [{Tag, Val}]</code> <p><c>InfoList</c> is a list of tuples. Its contents depends on <c><anno>Options</anno></c>, see below.</p> - <p>Also, when <c>OptionList == []</c> only visible nodes, that + <p>Also, when <c>OptionList == []</c>, only visible nodes, that is, nodes that appear in the result of - <seealso marker="erts:erlang#nodes/0">nodes/0</seealso>, are - monitored.</p> + <seealso marker="erts:erlang#nodes/0"><c>erlang:nodes/0</c></seealso>, + are monitored.</p> <p><c><anno>Option</anno></c> can be any of the following:</p> <taglist> <tag><c>{node_type, NodeType}</c></tag> <item> - <p>Currently valid values for <c>NodeType</c> are:</p> + <p>Valid values for <c>NodeType</c>:</p> <taglist> <tag><c>visible</c></tag> - <item>Subscribe to node status change messages for visible + <item><p>Subscribe to node status change messages for visible nodes only. The tuple <c>{node_type, visible}</c> is - included in <c>InfoList</c>.</item> + included in <c>InfoList</c>.</p></item> <tag><c>hidden</c></tag> - <item>Subscribe to node status change messages for hidden + <item><p>Subscribe to node status change messages for hidden nodes only. The tuple <c>{node_type, hidden}</c> is - included in <c>InfoList</c>.</item> + included in <c>InfoList</c>.</p></item> <tag><c>all</c></tag> - <item>Subscribe to node status change messages for both + <item><p>Subscribe to node status change messages for both visible and hidden nodes. The tuple - <c>{node_type, visible | hidden}</c> is included in - <c>InfoList</c>.</item> + <c>{node_type, visible | hidden}</c> is included in + <c>InfoList</c>.</p></item> </taglist> </item> <tag><c>nodedown_reason</c></tag> <item> <p>The tuple <c>{nodedown_reason, Reason}</c> is included in - <c>InfoList</c> in <c>nodedown</c> messages. <c>Reason</c> - can be:</p> + <c>InfoList</c> in <c>nodedown</c> messages.</p> + <p><c>Reason</c> can be any of the following:</p> <taglist> <tag><c>connection_setup_failed</c></tag> - <item>The connection setup failed (after <c>nodeup</c> - messages had been sent).</item> + <item><p>The connection setup failed (after <c>nodeup</c> + messages were sent).</p></item> <tag><c>no_network</c></tag> - <item>No network available.</item> + <item><p>No network is available.</p></item> <tag><c>net_kernel_terminated</c></tag> - <item>The <c>net_kernel</c> process terminated.</item> + <item><p>The <c>net_kernel</c> process terminated.</p></item> <tag><c>shutdown</c></tag> - <item>Unspecified connection shutdown.</item> + <item><p>Unspecified connection shutdown.</p></item> <tag><c>connection_closed</c></tag> - <item>The connection was closed.</item> + <item><p>The connection was closed.</p></item> <tag><c>disconnect</c></tag> - <item>The connection was disconnected (forced from the - current node).</item> + <item><p>The connection was disconnected (forced from the + current node).</p></item> <tag><c>net_tick_timeout</c></tag> - <item>Net tick timeout.</item> + <item><p>Net tick time-out.</p></item> <tag><c>send_net_tick_failed</c></tag> - <item>Failed to send net tick over the connection.</item> + <item><p>Failed to send net tick over the connection.</p></item> <tag><c>get_status_failed</c></tag> - <item>Status information retrieval from the <c>Port</c> - holding the connection failed.</item> + <item><p>Status information retrieval from the <c>Port</c> + holding the connection failed.</p></item> </taglist> </item> </taglist> </desc> </func> - <func> - <name name="get_net_ticktime" arity="0"/> - <fsummary>Get <c>net_ticktime</c></fsummary> - <desc> - <p>Gets <c>net_ticktime</c> (see - <seealso marker="kernel_app">kernel(6)</seealso>).</p> - <p>Currently defined return values (<c><anno>Res</anno></c>):</p> - <taglist> - <tag><c><anno>NetTicktime</anno></c></tag> - <item> - <p><c>net_ticktime</c> is <c><anno>NetTicktime</anno></c> seconds.</p> - </item> - <tag><c>{ongoing_change_to, <anno>NetTicktime</anno>}</c></tag> - <item> - <p><c>net_kernel</c> is currently changing - <c>net_ticktime</c> to <c><anno>NetTicktime</anno></c> seconds.</p> - </item> - <tag><c>ignored</c></tag> - <item> - <p>The local node is not alive.</p> - </item> - </taglist> - </desc> - </func> + <func> <name name="set_net_ticktime" arity="1"/> <name name="set_net_ticktime" arity="2"/> - <fsummary>Set <c>net_ticktime</c></fsummary> + <fsummary>Set <c>net_ticktime</c>.</fsummary> <desc> <p>Sets <c>net_ticktime</c> (see - <seealso marker="kernel_app">kernel(6)</seealso>) to - <c><anno>NetTicktime</anno></c> seconds. <c><anno>TransitionPeriod</anno></c> defaults - to 60.</p> + <seealso marker="kernel_app"><c>kernel(6)</c></seealso>) to + <c><anno>NetTicktime</anno></c> seconds. + <c><anno>TransitionPeriod</anno></c> defaults to <c>60</c>.</p> <p>Some definitions:</p> <taglist> - <tag>The minimum transition traffic interval (<c>MTTI</c>)</tag> - <item> - <p><c>minimum(<anno>NetTicktime</anno>, PreviousNetTicktime)*1000 div 4</c> milliseconds.</p> - </item> - <tag>The transition period</tag> - <item> - <p>The time of the least number of consecutive <c>MTTI</c>s - to cover <c><anno>TransitionPeriod</anno></c> seconds following - the call to <c>set_net_ticktime/2</c> (i.e. - ((<c><anno>TransitionPeriod</anno>*1000 - 1) div MTTI + 1)*MTTI</c> - milliseconds).</p> - </item> + <tag>Minimum transition traffic interval (<c>MTTI</c>)</tag> + <item><p><c>minimum(<anno>NetTicktime</anno>, + PreviousNetTicktime)*1000 div 4</c> milliseconds.</p></item> + <tag>Transition period</tag> + <item><p>The time of the least number of consecutive <c>MTTI</c>s + to cover <c><anno>TransitionPeriod</anno></c> seconds following + the call to <c>set_net_ticktime/2</c> (that is, + ((<c><anno>TransitionPeriod</anno>*1000 - 1) div MTTI + 1)*MTTI</c> + milliseconds).</p></item> </taglist> - <p>If <c><![CDATA[<anno>NetTicktime</anno> < PreviousNetTicktime]]></c>, the actual - <c>net_ticktime</c> change will be done at the end of - the transition period; otherwise, at the beginning. During - the transition period, <c>net_kernel</c> will ensure that - there will be outgoing traffic on all connections at least + <p>If + <c><![CDATA[NetTicktime < PreviousNetTicktime]]></c>, + the <c>net_ticktime</c> change is done at the end of + the transition period; otherwise at the beginning. During + the transition period, <c>net_kernel</c> ensures that + there is outgoing traffic on all connections at least every <c>MTTI</c> millisecond.</p> <note> - <p>The <c>net_ticktime</c> changes have to be initiated on all + <p>The <c>net_ticktime</c> changes must be initiated on all nodes in the network (with the same <c><anno>NetTicktime</anno></c>) before the end of any transition period on any node; - otherwise, connections may erroneously be disconnected.</p> + otherwise connections can erroneously be disconnected.</p> </note> <p>Returns one of the following:</p> <taglist> <tag><c>unchanged</c></tag> <item> - <p><c>net_ticktime</c> already had the value of - <c><anno>NetTicktime</anno></c> and was left unchanged.</p> + <p><c>net_ticktime</c> already has the value of + <c><anno>NetTicktime</anno></c> and is left unchanged.</p> </item> <tag><c>change_initiated</c></tag> <item> - <p><c>net_kernel</c> has initiated the change of - <c>net_ticktime</c> to <c><anno>NetTicktime</anno></c> seconds.</p> + <p><c>net_kernel</c> initiated the change of + <c>net_ticktime</c> to <c><anno>NetTicktime</anno></c> + seconds.</p> </item> <tag><c>{ongoing_change_to, <anno>NewNetTicktime</anno>}</c></tag> <item> - <p>The request was <em>ignored</em>; because, - <c>net_kernel</c> was busy changing <c>net_ticktime</c> to + <p>The request is <em>ignored</em> because + <c>net_kernel</c> is busy changing <c>net_ticktime</c> to <c><anno>NewNetTicktime</anno></c> seconds.</p> </item> </taglist> </desc> </func> + <func> <name>start([Name]) -> {ok, pid()} | {error, Reason}</name> <name>start([Name, NameType]) -> {ok, pid()} | {error, Reason}</name> <name>start([Name, NameType, Ticktime]) -> {ok, pid()} | {error, Reason}</name> - <fsummary>Turn an Erlang runtime system into a distributed node</fsummary> + <fsummary>Turn an Erlang runtime system into a distributed node.</fsummary> <type> <v>Name = atom()</v> <v>NameType = shortnames | longnames</v> <v>Reason = {already_started, pid()} | term()</v> </type> <desc> - <p>Note that the argument is a list with exactly one, two or - three arguments. <c>NameType</c> defaults to <c>longnames</c> - and <c>Ticktime</c> to 15000.</p> <p>Turns a non-distributed node into a distributed node by starting <c>net_kernel</c> and other necessary processes.</p> + <p>Notice that the argument is a list with exactly one, two, or + three arguments. <c>NameType</c> defaults to <c>longnames</c> + and <c>Ticktime</c> to <c>15000</c>.</p> </desc> </func> + <func> <name name="stop" arity="0"/> - <fsummary>Turn a node into a non-distributed Erlang runtime system</fsummary> + <fsummary>Turn a node into a non-distributed Erlang runtime system.</fsummary> <desc> <p>Turns a distributed node into a non-distributed node. For other nodes in the network, this is the same as the node - going down. Only possible when the net kernel was started - using <c>start/1</c>, otherwise returns - <c>{error, not_allowed}</c>. Returns <c>{error, not_found}</c> - if the local node is not alive.</p> + going down. Only possible when the net kernel was started using + <seealso marker="#start/1"><c>start/1</c></seealso>, + otherwise <c>{error, not_allowed}</c> is returned. Returns + <c>{error, not_found}</c> if the local node is not alive.</p> </desc> </func> </funcs> diff --git a/lib/kernel/doc/src/os.xml b/lib/kernel/doc/src/os.xml index 079254bb93..739ac35d2a 100644 --- a/lib/kernel/doc/src/os.xml +++ b/lib/kernel/doc/src/os.xml @@ -29,98 +29,107 @@ <rev></rev> </header> <module>os</module> - <modulesummary>Operating System Specific Functions</modulesummary> + <modulesummary>Operating system-specific functions.</modulesummary> <description> - <p>The functions in this module are operating system specific. - Careless use of these functions will result in programs that will + <p>The functions in this module are operating system-specific. + Careless use of these functions results in programs that will only run on a specific platform. On the other hand, with careful - use these functions can be of help in enabling a program to run on + use, these functions can be of help in enabling a program to run on most platforms.</p> </description> <funcs> <func> <name name="cmd" arity="1"/> - <fsummary>Execute a command in a shell of the target OS</fsummary> + <fsummary>Execute a command in a shell of the target OS.</fsummary> <desc> - <p>Executes <c><anno>Command</anno></c> in a command shell of the target OS, - captures the standard output of the command and returns this + <p>Executes <c><anno>Command</anno></c> in a command shell of the + target OS, + captures the standard output of the command, and returns this result as a string. This function is a replacement of - the previous <c>unix:cmd/1</c>; on a Unix platform they are - equivalent.</p> - <p>Examples:</p> + the previous function <c>unix:cmd/1</c>; they are equivalent on a + Unix platform.</p> + <p><em>Examples:</em></p> <code type="none"> LsOut = os:cmd("ls"), % on unix platform DirOut = os:cmd("dir"), % on Win32 platform</code> - <p>Note that in some cases, standard output of a command when + <p>Notice that in some cases, standard output of a command when called from another program (for example, <c>os:cmd/1</c>) - may differ, compared to the standard output of the command + can differ, compared with the standard output of the command when called directly from an OS command shell.</p> </desc> </func> + <func> <name name="find_executable" arity="1"/> <name name="find_executable" arity="2"/> - <fsummary>Absolute filename of a program</fsummary> + <fsummary>Absolute filename of a program.</fsummary> <desc> - <p>These two functions look up an executable program given its - name and a search path, in the same way as the underlying - operating system. <c>find_executable/1</c> uses the current - execution path (that is, the environment variable PATH on + <p>These two functions look up an executable program, with the + specified name and a search path, in the same way as the underlying + OS. <c>find_executable/1</c> uses the current + execution path (that is, the environment variable <c>PATH</c> on Unix and Windows).</p> - <p><c><anno>Path</anno></c>, if given, should conform to the syntax of - execution paths on the operating system. The absolute - filename of the executable program <c><anno>Name</anno></c> is returned, - or <c>false</c> if the program was not found.</p> + <p><c><anno>Path</anno></c>, if specified, is to conform to the syntax + of execution paths on the OS. Returns the absolute filename of the + executable program <c><anno>Name</anno></c>, + or <c>false</c> if the program is not found.</p> </desc> </func> + <func> <name name="getenv" arity="0"/> - <fsummary>List all environment variables</fsummary> + <fsummary>List all environment variables.</fsummary> <desc> <p>Returns a list of all environment variables. - Each environment variable is given as a single string on + Each environment variable is expressed 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> + <p>If Unicode filename encoding is in effect (see the + <seealso marker="erts:erl#file_name_encoding"><c>erl</c> manual + page</seealso>), the strings can contain characters with + codepoints > 255.</p> </desc> </func> + <func> <name name="getenv" arity="1"/> - <fsummary>Get the value of an environment variable</fsummary> + <fsummary>Get the value of an environment variable.</fsummary> <desc> <p>Returns the <c><anno>Value</anno></c> of the environment variable - <c><anno>VarName</anno></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><anno>VarName</anno></c> and - <c><anno>Value</anno></c>) may contain characters with codepoints > 255.</p> + <c><anno>VarName</anno></c>, or <c>false</c> if the environment + variable is undefined.</p> + <p>If Unicode filename encoding is in effect (see the + <seealso marker="erts:erl#file_name_encoding"><c>erl</c> manual + page</seealso>), the strings <c><anno>VarName</anno></c> and + <c><anno>Value</anno></c> can contain characters with + codepoints > 255.</p> </desc> </func> + <func> <name name="getenv" arity="2"/> - <fsummary>Get the value of an environment variable</fsummary> + <fsummary>Get the value of an environment variable.</fsummary> <desc> <p>Returns the <c><anno>Value</anno></c> of the environment variable - <c><anno>VarName</anno></c>, or <c>DefaultValue</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><anno>VarName</anno></c> and - <c><anno>Value</anno></c>) may contain characters with codepoints > 255.</p> + <c><anno>VarName</anno></c>, or <c>DefaultValue</c> if the + environment variable is undefined.</p> + <p>If Unicode filename encoding is in effect (see the + <seealso marker="erts:erl#file_name_encoding"><c>erl</c> manual + page</seealso>), the strings <c><anno>VarName</anno></c> and + <c><anno>Value</anno></c> can contain characters with + codepoints > 255.</p> </desc> </func> + <func> <name name="getpid" arity="0"/> - <fsummary>Return the process identifier of the emulator process</fsummary> + <fsummary>Return the process identifier of the emulator + process.</fsummary> <desc> <p>Returns the process identifier of the current Erlang emulator - in the format most commonly used by the operating system - environment. <c><anno>Value</anno></c> is returned as a string containing + in the format most commonly used by the OS environment. + Returns <c><anno>Value</anno></c> as a string containing the (usually) numerical identifier for a process. On Unix, this is typically the return value of the <c>getpid()</c> system call. On Windows, @@ -128,88 +137,93 @@ DirOut = os:cmd("dir"), % on Win32 platform</code> system call is used.</p> </desc> </func> + <func> <name name="putenv" arity="2"/> - <fsummary>Set a new value for an environment variable</fsummary> + <fsummary>Set a new value for an environment variable.</fsummary> <desc> - <p>Sets a new <c><anno>Value</anno></c> for the environment variable + <p>Sets a new <c><anno>Value</anno></c> for environment variable <c><anno>VarName</anno></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><anno>VarName</anno></c> and - <c><anno>Value</anno></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> + <p>If Unicode filename encoding is in effect (see the + <seealso marker="erts:erl#file_name_encoding"><c>erl</c> manual + page</seealso>), the strings <c><anno>VarName</anno></c> and + <c><anno>Value</anno></c> can contain characters with + codepoints > 255.</p> + <p>On Unix platforms, the environment is set using UTF-8 encoding + if Unicode filename translation is in effect. On Windows, the + environment is set using wide character interfaces.</p> </desc> </func> + <func> <name name="system_time" arity="0"/> - <fsummary>Current OS system time</fsummary> + <fsummary>Current OS system time.</fsummary> <desc> - <p>Returns current + <p>Returns the current <seealso marker="erts:time_correction#OS_System_Time">OS system time</seealso> in <c>native</c> <seealso marker="erts:erlang#type_time_unit">time unit</seealso>.</p> - - <note><p>This time is <em>not</em> a monotonically increasing time.</p></note> + <note><p>This time is <em>not</em> a monotonically increasing time.</p> + </note> </desc> </func> + <func> <name name="system_time" arity="1"/> - <fsummary>Current OS system time</fsummary> + <fsummary>Current OS system time.</fsummary> <desc> - <p>Returns current + <p>Returns the current <seealso marker="erts:time_correction#OS_System_Time">OS system time</seealso> converted into the <c><anno>Unit</anno></c> passed as argument.</p> - - <p>Calling <c>os:system_time(<anno>Unit</anno>)</c> is equivalent to: - <seealso marker="erts:erlang#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso><c>(</c><seealso marker="#system_time/0"><c>os:system_time()</c></seealso><c>, + <p>Calling <c>os:system_time(<anno>Unit</anno>)</c> is equivalent to + <seealso marker="erts:erlang#convert_time_unit/3"><c>erlang:convert_time_unit</c></seealso>(<seealso marker="#system_time/0"><c>os:system_time()</c></seealso><c>, native, <anno>Unit</anno>)</c>.</p> - - <note><p>This time is <em>not</em> a monotonically increasing time.</p></note> + <note><p>This time is <em>not</em> a monotonically increasing time.</p> + </note> </desc> </func> + <func> <name name="timestamp" arity="0"/> - <fsummary>Current OS system time on the erlang:timestamp/0 format</fsummary> + <fsummary>Current OS system time on the <c>erlang:timestamp/0</c> format.</fsummary> <type_desc variable="Timestamp">Timestamp = {MegaSecs, Secs, MicroSecs}</type_desc> <desc> - <p>Returns current + <p>Returns the current <seealso marker="erts:time_correction#OS_System_Time">OS system time</seealso> - in the same format as <seealso marker="erts:erlang#timestamp/0">erlang:timestamp/0</seealso>. - The tuple can be used together with the function - <seealso marker="stdlib:calendar#now_to_universal_time/1">calendar:now_to_universal_time/1</seealso> - or <seealso marker="stdlib:calendar#now_to_local_time/1">calendar:now_to_local_time/1</seealso> to - get calendar time. Using the calendar time together with the <c>MicroSecs</c> part of the return - tuple from this function allows you to log timestamps in high resolution and consistent with the - time in the rest of the operating system.</p> - <p>Example of code formatting a string in the format "DD Mon YYYY HH:MM:SS.mmmmmm", where - DD is the day of month, Mon is the textual month name, YYYY is the year, HH:MM:SS is the time and - mmmmmm is the microseconds in six positions:</p> -<code> + in the same format as + <seealso marker="erts:erlang#timestamp/0"><c>erlang:timestamp/0</c></seealso>. + The tuple can be used together with function + <seealso marker="stdlib:calendar#now_to_universal_time/1"><c>calendar:now_to_universal_time/1</c></seealso> + or <seealso marker="stdlib:calendar#now_to_local_time/1"><c>calendar:now_to_local_time/1</c></seealso> + to get calendar time. Using the calendar time, together with the + <c>MicroSecs</c> part of the return tuple from this function, allows + you to log time stamps in high resolution and consistent with the + time in the rest of the OS.</p> + <p>Example of code formatting a string in format + "DD Mon YYYY HH:MM:SS.mmmmmm", where DD is the day of month, + Mon is the textual month name, YYYY is the year, HH:MM:SS is the time, + and mmmmmm is the microseconds in six positions:</p> + <code> -module(print_time). -export([format_utc_timestamp/0]). format_utc_timestamp() -> TS = {_,_,Micro} = os:timestamp(), - {{Year,Month,Day},{Hour,Minute,Second}} = - calendar:now_to_universal_time(TS), + {{Year,Month,Day},{Hour,Minute,Second}} = +calendar:now_to_universal_time(TS), Mstr = element(Month,{"Jan","Feb","Mar","Apr","May","Jun","Jul", - "Aug","Sep","Oct","Nov","Dec"}), + "Aug","Sep","Oct","Nov","Dec"}), io_lib:format("~2w ~s ~4w ~2w:~2..0w:~2..0w.~6..0w", - [Day,Mstr,Year,Hour,Minute,Second,Micro]). -</code> - - <p>The module above could be used in the following way:</p> -<pre> + [Day,Mstr,Year,Hour,Minute,Second,Micro]).</code> + <p>This module can be used as follows:</p> + <pre> 1> <input>io:format("~s~n",[print_time:format_utc_timestamp()]).</input> -29 Apr 2009 9:55:30.051711 -</pre> +29 Apr 2009 9:55:30.051711</pre> <p>OS system time can also be retreived by - <seealso marker="#system_time/0"><c>os:system_time/0</c></seealso>, - and <seealso marker="#system_time/1"><c>os:system_time/1</c></seealso>.</p> + <seealso marker="#system_time/0"><c>system_time/0</c></seealso> and + <seealso marker="#system_time/1"><c>system_time/1</c></seealso>.</p> </desc> </func> + <func> <name name="perf_counter" arity="0"/> <fsummary>Returns a performance counter</fsummary> @@ -227,7 +241,7 @@ format_utc_timestamp() -> high resolution timestamp. This counter is read directly from the hardware or operating system with the same guarantees. This means that two consecutive calls to the function are not guaranteed to be monotonic, though it most likely will be. - The performance counter till be converted to the resolution passed as an argument.</p> + The performance counter will be converted to the resolution passed as an argument.</p> <pre>1> <input>T1 = os:perf_counter(1000),receive after 10000 -> ok end,T2 = os:perf_counter(1000).</input> 176525861 2> <input>T2 - T1.</input> @@ -236,41 +250,43 @@ format_utc_timestamp() -> </func> <func> <name name="type" arity="0"/> - <fsummary>Return the OS family and, in some cases, OS name of the current operating system</fsummary> + <fsummary>Return the OS family and, in some cases, the OS name of the + current OS.</fsummary> <desc> - <p>Returns the <c><anno>Osfamily</anno></c> and, in some cases, <c><anno>Osname</anno></c> - of the current operating system.</p> - <p>On Unix, <c><anno>Osname</anno></c> will have same value as + <p>Returns the <c><anno>Osfamily</anno></c> and, in some cases, the + <c><anno>Osname</anno></c> of the current OS.</p> + <p>On Unix, <c><anno>Osname</anno></c> has the same value as <c>uname -s</c> returns, but in lower case. For example, on - Solaris 1 and 2, it will be <c>sunos</c>.</p> - <p>In Windows, <c><anno>Osname</anno></c> will be either <c>nt</c> (on - Windows NT), or <c>windows</c> (on Windows 95).</p> + Solaris 1 and 2, it is <c>sunos</c>.</p> + <p>On Windows, <c><anno>Osname</anno></c> is <c>nt</c>.</p> <note> - <p>Think twice before using this function. Use the - <c>filename</c> module if you want to inspect or build - file names in a portable way. - Avoid matching on the <c><anno>Osname</anno></c> atom.</p> + <p>Think twice before using this function. Use module + <seealso marker="stdlib:filename"><c>filename</c></seealso> + if you want to inspect or build filenames in a portable way. + Avoid matching on atom <c><anno>Osname</anno></c>.</p> </note> </desc> </func> + <func> <name name="unsetenv" arity="1"/> - <fsummary>Delete an environment variable</fsummary> + <fsummary>Delete an environment variable.</fsummary> <desc> <p>Deletes the environment variable <c><anno>VarName</anno></c>.</p> - <p>If Unicode filename encoding is in effect (see the <seealso - marker="erts:erl#file_name_encoding">erl manual - page</seealso>), the string (<c><anno>VarName</anno></c>) may - contain characters with codepoints > 255.</p> + <p>If Unicode filename encoding is in effect (see the + <seealso marker="erts:erl#file_name_encoding"><c>erl</c> manual + page</seealso>), the string <c><anno>VarName</anno></c> can + contain characters with codepoints > 255.</p> </desc> </func> + <func> <name name="version" arity="0"/> - <fsummary>Return the Operating System version</fsummary> + <fsummary>Return the OS versions.</fsummary> <desc> - <p>Returns the operating system version. + <p>Returns the OS version. On most systems, this function returns a tuple, but a string - will be returned instead if the system has versions which + is returned instead if the system has versions that cannot be expressed as three numbers.</p> <note> <p>Think twice before using this function. If you still need diff --git a/lib/kernel/doc/src/pg2.xml b/lib/kernel/doc/src/pg2.xml index 4a7a2d77b1..0631b317b4 100644 --- a/lib/kernel/doc/src/pg2.xml +++ b/lib/kernel/doc/src/pg2.xml @@ -30,135 +30,136 @@ <checked>[email protected]</checked> <date>1997-08-18</date> <rev>A2</rev> - <file>pg2.sgml</file> + <file>pg2.xml</file> </header> <module>pg2</module> - <modulesummary>Distributed Named Process Groups</modulesummary> + <modulesummary>Distributed named process groups.</modulesummary> <description> - <p>This module implements process groups. Each message may be sent - to one, some, or all members of the group. - </p> + <p>This module implements process groups. Each message can be sent + to one, some, or all group members.</p> <p>A group of processes can be accessed by a common name. For example, if there is a group named <c>foobar</c>, there can be a - set of processes (which can be located on different nodes) which + set of processes (which can be located on different nodes) that are all members of the group <c>foobar</c>. There are no special functions for sending a message to the group. Instead, client - functions should be written with the functions - <c>get_members/1</c> and <c>get_local_members/1</c> to find out - which processes are members of the group. Then the message can be - sent to one or more members of the group. - </p> - <p>If a member terminates, it is automatically removed from the - group. - </p> + functions are to be written with the functions + <seealso marker="#get_members/1"><c>get_members/1</c></seealso> and + <seealso marker="#get_local_members/1"><c>get_local_members/1</c></seealso> + to determine which processes are members of the group. + Then the message can be sent to one or more group members.</p> + <p>If a member terminates, it is automatically removed from the group.</p> <warning> - <p>This module is used by the <c>disk_log</c> module for + <p>This module is used by module + <seealso marker="disk_log"><c>disk_log</c></seealso> for managing distributed disk logs. The disk log names are used as - group names, which means that some action may need to be taken + group names, which means that some action can be needed to avoid name clashes.</p> </warning> </description> + <datatypes> <datatype> <name name="name"/> <desc><p>The name of a process group.</p></desc> </datatype> </datatypes> + <funcs> <func> <name name="create" arity="1"/> - <fsummary>Create a new, empty process group</fsummary> + <fsummary>Create a new, empty process group.</fsummary> <desc> <p>Creates a new, empty process group. The group is globally - visible on all nodes. If the group exists, nothing happens. - </p> + visible on all nodes. If the group exists, nothing happens.</p> </desc> </func> + <func> <name name="delete" arity="1"/> - <fsummary>Delete a process group</fsummary> + <fsummary>Delete a process group.</fsummary> <desc> - <p>Deletes a process group. - </p> + <p>Deletes a process group.</p> </desc> </func> + <func> <name name="get_closest_pid" arity="1"/> - <fsummary>Common dispatch function</fsummary> + <fsummary>Common dispatch function.</fsummary> <desc> - <p>This is a useful dispatch function which can be used from + <p>A useful dispatch function that can be used from client functions. It returns a process on the local node, if - such a process exist. Otherwise, it chooses one randomly. - </p> + such a process exists. Otherwise, it selects one randomly.</p> </desc> </func> + <func> - <name name="get_members" arity="1"/> - <fsummary>Return all processes in a group</fsummary> + <name name="get_local_members" arity="1"/> + <fsummary>Return all local processes in a group.</fsummary> <desc> - <p>Returns all processes in the group <c>Name</c>. This - function should be used from within a client function that - accesses the group. It is therefore optimized for speed. - </p> + <p>Returns all processes running on the local node in the + group <c>Name</c>. This function is to be used from + within a client function that accesses the group. It is therefore + optimized for speed.</p> </desc> </func> + <func> - <name name="get_local_members" arity="1"/> - <fsummary>Return all local processes in a group</fsummary> + <name name="get_members" arity="1"/> + <fsummary>Return all processes in a group.</fsummary> <desc> - <p>Returns all processes running on the local node in the - group <c>Name</c>. This function should to be used from - within a client function that accesses the group. It is therefore - optimized for speed. - </p> + <p>Returns all processes in the group <c>Name</c>. This + function is to be used from within a client function that + accesses the group. It is therefore optimized for speed.</p> </desc> </func> + <func> <name name="join" arity="2"/> - <fsummary>Join a process to a group</fsummary> + <fsummary>Join a process to a group.</fsummary> <desc> <p>Joins the process <c>Pid</c> to the group <c>Name</c>. - A process can join a group several times; it must then - leave the group the same number of times. - </p> + A process can join a group many times and must then + leave the group the same number of times.</p> </desc> </func> + <func> <name name="leave" arity="2"/> - <fsummary>Make a process leave a group</fsummary> + <fsummary>Make a process leave a group.</fsummary> <desc> <p>Makes the process <c>Pid</c> leave the group <c>Name</c>. If the process is not a member of the group, <c>ok</c> is - returned. - </p> - </desc> - </func> - <func> - <name name="which_groups" arity="0"/> - <fsummary>Return a list of all known groups</fsummary> - <desc> - <p>Returns a list of all known groups. - </p> + returned.</p> </desc> </func> + <func> <name name="start" arity="0"/> <name name="start_link" arity="0"/> - <fsummary>Start the pg2 server</fsummary> + <fsummary>Start the <c>pg2</c> server.</fsummary> <desc> - <p>Starts the pg2 server. Normally, the server does not need + <p>Starts the <c>pg2</c> server. Normally, the server does not need to be started explicitly, as it is started dynamically if it is needed. This is useful during development, but in a - target system the server should be started explicitly. Use - configuration parameters for <c>kernel</c> for this. - </p> + target system the server is to be started explicitly. Use the + configuration parameters for + <seealso marker="kernel_app#start_pg2"><c>kernel(6)</c></seealso> + for this.</p> + </desc> + </func> + + <func> + <name name="which_groups" arity="0"/> + <fsummary>Return a list of all known groups.</fsummary> + <desc> + <p>Returns a list of all known groups.</p> </desc> </func> </funcs> <section> <title>See Also</title> - <p><seealso marker="kernel_app">kernel(6)</seealso></p> + <p><seealso marker="kernel_app"><c>kernel(6)</c></seealso></p> </section> </erlref> diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml index 45526a2468..5cd77e0f6f 100644 --- a/lib/kernel/doc/src/ref_man.xml +++ b/lib/kernel/doc/src/ref_man.xml @@ -29,9 +29,7 @@ <rev></rev> </header> <description> - <p>The <em>Kernel</em> application has all the code necessary to run - the Erlang runtime system itself: file servers and code servers - and so on.</p> + </description> <xi:include href="kernel_app.xml"/> <xi:include href="application.xml"/> @@ -45,9 +43,9 @@ <xi:include href="error_handler.xml"/> <xi:include href="error_logger.xml"/> <xi:include href="file.xml"/> + <xi:include href="gen_sctp.xml"/> <xi:include href="gen_tcp.xml"/> <xi:include href="gen_udp.xml"/> - <xi:include href="gen_sctp.xml"/> <xi:include href="global.xml"/> <xi:include href="global_group.xml"/> <xi:include href="heart.xml"/> diff --git a/lib/kernel/doc/src/rpc.xml b/lib/kernel/doc/src/rpc.xml index d815c34eed..8cad9fe4fc 100644 --- a/lib/kernel/doc/src/rpc.xml +++ b/lib/kernel/doc/src/rpc.xml @@ -25,328 +25,388 @@ <title>rpc</title> <prepared>Claes Wikstrom</prepared> <docno>1</docno> - <date>96-09-10</date> + <date>1996-09-10</date> <rev>A</rev> </header> <module>rpc</module> - <modulesummary>Remote Procedure Call Services</modulesummary> + <modulesummary>Remote Procedure Call services.</modulesummary> <description> - <p>This module contains services which are similar to remote - procedure calls. It also contains broadcast facilities and + <p>This module contains services similar to Remote + Procedure Calls. It also contains broadcast facilities and parallel evaluators. A remote procedure call is a method to call a function on a remote node and collect the answer. It is used for collecting information on a remote node, or for running a function with some specific side effects on the remote node.</p> </description> + <datatypes> <datatype> <name name="key"/> <desc> - <p>As returned by <seealso marker="#async_call/4"> - <c>async_call/4</c>.</seealso></p> + <p>As returned by + <seealso marker="#async_call/4"><c>async_call/4</c></seealso>.</p> </desc> </datatype> </datatypes> + <funcs> <func> - <name name="call" arity="4"/> - <fsummary>Evaluate a function call on a node</fsummary> + <name name="abcast" arity="2"/> + <fsummary>Broadcast a message asynchronously to a registered process on + all nodes.</fsummary> <desc> - <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the node - <c><anno>Node</anno></c> and returns the corresponding value <c><anno>Res</anno></c>, or - <c>{badrpc, <anno>Reason</anno>}</c> if the call fails.</p> + <p>Equivalent to <c>abcast([node()|nodes()], <anno>Name</anno>, + <anno>Msg</anno>)</c>.</p> </desc> </func> + <func> - <name name="call" arity="5"/> - <fsummary>Evaluate a function call on a node</fsummary> + <name name="abcast" arity="3"/> + <fsummary>Broadcast a message asynchronously to a registered process on + specific nodes.</fsummary> <desc> - <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the node - <c><anno>Node</anno></c> and returns the corresponding value <c><anno>Res</anno></c>, or - <c>{badrpc, <anno>Reason</anno>}</c> if the call fails. <c><anno>Timeout</anno></c> is - a timeout value in milliseconds. If the call times out, - <c><anno>Reason</anno></c> is <c>timeout</c>.</p> - <p>If the reply arrives after the call times out, no message - will contaminate the caller's message queue, since this - function spawns off a middleman process to act as (a void) - destination for such an orphan reply. This feature also makes - this function more expensive than <c>call/4</c> at - the caller's end.</p> + <p>Broadcasts the message <c><anno>Msg</anno></c> asynchronously to + the registered process <c><anno>Name</anno></c> on the specified + nodes.</p> + </desc> + </func> + + <func> + <name name="async_call" arity="4"/> + <fsummary>Evaluate a function call on a node, asynchronous + version.</fsummary> + <desc> + <p>Implements <em>call streams with promises</em>, a type of + RPC that does not suspend the caller until the result is + finished. Instead, a key is returned, which can be used + later to collect the value. The key can be viewed as a + promise to deliver the answer.</p> + <p>In this case, the key <c><anno>Key</anno></c> is returned, which + can be used in a subsequent call to + <seealso marker="#yield/1"><c>yield/1</c></seealso> or + <seealso marker="#nb_yield/1"><c>nb_yield/1,2</c></seealso> + to retrieve the value of evaluating <c>apply(<anno>Module</anno>, + <anno>Function</anno>, <anno>Args</anno>)</c> on node + <c><anno>Node</anno></c>.</p> </desc> </func> + <func> <name name="block_call" arity="4"/> - <fsummary>Evaluate a function call on a node in the RPC server's context</fsummary> + <fsummary>Evaluate a function call on a node in the RPC server's + context.</fsummary> <desc> - <p>Like <c>call/4</c>, but the RPC server at <c><anno>Node</anno></c> does + <p>Same as <seealso marker="#call/4"><c>call/4</c></seealso>, + but the RPC server at <c><anno>Node</anno></c> does not create a separate process to handle the call. Thus, this function can be used if the intention of the call is to block the RPC server from any other incoming requests until - the request has been handled. The function can also be used + the request has been handled. The function can also be used for efficiency reasons when very small fast functions are - evaluated, for example BIFs that are guaranteed not to + evaluated, for example, BIFs that are guaranteed not to suspend.</p> </desc> </func> + <func> <name name="block_call" arity="5"/> - <fsummary>Evaluate a function call on a node in the RPC server's context</fsummary> + <fsummary>Evaluate a function call on a node in the RPC server's + context.</fsummary> <desc> - <p>Like <c>block_call/4</c>, but with a timeout value in - the same manner as <c>call/5</c>.</p> + <p>Same as + <seealso marker="#block_call/4"><c>block_call/4</c></seealso>, + but with a time-out value in the same manner as + <seealso marker="#call/5"><c>call/5</c></seealso>.</p> </desc> </func> + <func> - <name name="async_call" arity="4"/> - <fsummary>Evaluate a function call on a node, asynchronous version</fsummary> + <name name="call" arity="4"/> + <fsummary>Evaluate a function call on a node.</fsummary> <desc> - <p>Implements <em>call streams with promises</em>, a type of - RPC which does not suspend the caller until the result is - finished. Instead, a key is returned which can be used at a - later stage to collect the value. The key can be viewed as a - promise to deliver the answer.</p> - <p>In this case, the key <c><anno>Key</anno></c> is returned, which can be - used in a subsequent call to <c>yield/1</c> or - <c>nb_yield/1,2</c> to retrieve the value of evaluating - <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the node <c><anno>Node</anno></c>.</p> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, + <anno>Args</anno>)</c> on node <c><anno>Node</anno></c> and returns + the corresponding value <c><anno>Res</anno></c>, or + <c>{badrpc, <anno>Reason</anno>}</c> if the call fails.</p> </desc> </func> + <func> - <name name="yield" arity="1"/> - <fsummary>Deliver the result of evaluating a function call on a node (blocking)</fsummary> + <name name="call" arity="5"/> + <fsummary>Evaluate a function call on a node.</fsummary> <desc> - <p>Returns the promised answer from a previous - <c>async_call/4</c>. If the answer is available, it is - returned immediately. Otherwise, the calling process is - suspended until the answer arrives from <c>Node</c>.</p> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, + <anno>Args</anno>)</c> on node <c><anno>Node</anno></c> and returns + the corresponding value <c><anno>Res</anno></c>, or + <c>{badrpc, <anno>Reason</anno>}</c> if the call fails. + <c><anno>Timeout</anno></c> is + a time-out value in milliseconds. If the call times out, + <c><anno>Reason</anno></c> is <c>timeout</c>.</p> + <p>If the reply arrives after the call times out, no message + contaminates the caller's message queue, as this + function spawns off a middleman process to act as (a void) + destination for such an orphan reply. This feature also makes + this function more expensive than <c>call/4</c> at + the caller's end.</p> </desc> </func> + <func> - <name name="nb_yield" arity="1"/> - <fsummary>Deliver the result of evaluating a function call on a node (non-blocking)</fsummary> + <name name="cast" arity="4"/> + <fsummary>Run a function on a node ignoring the result.</fsummary> <desc> - <p>Equivalent to <c>nb_yield(<anno>Key</anno>, 0)</c>.</p> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, + <anno>Args</anno>)</c> on node + <c><anno>Node</anno></c>. No response is delivered and the calling + process is not suspended until the evaluation is complete, as + is the case with + <seealso marker="#call/4"><c>call/4,5</c></seealso>.</p> </desc> </func> + <func> - <name name="nb_yield" arity="2"/> - <fsummary>Deliver the result of evaluating a function call on a node (non-blocking)</fsummary> + <name name="eval_everywhere" arity="3"/> + <fsummary>Run a function on all nodes, ignoring the result.</fsummary> <desc> - <p>This is a non-blocking version of <c>yield/1</c>. It returns - the tuple <c>{value, <anno>Val</anno>}</c> when the computation has - finished, or <c>timeout</c> when <c><anno>Timeout</anno></c> milliseconds - has elapsed.</p> + <p>Equivalent to <c>eval_everywhere([node()|nodes()], + <anno>Module</anno>, <anno>Function</anno>, + <anno>Args</anno>)</c>.</p> </desc> </func> + + <func> + <name name="eval_everywhere" arity="4"/> + <fsummary>Run a function on specific nodes, ignoring the + result.</fsummary> + <desc> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, + <anno>Args</anno>)</c> on + the specified nodes. No answers are collected.</p> + </desc> + </func> + + <func> + <name name="multi_server_call" arity="2"/> + <fsummary>Interact with the servers on a number of nodes.</fsummary> + <desc> + <p>Equivalent to <c>multi_server_call([node()|nodes()], + <anno>Name</anno>, <anno>Msg</anno>)</c>.</p> + </desc> + </func> + + <func> + <name name="multi_server_call" arity="3"/> + <fsummary>Interact with the servers on a number of nodes.</fsummary> + <desc> + <p>Can be used when interacting with servers called + <c><anno>Name</anno></c> on the specified nodes. It is assumed that + the servers receive messages in the format + <c>{From, <anno>Msg</anno>}</c> and reply using + <c>From ! {<anno>Name</anno>, Node, <anno>Reply</anno>}</c>, where + <c>Node</c> is the name of the node where the server is located. + The function returns <c>{<anno>Replies</anno>, + <anno>BadNodes</anno>}</c>, where <c><anno>Replies</anno></c> is a + list of all <c><anno>Reply</anno></c> values, and + <c><anno>BadNodes</anno></c> is one of the following:</p> + <list type="bulleted"> + <item>A list of the nodes that do not exist</item> + <item>A list of the nodes where the server does not exist</item> + <item>A list of the nodes where the server terminatd before sending + any reply.</item> + </list> + </desc> + </func> + <func> <name name="multicall" arity="3"/> - <fsummary>Evaluate a function call on a number of nodes</fsummary> + <fsummary>Evaluate a function call on a number of nodes.</fsummary> <desc> - <p>Equivalent to <c>multicall([node()|nodes()], <anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>, infinity)</c>.</p> + <p>Equivalent to <c>multicall([node()|nodes()], <anno>Module</anno>, + <anno>Function</anno>, <anno>Args</anno>, infinity)</c>.</p> </desc> </func> + <func> <name name="multicall" arity="4" clause_i="1"/> - <fsummary>Evaluate a function call on a number of nodes</fsummary> + <fsummary>Evaluate a function call on a number of nodes.</fsummary> <desc> - <p>Equivalent to <c>multicall(<anno>Nodes</anno>, <anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>, infinity)</c>.</p> + <p>Equivalent to <c>multicall(<anno>Nodes</anno>, <anno>Module</anno>, + <anno>Function</anno>, <anno>Args</anno>, infinity)</c>.</p> </desc> </func> + <func> <name name="multicall" arity="4" clause_i="2"/> - <fsummary>Evaluate a function call on a number of nodes</fsummary> + <fsummary>Evaluate a function call on a number of nodes.</fsummary> <desc> - <p>Equivalent to <c>multicall([node()|nodes()], <anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>, <anno>Timeout</anno>)</c>.</p> + <p>Equivalent to <c>multicall([node()|nodes()], <anno>Module</anno>, + <anno>Function</anno>, <anno>Args</anno>, + <anno>Timeout</anno>)</c>.</p> </desc> </func> + <func> <name name="multicall" arity="5"/> - <fsummary>Evaluate a function call on a number of nodes</fsummary> + <fsummary>Evaluate a function call on a number of nodes.</fsummary> <desc> - <p>In contrast to an RPC, a multicall is an RPC which is sent + <p>In contrast to an RPC, a multicall is an RPC that is sent concurrently from one client to multiple servers. This is - useful for collecting some information from a set of nodes, + useful for collecting information from a set of nodes, or for calling a function on a set of nodes to achieve some side effects. It is semantically the same as iteratively making a series of RPCs on all the nodes, but the multicall - is faster as all the requests are sent at the same time + is faster, as all the requests are sent at the same time and are collected one by one as they come back.</p> - <p>The function evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> - on the specified nodes and collects the answers. It returns - <c>{<anno>ResL</anno>, <anno>BadNodes</anno>}</c>, where <c><anno>BadNodes</anno></c> is a list + <p>The function evaluates <c>apply(<anno>Module</anno>, + <anno>Function</anno>, <anno>Args</anno>)</c> + on the specified nodes and collects the answers. It returns + <c>{<anno>ResL</anno>, <anno>BadNodes</anno>}</c>, where + <c><anno>BadNodes</anno></c> is a list of the nodes that terminated or timed out during computation, and <c><anno>ResL</anno></c> is a list of the return values. <c><anno>Timeout</anno></c> is a time (integer) in milliseconds, or <c>infinity</c>.</p> <p>The following example is useful when new object code is to - be loaded on all nodes in the network, and also indicates - some side effects RPCs may produce:</p> + be loaded on all nodes in the network, and indicates + some side effects that RPCs can produce:</p> <code type="none"> -%% Find object code for module Mod -{Mod, Bin, File} = code:get_object_code(Mod), +%% Find object code for module Mod +{Mod, Bin, File} = code:get_object_code(Mod), -%% and load it on all nodes including this one +%% and load it on all nodes including this one {ResL, _} = rpc:multicall(code, load_binary, [Mod, File, Bin]), %% and then maybe check the ResL list.</code> </desc> </func> + <func> - <name name="cast" arity="4"/> - <fsummary>Run a function on a node ignoring the result</fsummary> + <name name="nb_yield" arity="1"/> + <fsummary>Deliver the result of evaluating a function call on a node + (non-blocking).</fsummary> <desc> - <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on the node - <c><anno>Node</anno></c>. No response is delivered and the calling - process is not suspended until the evaluation is complete, as - is the case with <c>call/4,5</c>.</p> + <p>Equivalent to <c>nb_yield(<anno>Key</anno>, 0)</c>.</p> </desc> </func> + <func> - <name name="eval_everywhere" arity="3"/> - <fsummary>Run a function on all nodes, ignoring the result</fsummary> + <name name="nb_yield" arity="2"/> + <fsummary>Deliver the result of evaluating a function call on a node + (non-blocking).</fsummary> <desc> - <p>Equivalent to <c>eval_everywhere([node()|nodes()], <anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c>.</p> + <p>Non-blocking version of + <seealso marker="#yield/1"><c>yield/1</c></seealso>. It returns + the tuple <c>{value, <anno>Val</anno>}</c> when the computation is + finished, or <c>timeout</c> when <c><anno>Timeout</anno></c> + milliseconds has elapsed.</p> </desc> </func> + <func> - <name name="eval_everywhere" arity="4"/> - <fsummary>Run a function on specific nodes, ignoring the result</fsummary> + <name name="parallel_eval" arity="1"/> + <fsummary>Evaluate many function calls on all nodes in + parallel.</fsummary> <desc> - <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on - the specified nodes. No answers are collected.</p> + <p>Evaluates, for every tuple in <c><anno>FuncCalls</anno></c>, + <c>apply(<anno>Module</anno>, <anno>Function</anno>, + <anno>Args</anno>)</c> on some node in + the network. Returns the list of return values, in the same + order as in <c><anno>FuncCalls</anno></c>.</p> </desc> </func> + <func> - <name name="abcast" arity="2"/> - <fsummary>Broadcast a message asynchronously to a registered process on all nodes</fsummary> + <name name="pinfo" arity="1"/> + <fsummary>Information about a process.</fsummary> <desc> - <p>Equivalent to <c>abcast([node()|nodes()], <anno>Name</anno>, <anno>Msg</anno>)</c>.</p> + <p>Location transparent version of the BIF + <seealso marker="erts:erlang#process_info/1"><c>erlang:process_info/1</c></seealso> in <c>ERTS</c>.</p> </desc> </func> + <func> - <name name="abcast" arity="3"/> - <fsummary>Broadcast a message asynchronously to a registered process on specific nodes</fsummary> + <name name="pinfo" arity="2" clause_i="1"/> + <name name="pinfo" arity="2" clause_i="2"/> + <fsummary>Information about a process.</fsummary> <desc> - <p>Broadcasts the message <c><anno>Msg</anno></c> asynchronously to - the registered process <c><anno>Name</anno></c> on the specified nodes.</p> + <p>Location transparent version of the BIF + <seealso marker="erts:erlang#process_info/2"><c>erlang:process_info/2</c></seealso> in <c>ERTS</c>.</p> + </desc> + </func> + + <func> + <name name="pmap" arity="3"/> + <fsummary>Parallell evaluation of mapping a function over a + list.</fsummary> + <desc> + <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, + [<anno>Elem</anno>|<anno>ExtraArgs</anno>])</c> for every element + <c><anno>Elem</anno></c> in <c><anno>List1</anno></c>, in parallel. + Returns the list of return values, in the same order as in + <c><anno>List1</anno></c>.</p> </desc> </func> + <func> <name name="sbcast" arity="2"/> - <fsummary>Broadcast a message synchronously to a registered process on all nodes</fsummary> + <fsummary>Broadcast a message synchronously to a registered process on + all nodes.</fsummary> <desc> - <p>Equivalent to <c>sbcast([node()|nodes()], <anno>Name</anno>, <anno>Msg</anno>)</c>.</p> + <p>Equivalent to <c>sbcast([node()|nodes()], <anno>Name</anno>, + <anno>Msg</anno>)</c>.</p> </desc> </func> + <func> <name name="sbcast" arity="3"/> - <fsummary>Broadcast a message synchronously to a registered process on specific nodes</fsummary> + <fsummary>Broadcast a message synchronously to a registered process on + specific nodes.</fsummary> <desc> <p>Broadcasts the message <c><anno>Msg</anno></c> synchronously to - the registered process <c><anno>Name</anno></c> on the specified nodes.</p> - <p>Returns <c>{<anno>GoodNodes</anno>, <anno>BadNodes</anno>}</c>, where <c><anno>GoodNodes</anno></c> - is the list of nodes which have <c><anno>Name</anno></c> as a registered - process.</p> + the registered process <c><anno>Name</anno></c> on the specified + nodes.</p> + <p>Returns <c>{<anno>GoodNodes</anno>, <anno>BadNodes</anno>}</c>, + where <c><anno>GoodNodes</anno></c> is the list of nodes that have + <c><anno>Name</anno></c> as a registered process.</p> <p>The function is synchronous in the sense that it is known that all servers have received the message when the call returns. It is not possible to know that the servers have - actually processed the message.</p> + processed the message.</p> <p>Any further messages sent to the servers, after this - function has returned, will be received by all servers after + function has returned, are received by all servers after this message.</p> </desc> </func> + <func> <name name="server_call" arity="4"/> - <fsummary>Interact with a server on a node</fsummary> + <fsummary>Interact with a server on a node.</fsummary> <desc> - <p>This function can be used when interacting with a server - called <c><anno>Name</anno></c> at node <c><anno>Node</anno></c>. It is assumed that - the server receives messages in the format - <c>{From, <anno>Msg</anno>}</c> and replies using <c>From ! {<anno>ReplyWrapper</anno>, <anno>Node</anno>, <anno>Reply</anno>}</c>. This function makes such + <p>Can be used when interacting with a server called + <c><anno>Name</anno></c> on node <c><anno>Node</anno></c>. It is + assumed that the server receives messages in the format + <c>{From, <anno>Msg</anno>}</c> and replies using + <c>From ! {<anno>ReplyWrapper</anno>, <anno>Node</anno>, + <anno>Reply</anno>}</c>. This function makes such a server call and ensures that the entire call is packed into - an atomic transaction which either succeeds or fails. It + an atomic transaction, which either succeeds or fails. It never hangs, unless the server itself hangs.</p> - <p>The function returns the answer <c><anno>Reply</anno></c> as produced by - the server <c><anno>Name</anno></c>, or <c>{error, <anno>Reason</anno>}</c>.</p> - </desc> - </func> - <func> - <name name="multi_server_call" arity="2"/> - <fsummary>Interact with the servers on a number of nodes</fsummary> - <desc> - <p>Equivalent to <c>multi_server_call([node()|nodes()], <anno>Name</anno>, <anno>Msg</anno>)</c>.</p> - </desc> - </func> - <func> - <name name="multi_server_call" arity="3"/> - <fsummary>Interact with the servers on a number of nodes</fsummary> - <desc> - <p>This function can be used when interacting with servers - called <c><anno>Name</anno></c> on the specified nodes. It is assumed that - the servers receive messages in the format <c>{From, <anno>Msg</anno>}</c> - and reply using <c>From ! {<anno>Name</anno>, Node, <anno>Reply</anno>}</c>, where - <c>Node</c> is the name of the node where the server is - located. The function returns <c>{<anno>Replies</anno>, <anno>BadNodes</anno>}</c>, - where <c><anno>Replies</anno></c> is a list of all <c><anno>Reply</anno></c> values and - <c><anno>BadNodes</anno></c> is a list of the nodes which did not exist, or - where the server did not exist, or where the server terminated - before sending any reply.</p> - </desc> - </func> - <func> - <name name="safe_multi_server_call" arity="2"/> - <name name="safe_multi_server_call" arity="3"/> - <fsummary>Interact with the servers on a number of nodes (deprecated)</fsummary> - <desc> - <warning> - <p>This function is deprecated. Use - <c>multi_server_call/2,3</c> instead.</p> - </warning> - <p>In Erlang/OTP R6B and earlier releases, - <c>multi_server_call/2,3</c> could not handle the case - where the remote node exists, but there is no server called - <c><anno>Name</anno></c>. Instead this function had to be used. In - Erlang/OTP R7B and later releases, however, the functions are - equivalent, except for this function being slightly slower.</p> - </desc> - </func> - <func> - <name name="parallel_eval" arity="1"/> - <fsummary>Evaluate several function calls on all nodes in parallel</fsummary> - <desc> - <p>For every tuple in <c><anno>FuncCalls</anno></c>, evaluates - <c>apply(<anno>Module</anno>, <anno>Function</anno>, <anno>Args</anno>)</c> on some node in - the network. Returns the list of return values, in the same - order as in <c><anno>FuncCalls</anno></c>.</p> - </desc> - </func> - <func> - <name name="pmap" arity="3"/> - <fsummary>Parallell evaluation of mapping a function over a list </fsummary> - <desc> - <p>Evaluates <c>apply(<anno>Module</anno>, <anno>Function</anno>, [<anno>Elem</anno>|<anno>ExtraArgs</anno>])</c>, - for every element <c><anno>Elem</anno></c> in <c><anno>List1</anno></c>, in parallel. - Returns the list of return values, in the same order as in - <c><anno>List1</anno></c>.</p> - </desc> - </func> - <func> - <name name="pinfo" arity="1"/> - <fsummary>Information about a process</fsummary> - <desc> - <p>Location transparent version of the BIF - <seealso marker="erts:erlang#process_info/1"> - <c>process_info/1</c></seealso>.</p> + <p>The function returns the answer <c><anno>Reply</anno></c> as + produced by the server <c><anno>Name</anno></c>, or + <c>{error, <anno>Reason</anno>}</c>.</p> </desc> </func> + <func> - <name name="pinfo" arity="2"/> - <fsummary>Information about a process</fsummary> + <name name="yield" arity="1"/> + <fsummary>Deliver the result of evaluating a function call on a node + (blocking).</fsummary> <desc> - <p>Location transparent version of the BIF - <seealso marker="erts:erlang#process_info/2"> - <c>process_info/2</c></seealso>.</p> + <p>Returns the promised answer from a previous + <seealso marker="#async_call/4"><c>async_call/4</c></seealso>. + If the answer is available, it is + returned immediately. Otherwise, the calling process is + suspended until the answer arrives from <c>Node</c>.</p> </desc> </func> </funcs> diff --git a/lib/kernel/doc/src/seq_trace.xml b/lib/kernel/doc/src/seq_trace.xml index dc25e011a0..1feb3fcb07 100644 --- a/lib/kernel/doc/src/seq_trace.xml +++ b/lib/kernel/doc/src/seq_trace.xml @@ -29,24 +29,17 @@ <rev>A</rev> </header> <module>seq_trace</module> - <modulesummary>Sequential Tracing of Messages</modulesummary> + <modulesummary>Sequential tracing of messages.</modulesummary> <description> <p>Sequential tracing makes it possible to trace all messages resulting from one initial message. Sequential tracing is - completely independent of the ordinary tracing in Erlang, which - is controlled by the <c>erlang:trace/3</c> BIF. See the chapter - <seealso marker="#whatis">What is Sequential Tracing</seealso> - below for more information about what sequential tracing is and - how it can be used.</p> - <p><c>seq_trace</c> provides functions which control all aspects of + independent of the ordinary tracing in Erlang, which + is controlled by the <c>erlang:trace/3</c> BIF. For more information + about what sequential tracing is and how it can be used, see section + <seealso marker="#whatis">Sequential Tracing</seealso>.</p> + <p><c>seq_trace</c> provides functions that control all aspects of sequential tracing. There are functions for activation, - deactivation, inspection and for collection of the trace output.</p> - <note> - <p>The implementation of sequential tracing is in beta status. - This means that the programming interface still might undergo - minor adjustments (possibly incompatible) based on feedback - from users.</p> - </note> + deactivation, inspection, and for collection of the trace output.</p> </description> <datatypes> <datatype> @@ -239,20 +232,21 @@ seq_trace:set_token(OldToken), % activate the trace token again </funcs> <section> - <title>Trace Messages Sent To the System Tracer</title> - <p>The format of the messages are:</p> + <title>Trace Messages Sent to the System Tracer</title> + <p>The format of the messages is one of the following, depending on if + flag <c>timestamp</c> of the trace token is set to <c>true</c> or + <c>false</c>:</p> <code type="none"> {seq_trace, Label, SeqTraceInfo, TimeStamp}</code> <p>or</p> <code type="none"> {seq_trace, Label, SeqTraceInfo}</code> - <p>depending on whether the <c>timestamp</c> flag of the trace - token is set to <c>true</c> or <c>false</c>. Where:</p> + <p>Where:</p> <code type="none"> Label = int() TimeStamp = {Seconds, Milliseconds, Microseconds} Seconds = Milliseconds = Microseconds = int()</code> - <p>The <c>SeqTraceInfo</c> can have the following formats:</p> + <p><c>SeqTraceInfo</c> can have the following formats:</p> <taglist> <tag><c>{send, Serial, From, To, Message}</c></tag> <item> @@ -262,141 +256,151 @@ TimeStamp = {Seconds, Milliseconds, Microseconds} <tag><c>{'receive', Serial, From, To, Message}</c></tag> <item> <p>Used when a process <c>To</c> receives a message with a - trace token that has the <c>'receive'</c> flag set to - <c>true</c>.</p> + trace token that has flag <c>'receive'</c> set to <c>true</c>.</p> </item> <tag><c>{print, Serial, From, _, Info}</c></tag> <item> - <p>Used when a process <c>From</c> has called + <p>Used when a process <c>From</c> has called <c>seq_trace:print(Label, TraceInfo)</c> and has a trace - token with the <c>print</c> flag set to <c>true</c> and + token with flag <c>print</c> set to <c>true</c>, and <c>label</c> set to <c>Label</c>.</p> </item> </taglist> <p><c>Serial</c> is a tuple <c>{PreviousSerial, ThisSerial}</c>, - where the first integer <c>PreviousSerial</c> denotes the serial - counter passed in the last received message which carried a trace - token. If the process is the first one in a new sequential trace, - <c>PreviousSerial</c> is set to the value of the process internal - "trace clock". The second integer <c>ThisSerial</c> is the serial - counter that a process sets on outgoing messages and it is based - on the process internal "trace clock" which is incremented by one - before it is attached to the trace token in the message.</p> + where:</p> + <list type="bulleted"> + <item><p>Integer <c>PreviousSerial</c> denotes the serial + counter passed in the last received message that carried a trace + token. If the process is the first in a new sequential trace, + <c>PreviousSerial</c> is set to the value of the process internal + "trace clock".</p></item> + <item><p>Integer <c>ThisSerial</c> is the serial + counter that a process sets on outgoing messages. It is based + on the process internal "trace clock", which is incremented by one + before it is attached to the trace token in the message.</p></item> + </list> </section> <section> <marker id="whatis"></marker> - <title>What is Sequential Tracing</title> + <title>Sequential Tracing</title> <p>Sequential tracing is a way to trace a sequence of messages sent between different local or remote processes, where the sequence - is initiated by one single message. In short it works like this:</p> + is initiated by a single message. In short, it works as follows:</p> <p>Each process has a <em>trace token</em>, which can be empty or - not empty. When not empty the trace token can be seen as + not empty. When not empty, the trace token can be seen as the tuple <c>{Label, Flags, Serial, From}</c>. The trace token is passed invisibly with each message.</p> - <p>In order to start a sequential trace the user must explicitly set + <p>To start a sequential trace, the user must explicitly set the trace token in the process that will send the first message in a sequence.</p> <p>The trace token of a process is set each time the process matches a message in a receive statement, according to the trace token carried by the received message, empty or not.</p> - <p>On each Erlang node a process can be set as the <em>system tracer</em>. This process will receive trace messages each time + <p>On each Erlang node, a process can be set as the <em>system tracer</em>. + This process receives trace messages each time a message with a trace token is sent or received (if the trace token flag <c>send</c> or <c>'receive'</c> is set). The system - tracer can then print each trace event, write it to a file or + tracer can then print each trace event, write it to a file, or whatever suitable.</p> <note> - <p>The system tracer will only receive those trace events that + <p>The system tracer only receives those trace events that occur locally within the Erlang node. To get the whole picture - of a sequential trace that involves processes on several Erlang + of a sequential trace, involving processes on many Erlang nodes, the output from the system tracer on each involved node - must be merged (off line).</p> + must be merged (offline).</p> </note> - <p>In the following sections Sequential Tracing and its most - fundamental concepts are described.</p> + <p>The following sections describe sequential tracing and its most + fundamental concepts.</p> </section> <section> <title>Trace Token</title> - <p>Each process has a current trace token. Initially the token is + <p>Each process has a current trace token. Initially, the token is empty. When the process sends a message to another process, a - copy of the current token will be sent "invisibly" along with + copy of the current token is sent "invisibly" along with the message.</p> - <p>The current token of a process is set in two ways, either</p> - <list type="ordered"> + <p>The current token of a process is set in one of the following two + ways:</p> + <list type="bulleted"> <item> - <p>explicitly by the process itself, through a call to - <c>seq_trace:set_token</c>, or</p> + <p>Explicitly by the process itself, through a call to + <c>seq_trace:set_token/1,2</c></p> </item> <item> - <p>when a message is received.</p> + <p>When a message is received</p> </item> </list> - <p>In both cases the current token will be set. In particular, if - the token of a message received is empty, the current token of + <p>In both cases, the current token is set. In particular, if + the token of a received message is empty, the current token of the process is set to empty.</p> - <p>A trace token contains a label, and a set of flags. Both - the label and the flags are set in 1 and 2 above.</p> + <p>A trace token contains a label and a set of flags. Both + the label and the flags are set in both alternatives above.</p> </section> <section> <title>Serial</title> - <p>The trace token contains a component which is called - <c>serial</c>. It consists of two integers <c>Previous</c> and + <p>The trace token contains a component called + <c>serial</c>. It consists of two integers, <c>Previous</c> and <c>Current</c>. The purpose is to uniquely identify each traced - event within a trace sequence and to order the messages - chronologically and in the different branches if any.</p> + event within a trace sequence, as well as to order the messages + chronologically and in the different branches, if any.</p> <p>The algorithm for updating <c>Serial</c> can be described as follows:</p> - <p>Let each process have two counters <c>prev_cnt</c> and - <c>curr_cnt</c> which both are set to 0 when a process is created. + <p>Let each process have two counters, <c>prev_cnt</c> and + <c>curr_cnt</c>, both are set to <c>0</c> when a process is created. The counters are updated at the following occasions:</p> <list type="bulleted"> <item> - <p><em>When the process is about to send a message and the trace token is not empty.</em></p> + <p><em>When the process is about to send a message and the trace token + is not empty.</em></p> <p>Let the serial of the trace token be <c>tprev</c> and - <c>tcurr</c>. <br></br> -<c>curr_cnt := curr_cnt + 1</c> <br></br> -<c>tprev := prev_cnt</c> <br></br> -<c>tcurr := curr_cnt</c></p> + <c>tcurr</c>.</p> + <pre> +curr_cnt := curr_cnt + 1 +tprev := prev_cnt +tcurr := curr_cnt</pre> <p>The trace token with <c>tprev</c> and <c>tcurr</c> is then passed along with the message.</p> </item> <item> - <p><em>When the process calls</em><c>seq_trace:print(Label, Info)</c>, <em>Label matches the label part of the trace token and the trace token print flag is true.</em></p> - <p>The same algorithm as for send above.</p> + <p><em>When the process calls</em> <c>seq_trace:print(Label, Info)</c>, + <c>Label</c> <em>matches the label part of the trace token and the + trace token print flag is <c>true</c>.</em></p> + <p>The algorithm is the same as for send above.</p> </item> <item> - <p><em>When a message is received and contains a nonempty trace token.</em></p> + <p><em>When a message is received and contains a non-empty trace + token.</em></p> <p>The process trace token is set to the trace token from the message.</p> <p>Let the serial of the trace token be <c>tprev</c> and - <c>tcurr</c>. <br></br> -<c><![CDATA[if (curr_cnt < tcurr )]]></c> <br></br> - - <c>curr_cnt := tcurr</c> <br></br> -<c>prev_cnt := tcurr</c></p> + <c>tcurr</c>.</p> + <code> +<![CDATA[if (curr_cnt < tcurr )]]> + curr_cnt := tcurr +prev_cnt := tcurr</code> </item> </list> - <p>The <c>curr_cnt</c> of a process is incremented each time + <p><c>curr_cnt</c> of a process is incremented each time the process is involved in a sequential trace. The counter can reach its limit (27 bits) if a process is very long-lived and is - involved in much sequential tracing. If the counter overflows it - will not be possible to use the serial for ordering of the trace - events. To prevent the counter from overflowing in the middle of - a sequential trace the function <c>seq_trace:reset_trace/0</c> - can be called to reset the <c>prev_cnt</c> and <c>curr_cnt</c> of - all processes in the Erlang node. This function will also set all - trace tokens in processes and their message queues to empty and - will thus stop all ongoing sequential tracing.</p> + involved in much sequential tracing. If the counter overflows, the + serial for ordering of the trace events cannot be used. To prevent + the counter from overflowing in the middle of + a sequential trace, function <c>seq_trace:reset_trace/0</c> + can be called to reset <c>prev_cnt</c> and <c>curr_cnt</c> of + all processes in the Erlang node. This function also sets all + trace tokens in processes and their message queues to empty, and + thus stops all ongoing sequential tracing.</p> </section> <section> - <title>Performance considerations</title> - <p>The performance degradation for a system which is enabled for - Sequential Tracing is negligible as long as no tracing is - activated. When tracing is activated there will of course be an - extra cost for each traced message but all other messages will be + <title>Performance Considerations</title> + <p>The performance degradation for a system that is enabled for + sequential tracing is negligible as long as no tracing is + activated. When tracing is activated, there is an + extra cost for each traced message, but all other messages are unaffected.</p> </section> @@ -404,13 +408,13 @@ TimeStamp = {Seconds, Milliseconds, Microseconds} <title>Ports</title> <p>Sequential tracing is not performed across ports.</p> <p>If the user for some reason wants to pass the trace token to a - port this has to be done manually in the code of the port + port, this must be done manually in the code of the port controlling process. The port controlling processes have to check the appropriate sequential trace settings (as obtained from - <c>seq_trace:get_token/1</c> and include trace information in + <c>seq_trace:get_token/1</c>) and include trace information in the message data sent to their respective ports.</p> <p>Similarly, for messages received from a port, a port controller - has to retrieve trace specific information, and set appropriate + has to retrieve trace-specific information, and set appropriate sequential trace flags through calls to <c>seq_trace:set_token/2</c>.</p> </section> @@ -418,23 +422,23 @@ TimeStamp = {Seconds, Milliseconds, Microseconds} <section> <title>Distribution</title> <p>Sequential tracing between nodes is performed transparently. - This applies to C-nodes built with Erl_Interface too. A C-node - built with Erl_Interface only maintains one trace token, which - means that the C-node will appear as one process from + This applies to C-nodes built with <c>Erl_Interface</c> too. A C-node + built with <c>Erl_Interface</c> only maintains one trace token, which + means that the C-node appears as one process from the sequential tracing point of view.</p> - <p>In order to be able to perform sequential tracing between + <p>To be able to perform sequential tracing between distributed Erlang nodes, the distribution protocol has been - extended (in a backward compatible way). An Erlang node which - supports sequential tracing can communicate with an older - (OTP R3B) node but messages passed within that node can of course + extended (in a backward compatible way). An Erlang node + supporting sequential tracing can communicate with an older + (Erlang/OTP R3B) node but messages passed within that node can not be traced.</p> </section> <section> - <title>Example of Usage</title> - <p>The example shown here will give rough idea of how the new - primitives can be used and what kind of output it will produce.</p> - <p>Assume that we have an initiating process with + <title>Example of Use</title> + <p>This example gives a rough idea of how the new + primitives can be used and what kind of output it produces.</p> + <p>Assume that you have an initiating process with <c><![CDATA[Pid == <0.30.0>]]></c> like this:</p> <code type="none"> -module(seqex). @@ -463,8 +467,8 @@ loop() -> PortController ! {ack,Ack} end, loop().</code> - <p>A possible output from the system's sequential_tracer (inspired - by AXE-10 and MD-110) could look like:</p> + <p>A possible output from the system's <c>sequential_tracer</c> can be + like this:</p> <pre> 17:<0.30.0> Info {0,1} WITH "**** Trace Started ****" @@ -475,7 +479,7 @@ loop() -> 17:<0.30.0> Received {2,4} FROM <0.31.0> WITH {ack,{received,the_message}}</pre> <p>The implementation of a system tracer process that produces - the printout above could look like this:</p> + this printout can look like this:</p> <code type="none"> tracer() -> receive @@ -502,16 +506,15 @@ print_trace({'receive',Serial,From,To,Message}) -> print_trace({send,Serial,From,To,Message}) -> io:format("~p Sent ~p TO ~p WITH~n~p~n", [From,Serial,To,Message]).</code> - <p>The code that creates a process that runs the tracer function - above and sets that process as the system tracer could look like - this:</p> + <p>The code that creates a process that runs this tracer function + and sets that process as the system tracer can look like this:</p> <code type="none"> start() -> Pid = spawn(?MODULE,tracer,[]), seq_trace:set_system_tracer(Pid), % set Pid as the system tracer ok.</code> - <p>With a function like <c>test/0</c> below the whole example can be - started.</p> + <p>With a function like <c>test/0</c>, the whole example can be + started:</p> <code type="none"> test() -> P = spawn(?MODULE, loop, [port]), diff --git a/lib/kernel/doc/src/user.xml b/lib/kernel/doc/src/user.xml index a11329f99a..38eac1c221 100644 --- a/lib/kernel/doc/src/user.xml +++ b/lib/kernel/doc/src/user.xml @@ -27,13 +27,13 @@ <title>user</title> <prepared>Robert Virding</prepared> <docno>1</docno> - <date>96-10-10</date> + <date>1996-10-10</date> <rev>A</rev> </header> <module>user</module> - <modulesummary>Standard I/O Server</modulesummary> + <modulesummary>Standard I/O server.</modulesummary> <description> - <p><c>user</c> is a server which responds to all the messages + <p><c>user</c> is a server that responds to all messages defined in the I/O interface. The code in <c>user.erl</c> can be used as a model for building alternative I/O servers.</p> </description> diff --git a/lib/kernel/doc/src/wrap_log_reader.xml b/lib/kernel/doc/src/wrap_log_reader.xml index 8c2fa91a1d..7fb9c1c023 100644 --- a/lib/kernel/doc/src/wrap_log_reader.xml +++ b/lib/kernel/doc/src/wrap_log_reader.xml @@ -26,39 +26,43 @@ <prepared>Esko Vierumäki</prepared> <responsible>Esko Vierumäki</responsible> <docno></docno> - <approved>nobody</approved> - <checked>no</checked> - <date>98-09-21</date> + <approved></approved> + <checked></checked> + <date>1998-09-21</date> <rev>A</rev> <file>wrap_log_reader.sgml</file> </header> <module>wrap_log_reader</module> - <modulesummary>A function to read internally formatted wrap disk logs</modulesummary> + <modulesummary>A service to read internally formatted wrap disk logs. + </modulesummary> <description> - <p><c>wrap_log_reader</c> is a function to read internally formatted - wrap disk logs, refer to disk_log(3). <c>wrap_log_reader</c> does not - interfere with disk_log activities; there is however a known bug in this - version of the <c>wrap_log_reader</c>, see chapter <c>bugs</c> below. - </p> - <p>A wrap disk log file consists of several files, called index files. - A log file can be opened and closed. It is also possible to open just one index file - separately. If an non-existent or a non-internally formatted file is opened, - an error message is returned. If the file is corrupt, no attempt to repair it - will be done but an error message is returned. - </p> - <p>If a log is configured to be distributed, there is a possibility that all items - are not loggen on all nodes. <c>wrap_log_reader</c> does only read the log on - the called node, it is entirely up to the user to be sure that all items are read. - </p> + <p>This module makes it possible to read internally formatted + wrap disk logs, see + <seealso marker="disk_log"><c>disk_log(3)</c></seealso>. + <c>wrap_log_reader</c> does not + interfere with <c>disk_log</c> activities; there is however a bug in this + version of the <c>wrap_log_reader</c>, see section + <seealso marker="#bugs">Known Limitations</seealso>.</p> + <p>A wrap disk log file consists of many files, called index files. A log + file can be opened and closed. Also, a single index file can be opened + separately. If a non-existent or non-internally formatted file is opened, + an error message is returned. If the file is corrupt, no attempt is made + to repair it, but an error message is returned.</p> + <p>If a log is configured to be distributed, it is possible that all items + are not logged on all nodes. <c>wrap_log_reader</c> only reads the log on + the called node; it is up to the user to be sure that all items + are read.</p> </description> + <datatypes> <datatype> <name name="continuation"/> - <desc><p>Continuation returned by - <c>open/1,2</c> or <c>chunk/1,2</c>.</p> + <desc> + <p>Continuation returned by <c>open/1,2</c> or <c>chunk/1,2</c>.</p> </desc> </datatype> </datatypes> + <funcs> <func> <name name="chunk" arity="1"/> @@ -66,87 +70,83 @@ <fsummary>Read a chunk of objects written to a wrap log.</fsummary> <type name="chunk_ret"/> <desc> - <p>This function makes it possible to efficiently read the - terms which have been appended to a log. It minimises disk - I/O by reading large 8K chunks from the file. - </p> - <p>The first time <c>chunk</c> is called an initial - continuation returned from the <c>open/1</c>, <c>open/2</c> must be provided. - </p> + <p>Enables to efficiently read the + terms that are appended to a log. Minimises disk + I/O by reading 64 kilobyte chunks from the file.</p> + <p>The first time <c>chunk()</c> is called, an initial + continuation returned from <c>open/1</c> or <c>open/2</c> must be + provided.</p> <p>When <c>chunk/3</c> is called, <c><anno>N</anno></c> controls the maximum number of terms that are read from the log in each - chunk. Default is <c>infinity</c>, which means that all the + chunk. Defaults to <c>infinity</c>, which means that all the terms contained in the 8K chunk are read. If less than - <c><anno>N</anno></c> terms are returned, this does not necessarily mean - that end of file is reached. - </p> - <p>The <c>chunk</c> function returns a tuple - <c>{<anno>Continuation2</anno>, <anno>Terms</anno>}</c>, where <c><anno>Terms</anno></c> is a list + <c><anno>N</anno></c> terms are returned, this does not necessarily + mean that end of file is reached.</p> + <p>Returns a tuple <c>{<anno>Continuation2</anno>, + <anno>Terms</anno>}</c>, where <c><anno>Terms</anno></c> is a list of terms found in the log. <c><anno>Continuation2</anno></c> is yet - another continuation which must be passed on into any - subsequent calls to <c>chunk</c>. With a series of calls to - <c>chunk</c> it is then possible to extract all terms from a - log. - </p> - <p>The <c>chunk</c> function returns a tuple - <c>{<anno>Continuation2</anno>, <anno>Terms</anno>, <anno>Badbytes</anno>}</c> if the log is opened - in read only mode and the read chunk is corrupt. <c><anno>Badbytes</anno></c> + another continuation that must be passed on to any + subsequent calls to <c>chunk()</c>. With a series of calls to + <c>chunk()</c>, it is then possible to extract all terms from a log.</p> + <p>Returns a tuple <c>{<anno>Continuation2</anno>, + <anno>Terms</anno>, <anno>Badbytes</anno>}</c> if the log is opened + in read only mode and the read chunk is corrupt. + <c><anno>Badbytes</anno></c> indicates the number of non-Erlang terms found in the chunk. - Note also that the log is not repaired. - </p> - <p><c>chunk</c> returns <c>{<anno>Continuation2</anno>, eof}</c> when the end of the log is - reached, and <c>{error, <anno>Reason</anno>}</c> if an error occurs. - </p> - <p>The returned continuation may or may not be valid in the next call to - <c>chunk</c>. This is because the log may wrap and delete - the file into which the continuation points. To make sure - this does not happen, the log can be blocked during the - search. - </p> + Notice that the log is not repaired.</p> + <p>Returns <c>{<anno>Continuation2</anno>, eof}</c> when + the end of the log is reached, and <c>{error, <anno>Reason</anno>}</c> + if an error occurs.</p> + <p>The returned continuation either is or is not valid in the next call + to this function. This is because the log can wrap and delete + the file into which the continuation points. To ensure + this does not occur, the log can be blocked during the search.</p> </desc> </func> + <func> <name name="close" arity="1"/> - <fsummary>Close a log</fsummary> + <fsummary>Close a log.</fsummary> <desc> - <p>This function closes a log file properly. - </p> + <p>Closes a log file properly.</p> </desc> </func> + <func> <name name="open" arity="1"/> <name name="open" arity="2"/> - <fsummary>Open a log file</fsummary> + <fsummary>Open a log file.</fsummary> <type name="open_ret"/> <desc> - <p><c><anno>Filename</anno></c> specifies the name of the file which is to be read. </p> - <p><c><anno>N</anno></c> specifies the index of the file which is to be read. - If <c><anno>N</anno></c> is omitted the whole wrap log file will be read; if it - is specified only the specified index file will be read. - </p> - <p>The <c>open</c> function returns <c>{ok, <anno>Continuation</anno>}</c> if the - log/index file was successfully opened. The <c><anno>Continuation</anno></c> - is to be used when chunking or closing the file. - </p> - <p>The function returns <c>{error, <anno>Reason</anno>}</c> for all errors. - </p> + <p><c><anno>Filename</anno></c> specifies the name of the file to be + read.</p> + <p><c><anno>N</anno></c> specifies the index of the file to be read. + If <c><anno>N</anno></c> is omitted, the whole wrap log file is read; + if it is specified, only the specified index file is read.</p> + <p>Returns <c>{ok, <anno>Continuation</anno>}</c> if the + log/index file is opened successfully. + <c><anno>Continuation</anno></c> + is to be used when chunking or closing the file.</p> + <p>Returns <c>{error, <anno>Reason</anno>}</c> for all errors.</p> </desc> </func> </funcs> <section> - <title>Bugs</title> - <p>This version of the <c>wrap_log_reader</c> does not detect if the <c>disk_log</c> - wraps to a new index file between a <c>wrap_log_reader:open</c> and the first - <c>wrap_log_reader:chunk</c>. - In this case the chuck will actually read the last logged items in the log file, - because the opened index file was truncated by the <c>disk_log</c>. - </p> + <title>Known Limitations</title> + <marker id="bugs"/> + <p>This version of <c>wrap_log_reader</c> does not detect if + <c>disk_log</c> wraps to a new index file between a call to + <c>wrap_log_reader:open()</c> and the first call to + <c>wrap_log_reader:chunk()</c>. + If this occurs, the call to <c>chunk()</c> reads the last logged + items in the log file, as the opened index file was truncated by + <c>disk_log</c>.</p> </section> <section> <title>See Also</title> - <p><seealso marker="disk_log">disk_log(3)</seealso></p> + <p><seealso marker="disk_log"><c>disk_log(3)</c></seealso></p> </section> </erlref> diff --git a/lib/kernel/doc/src/zlib_stub.xml b/lib/kernel/doc/src/zlib_stub.xml index ac1112ad32..b111581b10 100644 --- a/lib/kernel/doc/src/zlib_stub.xml +++ b/lib/kernel/doc/src/zlib_stub.xml @@ -31,13 +31,9 @@ <rev>A</rev> </header> <module>zlib</module> - <modulesummary>Zlib Compression interface.</modulesummary> - <description><p> - - The module zlib is moved to the runtime system - application. Please see <seealso - marker="erts:zlib">zlib(3)</seealso> in the - erts reference manual instead. - - </p></description> + <modulesummary>Zlib compression interface.</modulesummary> + <description> + <p>This module is moved to the + <seealso marker="erts:zlib"><c>ERTS</c></seealso> application.</p> + </description> </erlref> diff --git a/lib/kernel/include/dist.hrl b/lib/kernel/include/dist.hrl index 02fa202c38..d6bccdf474 100644 --- a/lib/kernel/include/dist.hrl +++ b/lib/kernel/include/dist.hrl @@ -39,3 +39,4 @@ -define(DFLAG_SMALL_ATOM_TAGS, 16#4000). -define(DFLAG_UTF8_ATOMS, 16#10000). -define(DFLAG_MAP_TAG, 16#20000). +-define(DFLAG_BIG_CREATION, 16#40000). diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index b9cccf09f7..8d0a2fbf66 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -258,7 +258,7 @@ is_sticky(Mod) when is_atom(Mod) -> call({is_sticky,Mod}). -spec set_path(Path) -> 'true' | {'error', What} when Path :: [Dir :: file:filename()], - What :: 'bad_directory' | 'bad_path'. + What :: 'bad_directory'. set_path(PathList) when is_list(PathList) -> call({set_path,PathList}). -spec get_path() -> Path when diff --git a/lib/kernel/src/code_server.erl b/lib/kernel/src/code_server.erl index 153bc0c534..25dddb1f6c 100644 --- a/lib/kernel/src/code_server.erl +++ b/lib/kernel/src/code_server.erl @@ -67,7 +67,9 @@ init(Ref, Parent, [Root,Mode]) -> %% Pre-loaded modules are always sticky. ets:insert(Db, [{M,preloaded},{{sticky,M},true}]) end, erlang:pre_loaded()), - ets:insert(Db, init:fetch_loaded()), + Loaded0 = init:fetch_loaded(), + Loaded = [{M,filename:join([P])} || {M,P} <- Loaded0], %Normalize. + ets:insert(Db, Loaded), IPath = case Mode of diff --git a/lib/kernel/src/dist_util.erl b/lib/kernel/src/dist_util.erl index a9dfd16b3e..580a896357 100644 --- a/lib/kernel/src/dist_util.erl +++ b/lib/kernel/src/dist_util.erl @@ -118,7 +118,8 @@ make_this_flags(RequestType, OtherNode) -> ?DFLAG_DIST_HDR_ATOM_CACHE bor ?DFLAG_SMALL_ATOM_TAGS bor ?DFLAG_UTF8_ATOMS bor - ?DFLAG_MAP_TAG). + ?DFLAG_MAP_TAG bor + ?DFLAG_BIG_CREATION). handshake_other_started(#hs_data{request_type=ReqType}=HSData0) -> {PreOtherFlags,Node,Version} = recv_name(HSData0), @@ -196,7 +197,7 @@ check_dflag_xnc(#hs_data{other_node = Node, error_msg("** ~w: Connection attempt ~s node ~w ~s " "since it cannot handle extended ~s. " "**~n", [node(), Dir, Node, How, What]), - ?shutdown(Node) + ?shutdown2(Node, {check_dflag_xnc_failed, What}) end. @@ -576,13 +577,13 @@ recv_challenge(#hs_data{socket=Socket,other_node=Node, [Node, Challenge,Version]), {Flags,Challenge}; _ -> - ?shutdown(no_node) + ?shutdown2(no_node, {recv_challenge_failed, no_node, Ns}) catch error:badarg -> - ?shutdown(no_node) + ?shutdown2(no_node, {recv_challenge_failed, no_node, Ns}) end; - _ -> - ?shutdown(no_node) + Other -> + ?shutdown2(no_node, {recv_challenge_failed, Other}) end. @@ -626,10 +627,10 @@ recv_challenge_ack(#hs_data{socket = Socket, f_recv = FRecv, _ -> error_msg("** Connection attempt to " "disallowed node ~w ** ~n", [NodeB]), - ?shutdown(NodeB) + ?shutdown2(NodeB, {recv_challenge_ack_failed, bad_cookie}) end; - _ -> - ?shutdown(NodeB) + Other -> + ?shutdown2(NodeB, {recv_challenge_ack_failed, Other}) end. recv_status(#hs_data{kernel_pid = Kernel, socket = Socket, @@ -639,7 +640,7 @@ recv_status(#hs_data{kernel_pid = Kernel, socket = Socket, Stat = list_to_atom(StrStat), ?debug({dist_util,self(),recv_status, Node, Stat}), case Stat of - not_allowed -> ?shutdown(Node); + not_allowed -> ?shutdown2(Node, {recv_status_failed, not_allowed}); nok -> %% wait to be killed by net_kernel receive @@ -656,10 +657,10 @@ recv_status(#hs_data{kernel_pid = Kernel, socket = Socket, end; _ -> Stat end; - _Error -> + Error -> ?debug({dist_util,self(),recv_status_error, - Node, _Error}), - ?shutdown(Node) + Node, Error}), + ?shutdown2(Node, {recv_status_failed, Error}) end. @@ -758,7 +759,7 @@ setup_timer(Pid, Timeout) -> setup_timer(Pid, Timeout) after Timeout -> ?trace("Timer expires ~p, ~p~n",[Pid, Timeout]), - ?shutdown(timer) + ?shutdown2(timer, setup_timer_timeout) end. reset_timer(Timer) -> diff --git a/lib/kernel/src/file.erl b/lib/kernel/src/file.erl index d8dc152326..58b601e456 100644 --- a/lib/kernel/src/file.erl +++ b/lib/kernel/src/file.erl @@ -1227,7 +1227,8 @@ change_time(Name, {{AY, AM, AD}, {AH, AMin, ASec}}=Atime, %% Send data using sendfile %% --define(MAX_CHUNK_SIZE, (1 bsl 20)*20). %% 20 MB, has to fit in primary memory +%% 1 MB, Windows seems to behave badly if it is much larger then this +-define(MAX_CHUNK_SIZE, (1 bsl 20)). -spec sendfile(RawFile, Socket, Offset, Bytes, Opts) -> {'ok', non_neg_integer()} | {'error', inet:posix() | diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index e9d2e68472..6f9ce17c0c 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -52,10 +52,6 @@ parallel_eval/1, pmap/3, pinfo/1, pinfo/2]). -%% Deprecated calls. --deprecated([{safe_multi_server_call,2},{safe_multi_server_call,3}]). --export([safe_multi_server_call/2,safe_multi_server_call/3]). - %% gen_server exports -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -591,27 +587,6 @@ multi_server_call(Nodes, Name, Msg) Monitors = send_nodes(Nodes, Name, Msg, []), rec_nodes(Name, Monitors). -%% Deprecated functions. Were only needed when communicating with R6 nodes. - --spec safe_multi_server_call(Name, Msg) -> {Replies, BadNodes} when - Name :: atom(), - Msg :: term(), - Replies :: [Reply :: term()], - BadNodes :: [node()]. - -safe_multi_server_call(Name, Msg) -> - multi_server_call(Name, Msg). - --spec safe_multi_server_call(Nodes, Name, Msg) -> {Replies, BadNodes} when - Nodes :: [node()], - Name :: atom(), - Msg :: term(), - Replies :: [Reply :: term()], - BadNodes :: [node()]. - -safe_multi_server_call(Nodes, Name, Msg) -> - multi_server_call(Nodes, Name, Msg). - rec_nodes(Name, Nodes) -> rec_nodes(Name, Nodes, [], []). @@ -752,6 +727,11 @@ pinfo(Pid) -> -spec pinfo(Pid, Item) -> {Item, Info} | undefined | [] when Pid :: pid(), Item :: atom(), + Info :: term(); + (Pid, ItemList) -> [{Item, Info}] | undefined | [] when + Pid :: pid(), + Item :: atom(), + ItemList :: [Item], Info :: term(). pinfo(Pid, Item) when node(Pid) =:= node() -> diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index f81015be27..7f9718a354 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -34,7 +34,8 @@ purge_stacktrace/1, mult_lib_roots/1, bad_erl_libs/1, code_archive/1, code_archive2/1, on_load/1, on_load_binary/1, on_load_embedded/1, on_load_errors/1, big_boot_embedded/1, - native_early_modules/1, get_mode/1]). + native_early_modules/1, get_mode/1, + normalized_paths/1]). -export([init_per_testcase/2, end_per_testcase/2, init_per_suite/1, end_per_suite/1]). @@ -61,7 +62,7 @@ all() -> purge_stacktrace, mult_lib_roots, bad_erl_libs, code_archive, code_archive2, on_load, on_load_binary, on_load_embedded, on_load_errors, - big_boot_embedded, native_early_modules, get_mode]. + big_boot_embedded, native_early_modules, get_mode, normalized_paths]. groups() -> []. @@ -1220,6 +1221,9 @@ on_load_embedded_1(Config) -> case file:make_symlink(OnLoadApp, LinkName) of {error,enotsup} -> throw({skip,"Support for symlinks required"}); + {error,eperm} -> + %% On Windows, we may not have permissions to create symlinks. + throw({skip,"Support for symlinks required"}); ok -> ok end, @@ -1451,6 +1455,22 @@ native_early_modules_1(Architecture) -> get_mode(Config) when is_list(Config) -> interactive = code:get_mode(). +%% Make sure that the paths for all loaded modules have been normalized. +normalized_paths(_Config) -> + do_normalized_paths(erlang:loaded()). + +do_normalized_paths([M|Ms]) -> + case code:which(M) of + Special when is_atom(Special) -> + do_normalized_paths(Ms); + File when is_list(File) -> + File = filename:join([File]), + do_normalized_paths(Ms) + end; +do_normalized_paths([]) -> + ok. + + %%----------------------------------------------------------------- %% error_logger handler. %% (Copied from stdlib/test/proc_lib_SUITE.erl.) diff --git a/lib/kernel/test/error_logger_warn_SUITE.erl b/lib/kernel/test/error_logger_warn_SUITE.erl index 8ac23e1d76..a8087e11f9 100644 --- a/lib/kernel/test/error_logger_warn_SUITE.erl +++ b/lib/kernel/test/error_logger_warn_SUITE.erl @@ -366,7 +366,7 @@ rb_basic() -> 0 = one_rb_findstr([info_msg],pid_to_list(Self)), 0 = one_rb_findstr([info_report],pid_to_list(Self)), 2 = one_rb_findstr([],pid_to_list(Self)), - true = (one_rb_findstr([progress],"===") > 4), + true = (one_rb_findstr([progress],"===") > 3), rb:stop(), application:stop(sasl), stop_node(Node), @@ -396,7 +396,7 @@ rb_warnings_info() -> 1 = one_rb_findstr([info_msg],pid_to_list(Self)), 1 = one_rb_findstr([info_report],pid_to_list(Self)), 2 = one_rb_findstr([],pid_to_list(Self)), - true = (one_rb_findstr([progress],"===") > 4), + true = (one_rb_findstr([progress],"===") > 3), rb:stop(), application:stop(sasl), stop_node(Node), @@ -426,7 +426,7 @@ rb_warnings_errors() -> 0 = one_rb_findstr([info_msg],pid_to_list(Self)), 0 = one_rb_findstr([info_report],pid_to_list(Self)), 2 = one_rb_findstr([],pid_to_list(Self)), - true = (one_rb_findstr([progress],"===") > 4), + true = (one_rb_findstr([progress],"===") > 3), rb:stop(), application:stop(sasl), stop_node(Node), @@ -459,7 +459,7 @@ rb_trunc() -> 0 = one_rb_findstr([info_msg],pid_to_list(Self)), 0 = one_rb_findstr([info_report],pid_to_list(Self)), 1 = one_rb_findstr([],pid_to_list(Self)), - true = (one_rb_findstr([progress],"===") > 4), + true = (one_rb_findstr([progress],"===") > 3), rb:stop(), application:stop(sasl), stop_node(Node), diff --git a/lib/kernel/test/file_SUITE.erl b/lib/kernel/test/file_SUITE.erl index e3551df7f5..5f049c6f99 100644 --- a/lib/kernel/test/file_SUITE.erl +++ b/lib/kernel/test/file_SUITE.erl @@ -83,7 +83,7 @@ -export([unicode/1]). -export([altname/1]). --export([large_file/0, large_file/1, large_write/1]). +-export([large_file/0, large_file/1, large_write/0, large_write/1]). -export([read_line_1/1, read_line_2/1, read_line_3/1,read_line_4/1]). @@ -3663,6 +3663,9 @@ do_large_file(Name) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +large_write() -> + [{timetrap,{minutes,20}}]. + large_write(Config) when is_list(Config) -> run_large_file_test(Config, fun(Name) -> do_large_write(Name) end, diff --git a/lib/kernel/test/file_name_SUITE.erl b/lib/kernel/test/file_name_SUITE.erl index e6cc3df389..10b6b105d0 100644 --- a/lib/kernel/test/file_name_SUITE.erl +++ b/lib/kernel/test/file_name_SUITE.erl @@ -139,7 +139,12 @@ home_dir(Config) when is_list(Config) -> test_server:stop_node(Node), ok after - os:putenv(SaveOldName,SaveOldValue), + case SaveOldValue of + false -> + os:unsetenv(SaveOldName); + _ -> + os:putenv(SaveOldName,SaveOldValue) + end, rm_rf(prim_file,NewHome) end catch @@ -185,9 +190,7 @@ normal(Config) when is_list(Config) -> try Priv = proplists:get_value(priv_dir, Config), file:set_cwd(Priv), - put(file_module,prim_file), ok = check_normal(prim_file), - put(file_module,file), ok = check_normal(file), %% If all is good, delete dir again (avoid hanging dir on windows) rm_rf(file,"normal_dir"), @@ -207,9 +210,7 @@ icky(Config) when is_list(Config) -> try Priv = proplists:get_value(priv_dir, Config), file:set_cwd(Priv), - put(file_module,prim_file), ok = check_icky(prim_file), - put(file_module,file), ok = check_icky(file), %% If all is good, delete dir again (avoid hanging dir on windows) rm_rf(file,"icky_dir"), @@ -228,12 +229,10 @@ very_icky(Config) when is_list(Config) -> try Priv = proplists:get_value(priv_dir, Config), file:set_cwd(Priv), - put(file_module,prim_file), case check_very_icky(prim_file) of need_unicode_mode -> {skipped,"VM needs to be started in Unicode filename mode"}; ok -> - put(file_module,file), ok = check_very_icky(file), %% If all is good, delete dir again %% (avoid hanging dir on windows) @@ -249,10 +248,12 @@ very_icky(Config) when is_list(Config) -> check_normal(Mod) -> {ok,Dir} = Mod:get_cwd(), try - make_normal_dir(Mod), + NormalDir = make_normal_dir(Mod, "normal_dir"), + io:format("Normaldir = ~p\n", [NormalDir]), + L1 = lists:sort(list(NormalDir)), {ok, L0} = Mod:list_dir("."), + io:format("L0 = ~p\n", [L0]), L1 = lists:sort(L0), - L1 = lists:sort(list(normal_dir())), {ok,D2} = Mod:get_cwd(), true = is_list(D2), case Mod:altname("fil1") of @@ -262,45 +263,45 @@ check_normal(Mod) -> ok end, [ true = is_list(El) || El <- L1], - Syms = [ {S,Targ,list_to_binary(get_data(Targ,normal_dir()))} - || {T,S,Targ} <- normal_dir(), T =:= symlink ], + Syms = [ {S,Targ,list_to_binary(get_data(Targ, NormalDir))} + || {T,S,Targ} <- NormalDir, T =:= symlink ], [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], [ {ok, Targ} = fixlink(Mod:read_link(SymL)) || {SymL,Targ,_} <- Syms ], - chk_cre_dir(Mod,[{directory,"temp_dir",normal_dir()}]), + {ok,BeginAt} = Mod:get_cwd(), true = is_list(BeginAt), + TempDir = "temp_dir", + make_normal_dir(Mod, TempDir), {error,enoent} = Mod:set_cwd("tmp_dir"), - ok = Mod:set_cwd("temp_dir"), {ok, NowAt} = Mod:get_cwd(), true = BeginAt =/= NowAt, ok = Mod:set_cwd(".."), {ok,BeginAt} = Mod:get_cwd(), - rm_r(Mod,"temp_dir"), + rm_r(Mod, TempDir), true = is_list(Dir), [ true = is_list(FN) || FN <- L0 ], - case has_links() of - true -> - ok = Mod:make_link("fil1","nisse"), + case Mod:make_link("fil1","nisse") of + ok -> {ok, <<"fil1">>} = Mod:read_file("nisse"), {ok, #file_info{type = regular}} = Mod:read_link_info("nisse"), ok = Mod:delete("nisse"), {ok, <<"fil1">>} = Mod:read_file("fil1"), {error,enoent} = Mod:read_file("nisse"), {error,enoent} = Mod:read_link_info("nisse"); - false -> + {error,enotsup} -> ok end, [ begin {ok, FD} = Mod:open(Name,[read]), {ok, Content} = Mod:read(FD,1024), ok = file:close(FD) - end || {regular,Name,Content} <- normal_dir() ], + end || {regular,Name,Content} <- NormalDir ], [ begin {ok, FD} = Mod:open(Name,[read,binary]), BC = list_to_binary(Content), {ok, BC} = Mod:read(FD,1024), ok = file:close(FD) - end || {regular,Name,Content} <- normal_dir() ], + end || {regular,Name,Content} <- NormalDir ], Mod:rename("fil1","tmp_fil1"), {ok, <<"fil1">>} = Mod:read_file("tmp_fil1"), {error,enoent} = Mod:read_file("fil1"), @@ -333,11 +334,11 @@ check_icky(Mod) -> try true=(length("åäö") =:= 3), UniMode = file:native_name_encoding() =/= latin1, - make_icky_dir(Mod), + IckyDir = make_icky_dir(Mod, "icky_dir"), {ok, L0} = Mod:list_dir_all("."), L1 = lists:sort(L0), - io:format("~p~n~p~n~n",[L1,lists:sort(list(icky_dir()))]), - L1 = lists:sort(convlist(list(icky_dir()))), + io:format("~p~n~p~n~n",[L1,lists:sort(list(IckyDir))]), + L1 = lists:sort(convlist(list(IckyDir))), {ok,D2} = Mod:get_cwd(), true = is_list(D2), %% Altname only on windows, and there are no non native filenames there @@ -348,16 +349,16 @@ check_icky(Mod) -> %% ok %% end, [ true = ((is_list(El) or (UniMode and is_binary(El)))) || El <- L1], - Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,icky_dir()))} - || {T,S,Targ} <- icky_dir(), T =:= symlink ], + Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,IckyDir))} + || {T,S,Targ} <- IckyDir, T =:= symlink ], [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], [ {ok, Targ} = fixlink(Mod:read_link_all(SymL)) || {SymL,Targ,_} <- Syms ], - chk_cre_dir(Mod,[{directory,"åäö_dir",icky_dir()}]), + {ok,BeginAt} = Mod:get_cwd(), true = is_list(BeginAt), + _ = make_icky_dir(Mod, "åäö_dir"), {error,enoent} = Mod:set_cwd("åä_dir"), - ok = Mod:set_cwd("åäö_dir"), {ok, NowAt} = Mod:get_cwd(), true = is_list(NowAt), true = BeginAt =/= NowAt, @@ -365,10 +366,11 @@ check_icky(Mod) -> {ok,BeginAt} = Mod:get_cwd(), rm_r2(Mod,"åäö_dir"), {OS,_} = os:type(), + %% Check that treat_icky really converts to the same as the OS case UniMode of true -> - chk_cre_dir(Mod,[{directory,"åäö_dir",[]}]), + ok = Mod:make_dir("åäö_dir"), ok = Mod:set_cwd("åäö_dir"), ok = Mod:write_file(<<"ååå">>,<<"hello">>), Treated = treat_icky(<<"ååå">>), @@ -381,17 +383,17 @@ check_icky(Mod) -> ok end, - chk_cre_dir(Mod,[{directory,treat_icky(<<"åäö_dir">>),icky_dir()}]), + _ = make_icky_dir(Mod, treat_icky(<<"åäö_dir">>)), if UniMode and (OS =/= win32) -> {error,enoent} = Mod:set_cwd("åäö_dir"); true -> ok end, + ok = Mod:set_cwd(".."), {ok,BeginAt} = Mod:get_cwd(), - case has_links() of - true -> - ok = Mod:make_link("fil1","nisseö"), + case Mod:make_link("fil1", "nisseö") of + ok -> {ok, <<"fil1">>} = Mod:read_file("nisseö"), {ok, #file_info{type = regular}} = Mod:read_link_info("nisseö"), ok = Mod:delete("nisseö"), @@ -404,20 +406,20 @@ check_icky(Mod) -> {error,enoent} = Mod:read_link_info("nisseö"), {error,enoent} = Mod:read_file(treat_icky(<<"nisseö">>)), {error,enoent} = Mod:read_link_info(treat_icky(<<"nisseö">>)); - false -> + {error,enotsup} -> ok end, [ begin {ok, FD} = Mod:open(Name,[read]), {ok, Content} = Mod:read(FD,1024), ok = file:close(FD) - end || {regular,Name,Content} <- icky_dir() ], + end || {regular,Name,Content} <- IckyDir ], [ begin {ok, FD} = Mod:open(Name,[read,binary]), BC = list_to_binary([Content]), {ok, BC} = Mod:read(FD,1024), ok = file:close(FD) - end || {regular,Name,Content} <- icky_dir() ], + end || {regular,Name,Content} <- IckyDir ], Mod:rename("åäö2","åäö_fil1"), {ok, <<"åäö2">>} = Mod:read_file("åäö_fil1"), {error,enoent} = Mod:read_file("åäö2"), @@ -471,33 +473,33 @@ check_very_icky(Mod) -> true -> ok end, - make_very_icky_dir(Mod), - {ok, L0} = Mod:list_dir_all("."), - L1 = lists:sort(L0), - L1 = lists:sort(convlist(list(very_icky_dir()))), + VeryIckyDir = make_very_icky_dir(Mod, "very_icky_dir"), + Expected = lists:sort(convlist(list(VeryIckyDir))), + {ok, Actual} = Mod:list_dir_all("."), + Expected = lists:sort(Actual), {ok,D2} = Mod:get_cwd(), true = is_list(D2), - [ true = ((is_list(El) or is_binary(El))) || El <- L1], - Syms = [ {S,conv(Targ),list_to_binary(get_data(Targ,very_icky_dir()))} - || {T,S,Targ} <- very_icky_dir(), T =:= symlink ], + [ true = ((is_list(El) or is_binary(El))) || El <- Expected], + Syms = [{S,conv(Targ),list_to_binary(get_data(Targ, VeryIckyDir))} + || {symlink,S,Targ} <- VeryIckyDir], [ {ok, Cont} = Mod:read_file(SymL) || {SymL,_,Cont} <- Syms ], [ {ok, Targ} = fixlink(Mod:read_link_all(SymL)) || {SymL,Targ,_} <- Syms ], - chk_cre_dir(Mod,[{directory,[1088,1079,1091]++"_dir",very_icky_dir()}]), + {ok,BeginAt} = Mod:get_cwd(), + OtherDir = [1088,1079,1091] ++ "_dir", true = is_list(BeginAt), + make_very_icky_dir(Mod, OtherDir), {error,enoent} = Mod:set_cwd("åä_dir"), - ok = Mod:set_cwd([1088,1079,1091]++"_dir"), {ok, NowAt} = Mod:get_cwd(), true = is_list(NowAt), true = BeginAt =/= NowAt, ok = Mod:set_cwd(".."), {ok,BeginAt} = Mod:get_cwd(), - rm_r2(Mod,[1088,1079,1091]++"_dir"), + rm_r2(Mod, OtherDir), - case has_links() of - true -> - ok = Mod:make_link("fil1","nisse"++[1088,1079,1091]), + case Mod:make_link("fil1","nisse"++[1088,1079,1091]) of + ok -> {ok, <<"fil1">>} = Mod:read_file("nisse"++[1088,1079,1091]), {ok, #file_info{type = regular}} = @@ -513,20 +515,20 @@ check_very_icky(Mod) -> {error,enoent} = Mod:read_link_info("nisse"++[1088,1079,1091]), {error,enoent} = Mod:read_file(<<"nisseö">>), {error,enoent} = Mod:read_link_info(<<"nisseö">>); - false -> + {error,enotsup} -> ok end, [ begin {ok, FD} = Mod:open(Name,[read]), {ok, Content} = Mod:read(FD,1024), ok = file:close(FD) - end || {regular,Name,Content} <- very_icky_dir() ], + end || {regular,Name,Content} <- VeryIckyDir ], [ begin {ok, FD} = Mod:open(Name,[read,binary]), BC = list_to_binary([Content]), {ok, BC} = Mod:read(FD,1024), ok = file:close(FD) - end || {regular,Name,Content} <- very_icky_dir() ], + end || {regular,Name,Content} <- VeryIckyDir ], Mod:rename([956,965,963,954,959,49], [956,965,963,954,959]++"_fil1"), {ok, <<"åäö2">>} = Mod:read_file([956,965,963,954,959]++"_fil1"), @@ -610,90 +612,35 @@ rm_r2(Mod,Dir) -> {ok, #file_info{type = symlink}} -> ok = Mod:delete(Dir) end. -chk_cre_dir(_,[]) -> - ok; -chk_cre_dir(Mod,[{regular,Name,Content}|T]) -> - %% io:format("~p~n",[Name]), - ok = Mod:write_file(Name,Content), - chk_cre_dir(Mod,T); -chk_cre_dir(Mod,[{link,Name,Target}|T]) -> - ok = Mod:make_link(Target,Name), - chk_cre_dir(Mod,T); -chk_cre_dir(Mod,[{symlink,Name,Target}|T]) -> - ok = Mod:make_symlink(Target,Name), - chk_cre_dir(Mod,T); -chk_cre_dir(Mod,[{directory,Name,Content}|T]) -> - ok = Mod:make_dir(Name), - %% io:format("Content = ~p~n",[Content]), - Content2 = [{Ty,filename:join(Name,N),case Ty of link -> filename:join(Name,C); _ -> C end} || {Ty,N,C} <- Content ], - %% io:format("Content2 = ~p~n",[Content2]), - chk_cre_dir(Mod,Content2), - chk_cre_dir(Mod,T). -has_links() -> - case os:type() of - {win32,_} -> - case os:version() of - {N,NN,_} when (N > 5) andalso (NN >= 1) -> - true; - _ -> - false - end; - _ -> - true - end. +make_normal_dir(Mod, DirName) -> + Dir = [{regular,"fil1","fil1"}, + {regular,"fil2","fil2"}, + {hardlink,"fil3","fil2"}, + {symlink,"fil4","fil2"}, + {directory,"subdir", + [{regular,"subfil1","subfil1"}]}], + rm_rf(Mod, DirName), + Mod:make_dir(DirName), + Mod:set_cwd(DirName), + make_dir_contents(Dir, Mod). -make_normal_dir(Mod) -> - rm_rf(Mod,"normal_dir"), - Mod:make_dir("normal_dir"), - Mod:set_cwd("normal_dir"), - Mod:write_file("fil1","fil1"), - Mod:write_file("fil2","fil2"), - case has_links() of - true -> - Mod:make_link("fil2","fil3"), - Mod:make_symlink("fil2","fil4"); - _ -> - ok - end, - Mod:make_dir("subdir"), - Mod:write_file(filename:join("subdir","subfil1"),"subfil1"), - ok. - -normal_dir() -> - [{regular,"fil1","fil1"}, - {regular,"fil2","fil2"}] ++ - case has_links() of - true -> - [{regular,"fil3","fil2"}, - {symlink,"fil4","fil2"}]; - false -> - [] - end ++ - [{directory,"subdir", - [{regular,"subfil1","subfil1"}]}]. - -make_icky_dir(Mod) -> - rm_rf(Mod,"icky_dir"), - Icky=icky_dir(), - chk_cre_dir(Mod,[{directory,"icky_dir",linkify([],Icky)}]), - Mod:set_cwd("icky_dir"), - ok. - -linkify(_Passed,[]) -> - []; -linkify(Passed,[{regular,Name,Content}|T]) -> - Regulars = [ {N,C} || {regular,N,C} <- Passed, N =/= Name ], - case lists:keysearch(Content,2,Regulars) of - {value, {Linkto, Content}} -> - [{link,Name,Linkto} | linkify(Passed,T)]; - _ -> - [{regular,Name,Content} | linkify([{regular,Name,Content}|Passed],T)] - end; -linkify(Passed,[{directory, Name, Content}|T]) -> - [{directory,Name, linkify(Content,Content)}|linkify(Passed,T)]; -linkify(Passed,[H|T]) -> - [H|linkify([H|Passed],T)]. +make_icky_dir(Mod, IckyDirName) -> + Icky = [{regular,"fil1","fil1"}, + {regular,"åäö2","åäö2"}, + {hardlink,"åäö3","åäö2"}, + {symlink,"åäö4","åäö2"}, + {regular,treat_icky(<<"åäö5">>),"åäö5"}, + {symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}, + {directory,treat_icky(<<"åäösubdir2">>), + [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, + {regular,"åäösubfil3","åäösubfil13"}]}, + {directory,"åäösubdir", + [{regular,"åäösubfil1","åäösubfil1"}]}], + rm_rf(Mod, IckyDirName), + ok = Mod:make_dir(IckyDirName), + ok = Mod:set_cwd(IckyDirName), + make_dir_contents(Icky, Mod). hopeless_darwin() -> case {os:type(),os:version()} of @@ -703,58 +650,24 @@ hopeless_darwin() -> false end. -icky_dir() -> - [{regular,"fil1","fil1"}, - {regular,"åäö2","åäö2"}] ++ - case has_links() of - true -> - [{regular,"åäö3","åäö2"}, - {symlink,"åäö4","åäö2"}]; - false -> - [] - end ++ - [{regular,treat_icky(<<"åäö5">>),"åäö5"}] ++ - case has_links() of - true -> - [{symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}]; - false -> - [] - end ++ - [{directory,treat_icky(<<"åäösubdir2">>), - [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, - {regular,"åäösubfil3","åäösubfil13"}]}, - {directory,"åäösubdir", - [{regular,"åäösubfil1","åäösubfil1"}]}]. - -make_very_icky_dir(Mod) -> - rm_rf(Mod,"very_icky_dir"), - Icky=very_icky_dir(), - chk_cre_dir(Mod,[{directory,"very_icky_dir",linkify([],Icky)}]), - Mod:set_cwd("very_icky_dir"), - ok. - -very_icky_dir() -> - [{regular,"fil1","fil1"}, - {regular,[956,965,963,954,959,49],"åäö2"}] ++ - case has_links() of - true -> - [{regular,[956,965,963,954,959,50],"åäö2"}, - {symlink,[956,965,963,954,959,51],[956,965,963,954,959,49]}]; - false -> - [] - end ++ - [{regular,treat_icky(<<"åäö5">>),"åäö5"}] ++ - case has_links() of - true -> - [{symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}]; - false -> - [] - end ++ - [{directory,treat_icky(<<"åäösubdir2">>), - [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, - {regular,"åäösubfil3","åäösubfil13"}]}, - {directory,[956,965,963,954,959]++"subdir1", - [{regular,[956,965,963,954,959]++"subfil1","åäösubfil1"}]}]. +make_very_icky_dir(Mod, DirName) -> + Desc = [{regular,"fil1","fil1"}, + {regular,[956,965,963,954,959,49],"åäö2"}, + {hardlink,[956,965,963,954,959,50], + [956,965,963,954,959,49], + "åäö2"}, + {symlink,[956,965,963,954,959,51],[956,965,963,954,959,49]}, + {regular,treat_icky(<<"åäö5">>),"åäö5"}, + {symlink,treat_icky(<<"åäö6">>),treat_icky(<<"åäö5">>)}, + {directory,treat_icky(<<"åäösubdir2">>), + [{regular,treat_icky(<<"åäösubfil2">>),"åäösubfil12"}, + {regular,"åäösubfil3","åäösubfil13"}]}, + {directory,[956,965,963,954,959]++"subdir1", + [{regular,[956,965,963,954,959]++"subfil1","åäösubfil1"}]}], + rm_rf(Mod, DirName), + ok = Mod:make_dir(DirName), + ok = Mod:set_cwd(DirName), + make_dir_contents(Desc, Mod). %% Some OS'es simply do not allow non UTF8 filenames treat_icky(Bin) -> @@ -827,6 +740,48 @@ conv(L) -> end. +make_dir_contents([{regular,Name,Contents}=H|T], Mod) -> + ok = Mod:write_file(Name, Contents), + [H|make_dir_contents(T, Mod)]; +make_dir_contents([{hardlink,Target,Name}|T], Mod) -> + case Mod:make_link(Name, Target) of + ok -> + [{regular,Target,Name}|make_dir_contents(T, Mod)]; + {error,enotsup} -> + make_dir_contents(T, Mod) + end; +make_dir_contents([{hardlink,Target,Name,Contents}|T], Mod) -> + case Mod:make_link(Name, Target) of + ok -> + [{regular,Target,Contents}|make_dir_contents(T, Mod)]; + {error,enotsup} -> + make_dir_contents(T, Mod) + end; +make_dir_contents([{symlink,Target,Name}=H|T], Mod) -> + case Mod:make_symlink(Name, Target) of + ok -> + [H|make_dir_contents(T, Mod)]; + {error,enotsup} -> + make_dir_contents(T, Mod); + {error,eperm} -> + make_dir_contents(T, Mod) + end; +make_dir_contents([{directory,Dir,C0}|T], Mod) -> + ok = Mod:make_dir(Dir), + C1 = [case Op of + Link when Link =:= hardlink; Link =:= symlink -> + {Op,filename:join(Dir, Name0),filename:join(Dir, Extra)}; + _ -> + {Op,filename:join(Dir, Name0),Extra} + end || {Op,Name0,Extra} <- C0], + C2 = make_dir_contents(C1, Mod), + C = [{Op,filename:basename(Name0),Extra} || + {Op,Name0,Extra} <- C2], + [{directory,Dir,C}|make_dir_contents(T, Mod)]; +make_dir_contents([], _Mod) -> + []. + + rand_comp_decomp(Max) -> N = rand:uniform(Max), L = [ rand_decomp() || _ <- lists:seq(1,N) ], diff --git a/lib/kernel/test/gen_sctp_SUITE.erl b/lib/kernel/test/gen_sctp_SUITE.erl index e025f075bb..f836b2aa94 100644 --- a/lib/kernel/test/gen_sctp_SUITE.erl +++ b/lib/kernel/test/gen_sctp_SUITE.erl @@ -29,7 +29,8 @@ init_per_group/2,end_per_group/2, init_per_testcase/2, end_per_testcase/2]). -export( - [basic/1, + [skip_old_solaris/1, + basic/1, api_open_close/1,api_listen/1,api_connect_init/1,api_opts/1, xfer_min/1,xfer_active/1,def_sndrcvinfo/1,implicit_inet6/1, open_multihoming_ipv4_socket/1, @@ -46,20 +47,28 @@ suite() -> [{ct_hooks,[ts_install_cth]}, {timetrap,{minutes,1}}]. -all() -> - [basic, api_open_close, api_listen, api_connect_init, - api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6, - open_multihoming_ipv4_socket, - open_unihoming_ipv6_socket, - open_multihoming_ipv6_socket, - open_multihoming_ipv4_and_ipv6_socket, active_n, - basic_stream, xfer_stream_min, peeloff_active_once, - peeloff_active_true, peeloff_active_n, buffers, - names_unihoming_ipv4, names_unihoming_ipv6, - names_multihoming_ipv4, names_multihoming_ipv6]. +all() -> + G = case is_old_solaris() of + true -> old_solaris; + false -> extensive + end, + [{group,smoke}, + {group,G}]. groups() -> - []. + [{smoke,[],[basic,basic_stream]}, + {old_solaris,[],[skip_old_solaris]}, + {extensive,[], + [api_open_close, api_listen, api_connect_init, + api_opts, xfer_min, xfer_active, def_sndrcvinfo, implicit_inet6, + open_multihoming_ipv4_socket, + open_unihoming_ipv6_socket, + open_multihoming_ipv6_socket, + open_multihoming_ipv4_and_ipv6_socket, active_n, + xfer_stream_min, peeloff_active_once, + peeloff_active_true, peeloff_active_n, buffers, + names_unihoming_ipv4, names_unihoming_ipv6, + names_multihoming_ipv4, names_multihoming_ipv6]}]. init_per_suite(_Config) -> case gen_sctp:open() of @@ -91,7 +100,11 @@ end_per_testcase(_Func, _Config) -> -define(LOGVAR(Var), begin io:format(??Var" = ~p~n", [Var]) end). +is_old_solaris() -> + os:type() =:= {unix,sunos} andalso os:version() < {5,12,0}. +skip_old_solaris(_Config) -> + {skip,"Unreliable test cases and/or implementation on old Solaris"}. %% Hello world. basic(Config) when is_list(Config) -> diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index 63aa19833a..6b64c83fc2 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -2320,70 +2320,82 @@ active_once_closed(Config) when is_list(Config) -> %% Test the send_timeout socket option. send_timeout(Config) when is_list(Config) -> + Dir = filename:dirname(code:which(?MODULE)), + {ok,RNode} = test_server:start_node(?UNIQ_NODE_NAME, slave, + [{args,"-pa " ++ Dir}]), + %% Basic - BasicFun = - fun(AutoClose) -> - {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose), - {error,timeout} = - Loop(fun() -> - Res = gen_tcp:send(A,<<1:10000>>), - %%erlang:display(Res), - Res - end), - %% Check that the socket is not busy/closed... - Error = after_send_timeout(AutoClose), - {error,Error} = gen_tcp:send(A,<<"Hej">>), - test_server:stop_node(RNode) - end, - BasicFun(false), - BasicFun(true), - %% Check timeout length + send_timeout_basic(false, RNode), + send_timeout_basic(true, RNode), + + BinData = <<1:10000>>, + + %% Check timeout length. Self = self(), Pid = spawn(fun() -> - {Loop,A,RNode} = setup_timeout_sink(1000, true), - {error,timeout} = Loop(fun() -> - Res = gen_tcp:send(A,<<1:10000>>), - %%erlang:display(Res), - Self ! Res, - Res - end), - test_server:stop_node(RNode) + A = setup_timeout_sink(RNode, 1000, true), + Send = fun() -> + Res = gen_tcp:send(A, BinData), + Self ! Res, + Res + end, + {error,timeout} = timeout_sink_loop(Send) end), Diff = get_max_diff(), io:format("Max time for send: ~p~n",[Diff]), true = (Diff > 500) and (Diff < 1500), - %% Let test_server slave die... + + %% Wait for the process to die. Mon = erlang:monitor(process, Pid), receive {'DOWN',Mon,process,Pid,_} -> ok end, + %% Check that parallell writers do not hang forever - ParaFun = - fun(AutoClose) -> - {Loop,A,RNode} = setup_timeout_sink(1000, AutoClose), - SenderFun = fun() -> - {error,Error} = - Loop(fun() -> - gen_tcp:send(A, <<1:10000>>) - end), - Self ! {error,Error} - end, - spawn_link(SenderFun), - spawn_link(SenderFun), - receive - {error,timeout} -> ok - after 10000 -> - exit(timeout) - end, - NextErr = after_send_timeout(AutoClose), - receive - {error,NextErr} -> ok - after 10000 -> - exit(timeout) - end, - {error,NextErr} = gen_tcp:send(A,<<"Hej">>), - test_server:stop_node(RNode) - end, - ParaFun(false), - ParaFun(true), + send_timeout_para(false, RNode), + send_timeout_para(true, RNode), + + test_server:stop_node(RNode), + + ok. + +send_timeout_basic(AutoClose, RNode) -> + BinData = <<1:10000>>, + + A = setup_timeout_sink(RNode, 1000, AutoClose), + Send = fun() -> gen_tcp:send(A, BinData) end, + {error,timeout} = timeout_sink_loop(Send), + + %% Check that the socket is not busy/closed... + Error = after_send_timeout(AutoClose), + {error,Error} = gen_tcp:send(A, <<"Hej">>), + ok. + +send_timeout_para(AutoClose, RNode) -> + BinData = <<1:10000>>, + + A = setup_timeout_sink(RNode, 1000, AutoClose), + Self = self(), + SenderFun = fun() -> + Send = fun() -> gen_tcp:send(A, BinData) end, + {error,Error} = timeout_sink_loop(Send), + Self ! {error,Error} + end, + spawn_link(SenderFun), + spawn_link(SenderFun), + + receive + {error,timeout} -> ok + after 10000 -> + exit(timeout) + end, + + NextErr = after_send_timeout(AutoClose), + receive + {error,NextErr} -> ok + after 10000 -> + exit(timeout) + end, + + {error,NextErr} = gen_tcp:send(A, <<"Hej">>), ok. mad_sender(S) -> @@ -2406,31 +2418,33 @@ flush() -> %% Test the send_timeout socket option for active sockets. send_timeout_active(Config) when is_list(Config) -> - %% Basic - BasicFun = - fun(AutoClose) -> - {Loop,A,RNode,C} = setup_active_timeout_sink(1, AutoClose), - inet:setopts(A, [{active, once}]), - Mad = spawn_link(RNode,fun() -> mad_sender(C) end), - {error,timeout} = - Loop(fun() -> - receive - {tcp, _Sock, _Data} -> - inet:setopts(A, [{active, once}]), - Res = gen_tcp:send(A,lists:duplicate(1000, $a)), - Res; - Err -> - io:format("sock closed: ~p~n", [Err]), - Err - end - end), - unlink(Mad), - exit(Mad,kill), - test_server:stop_node(RNode) + Dir = filename:dirname(code:which(?MODULE)), + {ok,RNode} = test_server:start_node(?UNIQ_NODE_NAME, slave, + [{args,"-pa " ++ Dir}]), + do_send_timeout_active(false, RNode), + do_send_timeout_active(true, RNode), + test_server:stop_node(RNode), + ok. + +do_send_timeout_active(AutoClose, RNode) -> + {A,C} = setup_active_timeout_sink(RNode, 1, AutoClose), + inet:setopts(A, [{active, once}]), + Mad = spawn_link(RNode, fun() -> mad_sender(C) end), + ListData = lists:duplicate(1000, $a), + F = fun() -> + receive + {tcp, _Sock, _Data} -> + inet:setopts(A, [{active, once}]), + Res = gen_tcp:send(A, ListData), + Res; + Err -> + io:format("sock closed: ~p~n", [Err]), + Err + end end, - BasicFun(false), - flush(), - BasicFun(true), + {error,timeout} = timeout_sink_loop(F), + unlink(Mad), + exit(Mad, kill), flush(), ok. @@ -2475,7 +2489,7 @@ setup_closed_ao() -> Dir = filename:dirname(code:which(?MODULE)), {ok,R} = test_server:start_node(?UNIQ_NODE_NAME, slave, [{args,"-pa " ++ Dir}]), - Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))), + Host = get_hostname(node()), {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2}]), Fun = fun(F) -> receive @@ -2514,11 +2528,8 @@ setup_closed_ao() -> test_server:stop_node(R), {Loop,A}. -setup_timeout_sink(Timeout, AutoClose) -> - Dir = filename:dirname(code:which(?MODULE)), - {ok,R} = test_server:start_node(?UNIQ_NODE_NAME, slave, - [{args,"-pa " ++ Dir}]), - Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))), +setup_timeout_sink(RNode, Timeout, AutoClose) -> + Host = get_hostname(node()), {ok, L} = gen_tcp:listen(0, [{active,false},{packet,2}, {send_timeout,Timeout}, {send_timeout_close,AutoClose}]), @@ -2529,7 +2540,7 @@ setup_timeout_sink(Timeout, AutoClose) -> die -> ok end end, - Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]), + Pid = rpc:call(RNode, erlang, spawn, [fun() -> Fun(Fun) end]), {ok, Port} = inet:port(L), Remote = fun(Fu) -> Pid ! {self(), Fu}, @@ -2543,36 +2554,23 @@ setup_timeout_sink(Timeout, AutoClose) -> {ok,A} = gen_tcp:accept(L), gen_tcp:send(A,"Hello"), {ok, "Hello"} = Remote(fun() -> gen_tcp:recv(C,0) end), - Loop2 = fun(_,_,0) -> - {failure, timeout}; - (L2,F2,N) -> - Ret = F2(), - io:format("~p~n",[Ret]), - case Ret of - ok -> receive after 1 -> ok end, - L2(L2,F2,N-1); - Other -> Other - end - end, - Loop = fun(F3) -> Loop2(Loop2,F3,1000) end, - {Loop,A,R}. - -setup_active_timeout_sink(Timeout, AutoClose) -> - Dir = filename:dirname(code:which(?MODULE)), - {ok,R} = test_server:start_node(?UNIQ_NODE_NAME, slave, - [{args,"-pa " ++ Dir}]), - Host = list_to_atom(lists:nth(2,string:tokens(atom_to_list(node()),"@"))), - {ok, L} = gen_tcp:listen(0, [binary,{active,false},{packet,0},{nodelay, true},{keepalive, true}, - {send_timeout,Timeout}, - {send_timeout_close,AutoClose}]), + A. + +setup_active_timeout_sink(RNode, Timeout, AutoClose) -> + Host = get_hostname(node()), + ListenOpts = [binary,{active,false},{packet,0}, + {nodelay,true},{keepalive,true}, + {send_timeout,Timeout},{send_timeout_close,AutoClose}], + {ok, L} = gen_tcp:listen(0, ListenOpts), Fun = fun(F) -> receive {From,X} when is_function(X) -> - From ! {self(),X()}, F(F); + From ! {self(),X()}, + F(F); die -> ok end end, - Pid = rpc:call(R,erlang,spawn,[fun() -> Fun(Fun) end]), + Pid = rpc:call(RNode, erlang, spawn, [fun() -> Fun(Fun) end]), {ok, Port} = inet:port(L), Remote = fun(Fu) -> Pid ! {self(), Fu}, @@ -2580,26 +2578,22 @@ setup_active_timeout_sink(Timeout, AutoClose) -> end end, {ok, C} = Remote(fun() -> - gen_tcp:connect(Host,Port, - [{active,false}]) + gen_tcp:connect(Host, Port, [{active,false}]) end), {ok,A} = gen_tcp:accept(L), - gen_tcp:send(A,"Hello"), - {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C,0) end), - Loop2 = fun(_,_,0) -> - {failure, timeout}; - (L2,F2,N) -> - Ret = F2(), - io:format("~p~n",[Ret]), - case Ret of - ok -> receive after 1 -> ok end, - L2(L2,F2,N-1); - Other -> Other - end - end, - Loop = fun(F3) -> Loop2(Loop2,F3,1000) end, - {Loop,A,R,C}. + gen_tcp:send(A, "Hello"), + {ok, "H"++_} = Remote(fun() -> gen_tcp:recv(C, 0) end), + {A,C}. +timeout_sink_loop(Action) -> + Ret = Action(), + case Ret of + ok -> + receive after 1 -> ok end, + timeout_sink_loop(Action); + Other -> + Other + end. has_superfluous_schedulers() -> case {erlang:system_info(schedulers), @@ -3016,3 +3010,7 @@ oct_aloop(S,X,Times) -> end. ok({ok,V}) -> V. + +get_hostname(Name) -> + "@"++Host = lists:dropwhile(fun(C) -> C =/= $@ end, atom_to_list(Name)), + Host. diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl index accac63aa1..548b27db97 100644 --- a/lib/kernel/test/heart_SUITE.erl +++ b/lib/kernel/test/heart_SUITE.erl @@ -37,6 +37,11 @@ -define(DEFAULT_TIMEOUT_SECS, 120). +-define(UNIQ_NODE_NAME, + list_to_atom(?MODULE_STRING ++ "__" ++ + atom_to_list(?FUNCTION_NAME) ++ "_" ++ + integer_to_list(erlang:unique_integer([positive])))). + init_per_testcase(_Func, Config) -> Config. @@ -118,7 +123,7 @@ start_check(Type, Name, Envs) -> {ok, Node}. start(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), rpc:call(Node, init, reboot, []), receive {nodedown, Node} -> ok @@ -143,12 +148,12 @@ start(Config) when is_list(Config) -> %% Purpose: %% Check that a node is up and running after a init:restart/0 restart(Config) when is_list(Config) -> - {ok, Node} = start_check(loose, heart_test), + {ok, Node} = start_check(loose, ?UNIQ_NODE_NAME), rpc:call(Node, init, restart, []), receive {nodedown, Node} -> ok - after 2000 -> + after 5000 -> ct:fail(node_not_closed) end, timer:sleep(5000), @@ -159,7 +164,7 @@ restart(Config) when is_list(Config) -> %% Purpose: %% Check that a node is up and running after a init:reboot/0 reboot(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), ok = rpc:call(Node, heart, set_cmd, [atom_to_list(lib:progname()) ++ @@ -193,7 +198,8 @@ node_start_immediately_after_crash(Config) when is_list(Config) -> node_start_immediately_after_crash_test(Config) when is_list(Config) -> - {ok, Node} = start_check(loose, heart_test_imm, [{"ERL_CRASH_DUMP_SECONDS", "0"}]), + {ok, Node} = start_check(loose, ?UNIQ_NODE_NAME, + [{"ERL_CRASH_DUMP_SECONDS", "0"}]), ok = rpc:call(Node, heart, set_cmd, [atom_to_list(lib:progname()) ++ @@ -243,7 +249,8 @@ node_start_soon_after_crash(Config) when is_list(Config) -> end. node_start_soon_after_crash_test(Config) when is_list(Config) -> - {ok, Node} = start_check(loose, heart_test_soon, [{"ERL_CRASH_DUMP_SECONDS", "10"}]), + {ok, Node} = start_check(loose, ?UNIQ_NODE_NAME, + [{"ERL_CRASH_DUMP_SECONDS", "10"}]), ok = rpc:call(Node, heart, set_cmd, [atom_to_list(lib:progname()) ++ @@ -286,7 +293,7 @@ node_check_up_down(Node, Tmo) -> %% Only tests bad command, correct behaviour is tested in reboot/1. set_cmd(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), Cmd = wrong_atom, {error, {bad_cmd, Cmd}} = rpc:call(Node, heart, set_cmd, [Cmd]), Cmd1 = lists:duplicate(2047, $a), @@ -299,7 +306,7 @@ set_cmd(Config) when is_list(Config) -> ok. clear_cmd(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), ok = rpc:call(Node, heart, set_cmd, [atom_to_list(lib:progname()) ++ " -noshell -heart " ++ name(Node) ++ "&"]), @@ -337,7 +344,7 @@ clear_cmd(Config) when is_list(Config) -> ok. get_cmd(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), Cmd = "test", ok = rpc:call(Node, heart, set_cmd, [Cmd]), {ok, Cmd} = rpc:call(Node, heart, get_cmd, []), @@ -345,7 +352,7 @@ get_cmd(Config) when is_list(Config) -> ok. callback_api(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), none = rpc:call(Node, heart, get_callback, []), M0 = self(), F0 = ok, @@ -379,7 +386,7 @@ callback_api(Config) when is_list(Config) -> ok. options_api(Config) when is_list(Config) -> - {ok, Node} = start_check(slave, heart_test), + {ok, Node} = start_check(slave, ?UNIQ_NODE_NAME), none = rpc:call(Node, heart, get_options, []), M0 = self(), F0 = ok, @@ -474,7 +481,7 @@ kill_pid(Config) when is_list(Config) -> ok = do_kill_pid(Config). do_kill_pid(_Config) -> - Name = heart_test, + Name = ?UNIQ_NODE_NAME, Env = [{"HEART_COMMAND", "nickeNyfikenFarEttJobb"}], {ok,Node} = start_node_run(Name,Env,suicide_by_heart,[]), ok = wait_for_node(Node,15), diff --git a/lib/kernel/test/inet_SUITE.erl b/lib/kernel/test/inet_SUITE.erl index b9e2ada0c3..c93b10fa1c 100644 --- a/lib/kernel/test/inet_SUITE.erl +++ b/lib/kernel/test/inet_SUITE.erl @@ -1075,6 +1075,10 @@ check_ifopts( #ifopts{addrs=Addrs}=Ifopts) -> check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask,Broadaddr}|Addrs]}); check_ifopts( + [{addr,Addr},{netmask,Netmask},{dstaddr,_}|Opts], + #ifopts{addrs=Addrs}=Ifopts) -> + check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask}|Addrs]}); +check_ifopts( [{addr,Addr},{netmask,Netmask}|Opts], #ifopts{addrs=Addrs}=Ifopts) -> check_ifopts(Opts, Ifopts#ifopts{addrs=[{Addr,Netmask}|Addrs]}); @@ -1118,9 +1122,13 @@ simple_netns(Config) when is_list(Config) -> jog_netns_opt(L), ok = gen_tcp:close(L), %% - {ok,S} = gen_sctp:open(), - jog_netns_opt(S), - ok = gen_sctp:close(S); + case gen_sctp:open() of + {ok,S} -> + jog_netns_opt(S), + ok = gen_sctp:close(S); + {error,eprotonosupport} -> + ok + end; {error,einval} -> {skip,"setns() not supported"} end. @@ -1134,24 +1142,28 @@ jog_netns_opt(S) -> ok. +%% Smoke test netns support. simple_netns_open(Config) when is_list(Config) -> + %% Note: {error,enoent} will be returned if the run-time executable + %% has support for netns, but /proc/self/ns/net is missing. case gen_udp:open(0, [binary,{netns,"/"},inet]) of {ok,U} -> ok = gen_udp:close(U); - {error,E1} when E1 =:= einval; E1 =:= eperm -> + {error,E1} when E1 =:= einval; E1 =:= eperm; E1 =:= enoent -> ok end, case gen_tcp:listen(0, [binary,{netns,"/"},inet]) of {ok,T} -> ok = gen_tcp:close(T); - {error,E2} when E2 =:= einval; E2 =:= eperm -> + {error,E2} when E2 =:= einval; E2 =:= eperm; E2 =:= enoent -> ok end, try gen_sctp:open(0, [binary,{netns,"/"},inet]) of {ok,S} -> ok = gen_sctp:close(S); {error,E3} - when E3 =:= einval; E3 =:= eperm; E3 =:= eprotonosupport -> + when E3 =:= einval; E3 =:= eperm; + E3 =:= enoent; E3 =:= eprotonosupport -> ok catch error:badarg -> diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index aace38cb5a..e7b44a714c 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -249,7 +249,8 @@ boot_var(Config) when is_list(Config) -> {ok, Node} = start_node(init_test, "-boot " ++ BootScript ++ - " -boot_var TEST_VAR " ++ TEST_VAR), + " -boot_var TEST_VAR \"" ++ + TEST_VAR ++ "\""), stop_node(Node), Res = ok; _ -> diff --git a/lib/kernel/test/prim_file_SUITE.erl b/lib/kernel/test/prim_file_SUITE.erl index 5ab85fa392..cc7f2f6713 100644 --- a/lib/kernel/test/prim_file_SUITE.erl +++ b/lib/kernel/test/prim_file_SUITE.erl @@ -1025,17 +1025,15 @@ file_write_file_info_opts(Config) when is_list(Config) -> %% REM: determine date range dependent on time_t = Uint32 | Sint32 | Sint64 %% Determine time_t on os:type()? - lists:foreach(fun - ({FI, Opts}) -> + lists:foreach(fun ({FI, Opts}) -> ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI, Opts]) - end, [ - {#file_info{ mode=8#400, atime = Time, mtime = Time, ctime = Time}, Opts} || + end, [ {#file_info{ mode=8#400, atime = Time, mtime = Time, ctime = Time}, Opts} || Opts <- [[{time, universal}],[{time, local}]], Time <- [ {{1970,1,1},{0,0,0}}, {{1970,1,1},{0,0,1}}, - {{1969,12,31},{23,59,59}}, - {{1908,2,3},{23,59,59}}, + % {{1969,12,31},{23,59,59}}, + % {{1908,2,3},{23,59,59}}, {{2012,2,3},{23,59,59}}, {{2037,2,3},{23,59,59}}, erlang:localtime() @@ -1070,8 +1068,9 @@ file_write_read_file_info_opts(Config) when is_list(Config) -> ok = file_write_read_file_info_opts(Handle, Name, {{1989, 04, 28}, {19,30,22}}, [{time, local}]), ok = file_write_read_file_info_opts(Handle, Name, {{1989, 04, 28}, {19,30,22}}, [{time, universal}]), - ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, local}]), - ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, universal}]), + %% will not work on platforms with unsigned time_t + %ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, local}]), + %ok = file_write_read_file_info_opts(Handle, Name, {{1930, 04, 28}, {19,30,22}}, [{time, universal}]), ok = file_write_read_file_info_opts(Handle, Name, 1, [{time, posix}]), ok = file_write_read_file_info_opts(Handle, Name, -1, [{time, posix}]), ok = file_write_read_file_info_opts(Handle, Name, 300000, [{time, posix}]), @@ -1085,7 +1084,9 @@ file_write_read_file_info_opts(Handle, Name, Mtime, Opts) -> {ok, FI} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]), FI2 = FI#file_info{ mtime = Mtime }, ok = ?PRIM_FILE_call(write_file_info, Handle, [Name, FI2, Opts]), - {ok, FI2} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]), + {ok, FI3} = ?PRIM_FILE_call(read_file_info, Handle, [Name, Opts]), + io:format("Expecting mtime = ~p, got ~p~n", [FI2#file_info.mtime, FI3#file_info.mtime]), + FI2 = FI3, ok. diff --git a/lib/mnesia/doc/src/notes.xml b/lib/mnesia/doc/src/notes.xml index 5b6871c613..4a68e76d50 100644 --- a/lib/mnesia/doc/src/notes.xml +++ b/lib/mnesia/doc/src/notes.xml @@ -39,7 +39,23 @@ thus constitutes one section in this document. The title of each section is the version number of Mnesia.</p> - <section><title>Mnesia 4.13.3</title> + <section><title>Mnesia 4.13.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Mnesia transactions could hang while waiting on a + response from a node who had stopped.</p> + <p> + Own Id: OTP-13423</p> + </item> + </list> + </section> + +</section> + +<section><title>Mnesia 4.13.3</title> <section><title>Fixed Bugs and Malfunctions</title> <list> diff --git a/lib/mnesia/src/mnesia_tm.erl b/lib/mnesia/src/mnesia_tm.erl index 29245d0a06..6888f61dbd 100644 --- a/lib/mnesia/src/mnesia_tm.erl +++ b/lib/mnesia/src/mnesia_tm.erl @@ -1692,13 +1692,10 @@ commit_participant(Coord, Tid, Bin, C0, DiscNs, _RamNs) -> ?eval_debug_fun({?MODULE, commit_participant, undo_prepare}, [{tid, Tid}]); - {'EXIT', _, _} -> + {'EXIT', _MnesiaTM, Reason} -> + reply(Coord, {do_abort, Tid, self(), {bad_commit,Reason}}), mnesia_recover:log_decision(D#decision{outcome = aborted}), - ?eval_debug_fun({?MODULE, commit_participant, exit_log_abort}, - [{tid, Tid}]), - mnesia_schema:undo_prepare_commit(Tid, C0), - ?eval_debug_fun({?MODULE, commit_participant, exit_undo_prepare}, - [{tid, Tid}]); + mnesia_schema:undo_prepare_commit(Tid, C0); Msg -> verbose("** ERROR ** commit_participant ~p, got unexpected msg: ~p~n", @@ -2210,8 +2207,6 @@ reconfigure_coordinators(N, [{Tid, [Store | _]} | Coordinators]) -> true -> send_mnesia_down(Tid, Store, N) end; - aborted -> - ignore; % avoid spurious mnesia_down messages _ -> %% Tell the coordinator about the mnesia_down send_mnesia_down(Tid, Store, N) diff --git a/lib/mnesia/vsn.mk b/lib/mnesia/vsn.mk index 843d9d18d4..194bc439a0 100644 --- a/lib/mnesia/vsn.mk +++ b/lib/mnesia/vsn.mk @@ -1 +1 @@ -MNESIA_VSN = 4.13.3 +MNESIA_VSN = 4.13.4 diff --git a/lib/os_mon/test/cpu_sup_SUITE.erl b/lib/os_mon/test/cpu_sup_SUITE.erl index 7a689095c5..11648d3547 100644 --- a/lib/os_mon/test/cpu_sup_SUITE.erl +++ b/lib/os_mon/test/cpu_sup_SUITE.erl @@ -21,7 +21,7 @@ -include_lib("common_test/include/ct.hrl"). %% Test server specific exports --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). +-export([all/0, suite/0]). -export([init_per_suite/1, end_per_suite/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -31,186 +31,163 @@ -export([port/1]). -export([terminate/1, unavailable/1, restart/1]). -%% Default timetrap timeout (set in init_per_testcase) --define(default_timeout, ?t:minutes(1)). - init_per_suite(Config) when is_list(Config) -> - ?line ok = application:start(os_mon), + ok = application:start(os_mon), Config. end_per_suite(Config) when is_list(Config) -> - ?line ok = application:stop(os_mon), + ok = application:stop(os_mon), Config. init_per_testcase(unavailable, Config) -> terminate(Config), init_per_testcase(dummy, Config); init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?default_timeout), - [{watchdog, Dog} | Config]. + Config. end_per_testcase(unavailable, Config) -> restart(Config), end_per_testcase(dummy, Config); -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> case test_server:os_type() of - {unix, sunos} -> - [load_api, util_api, util_values, port, unavailable]; - {unix, linux} -> - [load_api, util_api, util_values, port, unavailable]; - {unix, freebsd} -> - [load_api, util_api, util_values, port, unavailable]; - {unix, _OSname} -> [load_api]; - _OS -> [unavailable] + {unix, sunos} -> + [load_api, util_api, util_values, port, unavailable]; + {unix, linux} -> + [load_api, util_api, util_values, port, unavailable]; + {unix, freebsd} -> + [load_api, util_api, util_values, port, unavailable]; + {unix, _OSname} -> [load_api]; + _OS -> [unavailable] end. -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -load_api(suite) -> - []; -load_api(doc) -> - ["Test of load API functions"]; +%% Test of load API functions load_api(Config) when is_list(Config) -> %% nprocs() - ?line N = cpu_sup:nprocs(), - ?line true = is_integer(N), - ?line true = N>0, - ?line true = N<1000000, + N = cpu_sup:nprocs(), + true = is_integer(N), + true = N>0, + true = N<1000000, %% avg1() - ?line Load1 = cpu_sup:avg1(), - ?line true = is_integer(Load1), - ?line true = Load1>0, + Load1 = cpu_sup:avg1(), + true = is_integer(Load1), + true = Load1>0, %% avg5() - ?line Load5 = cpu_sup:avg5(), - ?line true = is_integer(Load5), - ?line true = Load5>0, + Load5 = cpu_sup:avg5(), + true = is_integer(Load5), + true = Load5>0, %% avg15() - ?line Load15 = cpu_sup:avg15(), - ?line true = is_integer(Load15), - ?line true = Load15>0, + Load15 = cpu_sup:avg15(), + true = is_integer(Load15), + true = Load15>0, ok. -util_api(suite) -> - []; -util_api(doc) -> - ["Test of utilization API functions"]; +%% Test of utilization API functions util_api(Config) when is_list(Config) -> %% Some useful funs when testing util/1 BusyP = fun({user, _Share}) -> true; - ({nice_user, _Share}) -> true; - ({kernel, _Share}) -> true; - ({hard_irq, _Share}) -> true; - ({soft_irq, _Share}) -> true; - (_) -> false - end, + ({nice_user, _Share}) -> true; + ({kernel, _Share}) -> true; + ({hard_irq, _Share}) -> true; + ({soft_irq, _Share}) -> true; + (_) -> false + end, NonBusyP = fun({wait, _Share}) -> true; - ({idle, _Share}) -> true; - ({steal, _Share}) -> true; - (_) -> false - end, + ({idle, _Share}) -> true; + ({steal, _Share}) -> true; + (_) -> false + end, Sum = fun({_Tag, X}, Acc) -> Acc+X end, %% util() - ?line Util1 = cpu_sup:util(), - ?line true = is_number(Util1), - ?line true = Util1>0, - ?line Util2 = cpu_sup:util(), - ?line true = is_number(Util2), - ?line true = Util2>0, + Util1 = cpu_sup:util(), + true = is_number(Util1), + true = Util1>0, + Util2 = cpu_sup:util(), + true = is_number(Util2), + true = Util2>0, %% util([]) - ?line {all, Busy1, NonBusy1, []} = cpu_sup:util([]), - ?line 100.00 = Busy1 + NonBusy1, + {all, Busy1, NonBusy1, []} = cpu_sup:util([]), + 100.00 = Busy1 + NonBusy1, %% util([detailed]) - ?line {Cpus2, Busy2, NonBusy2, []} = cpu_sup:util([detailed]), - ?line true = lists:all(fun(X) -> is_integer(X) end, Cpus2), - ?line true = lists:all(BusyP, Busy2), - ?line true = lists:all(NonBusyP, NonBusy2), - ?line 100.00 = lists:foldl(Sum,0,Busy2)+lists:foldl(Sum,0,NonBusy2), + {Cpus2, Busy2, NonBusy2, []} = cpu_sup:util([detailed]), + true = lists:all(fun(X) -> is_integer(X) end, Cpus2), + true = lists:all(BusyP, Busy2), + true = lists:all(NonBusyP, NonBusy2), + 100.00 = lists:foldl(Sum,0,Busy2)+lists:foldl(Sum,0,NonBusy2), %% util([per_cpu]) - ?line [{Cpu3, Busy3, NonBusy3, []}|_] = cpu_sup:util([per_cpu]), - ?line true = is_integer(Cpu3), - ?line 100.00 = Busy3 + NonBusy3, + [{Cpu3, Busy3, NonBusy3, []}|_] = cpu_sup:util([per_cpu]), + true = is_integer(Cpu3), + 100.00 = Busy3 + NonBusy3, %% util([detailed, per_cpu]) - ?line [{Cpu4, Busy4, NonBusy4, []}|_] = - cpu_sup:util([detailed, per_cpu]), - ?line true = is_integer(Cpu4), - ?line true = lists:all(BusyP, Busy2), - ?line true = lists:all(NonBusyP, NonBusy2), - ?line 100.00 = lists:foldl(Sum,0,Busy4)+lists:foldl(Sum,0,NonBusy4), + [{Cpu4, Busy4, NonBusy4, []}|_] = + cpu_sup:util([detailed, per_cpu]), + true = is_integer(Cpu4), + true = lists:all(BusyP, Busy2), + true = lists:all(NonBusyP, NonBusy2), + 100.00 = lists:foldl(Sum,0,Busy4)+lists:foldl(Sum,0,NonBusy4), %% bad util/1 calls - ?line {'EXIT',{badarg,_}} = (catch cpu_sup:util(detailed)), - ?line {'EXIT',{badarg,_}} = (catch cpu_sup:util([detialed])), + {'EXIT',{badarg,_}} = (catch cpu_sup:util(detailed)), + {'EXIT',{badarg,_}} = (catch cpu_sup:util([detialed])), ok. -define(SPIN_TIME, 1000). -util_values(suite) -> - []; -util_values(doc) -> - ["Test utilization values"]; +%% Test utilization values util_values(Config) when is_list(Config) -> Tester = self(), Ref = make_ref(), Loop = fun (L) -> L(L) end, Spinner = fun () -> - Looper = spawn_link(fun () -> Loop(Loop) end), - receive after ?SPIN_TIME -> ok end, - unlink(Looper), - exit(Looper, kill), - Tester ! Ref - end, + Looper = spawn_link(fun () -> Loop(Loop) end), + receive after ?SPIN_TIME -> ok end, + unlink(Looper), + exit(Looper, kill), + Tester ! Ref + end, - ?line cpu_sup:util(), + cpu_sup:util(), - ?line spawn_link(Spinner), - ?line receive Ref -> ok end, - ?line HighUtil1 = cpu_sup:util(), + spawn_link(Spinner), + receive Ref -> ok end, + HighUtil1 = cpu_sup:util(), - ?line receive after ?SPIN_TIME -> ok end, - ?line LowUtil1 = cpu_sup:util(), + receive after ?SPIN_TIME -> ok end, + LowUtil1 = cpu_sup:util(), - ?line spawn_link(Spinner), - ?line receive Ref -> ok end, - ?line HighUtil2 = cpu_sup:util(), + spawn_link(Spinner), + receive Ref -> ok end, + HighUtil2 = cpu_sup:util(), - ?line receive after ?SPIN_TIME -> ok end, - ?line LowUtil2 = cpu_sup:util(), + receive after ?SPIN_TIME -> ok end, + LowUtil2 = cpu_sup:util(), Utils = [{high1,HighUtil1}, {low1,LowUtil1}, - {high2,HighUtil2}, {low2,LowUtil2}], - ?t:format("Utils: ~p~n", [Utils]), + {high2,HighUtil2}, {low2,LowUtil2}], + io:format("Utils: ~p~n", [Utils]), - ?line false = LowUtil1 > HighUtil1, - ?line false = LowUtil1 > HighUtil2, - ?line false = LowUtil2 > HighUtil1, - ?line false = LowUtil2 > HighUtil2, + false = LowUtil1 > HighUtil1, + false = LowUtil1 > HighUtil2, + false = LowUtil2 > HighUtil1, + false = LowUtil2 > HighUtil2, ok. @@ -218,76 +195,66 @@ util_values(Config) when is_list(Config) -> % Outdated % The portprogram is now restarted if killed, and not by os_mon... -port(suite) -> - []; -port(doc) -> - ["Test that cpu_sup handles a terminating port program"]; +%% Test that cpu_sup handles a terminating port program port(Config) when is_list(Config) -> case cpu_sup_os_pid() of - {ok, PidStr} -> - %% Monitor cpu_sup - ?line MonRef = erlang:monitor(process, cpu_sup), - ?line N1 = cpu_sup:nprocs(), - ?line true = N1>0, - - %% Kill the port program - case os:cmd("kill -9 " ++ PidStr) of - [] -> - %% cpu_sup should not terminate - receive - {'DOWN', MonRef, _, _, Reason} -> - ?line ?t:fail({unexpected_exit_reason, Reason}) - after 3000 -> - ok - end, - - %% Give cpu_sup time to restart cpu_sup port - ?t:sleep(?t:seconds(3)), - ?line N2 = cpu_sup:nprocs(), - ?line true = N2>0, - - erlang:demonitor(MonRef), - ok; - - Line -> - erlang:demonitor(MonRef), - {skip, {not_killed, Line}} - end; - _ -> - {skip, os_pid_not_found } + {ok, PidStr} -> + %% Monitor cpu_sup + MonRef = erlang:monitor(process, cpu_sup), + N1 = cpu_sup:nprocs(), + true = N1>0, + + %% Kill the port program + case os:cmd("kill -9 " ++ PidStr) of + [] -> + %% cpu_sup should not terminate + receive + {'DOWN', MonRef, _, _, Reason} -> + ct:fail({unexpected_exit_reason, Reason}) + after 3000 -> + ok + end, + + %% Give cpu_sup time to restart cpu_sup port + ct:sleep({seconds, 3}), + N2 = cpu_sup:nprocs(), + true = N2>0, + + erlang:demonitor(MonRef), + ok; + + Line -> + erlang:demonitor(MonRef), + {skip, {not_killed, Line}} + end; + _ -> + {skip, os_pid_not_found } end. -terminate(suite) -> - []; terminate(Config) when is_list(Config) -> ok = application:set_env(os_mon, start_cpu_sup, false), _ = supervisor:terminate_child(os_mon_sup, cpu_sup), ok. -unavailable(suite) -> - []; -unavailable(doc) -> - ["Test correct behaviour when service is unavailable"]; +%% Test correct behaviour when service is unavailable unavailable(Config) when is_list(Config) -> %% Make sure all API functions return their dummy values - ?line 0 = cpu_sup:nprocs(), - ?line 0 = cpu_sup:avg1(), - ?line 0 = cpu_sup:avg5(), - ?line 0 = cpu_sup:avg15(), - ?line 0 = cpu_sup:util(), - ?line {all,0,0,[]} = cpu_sup:util([]), - ?line {all,0,0,[]} = cpu_sup:util([detailed]), - ?line {all,0,0,[]} = cpu_sup:util([per_cpu]), - ?line {all,0,0,[]} = cpu_sup:util([detailed,per_cpu]), + 0 = cpu_sup:nprocs(), + 0 = cpu_sup:avg1(), + 0 = cpu_sup:avg5(), + 0 = cpu_sup:avg15(), + 0 = cpu_sup:util(), + {all,0,0,[]} = cpu_sup:util([]), + {all,0,0,[]} = cpu_sup:util([detailed]), + {all,0,0,[]} = cpu_sup:util([per_cpu]), + {all,0,0,[]} = cpu_sup:util([detailed,per_cpu]), ok. -restart(suite) -> - []; restart(Config) when is_list(Config) -> - ?line ok = application:set_env(os_mon, start_cpu_sup, true), - ?line {ok, _Pid} = supervisor:restart_child(os_mon_sup, cpu_sup), + ok = application:set_env(os_mon, start_cpu_sup, true), + {ok, _Pid} = supervisor:restart_child(os_mon_sup, cpu_sup), ok. %% Aux @@ -295,6 +262,6 @@ restart(Config) when is_list(Config) -> cpu_sup_os_pid() -> Str = os:cmd("ps -e | grep '[c]pu_sup'"), case io_lib:fread("~s", Str) of - {ok, [Pid], _Rest} -> {ok, Pid}; - _ -> {error, pid_not_found} + {ok, [Pid], _Rest} -> {ok, Pid}; + _ -> {error, pid_not_found} end. diff --git a/lib/os_mon/test/disksup_SUITE.erl b/lib/os_mon/test/disksup_SUITE.erl index 6cdd8a4fa9..ad61985014 100644 --- a/lib/os_mon/test/disksup_SUITE.erl +++ b/lib/os_mon/test/disksup_SUITE.erl @@ -21,7 +21,7 @@ -include_lib("common_test/include/ct.hrl"). %% Test server specific exports --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). +-export([all/0, suite/0]). -export([init_per_suite/1, end_per_suite/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -32,9 +32,6 @@ -export([otp_5910/1]). -export([posix_only/1]). -%% Default timetrap timeout (set in init_per_testcase) --define(default_timeout, ?t:minutes(1)). - init_per_suite(Config) when is_list(Config) -> ok = application:start(os_mon), Config. @@ -47,19 +44,18 @@ init_per_testcase(unavailable, Config) -> terminate(Config), init_per_testcase(dummy, Config); init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?default_timeout), - [{watchdog,Dog} | Config]. + Config. end_per_testcase(TC, Config) when TC =:= unavailable; TC =:= posix_only -> restart(Config), end_per_testcase(dummy, Config); -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), +end_per_testcase(_Case, _Config) -> ok. -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> Bugs = [otp_5910], @@ -70,18 +66,7 @@ all() -> _OS -> [unavailable] end. -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -api(suite) -> []; -api(doc) -> ["Test of API functions"]; +%% Test of API functions api(Config) when is_list(Config) -> %% get_disk_data() @@ -110,8 +95,7 @@ api(Config) when is_list(Config) -> ok. -config(suite) -> []; -config(doc) -> ["Test configuration"]; +%% Test configuration config(Config) when is_list(Config) -> %% Change configuration parameters and make sure change is reflected @@ -147,8 +131,8 @@ config(Config) when is_list(Config) -> %% changes too much during its course, or if there are timing problems %% with the alarm_handler receiving the alarms too late %%---------------------------------------------------------------------- -alarm(suite) -> []; -alarm(doc) -> ["Test that alarms are set and cleared"]; + +%% Test that alarms are set and cleared alarm(Config) when is_list(Config) -> %% Find out how many disks exceed the threshold @@ -162,7 +146,7 @@ alarm(Config) when is_list(Config) -> true; true -> dump_info(), - ?t:fail({bad_alarms, Threshold1, Data1, Alarms1}) + ct:fail({bad_alarms, Threshold1, Data1, Alarms1}) end, %% Try to find a disk with space usage below Threshold1, @@ -187,7 +171,7 @@ alarm(Config) when is_list(Config) -> true; true -> dump_info(), - ?t:fail({bad_alarms, Threshold2, Data2, Alarms2}) + ct:fail({bad_alarms, Threshold2, Data2, Alarms2}) end; false -> ignore @@ -215,7 +199,7 @@ alarm(Config) when is_list(Config) -> ok; true -> dump_info(), - ?t:fail({bad_alarms, Threshold3, Data3, Alarms3}) + ct:fail({bad_alarms, Threshold3, Data3, Alarms3}) end; 100 -> ignore @@ -271,9 +255,7 @@ until(Fun, [H|T]) -> end; until(_Fun, []) -> false. -port(suite) -> []; -port(doc) -> - ["Test that disksup handles a terminating port program"]; +%% Test that disksup handles a terminating port program port(Config) when is_list(Config) -> Str = os:cmd("ps -ef | grep '[d]isksup'"), case io_lib:fread("~s ~s", Str) of @@ -293,14 +275,14 @@ port(Config) when is_list(Config) -> {'DOWN', MonRef, _, _, {port_died, _Reason}} -> ok; {'DOWN', MonRef, _, _, Reason} -> - ?t:fail({unexpected_exit_reason, Reason}) + ct:fail({unexpected_exit_reason, Reason}) after 3000 -> - ?t:fail({still_alive, Str}) + ct:fail({still_alive, Str}) end, %% Give os_mon_sup time to restart disksup - ?t:sleep(?t:seconds(3)), + ct:sleep({seconds,3}), [{_Disk2,Kbyte2,_Cap2}|_] = disksup:get_disk_data(), true = Kbyte2>0, @@ -314,15 +296,12 @@ port(Config) when is_list(Config) -> {skip, {os_pid_not_found, Str}} end. -terminate(suite) -> []; terminate(Config) when is_list(Config) -> ok = application:set_env(os_mon, start_disksup, false), ok = supervisor:terminate_child(os_mon_sup, disksup), ok. -unavailable(suite) -> []; -unavailable(doc) -> - ["Test correct behaviour when service is unavailable"]; +%% Test correct behaviour when service is unavailable unavailable(Config) when is_list(Config) -> %% Make sure all API functions return their dummy values @@ -333,18 +312,16 @@ unavailable(Config) when is_list(Config) -> ok = disksup:set_almost_full_threshold(0.9), ok. -restart(suite) -> - []; restart(Config) when is_list(Config) -> ok = application:set_env(os_mon, start_disksup, true), ok = application:set_env(os_mon, disksup_posix_only, false), - {ok, _Pid} = supervisor:restart_child(os_mon_sup, disksup), - ok. + case supervisor:restart_child(os_mon_sup, disksup) of + {ok, _Pid} -> ok; + {error, running} -> ok + end. -otp_5910(suite) -> []; -otp_5910(doc) -> - ["Test that alarms are cleared if disksup crashes or " - "if OS_Mon is stopped"]; +%% Test that alarms are cleared if disksup crashes or +%% if OS_Mon is stopped otp_5910(Config) when is_list(Config) -> %% Make sure disksup sets at least one alarm @@ -365,12 +342,12 @@ otp_5910(Config) when is_list(Config) -> Alarms = get_alarms(), if Over==0 -> - ?t:fail({threshold_too_low, Data2, Threshold}); + ct:fail({threshold_too_low, Data2, Threshold}); Over==length(Alarms) -> ok; true -> dump_info(), - ?t:fail({bad_alarms, Threshold, Data2, Alarms}) + ct:fail({bad_alarms, Threshold, Data2, Alarms}) end, %% Kill disksup @@ -378,23 +355,23 @@ otp_5910(Config) when is_list(Config) -> %% Wait a little to make sure disksup has been restarted, %% then make sure the alarms are set once, but not twice - ?t:sleep(?t:seconds(1)), + ct:sleep({seconds,1}), Data3 = disksup:get_disk_data(), Alarms2 = get_alarms(), if length(Alarms2)==length(Alarms) -> ok; true -> dump_info(), - ?t:fail({bad_alarms,Threshold,Data3,Alarms,Alarms2}) + ct:fail({bad_alarms,Threshold,Data3,Alarms,Alarms2}) end, %% Stop OS_Mon and make sure all disksup alarms are cleared ok = application:stop(os_mon), - ?t:sleep(?t:seconds(1)), + ct:sleep({seconds,1}), Alarms3 = get_alarms(), case get_alarms() of [] -> ok; - _ -> ?t:fail({alarms_not_cleared, Alarms3}) + _ -> ct:fail({alarms_not_cleared, Alarms3}) end, %% Reset threshold and restart OS_Mon @@ -403,8 +380,7 @@ otp_5910(Config) when is_list(Config) -> ok = application:start(os_mon), ok. -posix_only(suite) -> []; -posix_only(doc) -> ["Test disksup_posix_only option"]; +%% Test disksup_posix_only option posix_only(Config) when is_list(Config) -> %% Set option and restart disksup ok = application:set_env(os_mon, disksup_posix_only, true), diff --git a/lib/os_mon/test/memsup_SUITE.erl b/lib/os_mon/test/memsup_SUITE.erl index 170951f082..fcd1417693 100644 --- a/lib/os_mon/test/memsup_SUITE.erl +++ b/lib/os_mon/test/memsup_SUITE.erl @@ -21,7 +21,7 @@ -include_lib("common_test/include/ct.hrl"). %% Test server specific exports --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). +-export([all/0, suite/0]). -export([init_per_suite/1, end_per_suite/1]). -export([init_per_testcase/2, end_per_testcase/2]). @@ -30,384 +30,362 @@ -export([config/1, timeout/1, unavailable/1, port/1]). -export([otp_5910/1]). -%% Default timetrap timeout (set in init_per_testcase) --define(default_timeout, ?t:minutes(1)). - init_per_suite(Config) when is_list(Config) -> - ?line ok = application:start(os_mon), + ok = application:start(os_mon), Config. end_per_suite(Config) when is_list(Config) -> - ?line ok = application:stop(os_mon), + ok = application:stop(os_mon), Config. init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?default_timeout), - [{watchdog,Dog} | Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), Config. -suite() -> [{ct_hooks,[ts_install_cth]}]. +end_per_testcase(_Case, _Config) -> + ok. + +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> All = case test_server:os_type() of - {unix, sunos} -> - [api, alarm1, alarm2, process, config, timeout, - unavailable, port]; - {unix, linux} -> - [api, alarm1, alarm2, process, timeout]; - _OS -> [api, alarm1, alarm2, process] - end, + {unix, sunos} -> + [api, alarm1, alarm2, process, config, timeout, + unavailable, port]; + {unix, linux} -> + [api, alarm1, alarm2, process, timeout]; + _OS -> [api, alarm1, alarm2, process] + end, Bugs = [otp_5910], All ++ Bugs. -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - -api(suite) -> - []; -api(doc) -> - ["Test of API functions"]; +%% Test of API functions api(Config) when is_list(Config) -> %% get_memory_data() - ?line RegMemData = memsup:get_memory_data(), + RegMemData = memsup:get_memory_data(), case RegMemData of - {TotMem, AllBytes, {Pid, PidBytes}} when is_integer(TotMem), - is_integer(AllBytes), - is_pid(Pid), - is_integer(PidBytes) -> - ok; - {0, 0, _WorstPid} -> - ?line ?t:fail(first_data_collection_failed); - _ -> - ?line ?t:fail({bad_return, RegMemData}) + {TotMem, AllBytes, {Pid, PidBytes}} when is_integer(TotMem), + is_integer(AllBytes), + is_pid(Pid), + is_integer(PidBytes) -> + ok; + {0, 0, _WorstPid} -> + ct:fail(first_data_collection_failed); + _ -> + ct:fail({bad_return, RegMemData}) end, %% get_system_memory_data() - ?line ExtMemData = memsup:get_system_memory_data(), - Tags = [ total_memory, - free_memory, - system_total_memory, - largest_free, - number_of_free, - free_swap, - total_swap, - cached_memory, - buffered_memory, - shared_memory], - - ?line true = lists:all(fun({Tag,Value}) when is_atom(Tag), - is_integer(Value) -> - lists:member(Tag, Tags); - (_) -> - false - end, - ExtMemData), + ExtMemData = memsup:get_system_memory_data(), + Tags = [total_memory, + free_memory, + system_total_memory, + largest_free, + number_of_free, + free_swap, + total_swap, + cached_memory, + buffered_memory, + shared_memory], + + true = lists:all(fun({Tag,Value}) when is_atom(Tag), + is_integer(Value) -> + lists:member(Tag, Tags); + (_) -> + false + end, ExtMemData), %% get_os_wordsize() - ?line ok = case memsup:get_os_wordsize() of - 32 -> ok; - 64 -> ok; - unsupported_os -> ok; - _ -> error - end, + ok = case memsup:get_os_wordsize() of + 32 -> ok; + 64 -> ok; + unsupported_os -> ok; + _ -> error + end, %% get_check_interval() - ?line 60000 = memsup:get_check_interval(), + 60000 = memsup:get_check_interval(), %% set_check_interval(Minutes) - ?line ok = memsup:set_check_interval(2), - ?line 120000 = memsup:get_check_interval(), - ?line {'EXIT',{badarg,_}} = - (catch memsup:set_check_interval(0.2)), - ?line 120000 = memsup:get_check_interval(), - ?line ok = memsup:set_check_interval(1), + ok = memsup:set_check_interval(2), + 120000 = memsup:get_check_interval(), + {'EXIT',{badarg,_}} = + (catch memsup:set_check_interval(0.2)), + 120000 = memsup:get_check_interval(), + ok = memsup:set_check_interval(1), %% get_procmem_high_watermark() - ?line 5 = memsup:get_procmem_high_watermark(), + 5 = memsup:get_procmem_high_watermark(), %% set_procmem_high_watermark() - ?line ok = memsup:set_procmem_high_watermark(0.1), - ?line 10 = memsup:get_procmem_high_watermark(), - ?line {'EXIT',{badarg,_}} = - (catch memsup:set_procmem_high_watermark(-0.1)), - ?line 10 = memsup:get_procmem_high_watermark(), - ?line ok = memsup:set_procmem_high_watermark(0.05), + ok = memsup:set_procmem_high_watermark(0.1), + 10 = memsup:get_procmem_high_watermark(), + {'EXIT',{badarg,_}} = + (catch memsup:set_procmem_high_watermark(-0.1)), + 10 = memsup:get_procmem_high_watermark(), + ok = memsup:set_procmem_high_watermark(0.05), %% get_sysmem_high_watermark() - ?line 80 = memsup:get_sysmem_high_watermark(), + 80 = memsup:get_sysmem_high_watermark(), %% set_sysmem_high_watermark() - ?line ok = memsup:set_sysmem_high_watermark(0.9), - ?line 90 = memsup:get_sysmem_high_watermark(), - ?line {'EXIT',{badarg,_}} = - (catch memsup:set_sysmem_high_watermark(-0.9)), - ?line 90 = memsup:get_sysmem_high_watermark(), - ?line ok = memsup:set_sysmem_high_watermark(0.8), + ok = memsup:set_sysmem_high_watermark(0.9), + 90 = memsup:get_sysmem_high_watermark(), + {'EXIT',{badarg,_}} = + (catch memsup:set_sysmem_high_watermark(-0.9)), + 90 = memsup:get_sysmem_high_watermark(), + ok = memsup:set_sysmem_high_watermark(0.8), %% get|set_helper_timeout - ?line 30 = memsup:get_helper_timeout(), - ?line ok = memsup:set_helper_timeout(29), - ?line 29 = memsup:get_helper_timeout(), - ?line {'EXIT',{badarg,_}} = (catch memsup:set_helper_timeout(31.0)), - ?line 29 = memsup:get_helper_timeout(), + 30 = memsup:get_helper_timeout(), + ok = memsup:set_helper_timeout(29), + 29 = memsup:get_helper_timeout(), + {'EXIT',{badarg,_}} = (catch memsup:set_helper_timeout(31.0)), + 29 = memsup:get_helper_timeout(), ok. %%---------------------------------------------------------------------- %% NOTE: The test case is a bit weak as it will fail if the memory %% usage changes too much during its course. %%---------------------------------------------------------------------- -alarm1(suite) -> - []; -alarm1(doc) -> - ["Test alarms when memsup_system_only==false"]; + +%% Test alarms when memsup_system_only==false alarm1(Config) when is_list(Config) -> %% If system memory usage is too high, the testcase cannot %% be run correctly - ?line {Total, Alloc, {_Pid,_PidAlloc}} = memsup:get_memory_data(), + {Total, Alloc, {_Pid,_PidAlloc}} = memsup:get_memory_data(), io:format("alarm1: Total: ~p, Alloc: ~p~n", [Total, Alloc]), - ?line SysUsage = Alloc/Total, + SysUsage = Alloc/Total, if - SysUsage>0.99 -> - {skip, sys_mem_too_high}; - true -> - alarm1(Config, SysUsage) + SysUsage > 0.99 -> + {skip, sys_mem_too_high}; + true -> + alarm1(Config, SysUsage) end. alarm1(_Config, SysUsage) -> %% Set a long memory check interval, we will force memory checks %% instead - ?line ok = memsup:set_check_interval(60), + ok = memsup:set_check_interval(60), %% Check thresholds - ?line SysThreshold = (memsup:get_sysmem_high_watermark()/100), - ?line ProcThreshold = (memsup:get_procmem_high_watermark()/100), + SysThreshold = (memsup:get_sysmem_high_watermark()/100), + ProcThreshold = (memsup:get_procmem_high_watermark()/100), %% Check if a system alarm already should be set or not SysP = if - SysUsage>SysThreshold -> true; - SysUsage=<SysThreshold -> false - end, + SysUsage>SysThreshold -> true; + SysUsage=<SysThreshold -> false + end, %% If system memory is higher than threshold, make sure the system %% alarm is set. Otherwise, make sure it is not set case alarm_set(system_memory_high_watermark) of - {true, []} when SysP -> - ok; - false when not SysP -> - ok; - _ -> - ?line ?t:fail({sys_alarm, SysUsage, SysThreshold}) + {true, []} when SysP -> + ok; + false when not SysP -> + ok; + _ -> + ct:fail({sys_alarm, SysUsage, SysThreshold}) end, %% Lower/raise the threshold to clear/set the alarm NewSysThreshold = if - SysP -> - Value = 1.1*SysUsage, - if - Value > 0.99 -> 0.99; - true -> Value - end; - not SysP -> 0.9*SysUsage - end, + SysP -> + Value = 1.1*SysUsage, + if + Value > 0.99 -> 0.99; + true -> Value + end; + not SysP -> 0.9*SysUsage + end, - ?line ok = memsup:set_sysmem_high_watermark(NewSysThreshold), + ok = memsup:set_sysmem_high_watermark(NewSysThreshold), %% Initiate and wait for a new data collection - ?line ok = force_collection(), + ok = force_collection(), %% Make sure the alarm is cleared/set - ?t:sleep(?t:seconds(5)), + ct:sleep({seconds,5}), case alarm_set(system_memory_high_watermark) of - {true, []} when not SysP -> - ok; - false when SysP -> - ok; - _ -> - ?line ?t:fail({sys_alarm, SysUsage, NewSysThreshold}) + {true, []} when not SysP -> + ok; + false when SysP -> + ok; + _ -> + ct:fail({sys_alarm, SysUsage, NewSysThreshold}) end, %% Reset the threshold to set/clear the alarm again - ?line ok = memsup:set_sysmem_high_watermark(SysThreshold), - ?line ok = force_collection(), - ?t:sleep(?t:seconds(1)), + ok = memsup:set_sysmem_high_watermark(SysThreshold), + ok = force_collection(), + ct:sleep({seconds,1}), case alarm_set(system_memory_high_watermark) of - {true, []} when SysP -> - ok; - false when not SysP -> - ok; - _ -> - ?line ?t:fail({sys_alarm, SysUsage, SysThreshold}) + {true, []} when SysP -> + ok; + false when not SysP -> + ok; + _ -> + ct:fail({sys_alarm, SysUsage, SysThreshold}) end, %% Check memory usage - ?line {Total2, _, {WorstPid, PidAlloc}} = memsup:get_memory_data(), + {Total2, _, {WorstPid, PidAlloc}} = memsup:get_memory_data(), %% Check if a process alarm already should be set or not PidUsage = PidAlloc/Total2, ProcP = if - PidUsage>ProcThreshold -> true; - PidUsage=<ProcThreshold -> false - end, + PidUsage>ProcThreshold -> true; + PidUsage=<ProcThreshold -> false + end, %% Make sure the process alarm is set/not set accordingly case alarm_set(process_memory_high_watermark) of - {true, WorstPid} when ProcP -> - ok; - false when not ProcP -> - ok; - {true, BadPid1} when ProcP -> - ?line ?t:fail({proc_alarm, WorstPid, BadPid1}); - _ -> - ?line ?t:fail({proc_alarm, PidUsage, ProcThreshold}) + {true, WorstPid} when ProcP -> + ok; + false when not ProcP -> + ok; + {true, BadPid1} when ProcP -> + ct:fail({proc_alarm, WorstPid, BadPid1}); + _ -> + ct:fail({proc_alarm, PidUsage, ProcThreshold}) end, %% Lower/raise the threshold to clear/set the alarm NewProcThreshold = if - ProcP -> 1.1*PidUsage; - not ProcP -> 0.9*PidUsage - end, - ?line ok = memsup:set_procmem_high_watermark(NewProcThreshold), - ?line ok = force_collection(), - ?t:sleep(?t:seconds(1)), + ProcP -> 1.1*PidUsage; + not ProcP -> 0.9*PidUsage + end, + ok = memsup:set_procmem_high_watermark(NewProcThreshold), + ok = force_collection(), + ct:sleep({seconds,1}), case alarm_set(process_memory_high_watermark) of - {true, WorstPid} when not ProcP -> - ok; - false when ProcP -> - ok; - {true, BadPid2} when not ProcP -> - ?line test_server:fail({proc_alarm, WorstPid, BadPid2}); - _ -> - ?line ?t:fail({proc_alarm, PidUsage, ProcThreshold}) + {true, WorstPid} when not ProcP -> + ok; + false when ProcP -> + ok; + {true, BadPid2} when not ProcP -> + ct:fail({proc_alarm, WorstPid, BadPid2}); + _ -> + ct:fail({proc_alarm, PidUsage, ProcThreshold}) end, %% Reset the threshold to clear/set the alarm - ?line ok = memsup:set_procmem_high_watermark(ProcThreshold), - ?line ok = force_collection(), - ?t:sleep(?t:seconds(1)), + ok = memsup:set_procmem_high_watermark(ProcThreshold), + ok = force_collection(), + ct:sleep({seconds,1}), case alarm_set(process_memory_high_watermark) of - {true, WorstPid} when ProcP -> - ok; - false when not ProcP -> - ok; - {true, BadPid3} when ProcP -> - ?line test_server:fail({proc_alarm, WorstPid, BadPid3}); - _ -> - ?line ?t:fail({proc_alarm, PidUsage, ProcThreshold}) + {true, WorstPid} when ProcP -> + ok; + false when not ProcP -> + ok; + {true, BadPid3} when ProcP -> + ct:fail({proc_alarm, WorstPid, BadPid3}); + _ -> + ct:fail({proc_alarm, PidUsage, ProcThreshold}) end, %% Reset memory check interval - ?line ok = memsup:set_check_interval(1), + ok = memsup:set_check_interval(1), ok. -alarm2(suite) -> - []; -alarm2(doc) -> - ["Test alarms when memsup_system_only==true"]; +%% Test alarms when memsup_system_only==true alarm2(Config) when is_list(Config) -> %% If system memory usage is too high, the testcase cannot %% be run correctly - ?line {Total, Alloc, {_Pid,_PidAlloc}} = memsup:get_memory_data(), - ?line SysUsage = Alloc/Total, + {Total, Alloc, {_Pid,_PidAlloc}} = memsup:get_memory_data(), + SysUsage = Alloc/Total, if - SysUsage>0.99 -> - {skip, sys_mem_too_high}; - true -> - alarm2(Config, SysUsage) + SysUsage>0.99 -> + {skip, sys_mem_too_high}; + true -> + alarm2(Config, SysUsage) end. alarm2(_Config, _SysUsage) -> %% Change memsup_system_only and restart memsup - ?line ok = application:set_env(os_mon, memsup_system_only, true), - ?line ok = supervisor:terminate_child(os_mon_sup, memsup), - ?line {ok, _Memsup1} = supervisor:restart_child(os_mon_sup, memsup), + ok = application:set_env(os_mon, memsup_system_only, true), + ok = supervisor:terminate_child(os_mon_sup, memsup), + {ok, _Memsup1} = supervisor:restart_child(os_mon_sup, memsup), %% Set a long memory check interval, we will force memory checks %% instead - ?line ok = memsup:set_check_interval(60), + ok = memsup:set_check_interval(60), %% Check data and thresholds - ?line {Total, Alloc, undefined} = memsup:get_memory_data(), - ?line SysThreshold = (memsup:get_sysmem_high_watermark()/100), - ?line true = is_integer(memsup:get_procmem_high_watermark()), + {Total, Alloc, undefined} = memsup:get_memory_data(), + SysThreshold = (memsup:get_sysmem_high_watermark()/100), + true = is_integer(memsup:get_procmem_high_watermark()), %% Check if a system alarm already should be set or not - ?line SysUsage = Alloc/Total, + SysUsage = Alloc/Total, SysP = if - SysUsage>SysThreshold -> true; - SysUsage=<SysThreshold -> false - end, + SysUsage>SysThreshold -> true; + SysUsage=<SysThreshold -> false + end, %% If system memory is higher than threshold, make sure the system %% alarm is set. Otherwise, make sure it is not set case alarm_set(system_memory_high_watermark) of - {true, []} when SysP -> - ok; - false when not SysP -> - ok; - _ -> - ?line ?t:fail({sys_alarm, SysUsage, SysThreshold}) + {true, []} when SysP -> + ok; + false when not SysP -> + ok; + _ -> + ct:fail({sys_alarm, SysUsage, SysThreshold}) end, %% Lower/raise the threshold to clear/set the alarm NewSysThreshold = if - SysP -> - Value = 1.1*SysUsage, - if - Value > 0.99 -> 0.99; - true -> Value - end; - not SysP -> 0.9*SysUsage - end, + SysP -> + Value = 1.1*SysUsage, + if + Value > 0.99 -> 0.99; + true -> Value + end; + not SysP -> 0.9*SysUsage + end, - ?line ok = memsup:set_sysmem_high_watermark(NewSysThreshold), + ok = memsup:set_sysmem_high_watermark(NewSysThreshold), %% Initiate and wait for a new data collection - ?line ok = force_collection(), + ok = force_collection(), %% Make sure the alarm is cleared/set - ?t:sleep(?t:seconds(1)), + ct:sleep({seconds,1}), case alarm_set(system_memory_high_watermark) of - {true, []} when not SysP -> - ok; - false when SysP -> - ok; - _ -> - ?line ?t:fail({sys_alarm, SysUsage, NewSysThreshold}) + {true, []} when not SysP -> + ok; + false when SysP -> + ok; + _ -> + ct:fail({sys_alarm, SysUsage, NewSysThreshold}) end, %% Reset the threshold to set/clear the alarm again - ?line ok = memsup:set_sysmem_high_watermark(SysThreshold), - ?line ok = force_collection(), - ?t:sleep(?t:seconds(1)), + ok = memsup:set_sysmem_high_watermark(SysThreshold), + ok = force_collection(), + ct:sleep({seconds,1}), case alarm_set(system_memory_high_watermark) of - {true, []} when SysP -> - ok; - false when not SysP -> - ok; - _ -> - ?line ?t:fail({sys_alarm, SysUsage, SysThreshold}) + {true, []} when SysP -> + ok; + false when not SysP -> + ok; + _ -> + ct:fail({sys_alarm, SysUsage, SysThreshold}) end, %% Reset memsup_system_only and restart memsup %% (memory check interval is then automatically reset) - ?line ok = application:set_env(os_mon, memsup_system_only, false), - ?line ok = supervisor:terminate_child(os_mon_sup, memsup), - ?line {ok, _Memsup2} = supervisor:restart_child(os_mon_sup, memsup), + ok = application:set_env(os_mon, memsup_system_only, false), + ok = supervisor:terminate_child(os_mon_sup, memsup), + {ok, _Memsup2} = supervisor:restart_child(os_mon_sup, memsup), ok. @@ -420,39 +398,36 @@ alarm_set(Alarm, [_|T]) -> alarm_set(_Alarm, []) -> false. -process(suite) -> - []; -process(doc) -> - ["Make sure memsup discovers a process grown very large"]; +%% Make sure memsup discovers a process grown very large process(Config) when is_list(Config) -> %% Set a long memory check interval, we will force memory checks %% instead - ?line ok = memsup:set_check_interval(60), + ok = memsup:set_check_interval(60), %% Collect data MemData = memsup:get_memory_data(), io:format("process: memsup:get_memory_data() = ~p~n", [MemData]), - ?line {_Total,_Free,{_,Bytes}} = MemData, + {_Total,_Free,{_,Bytes}} = MemData, %% Start a new process larger than Worst - ?line WorsePid = spawn(fun() -> new_hog(Bytes) end), - ?t:sleep(?t:seconds(1)), + WorsePid = spawn(fun() -> new_hog(Bytes) end), + ct:sleep({seconds,1}), %% Initiate and wait for a new data collection - ?line ok = force_collection(), + ok = force_collection(), %% Check that get_memory_data() returns updated result - ?line case memsup:get_memory_data() of - {_, _, {WorsePid, _MoreBytes}} -> - ok; - {_, _, BadWorst} -> - ?line ?t:fail({worst_pid, BadWorst}) - end, + case memsup:get_memory_data() of + {_, _, {WorsePid, _MoreBytes}} -> + ok; + {_, _, BadWorst} -> + ct:fail({worst_pid, BadWorst}) + end, %% Reset memory check interval - ?line exit(WorsePid, done), - ?line ok = memsup:set_check_interval(1), + exit(WorsePid, done), + ok = memsup:set_check_interval(1), ok. new_hog(Bytes) -> @@ -463,110 +438,101 @@ new_hog(Bytes) -> new_hog_1(List) -> receive - _Any -> exit(List) + _Any -> exit(List) end. -config(suite) -> - []; -config(doc) -> - ["Test configuration"]; +%% Test configuration config(Config) when is_list(Config) -> %% Change configuration parameters and make sure change is reflected %% when memsup is restarted - ?line ok = application:set_env(os_mon, memory_check_interval, 2), - ?line ok = - application:set_env(os_mon, system_memory_high_watermark, 0.9), - ?line ok = - application:set_env(os_mon, process_memory_high_watermark, 0.1), - ?line ok = application:set_env(os_mon, memsup_helper_timeout, 35), - ?line ok = application:set_env(os_mon, memsup_system_only, true), - - ?line ok = supervisor:terminate_child(os_mon_sup, memsup), - ?line {ok, _Child1} = supervisor:restart_child(os_mon_sup, memsup), - - ?line 120000 = memsup:get_check_interval(), - ?line 90 = memsup:get_sysmem_high_watermark(), - ?line 10 = memsup:get_procmem_high_watermark(), - ?line 35 = memsup:get_helper_timeout(), + ok = application:set_env(os_mon, memory_check_interval, 2), + ok = + application:set_env(os_mon, system_memory_high_watermark, 0.9), + ok = + application:set_env(os_mon, process_memory_high_watermark, 0.1), + ok = application:set_env(os_mon, memsup_helper_timeout, 35), + ok = application:set_env(os_mon, memsup_system_only, true), + + ok = supervisor:terminate_child(os_mon_sup, memsup), + {ok, _Child1} = supervisor:restart_child(os_mon_sup, memsup), + + 120000 = memsup:get_check_interval(), + 90 = memsup:get_sysmem_high_watermark(), + 10 = memsup:get_procmem_high_watermark(), + 35 = memsup:get_helper_timeout(), %% Also try this with bad parameter values, should be ignored - ?line ok = application:set_env(os_mon, memory_check_interval, 0.2), - ?line ok = - application:set_env(os_mon, system_memory_high_watermark, -0.9), - ?line ok = - application:set_env(os_mon, process_memory_high_watermark,-0.1), - ?line ok = application:set_env(os_mon, memsup_helper_timeout, 0.35), - ?line ok = application:set_env(os_mon, memsup_system_only, arne), - - ?line ok = supervisor:terminate_child(os_mon_sup, memsup), - ?line {ok, _Child2} = supervisor:restart_child(os_mon_sup, memsup), - - ?line 60000 = memsup:get_check_interval(), - ?line 80 = memsup:get_sysmem_high_watermark(), - ?line 5 = memsup:get_procmem_high_watermark(), - ?line 30 = memsup:get_helper_timeout(), + ok = application:set_env(os_mon, memory_check_interval, 0.2), + ok = + application:set_env(os_mon, system_memory_high_watermark, -0.9), + ok = + application:set_env(os_mon, process_memory_high_watermark,-0.1), + ok = application:set_env(os_mon, memsup_helper_timeout, 0.35), + ok = application:set_env(os_mon, memsup_system_only, arne), + + ok = supervisor:terminate_child(os_mon_sup, memsup), + {ok, _Child2} = supervisor:restart_child(os_mon_sup, memsup), + + 60000 = memsup:get_check_interval(), + 80 = memsup:get_sysmem_high_watermark(), + 5 = memsup:get_procmem_high_watermark(), + 30 = memsup:get_helper_timeout(), %% Reset configuration parameters - ?line ok = application:set_env(os_mon, memory_check_interval, 1), - ?line ok = - application:set_env(os_mon, system_memory_high_watermark, 0.8), - ?line ok = - application:set_env(os_mon, process_memory_high_watermark,0.05), - ?line ok = application:set_env(os_mon, memsup_helper_timeout, 30), - ?line ok = application:set_env(os_mon, memsup_system_only, false), + ok = application:set_env(os_mon, memory_check_interval, 1), + ok = + application:set_env(os_mon, system_memory_high_watermark, 0.8), + ok = + application:set_env(os_mon, process_memory_high_watermark,0.05), + ok = application:set_env(os_mon, memsup_helper_timeout, 30), + ok = application:set_env(os_mon, memsup_system_only, false), ok. -unavailable(suite) -> - []; -unavailable(doc) -> - ["Test correct behaviour when service is unavailable"]; +%% Test correct behaviour when service is unavailable unavailable(Config) when is_list(Config) -> %% Close memsup - ?line ok = application:set_env(os_mon, start_memsup, false), - ?line ok = supervisor:terminate_child(os_mon_sup, memsup), + ok = application:set_env(os_mon, start_memsup, false), + ok = supervisor:terminate_child(os_mon_sup, memsup), %% Make sure all API functions return their dummy values - ?line {0,0,{_Pid,0}} = memsup:get_memory_data(), - ?line ok = application:set_env(os_mon, memsup_system_only, true), - ?line {0,0,undefined} = memsup:get_memory_data(), - ?line ok = application:set_env(os_mon, memsup_system_only, false), - ?line [] = memsup:get_system_memory_data(), - ?line 0 = memsup:get_os_wordsize(), - ?line 60000 = memsup:get_check_interval(), - ?line ok = memsup:set_check_interval(2), - ?line 5 = memsup:get_procmem_high_watermark(), - ?line ok = memsup:set_procmem_high_watermark(0.10), - ?line 80 = memsup:get_sysmem_high_watermark(), - ?line ok = memsup:set_sysmem_high_watermark(0.90), - ?line 30 = memsup:get_helper_timeout(), - ?line ok = memsup:set_helper_timeout(35), + {0,0,{_Pid,0}} = memsup:get_memory_data(), + ok = application:set_env(os_mon, memsup_system_only, true), + {0,0,undefined} = memsup:get_memory_data(), + ok = application:set_env(os_mon, memsup_system_only, false), + [] = memsup:get_system_memory_data(), + 0 = memsup:get_os_wordsize(), + 60000 = memsup:get_check_interval(), + ok = memsup:set_check_interval(2), + 5 = memsup:get_procmem_high_watermark(), + ok = memsup:set_procmem_high_watermark(0.10), + 80 = memsup:get_sysmem_high_watermark(), + ok = memsup:set_sysmem_high_watermark(0.90), + 30 = memsup:get_helper_timeout(), + ok = memsup:set_helper_timeout(35), %% Start memsup again, - ?line ok = application:set_env(os_mon, start_memsup, true), - ?line {ok, _Child} = supervisor:restart_child(os_mon_sup, memsup), + ok = application:set_env(os_mon, start_memsup, true), + {ok, _Child} = supervisor:restart_child(os_mon_sup, memsup), ok. -timeout(suite) -> - []; -timeout(doc) -> - ["Test stability of memsup when data collection times out"]; +%% Test stability of memsup when data collection times out timeout(Config) when is_list(Config) -> %% Set a long memory check interval and memsup_helper timeout, %% we will force memory checks instead and fake timeouts - ?line ok = memsup:set_check_interval(60), - ?line ok = memsup:set_helper_timeout(3600), + ok = memsup:set_check_interval(60), + ok = memsup:set_helper_timeout(3600), %% Provoke a timeout during memory collection - ?line memsup ! time_to_collect, - ?line memsup ! reg_collection_timeout, + memsup ! time_to_collect, + memsup ! reg_collection_timeout, %% Not much we can check though, except that memsup is still running - ?line {_,_,_} = memsup:get_memory_data(), + {_,_,_} = memsup:get_memory_data(), %% Provoke a timeout during extensive memory collection %% We fake a gen_server:call/2 to be able to send a timeout message @@ -574,140 +540,133 @@ timeout(Config) when is_list(Config) -> %% Linux should be handled the same way as solaris. -% TimeoutMsg = case ?t:os_type() of -% {unix, sunos} -> ext_collection_timeout; -% {unix, linux} -> reg_collection_timeout -% end, + % TimeoutMsg = case ?t:os_type() of + % {unix, sunos} -> ext_collection_timeout; + % {unix, linux} -> reg_collection_timeout + % end, TimeoutMsg = ext_collection_timeout, - ?line Pid = whereis(memsup), - ?line Mref = erlang:monitor(process, Pid), - ?line Pid ! {'$gen_call', {self(), Mref}, get_system_memory_data}, - ?line Pid ! TimeoutMsg, + Pid = whereis(memsup), + Mref = erlang:monitor(process, Pid), + Pid ! {'$gen_call', {self(), Mref}, get_system_memory_data}, + Pid ! TimeoutMsg, receive - {Mref, []} -> - erlang:demonitor(Mref), - ?line ok; - {Mref, Res} -> - erlang:demonitor(Mref), - ?line ?t:fail({unexpected_result, Res}); - {'DOWN', Mref, _, _, _} -> - ?line ?t:fail(no_result) + {Mref, []} -> + erlang:demonitor(Mref), + ok; + {Mref, Res} -> + erlang:demonitor(Mref), + ct:fail({unexpected_result, Res}); + {'DOWN', Mref, _, _, _} -> + ct:fail(no_result) end, %% Reset memory check interval and memsup_helper timeout - ?line ok = memsup:set_check_interval(1), - ?line ok = memsup:set_helper_timeout(30), - ?line memsup ! time_to_collect, + ok = memsup:set_check_interval(1), + ok = memsup:set_helper_timeout(30), + memsup ! time_to_collect, - ?line [_|_] = memsup:get_system_memory_data(), + [_|_] = memsup:get_system_memory_data(), ok. -port(suite) -> - []; -port(doc) -> - ["Test that memsup handles a terminating port program"]; +%% Test that memsup handles a terminating port program port(Config) when is_list(Config) -> - ?line Str = os:cmd("ps -e | grep '[m]emsup'"), + Str = os:cmd("ps -e | grep '[m]emsup'"), case io_lib:fread("~s", Str) of - {ok, [Pid], _Rest} -> - - %% Monitor memsup - ?line MonRef = erlang:monitor(process, memsup), - ?line {Total1,_Alloc1,_Worst1} = memsup:get_memory_data(), - ?line true = Total1>0, - - %% Kill the port program - case os:cmd("kill -9 " ++ Pid) of - [] -> - - %% memsup should now terminate - receive - {'DOWN', MonRef, _, _, {port_died, _Reason}} -> - ok; - {'DOWN', MonRef, _, _, Reason} -> - ?line ?t:fail({unexpected_exit_reason, Reason}) - after - 3000 -> - ?line ?t:fail(still_alive) - end, - - %% Give os_mon_sup time to restart memsup - ?t:sleep(?t:seconds(3)), - ?line {Total2,_Alloc2,_Worst2} = - memsup:get_memory_data(), - ?line true = Total2>0, - - ok; - - Line -> - erlang:demonitor(MonRef), - {skip, {not_killed, Line}} - end; - _ -> - {skip, {os_pid_not_found, Str}} + {ok, [Pid], _Rest} -> + + %% Monitor memsup + MonRef = erlang:monitor(process, memsup), + {Total1,_Alloc1,_Worst1} = memsup:get_memory_data(), + true = Total1>0, + + %% Kill the port program + case os:cmd("kill -9 " ++ Pid) of + [] -> + + %% memsup should now terminate + receive + {'DOWN', MonRef, _, _, {port_died, _Reason}} -> + ok; + {'DOWN', MonRef, _, _, Reason} -> + ct:fail({unexpected_exit_reason, Reason}) + after + 3000 -> + ct:fail(still_alive) + end, + + %% Give os_mon_sup time to restart memsup + ct:sleep({seconds,3}), + {Total2,_Alloc2,_Worst2} = + memsup:get_memory_data(), + true = Total2>0, + + ok; + + Line -> + erlang:demonitor(MonRef), + {skip, {not_killed, Line}} + end; + _ -> + {skip, {os_pid_not_found, Str}} end. -otp_5910(suite) -> - []; -otp_5910(doc) -> - ["Test that alarms are cleared and not set twice"]; +%% Test that alarms are cleared and not set twice otp_5910(Config) when is_list(Config) -> Alarms = - [system_memory_high_watermark, process_memory_high_watermark], + [system_memory_high_watermark, process_memory_high_watermark], %% Make sure memsup sets both alarms - ?line ok = application:set_env(os_mon, memory_check_interval, 60), - ?line ok = memsup:set_check_interval(60), - ?line SysThreshold = (memsup:get_sysmem_high_watermark()/100), - ?line ProcThreshold = (memsup:get_procmem_high_watermark()/100), + ok = application:set_env(os_mon, memory_check_interval, 60), + ok = memsup:set_check_interval(60), + SysThreshold = (memsup:get_sysmem_high_watermark()/100), + ProcThreshold = (memsup:get_procmem_high_watermark()/100), MemData = memsup:get_memory_data(), io:format("otp_5910: memsup:get_memory_data() = ~p~n", [MemData]), - ?line {Total, Alloc, {_Pid, _Bytes}} = MemData, - ?line Pid = spawn_opt(fun() -> - receive - die -> ok - end - end, [{min_heap_size, 1000}]), + {Total, Alloc, {_Pid, _Bytes}} = MemData, + Pid = spawn_opt(fun() -> + receive + die -> ok + end + end, [{min_heap_size, 1000}]), %% Create a process guaranteed to live, be constant and %% break memsup process limit - ?line {memory, Bytes} = erlang:process_info(Pid,memory), - ?line SysUsage = Alloc/Total, - ?line ProcUsage = Bytes/Total, + {memory, Bytes} = erlang:process_info(Pid,memory), + SysUsage = Alloc/Total, + ProcUsage = Bytes/Total, if - SysUsage>SysThreshold -> - ok; - SysUsage=<SysThreshold -> - ?line ok = application:set_env(os_mon, - sys_mem_high_watermark, - 0.5 * SysUsage), - ?line ok = memsup:set_sysmem_high_watermark(0.5 * SysUsage) + SysUsage>SysThreshold -> + ok; + SysUsage=<SysThreshold -> + ok = application:set_env(os_mon, + sys_mem_high_watermark, + 0.5 * SysUsage), + ok = memsup:set_sysmem_high_watermark(0.5 * SysUsage) end, if - ProcUsage>ProcThreshold -> - ok; - ProcUsage=<ProcThreshold -> - ?line ok = application:set_env(os_mon, - proc_mem_high_watermark, - 0.5 * ProcUsage), - ?line ok = memsup:set_procmem_high_watermark(0.5 *ProcUsage) + ProcUsage>ProcThreshold -> + ok; + ProcUsage=<ProcThreshold -> + ok = application:set_env(os_mon, + proc_mem_high_watermark, + 0.5 * ProcUsage), + ok = memsup:set_procmem_high_watermark(0.5 *ProcUsage) end, - ?line ok = force_collection(), - ?t:sleep(?t:seconds(1)), + ok = force_collection(), + ct:sleep({seconds,1}), lists:foreach(fun(AlarmId) -> - case alarm_set(AlarmId) of - {true, _} -> ok; - false -> - ?line ?t:fail({alarm_not_set, - AlarmId}) - end - end, - Alarms), + case alarm_set(AlarmId) of + {true, _} -> ok; + false -> + ct:fail({alarm_not_set, AlarmId}) + end + end, + Alarms), %% Kill guaranteed process... Pid ! die, @@ -715,42 +674,41 @@ otp_5910(Config) when is_list(Config) -> exit(whereis(memsup), faked_memsup_crash), %% Wait a little to make sure memsup has been restarted, %% then make sure the alarms are set once, but not twice - ?t:sleep(?t:seconds(1)), - ?line MemUsage = memsup:get_memory_data(), + ct:sleep({seconds,1}), + MemUsage = memsup:get_memory_data(), SetAlarms = alarm_handler:get_alarms(), case lists:foldl(fun(system_memory_high_watermark, {S, P}) -> - {S+1, P}; - (process_memory_high_watermark, {S, P}) -> - {S, P+1}; - (_AlarmId, Acc0) -> - Acc0 - end, - {0, 0}, - SetAlarms) of - {0, 0} -> - ok; - _ -> - ?line ?t:fail({bad_number_of_alarms, SetAlarms, MemUsage}) + {S+1, P}; + (process_memory_high_watermark, {S, P}) -> + {S, P+1}; + (_AlarmId, Acc0) -> + Acc0 + end, + {0, 0}, + SetAlarms) of + {0, 0} -> + ok; + _ -> + ct:fail({bad_number_of_alarms, SetAlarms, MemUsage}) end, %% Stop OS_Mon and make sure all memsup alarms are cleared - ?line ok = application:stop(os_mon), - ?t:sleep(?t:seconds(1)), + ok = application:stop(os_mon), + ct:sleep({seconds,1}), lists:foreach(fun(AlarmId) -> - case alarm_set(AlarmId) of - false -> ok; - {true, _} -> - ?line ?t:fail({alarm_is_set, AlarmId}) - end - end, - Alarms), + case alarm_set(AlarmId) of + false -> ok; + {true, _} -> + ct:fail({alarm_is_set, AlarmId}) + end + end, + Alarms), %% Reset configuration and restart OS_Mon - ?line ok = application:set_env(os_mon,memory_check_interval,1), - ?line ok = application:set_env(os_mon,sys_mem_high_watermark,0.8), - ?line ok = application:set_env(os_mon,proc_mem_high_watermark,0.05), - ?line ok = application:start(os_mon), - + ok = application:set_env(os_mon,memory_check_interval,1), + ok = application:set_env(os_mon,sys_mem_high_watermark,0.8), + ok = application:set_env(os_mon,proc_mem_high_watermark,0.05), + ok = application:start(os_mon), ok. %%---------------------------------------------------------------------- @@ -765,30 +723,30 @@ force_collection() -> force_collection(TimerRef) -> receive - {trace, _Pid, 'receive', {collected_sys, _Sys}} -> - erlang:cancel_timer(TimerRef), - erlang:trace(whereis(memsup), false, ['receive']), - flush(), - ok; - {trace, _Pid, 'receive', reg_collection_timeout} -> - erlang:cancel_timer(TimerRef), - erlang:trace(whereis(memsup), false, ['receive']), - flush(), - collection_timeout; - timout -> - erlang:trace(whereis(memsup), false, ['receive']), - flush(), - timeout; - _Msg -> - force_collection(TimerRef) + {trace, _Pid, 'receive', {collected_sys, _Sys}} -> + erlang:cancel_timer(TimerRef), + erlang:trace(whereis(memsup), false, ['receive']), + flush(), + ok; + {trace, _Pid, 'receive', reg_collection_timeout} -> + erlang:cancel_timer(TimerRef), + erlang:trace(whereis(memsup), false, ['receive']), + flush(), + collection_timeout; + timout -> + erlang:trace(whereis(memsup), false, ['receive']), + flush(), + timeout; + _Msg -> + force_collection(TimerRef) end. flush() -> receive - {trace, _, _, _} -> - flush(); - timeout -> - flush() + {trace, _, _, _} -> + flush(); + timeout -> + flush() after 0 -> - ok + ok end. diff --git a/lib/os_mon/test/os_mon_SUITE.erl b/lib/os_mon/test/os_mon_SUITE.erl index 3cf8ae9c4c..033a5ae162 100644 --- a/lib/os_mon/test/os_mon_SUITE.erl +++ b/lib/os_mon/test/os_mon_SUITE.erl @@ -21,92 +21,58 @@ -include_lib("common_test/include/ct.hrl"). %% Test server specific exports --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]). +-export([all/0, suite/0]). %% Test cases -export([app_file/1, appup_file/1, config/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), - test_server:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> case test_server:os_type() of - {unix, sunos} -> [app_file, appup_file, config]; - _OS -> [app_file, appup_file] + {unix, sunos} -> [app_file, appup_file, config]; + _OS -> [app_file, appup_file] end. -groups() -> - []. - -init_per_suite(Config) -> - Config. - -end_per_suite(_Config) -> - ok. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - - -app_file(suite) -> - []; -app_file(doc) -> - ["Testing .app file"]; +%% Testing .app file app_file(Config) when is_list(Config) -> - ?line ok = test_server:app_test(os_mon), + ok = test_server:app_test(os_mon), ok. appup_file(Config) when is_list(Config) -> ok = test_server:appup_test(os_mon). -config(suite) -> - []; -config(doc) -> - ["Test OS_Mon configuration"]; +%% Test OS_Mon configuration config(Config) when is_list(Config) -> IsReg = fun(Name) -> is_pid(whereis(Name)) end, IsNotReg = fun(Name) -> undefined == whereis(Name) end, - ?line ok = application:start(os_mon), - ?line true = lists:all(IsReg, [cpu_sup, disksup, memsup]), - ?line ok = application:stop(os_mon), - - ?line ok = application:set_env(os_mon, start_cpu_sup, false), - ?line ok = application:start(os_mon), - ?line true = lists:all(IsReg, [disksup, memsup]), - ?line true = IsNotReg(cpu_sup), - ?line ok = application:stop(os_mon), - ?line ok = application:set_env(os_mon, start_cpu_sup, true), - - ?line ok = application:set_env(os_mon, start_disksup, false), - ?line ok = application:start(os_mon), - ?line true = lists:all(IsReg, [cpu_sup, memsup]), - ?line true = IsNotReg(disksup), - ?line ok = application:stop(os_mon), - ?line ok = application:set_env(os_mon, start_disksup, true), - - ?line ok = application:set_env(os_mon, start_memsup, false), - ?line ok = application:start(os_mon), - ?line true = lists:all(IsReg, [cpu_sup, disksup]), - ?line true = IsNotReg(memsup), - ?line ok = application:stop(os_mon), - ?line ok = application:set_env(os_mon, start_memsup, true), + ok = application:start(os_mon), + true = lists:all(IsReg, [cpu_sup, disksup, memsup]), + ok = application:stop(os_mon), + + ok = application:set_env(os_mon, start_cpu_sup, false), + ok = application:start(os_mon), + true = lists:all(IsReg, [disksup, memsup]), + true = IsNotReg(cpu_sup), + ok = application:stop(os_mon), + ok = application:set_env(os_mon, start_cpu_sup, true), + + ok = application:set_env(os_mon, start_disksup, false), + ok = application:start(os_mon), + true = lists:all(IsReg, [cpu_sup, memsup]), + true = IsNotReg(disksup), + ok = application:stop(os_mon), + ok = application:set_env(os_mon, start_disksup, true), + + ok = application:set_env(os_mon, start_memsup, false), + ok = application:start(os_mon), + true = lists:all(IsReg, [cpu_sup, disksup]), + true = IsNotReg(memsup), + ok = application:stop(os_mon), + ok = application:set_env(os_mon, start_memsup, true), ok. diff --git a/lib/os_mon/test/os_mon_mib_SUITE.erl b/lib/os_mon/test/os_mon_mib_SUITE.erl index d50b9c1bdc..f40d5f442c 100644 --- a/lib/os_mon/test/os_mon_mib_SUITE.erl +++ b/lib/os_mon/test/os_mon_mib_SUITE.erl @@ -35,25 +35,23 @@ -include_lib("snmp/include/snmp_types.hrl"). % Test server specific exports --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]). +-export([all/0, suite/0, groups/0, + init_per_suite/1, end_per_suite/1]). % Test cases must be exported. -export([update_load_table/1]). -export([get_mem_sys_mark/1, get_mem_proc_mark/1, get_disk_threshold/1, - get_load_table/1, get_disk_table/1, - real_snmp_request/1, load_unload/1]). + get_load_table/1, get_disk_table/1, + real_snmp_request/1, load_unload/1]). -export([sys_tot_mem/1, sys_used_mem/1, large_erl_process/1, - large_erl_process_mem/1, cpu_load/1, cpu_load5/1, cpu_load15/1, - os_wordsize/1, sys_tot_mem64/1, sys_used_mem64/1, - large_erl_process_mem64/1, disk_descr/1, disk_kbytes/1, - disk_capacity/1]). + large_erl_process_mem/1, cpu_load/1, cpu_load5/1, cpu_load15/1, + os_wordsize/1, sys_tot_mem64/1, sys_used_mem64/1, + large_erl_process_mem64/1, disk_descr/1, disk_kbytes/1, + disk_capacity/1]). --export([]). -export([otp_6351/1, otp_7441/1]). -define(TRAP_UDP, 5000). @@ -65,17 +63,11 @@ -define(MGR_PORT, 5001). %%--------------------------------------------------------------------- -init_per_testcase(_Case, Config) when is_list(Config) -> - Dog = test_server:timetrap(test_server:minutes(6)), - [{watchdog, Dog}|Config]. -end_per_testcase(_Case, Config) when is_list(Config) -> - Dog = ?config(watchdog, Config), - test_server:timetrap_cancel(Dog), - Config. - -suite() -> [{ct_hooks,[ts_install_cth]}, - {require, snmp_mgr_agent, snmp}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,6}}, + {require, snmp_mgr_agent, snmp}]. all() -> [load_unload, get_mem_sys_mark, get_mem_proc_mark, @@ -94,12 +86,6 @@ groups() -> {get_next_disk_table, [], [disk_descr, disk_kbytes, disk_capacity]}]. -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - %%--------------------------------------------------------------------- %%-------------------------------------------------------------------- @@ -112,9 +98,9 @@ end_per_group(_GroupName, Config) -> %% variable, but should NOT alter/remove any existing entries. %%-------------------------------------------------------------------- init_per_suite(Config) -> - ?line application:start(sasl), - ?line application:start(mnesia), - ?line application:start(os_mon), + application:start(sasl), + application:start(mnesia), + application:start(os_mon), ok = ct_snmp:start(Config,snmp_mgr_agent), @@ -130,7 +116,7 @@ init_per_suite(Config) -> %% Description: Cleanup after the whole suite %%-------------------------------------------------------------------- end_per_suite(Config) -> - PrivDir = ?config(priv_dir, Config), + PrivDir = proplists:get_value(priv_dir, Config), ConfDir = filename:join(PrivDir,"conf"), DbDir = filename:join(PrivDir,"db"), MgrDir = filename:join(PrivDir, "mgr"), @@ -152,92 +138,74 @@ end_per_suite(Config) -> %%--------------------------------------------------------------------- %% Test cases %%--------------------------------------------------------------------- -load_unload(doc) -> - ["Test to unload and the reload the OTP.mib "]; -load_unload(suite) -> []; + +%% Test to unload and the reload the OTP.mib load_unload(Config) when is_list(Config) -> - ?line os_mon_mib:unload(snmp_master_agent), - ?line os_mon_mib:load(snmp_master_agent), + os_mon_mib:unload(snmp_master_agent), + os_mon_mib:load(snmp_master_agent), ok. %%--------------------------------------------------------------------- -update_load_table(doc) -> - ["check os_mon_mib:update_load_table error handling"]; -update_load_table(suite) -> - []; +%% check os_mon_mib:update_load_table error handling update_load_table(Config) when is_list(Config) -> - ?line Node = start_node(), - ?line ok = rpc:call(Node,application,start,[sasl]), - ?line ok = rpc:call(Node,application,start,[os_mon]), - ?line ok = os_mon_mib:update_load_table(), - ?line rpc:call(Node,application,stop,[os_mon]), - ?line ok = os_mon_mib:update_load_table(), - ?line stop_node(Node), + Node = start_node(), + ok = rpc:call(Node,application,start,[sasl]), + ok = rpc:call(Node,application,start,[os_mon]), + ok = os_mon_mib:update_load_table(), + rpc:call(Node,application,stop,[os_mon]), + ok = os_mon_mib:update_load_table(), + stop_node(Node), ok. -otp_6351(doc) -> - ["like update_load_table, when memsup_system_only==true"]; -otp_6351(suite) -> - []; +%% like update_load_table, when memsup_system_only==true otp_6351(Config) when is_list(Config) -> - ?line Node = start_node(), - ?line ok = rpc:call(Node,application,start,[sasl]), - ?line ok = rpc:call(Node,application,load,[os_mon]), - ?line ok = rpc:call(Node,application,set_env, - [os_mon,memsup_system_only,true]), - ?line ok = rpc:call(Node,application,start,[os_mon]), - ?line Res = rpc:call(Node,os_mon_mib,get_load,[Node]), + Node = start_node(), + ok = rpc:call(Node,application,start,[sasl]), + ok = rpc:call(Node,application,load,[os_mon]), + ok = rpc:call(Node,application,set_env, + [os_mon,memsup_system_only,true]), + ok = rpc:call(Node,application,start,[os_mon]), + Res = rpc:call(Node,os_mon_mib,get_load,[Node]), if - is_tuple(Res), element(1, Res)==loadTable -> - ?line ok; - true -> - ?line ?t:fail(Res) + is_tuple(Res), element(1, Res)==loadTable -> + ok; + true -> + ct:fail(Res) end, - ?line rpc:call(Node,application,stop,[os_mon]), - ?line stop_node(Node), + rpc:call(Node,application,stop,[os_mon]), + stop_node(Node), ok. - - %%--------------------------------------------------------------------- -get_mem_sys_mark(doc) -> - ["Simulates a get call to test the instrumentation function " - "for the loadMemorySystemWatermark variable."]; -get_mem_sys_mark(suite) -> - []; +%% Simulates a get call to test the instrumentation function +%% for the loadMemorySystemWatermark variable. get_mem_sys_mark(Config) when is_list(Config) -> case os_mon_mib:mem_sys_mark(get) of - {value, SysMark} when is_integer(SysMark) -> - ok; - _ -> - ?line test_server:fail(sys_mark_value_not_integer) + {value, SysMark} when is_integer(SysMark) -> + ok; + _ -> + ct:fail(sys_mark_value_not_integer) end. %%--------------------------------------------------------------------- -get_mem_proc_mark(doc) -> - ["Simulates a get call to test the instrumentation function " - "for the loadMemoryErlProcWatermark variable."]; -get_mem_proc_mark(suite) -> - []; +%% Simulates a get call to test the instrumentation function +%% for the loadMemoryErlProcWatermark variable. get_mem_proc_mark(Config) when is_list(Config) -> case os_mon_mib:mem_proc_mark(get) of - {value, ProcMark} when is_integer(ProcMark) -> - ok; - _ -> - ?line test_server:fail(proc_mark_value_not_integer) + {value, ProcMark} when is_integer(ProcMark) -> + ok; + _ -> + ct:fail(proc_mark_value_not_integer) end. %%--------------------------------------------------------------------- -get_disk_threshold(doc) -> - ["Simulates a get call to test the instrumentation function " - "for the diskAlmostFullThreshold variable."]; -get_disk_threshold(suite) -> - []; +%% Simulates a get call to test the instrumentation function +%% for the diskAlmostFullThreshold variable. get_disk_threshold(Config) when is_list(Config) -> - case os_mon_mib:disk_threshold(get) of - {value, ProcMark} when is_integer(ProcMark) -> - ok; - _ -> - ?line test_server:fail(disk_threshold_value_not_integer) + case os_mon_mib:disk_threshold(get) of + {value, ProcMark} when is_integer(ProcMark) -> + ok; + _ -> + ct:fail(disk_threshold_value_not_integer) end. %%--------------------------------------------------------------------- @@ -247,11 +215,8 @@ get_disk_threshold(Config) when is_list(Config) -> %%% instrumentation functions directly as done in most test cases in %%% this test suite -get_load_table(doc) -> - ["Simulates get calls to test the instrumentation function " - "for the loadTable"]; -get_load_table(suite) -> - []; +%% Simulates get calls to test the instrumentation function +%% for the loadTable get_load_table(Config) when is_list(Config) -> NodeStr = atom_to_list(node()), @@ -259,376 +224,316 @@ get_load_table(Config) when is_list(Config) -> {_, _, {Pid, _}} = memsup:get_memory_data(), PidStr = lists:flatten(io_lib:format("~w", [Pid])), - ?line [{value, NodeStr},{value, PidStr}] = - os_mon_mib:load_table(get, [NodeLen | NodeStr], - [?loadErlNodeName, ?loadLargestErlProcess]), - - ?line Values = os_mon_mib:load_table(get, [NodeLen | NodeStr] , - [?loadSystemTotalMemory, - ?loadSystemUsedMemory, - ?loadLargestErlProcessUsedMemory, - ?loadCpuLoad, - ?loadCpuLoad5, - ?loadCpuLoad15, - ?loadOsWordsize, - ?loadSystemTotalMemory64, - ?loadSystemUsedMemory64, - ?loadLargestErlProcessUsedMemory64]), + [{value, NodeStr},{value, PidStr}] = + os_mon_mib:load_table(get, [NodeLen | NodeStr], + [?loadErlNodeName, ?loadLargestErlProcess]), + + Values = os_mon_mib:load_table(get, [NodeLen | NodeStr] , + [?loadSystemTotalMemory, + ?loadSystemUsedMemory, + ?loadLargestErlProcessUsedMemory, + ?loadCpuLoad, + ?loadCpuLoad5, + ?loadCpuLoad15, + ?loadOsWordsize, + ?loadSystemTotalMemory64, + ?loadSystemUsedMemory64, + ?loadLargestErlProcessUsedMemory64]), IsInt = fun({value, Val}) when is_integer(Val) -> - true; - (_) -> - false - end, + true; + (_) -> + false + end, NewValues = lists:filter(IsInt, Values), case length(NewValues) of - 10 -> - ok; - _ -> - ?line test_server:fail(value_not_integer) + 10 -> + ok; + _ -> + ct:fail(value_not_integer) end, - ?line [{noValue,noSuchInstance}, {noValue,noSuchInstance}, - {noValue,noSuchInstance}, {noValue,noSuchInstance}, - {noValue,noSuchInstance}, {noValue,noSuchInstance}, - {noValue,noSuchInstance}, {noValue,noSuchInstance}, - {noValue,noSuchInstance}, {noValue,noSuchInstance}, - {noValue,noSuchInstance}, {noValue,noSuchInstance}] = - os_mon_mib:load_table(get, [3, 102, 111, 111], - [?loadErlNodeName, - ?loadSystemTotalMemory, - ?loadSystemUsedMemory, - ?loadLargestErlProcess, - ?loadLargestErlProcessUsedMemory, - ?loadCpuLoad, - ?loadCpuLoad5, - ?loadCpuLoad15, - ?loadOsWordsize, - ?loadSystemTotalMemory64, - ?loadSystemUsedMemory64, - ?loadLargestErlProcessUsedMemory64]), + [{noValue,noSuchInstance}, {noValue,noSuchInstance}, + {noValue,noSuchInstance}, {noValue,noSuchInstance}, + {noValue,noSuchInstance}, {noValue,noSuchInstance}, + {noValue,noSuchInstance}, {noValue,noSuchInstance}, + {noValue,noSuchInstance}, {noValue,noSuchInstance}, + {noValue,noSuchInstance}, {noValue,noSuchInstance}] = + os_mon_mib:load_table(get, [3, 102, 111, 111], + [?loadErlNodeName, + ?loadSystemTotalMemory, + ?loadSystemUsedMemory, + ?loadLargestErlProcess, + ?loadLargestErlProcessUsedMemory, + ?loadCpuLoad, + ?loadCpuLoad5, + ?loadCpuLoad15, + ?loadOsWordsize, + ?loadSystemTotalMemory64, + ?loadSystemUsedMemory64, + ?loadLargestErlProcessUsedMemory64]), ok. %%--------------------------------------------------------------------- -sys_tot_mem(doc) -> - []; -sys_tot_mem(suite) -> - []; sys_tot_mem(Config) when is_list(Config) -> - ?line [{[?loadSystemTotalMemory, Len | NodeStr], Mem}] = - os_mon_mib:load_table(get_next, [], [?loadSystemTotalMemory]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + [{[?loadSystemTotalMemory, Len | NodeStr], Mem}] = + os_mon_mib:load_table(get_next, [], [?loadSystemTotalMemory]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), case Mem of - Mem when is_integer(Mem) -> - ok; - _ -> - ?line test_server:fail(sys_tot_mem_value_not_integer) + Mem when is_integer(Mem) -> + ok; + _ -> + ct:fail(sys_tot_mem_value_not_integer) end. -sys_used_mem(doc) -> - []; -sys_used_mem(suite) -> []; sys_used_mem(Config) when is_list(Config) -> - ?line [{[?loadSystemUsedMemory, Len | NodeStr], Mem}] = - os_mon_mib:load_table(get_next,[], [?loadSystemUsedMemory]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + [{[?loadSystemUsedMemory, Len | NodeStr], Mem}] = + os_mon_mib:load_table(get_next,[], [?loadSystemUsedMemory]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), case Mem of - Mem when is_integer(Mem) -> - ok; - _ -> - ?line test_server:fail(sys_used_mem_value_not_integer) + Mem when is_integer(Mem) -> + ok; + _ -> + ct:fail(sys_used_mem_value_not_integer) end. -large_erl_process(doc) -> - []; -large_erl_process(suite) -> - []; large_erl_process(Config) when is_list(Config) -> {_, _, {Pid, _}} = memsup:get_memory_data(), PidStr = lists:flatten(io_lib:format("~w", [Pid])), - ?line [{[?loadLargestErlProcess, Len | NodeStr], PidStr}] = - os_mon_mib:load_table(get_next,[], [?loadLargestErlProcess]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + [{[?loadLargestErlProcess, Len | NodeStr], PidStr}] = + os_mon_mib:load_table(get_next,[], [?loadLargestErlProcess]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), ok. -large_erl_process_mem(doc) -> - []; -large_erl_process_mem(suite) -> - []; large_erl_process_mem(Config) when is_list(Config) -> - ?line [{[?loadLargestErlProcessUsedMemory, Len | NodeStr], Mem}] = - os_mon_mib:load_table(get_next,[], - [?loadLargestErlProcessUsedMemory]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), - - case Mem of - Mem when is_integer(Mem) -> - ok; - _ -> - ?line test_server:fail(erl_pid_mem_value_not_integer) + [{[?loadLargestErlProcessUsedMemory, Len | NodeStr], Mem}] = + os_mon_mib:load_table(get_next,[], + [?loadLargestErlProcessUsedMemory]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + + case Mem of + Mem when is_integer(Mem) -> + ok; + _ -> + ct:fail(erl_pid_mem_value_not_integer) end. -cpu_load(doc) -> - []; -cpu_load(suite) -> - []; cpu_load(Config) when is_list(Config) -> - ?line [{[?loadCpuLoad, Len | NodeStr], Load}] = - os_mon_mib:load_table(get_next,[], [?loadCpuLoad]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + [{[?loadCpuLoad, Len | NodeStr], Load}] = + os_mon_mib:load_table(get_next,[], [?loadCpuLoad]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), case Load of - Load when is_integer(Load) -> - ok; - _ -> - ?line test_server:fail(cpu_load_value_not_integer) + Load when is_integer(Load) -> + ok; + _ -> + ct:fail(cpu_load_value_not_integer) end. -cpu_load5(doc) -> - []; -cpu_load5(suite) -> - []; cpu_load5(Config) when is_list(Config) -> - ?line [{[?loadCpuLoad5, Len | NodeStr], Load}] = - os_mon_mib:load_table(get_next,[], [?loadCpuLoad5]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + [{[?loadCpuLoad5, Len | NodeStr], Load}] = + os_mon_mib:load_table(get_next,[], [?loadCpuLoad5]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), case Load of - Load when is_integer(Load) -> - ok; - _ -> - ?line test_server:fail(cpu_load5_value_not_integer) + Load when is_integer(Load) -> + ok; + _ -> + ct:fail(cpu_load5_value_not_integer) end. -cpu_load15(doc) -> - []; -cpu_load15(suite) -> - []; cpu_load15(Config) when is_list(Config) -> - ?line [{[?loadCpuLoad15, Len | NodeStr], Load}] = - os_mon_mib:load_table(get_next,[], [?loadCpuLoad15]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), - - case Load of - Load when is_integer(Load) -> - ok; - _ -> - ?line test_server:fail(cpu_load15_value_not_integer) - end. - -os_wordsize(doc) -> - []; -os_wordsize(suite) -> - []; + [{[?loadCpuLoad15, Len | NodeStr], Load}] = + os_mon_mib:load_table(get_next,[], [?loadCpuLoad15]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + + case Load of + Load when is_integer(Load) -> + ok; + _ -> + ct:fail(cpu_load15_value_not_integer) + end. + os_wordsize(Config) when is_list(Config) -> - ?line [{[?loadOsWordsize, Len | NodeStr], Wordsize}] = - os_mon_mib:load_table(get_next,[], [?loadOsWordsize]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), - - case Wordsize of - Wordsize when is_integer(Wordsize) -> - ok; - _ -> - ?line test_server:fail(os_wordsize_value_not_integer) - end. - -sys_tot_mem64(doc) -> - []; -sys_tot_mem64(suite) -> - []; + [{[?loadOsWordsize, Len | NodeStr], Wordsize}] = + os_mon_mib:load_table(get_next,[], [?loadOsWordsize]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + + case Wordsize of + Wordsize when is_integer(Wordsize) -> + ok; + _ -> + ct:fail(os_wordsize_value_not_integer) + end. + sys_tot_mem64(Config) when is_list(Config) -> - ?line [{[?loadSystemTotalMemory64, Len | NodeStr], Mem}] = - os_mon_mib:load_table(get_next, [], [?loadSystemTotalMemory64]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + [{[?loadSystemTotalMemory64, Len | NodeStr], Mem}] = + os_mon_mib:load_table(get_next, [], [?loadSystemTotalMemory64]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), case Mem of - Mem when is_integer(Mem) -> - ok; - _ -> - ?line test_server:fail(sys_tot_mem_value_not_integer) + Mem when is_integer(Mem) -> + ok; + _ -> + ct:fail(sys_tot_mem_value_not_integer) end. -sys_used_mem64(doc) -> - []; -sys_used_mem64(suite) -> []; sys_used_mem64(Config) when is_list(Config) -> - ?line [{[?loadSystemUsedMemory64, Len | NodeStr], Mem}] = - os_mon_mib:load_table(get_next,[], [?loadSystemUsedMemory64]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + [{[?loadSystemUsedMemory64, Len | NodeStr], Mem}] = + os_mon_mib:load_table(get_next,[], [?loadSystemUsedMemory64]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), case Mem of - Mem when is_integer(Mem) -> - ok; - _ -> - ?line test_server:fail(sys_used_mem_value_not_integer) + Mem when is_integer(Mem) -> + ok; + _ -> + ct:fail(sys_used_mem_value_not_integer) end. -large_erl_process_mem64(doc) -> - []; -large_erl_process_mem64(suite) -> - []; large_erl_process_mem64(Config) when is_list(Config) -> - ?line [{[?loadLargestErlProcessUsedMemory64, Len | NodeStr], Mem}] = - os_mon_mib:load_table(get_next,[], - [?loadLargestErlProcessUsedMemory64]), - ?line Len = length(NodeStr), - ?line true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), - - case Mem of - Mem when is_integer(Mem) -> - ok; - _ -> - ?line test_server:fail(erl_pid_mem_value_not_integer) + [{[?loadLargestErlProcessUsedMemory64, Len | NodeStr], Mem}] = + os_mon_mib:load_table(get_next,[], + [?loadLargestErlProcessUsedMemory64]), + Len = length(NodeStr), + true = lists:member(list_to_atom(NodeStr), [node() | nodes()]), + + case Mem of + Mem when is_integer(Mem) -> + ok; + _ -> + ct:fail(erl_pid_mem_value_not_integer) end. %%--------------------------------------------------------------------- -get_disk_table(doc) -> - ["Simulates get calls to test the instrumentation function " - "for the diskTable."]; -get_disk_table(suite) -> - []; +%% Simulates get calls to test the instrumentation function +%% for the diskTable. get_disk_table(Config) when is_list(Config) -> DiskData = disksup:get_disk_data(), DiskDataLen = length(DiskData), if - DiskDataLen > 0 -> - ?line [{value, Value}] = - os_mon_mib:disk_table(get, [1,1], [?diskDescr]), - - case is_list(Value) of - true -> - ok; - false -> - ?line test_server:fail(value_not_a_string) - end, - - ?line Values = os_mon_mib:disk_table(get, [1,1], - [?diskId, - ?diskKBytes, - ?diskCapacity]), - - IsInt = fun({value, Val}) when is_integer(Val) -> - true; - (_) -> - false - end, - - NewValues = lists:filter(IsInt, Values), - - case length(NewValues) of - 3 -> - ok; - _ -> - ?line test_server:fail(value_not_integer) - end + DiskDataLen > 0 -> + [{value, Value}] = + os_mon_mib:disk_table(get, [1,1], [?diskDescr]), + + case is_list(Value) of + true -> + ok; + false -> + ct:fail(value_not_a_string) + end, + + Values = os_mon_mib:disk_table(get, [1,1], + [?diskId, + ?diskKBytes, + ?diskCapacity]), + + IsInt = fun({value, Val}) when is_integer(Val) -> + true; + (_) -> + false + end, + + NewValues = lists:filter(IsInt, Values), + + case length(NewValues) of + 3 -> + ok; + _ -> + ct:fail(value_not_integer) + end end, - ?line [{noValue,noSuchInstance}, {noValue,noSuchInstance}, - {noValue,noSuchInstance}, {noValue,noSuchInstance}] = - os_mon_mib:disk_table(get, [1, DiskDataLen + 1], [?diskId, - ?diskDescr, - ?diskKBytes, - ?diskCapacity]), + [{noValue,noSuchInstance}, {noValue,noSuchInstance}, + {noValue,noSuchInstance}, {noValue,noSuchInstance}] = + os_mon_mib:disk_table(get, [1, DiskDataLen + 1], [?diskId, + ?diskDescr, + ?diskKBytes, + ?diskCapacity]), ok. %%--------------------------------------------------------------------- -disk_descr(doc) -> - []; -disk_descr(suite) -> - []; disk_descr(Config) when is_list(Config) -> - ?line [{[?diskDescr, 1,1], Descr}] = - os_mon_mib:disk_table(get_next, [], [?diskDescr]), + [{[?diskDescr, 1,1], Descr}] = + os_mon_mib:disk_table(get_next, [], [?diskDescr]), case Descr of - Descr when is_list(Descr) -> - ok; - _ -> - ?line test_server:fail(disk_descr_value_not_a_string) + Descr when is_list(Descr) -> + ok; + _ -> + ct:fail(disk_descr_value_not_a_string) end. -disk_kbytes(doc) -> - []; -disk_kbytes(suite) -> []; disk_kbytes(Config) when is_list(Config) -> - ?line [{[?diskKBytes, 1,1], Kbytes}] = - os_mon_mib:disk_table(get_next,[], [?diskKBytes]), + [{[?diskKBytes, 1,1], Kbytes}] = + os_mon_mib:disk_table(get_next,[], [?diskKBytes]), case Kbytes of - Kbytes when is_integer(Kbytes) -> - ok; - _ -> - ?line test_server:fail(disk_kbytes_value_not_integer) + Kbytes when is_integer(Kbytes) -> + ok; + _ -> + ct:fail(disk_kbytes_value_not_integer) end. -disk_capacity(doc) -> - []; -disk_capacity(suite) -> []; disk_capacity(Config) when is_list(Config) -> - ?line [{[?diskCapacity, 1,1], Capacity}] = - os_mon_mib:disk_table(get_next,[], [?diskCapacity]), + [{[?diskCapacity, 1,1], Capacity}] = + os_mon_mib:disk_table(get_next,[], [?diskCapacity]), case Capacity of - Capacity when is_integer(Capacity) -> - ok; - _ -> - ?line test_server:fail(disk_capacity_value_not_integer) + Capacity when is_integer(Capacity) -> + ok; + _ -> + ct:fail(disk_capacity_value_not_integer) end. %%--------------------------------------------------------------------- -real_snmp_request(doc) -> - ["Starts an snmp manager and sends a real snmp-request. i.e. " - "sends a udp message on the correct format."]; -real_snmp_request(suite) -> []; +%% Starts an snmp manager and sends a real snmp-request. i.e. +%% sends a udp message on the correct format. real_snmp_request(Config) when is_list(Config) -> NodStr = atom_to_list(node()), Len = length(NodStr), {_, _, {Pid, _}} = memsup:get_memory_data(), PidStr = lists:flatten(io_lib:format("~w", [Pid])), io:format("FOO: ~p~n", [PidStr]), - ?line ok = snmp_get([?loadEntry ++ - [?loadLargestErlProcess, Len | NodStr]], - PidStr), - ?line ok = snmp_get_next([?loadEntry ++ - [?loadSystemUsedMemory, Len | NodStr]], - ?loadEntry ++ [?loadSystemUsedMemory + 1, Len - | NodStr], PidStr), - ?line ok = snmp_set([?loadEntry ++ [?loadLargestErlProcess, Len | NodStr]], - s, "<0.101.0>", Config), + ok = snmp_get([?loadEntry ++ + [?loadLargestErlProcess, Len | NodStr]], + PidStr), + ok = snmp_get_next([?loadEntry ++ + [?loadSystemUsedMemory, Len | NodStr]], + ?loadEntry ++ [?loadSystemUsedMemory + 1, Len + | NodStr], PidStr), + ok = snmp_set([?loadEntry ++ [?loadLargestErlProcess, Len | NodStr]], + s, "<0.101.0>", Config), ok. -otp_7441(doc) -> - ["Starts an snmp manager and requests total memory. Was previously - integer32 which was errornous on 64 bit machines."]; -otp_7441(suite) -> - []; +%% Starts an snmp manager and requests total memory. Was previously +%% integer32 which was errornous on 64 bit machines. otp_7441(Config) when is_list(Config) -> NodStr = atom_to_list(node()), Len = length(NodStr), Oids = [Oid|_] = [?loadEntry ++ [?loadSystemTotalMemory, Len | NodStr]], {noError,0,[#varbind{oid = Oid, variabletype = 'Unsigned32'}]} = - ct_snmp:get_values(os_mon_mib_test, Oids, snmp_mgr_agent), + ct_snmp:get_values(os_mon_mib_test, Oids, snmp_mgr_agent), ok. @@ -636,9 +541,8 @@ otp_7441(Config) when is_list(Config) -> %% Internal functions %%--------------------------------------------------------------------- start_node() -> - ?line Pa = filename:dirname(code:which(?MODULE)), - ?line {ok,Node} = test_server:start_node(testnisse, slave, - [{args, " -pa " ++ Pa}]), + Pa = filename:dirname(code:which(?MODULE)), + {ok,Node} = test_server:start_node(testnisse, slave, [{args, " -pa " ++ Pa}]), Node. stop_node(Node) -> @@ -648,27 +552,27 @@ del_dir(Dir) -> io:format("Deleting: ~s~n",[Dir]), {ok, Files} = file:list_dir(Dir), FullPathFiles = lists:map(fun(File) -> filename:join(Dir, File) end, - Files), + Files), lists:foreach(fun file:delete/1, FullPathFiles), file:del_dir(Dir). %%--------------------------------------------------------------------- snmp_get(Oids = [Oid |_], Result) -> {noError,0,[#varbind{oid = Oid, - variabletype = 'OCTET STRING', - value = Result}]} = - ct_snmp:get_values(os_mon_mib_test, Oids, snmp_mgr_agent), + variabletype = 'OCTET STRING', + value = Result}]} = + ct_snmp:get_values(os_mon_mib_test, Oids, snmp_mgr_agent), ok. snmp_get_next(Oids, NextOid, Result) -> {noError,0,[#varbind{oid = NextOid, - variabletype = 'OCTET STRING', - value = Result}]} = - ct_snmp:get_next_values(os_mon_mib_test, Oids, snmp_mgr_agent), + variabletype = 'OCTET STRING', + value = Result}]} = + ct_snmp:get_next_values(os_mon_mib_test, Oids, snmp_mgr_agent), ok. snmp_set(Oid, ValuType, Value, Config) -> {notWritable, _, _} = - ct_snmp:set_values(os_mon_mib_test, [{Oid, ValuType, Value}], - snmp_mgr_agent, Config), + ct_snmp:set_values(os_mon_mib_test, [{Oid, ValuType, Value}], + snmp_mgr_agent, Config), ok. diff --git a/lib/os_mon/test/os_sup_SUITE.erl b/lib/os_mon/test/os_sup_SUITE.erl index 8d37d9675e..0c1164be41 100644 --- a/lib/os_mon/test/os_sup_SUITE.erl +++ b/lib/os_mon/test/os_sup_SUITE.erl @@ -21,17 +21,13 @@ -include_lib("common_test/include/ct.hrl"). %% Test server specific exports --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). +-export([all/0, suite/0]). -export([init_per_suite/1, end_per_suite/1]). --export([init_per_testcase/2, end_per_testcase/2]). %% Test cases -export([message/1]). -export([config/1, port/1]). -%% Default timetrap timeout (set in init_per_testcase) --define(default_timeout, ?t:minutes(1)). - -define(TAG, test_tag). -define(MFA, {?MODULE, test_mfa, [?TAG]}). @@ -39,86 +35,64 @@ init_per_suite(Config) when is_list(Config) -> spawn(fun() -> message_receptor() end), - ?line application:load(os_mon), - ?line ok = application:set_env(os_mon, start_os_sup, true), - ?line ok = application:set_env(os_mon, os_sup_mfa, ?MFA), - ?line ok = application:set_env(os_mon, os_sup_enable, false), - ?line ok = application:start(os_mon), + application:load(os_mon), + ok = application:set_env(os_mon, start_os_sup, true), + ok = application:set_env(os_mon, os_sup_mfa, ?MFA), + ok = application:set_env(os_mon, os_sup_enable, false), + ok = application:start(os_mon), Config. end_per_suite(Config) when is_list(Config) -> - ?line application:stop(os_mon), - ?line ok = application:set_env(os_mon, start_os_sup, false), + application:stop(os_mon), + ok = application:set_env(os_mon, start_os_sup, false), MFA = {os_sup, error_report, [std_error]}, - ?line ok = application:set_env(os_mon, os_sup_mfa, MFA), - ?line ok = application:set_env(os_mon, os_sup_enable, true), - ?line exit(whereis(message_receptor), done), + ok = application:set_env(os_mon, os_sup_mfa, MFA), + ok = application:set_env(os_mon, os_sup_enable, true), + exit(whereis(message_receptor), done), Config. -init_per_testcase(_Case, Config) -> - Dog = ?t: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]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap,{minutes,1}}]. all() -> case test_server:os_type() of - {unix, sunos} -> [message, config, port]; - {win32, _OSname} -> [message]; - OS -> - Str = io_lib:format("os_sup not available for ~p", - [OS]), - {skip, lists:flatten(Str)} + {unix, sunos} -> [message, config, port]; + {win32, _OSname} -> [message]; + OS -> + Str = io_lib:format("os_sup not available for ~p", + [OS]), + {skip, lists:flatten(Str)} end. -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. - -message(suite) -> - []; -message(doc) -> - ["Test OS message handling"]; +%% Test OS message handling message(Config) when is_list(Config) -> %% Fake an OS message Data = "10H11386278426HSystem4HTest5HError5HTesto", - ?line os_sup_server ! {faked_port, {data, Data}}, + os_sup_server ! {faked_port, {data, Data}}, %% Check with message_receptor that it has been received - ?t:sleep(?t:seconds(1)), + ct:sleep({seconds,1}), Msg = - case ?t:os_type() of - {unix, sunos} -> - {?TAG, Data}; - {win32, _} -> - {?TAG,{{1138,627842,0},"System","Test","Error","Testo"}} - end, - ?line message_receptor ! {check, self(), Msg}, + case ?t:os_type() of + {unix, sunos} -> + {?TAG, Data}; + {win32, _} -> + {?TAG,{{1138,627842,0},"System","Test","Error","Testo"}} + end, + message_receptor ! {check, self(), Msg}, receive - {result, true} -> - ok; - {result, Rec} -> - ?t:fail({no_message, Rec}) + {result, true} -> + ok; + {result, Rec} -> + ct:fail({no_message, Rec}) end, ok. -config(suite) -> - []; -config(doc) -> - ["Test configuration"]; +%% Test configuration config(Config) when is_list(Config) -> %% os_sup_enable==true and os_sup_own/os_sup_syslogconf cannot @@ -130,45 +104,42 @@ config(Config) when is_list(Config) -> ok. -port(suite) -> - []; -port(doc) -> - ["Test that os_sup handles a terminating port program"]; +%% Test that os_sup handles a terminating port program port(Config) when is_list(Config) -> - ?line Str = os:cmd("ps -e | grep '[f]errule'"), + Str = os:cmd("ps -e | grep '[f]errule'"), case io_lib:fread("~s", Str) of - {ok, [Pid], _Rest} -> - - %% Monitor os_sup_server - ?line MonRef = erlang:monitor(process, os_sup_server), - - %% Kill the port program - case os:cmd("kill -9 " ++ Pid) of - [] -> - - %% os_sup_server should now terminate - receive - {'DOWN', MonRef, _, _, {port_died, _Reason}} -> - ok; - {'DOWN', MonRef, _, _, Reason} -> - ?line ?t:fail({unexpected_exit_reason, Reason}) - after - 3000 -> - ?line ?t:fail(still_alive) - end, - - %% Give os_mon_sup time to restart os_sup - ?t:sleep(?t:seconds(3)), - ?line true = is_pid(whereis(os_sup_server)), - - ok; - - Line -> - erlang:demonitor(MonRef), - {skip, {not_killed, Line}} - end; - _ -> - {skip, {os_pid_not_found}} + {ok, [Pid], _Rest} -> + + %% Monitor os_sup_server + MonRef = erlang:monitor(process, os_sup_server), + + %% Kill the port program + case os:cmd("kill -9 " ++ Pid) of + [] -> + + %% os_sup_server should now terminate + receive + {'DOWN', MonRef, _, _, {port_died, _Reason}} -> + ok; + {'DOWN', MonRef, _, _, Reason} -> + ct:fail({unexpected_exit_reason, Reason}) + after + 3000 -> + ct:fail(still_alive) + end, + + %% Give os_mon_sup time to restart os_sup + ct:sleep({seconds,3}), + true = is_pid(whereis(os_sup_server)), + + ok; + + Line -> + erlang:demonitor(MonRef), + {skip, {not_killed, Line}} + end; + _ -> + {skip, {os_pid_not_found}} end. %%---------------------------------------------------------------------- @@ -184,18 +155,18 @@ message_receptor() -> message_receptor(Received) -> receive - %% Check if a certain message has been received - {check, From, Msg} -> - case lists:member(Msg, Received) of - true -> - From ! {result, true}, - message_receptor(lists:delete(Msg, Received)); - false -> - From ! {result, Received}, - message_receptor(Received) - end; - - %% Save all other messages - Msg -> - message_receptor([Msg|Received]) + %% Check if a certain message has been received + {check, From, Msg} -> + case lists:member(Msg, Received) of + true -> + From ! {result, true}, + message_receptor(lists:delete(Msg, Received)); + false -> + From ! {result, Received}, + message_receptor(Received) + end; + + %% Save all other messages + Msg -> + message_receptor([Msg|Received]) end. diff --git a/lib/percept/src/percept.erl b/lib/percept/src/percept.erl index b603de89da..24ddf1bd14 100644 --- a/lib/percept/src/percept.erl +++ b/lib/percept/src/percept.erl @@ -226,7 +226,7 @@ stop_webserver(Port) -> parse_and_insert(Filename, DB) -> io:format("Parsing: ~p ~n", [Filename]), - T0 = erlang:now(), + T0 = erlang:monotonic_time(milli_seconds), Pid = dbg:trace_client(file, Filename, mk_trace_parser(self())), Ref = erlang:monitor(process, Pid), parse_and_insert_loop(Filename, Pid, Ref, DB, T0). @@ -239,8 +239,8 @@ parse_and_insert_loop(Filename, Pid, Ref, DB, T0) -> {parse_complete, {Pid, Count}} -> receive {'DOWN', Ref, process, Pid, normal} -> ok after 0 -> ok end, DB ! {action, consolidate}, - T1 = erlang:now(), - io:format("Parsed ~p entries in ~p s.~n", [Count, ?seconds(T1, T0)]), + T1 = erlang:monotonic_time(milli_seconds), + io:format("Parsed ~w entries in ~w ms.~n", [Count, T1 - T0]), io:format(" ~p created processes.~n", [length(percept_db:select({information, procs}))]), io:format(" ~p opened ports.~n", [length(percept_db:select({information, ports}))]), ok; diff --git a/lib/percept/test/egd_SUITE.erl b/lib/percept/test/egd_SUITE.erl index 65c942f1e4..3562ceae88 100644 --- a/lib/percept/test/egd_SUITE.erl +++ b/lib/percept/test/egd_SUITE.erl @@ -22,195 +22,160 @@ -include_lib("common_test/include/ct.hrl"). %% Test server specific exports --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). +-export([all/0, suite/0]). -export([init_per_suite/1, end_per_suite/1]). -export([init_per_testcase/2, end_per_testcase/2]). %% Test cases --export([ - image_create_and_destroy/1, - image_shape/1, - image_primitives/1, - image_colors/1, - image_font/1, - image_png_compliant/1 - ]). - -%% Default timetrap timeout (set in init_per_testcase) --define(default_timeout, ?t:minutes(1)). +-export([image_create_and_destroy/1, + image_shape/1, + image_primitives/1, + image_colors/1, + image_font/1, + image_png_compliant/1]). -init_per_suite(Config) when is_list(Config) -> - rand:seed(exsplus), - Config. - -end_per_suite(Config) when is_list(Config) -> - Config. - -init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?default_timeout), - [{max_size, 800}, {watchdog,Dog} | Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 1}}]. all() -> [image_create_and_destroy, image_shape, image_primitives, image_colors, image_font, image_png_compliant]. -groups() -> - []. -init_per_group(_GroupName, Config) -> +init_per_suite(Config) when is_list(Config) -> + rand:seed(exsplus), Config. -end_per_group(_GroupName, Config) -> +end_per_suite(Config) when is_list(Config) -> Config. +init_per_testcase(_Case, Config) -> + [{max_size, 800}|Config]. + +end_per_testcase(_Case, _Config) -> + ok. %%---------------------------------------------------------------------- %% Tests %%---------------------------------------------------------------------- -image_create_and_destroy(suite) -> - []; -image_create_and_destroy(doc) -> - ["Image creation and destroy test."]; +%% Image creation and destroy test. image_create_and_destroy(Config) when is_list(Config) -> - {W,H} = get_size(?config(max_size, Config)), - ?line Image = egd:create(W, H), - ?line ok = egd:destroy(Image), + {W,H} = get_size(proplists:get_value(max_size, Config)), + Image = egd:create(W, H), + ok = egd:destroy(Image), ok. -image_colors(suite) -> - []; -image_colors(doc) -> - ["Image color test."]; +%% Image color test. image_colors(Config) when is_list(Config) -> - {W,H} = get_size(?config(max_size, Config)), - ?line Image = egd:create(W, H), + {W,H} = get_size(proplists:get_value(max_size, Config)), + Image = egd:create(W, H), put(image_size, {W,H}), RGB = get_rgb(), - ?line Black = egd:color({0,0,0}), - ?line Red = egd:color({255,0,0}), - ?line Green = egd:color({0,255,0}), - ?line Blue = egd:color({0,0,255}), - ?line Random = egd:color(Image, RGB), - - ?line ok = egd:line(Image, get_point(), get_point(), Random), - ?line ok = egd:line(Image, get_point(), get_point(), Red), - ?line ok = egd:line(Image, get_point(), get_point(), Green), - ?line ok = egd:line(Image, get_point(), get_point(), Black), - ?line ok = egd:line(Image, get_point(), get_point(), Blue), + Black = egd:color({0,0,0}), + Red = egd:color({255,0,0}), + Green = egd:color({0,255,0}), + Blue = egd:color({0,0,255}), + Random = egd:color(Image, RGB), + + ok = egd:line(Image, get_point(), get_point(), Random), + ok = egd:line(Image, get_point(), get_point(), Red), + ok = egd:line(Image, get_point(), get_point(), Green), + ok = egd:line(Image, get_point(), get_point(), Black), + ok = egd:line(Image, get_point(), get_point(), Blue), HtmlDefaultNames = [black,silver,gray,white,maroon,red, - purple,fuchia,green,lime,olive,yellow,navy,blue,teal, - aqua], - - lists:foreach(fun - (ColorName) -> - ?line Color = egd:color(ColorName), - ?line ok = egd:line(Image, get_point(), get_point(), Color) - end, HtmlDefaultNames), - - ?line <<_/binary>> = egd:render(Image), - ?line ok = egd:destroy(Image), + purple,fuchia,green,lime,olive,yellow,navy,blue,teal, + aqua], + + lists:foreach(fun (ColorName) -> + Color = egd:color(ColorName), + ok = egd:line(Image, get_point(), get_point(), Color) + end, HtmlDefaultNames), + + <<_/binary>> = egd:render(Image), + ok = egd:destroy(Image), erase(image_size), ok. -image_shape(suite) -> - []; -image_shape(doc) -> - ["Image shape api test."]; +%% Image shape API test. image_shape(Config) when is_list(Config) -> - {W,H} = get_size(?config(max_size, Config)), + {W,H} = get_size(proplists:get_value(max_size, Config)), put(image_size, {W,H}), - ?line Im = egd:create(W, H), - - ?line Fgc = egd:color({255,0,0}), - - ?line ok = egd:line(Im, get_point(), get_point(), Fgc), - ?line ok = egd:rectangle(Im, get_point(), get_point(), Fgc), - ?line ok = egd:filledEllipse(Im, get_point(), get_point(), Fgc), - ?line ok = egd:arc(Im, get_point(), get_point(), Fgc), - ?line ok = egd:arc(Im, get_point(), get_point(), 100, Fgc), - + Im = egd:create(W, H), + + Fgc = egd:color({255,0,0}), + + ok = egd:line(Im, get_point(), get_point(), Fgc), + ok = egd:rectangle(Im, get_point(), get_point(), Fgc), + ok = egd:filledEllipse(Im, get_point(), get_point(), Fgc), + ok = egd:arc(Im, get_point(), get_point(), Fgc), + ok = egd:arc(Im, get_point(), get_point(), 100, Fgc), + Pt1 = get_point(), Pt2 = get_point(), - ?line ok = egd:filledRectangle(Im, Pt1, Pt2, Fgc), + ok = egd:filledRectangle(Im, Pt1, Pt2, Fgc), - ?line Bitmap = egd:render(Im, raw_bitmap), + Bitmap = egd:render(Im, raw_bitmap), - ?line ok = bitmap_point_has_color(Bitmap, {W,H}, Pt2, Fgc), - ?line ok = bitmap_point_has_color(Bitmap, {W,H}, Pt1, Fgc), + ok = bitmap_point_has_color(Bitmap, {W,H}, Pt2, Fgc), + ok = bitmap_point_has_color(Bitmap, {W,H}, Pt1, Fgc), - ?line <<_/binary>> = egd:render(Im, raw_bitmap, [{render_engine, alpha}]), + <<_/binary>> = egd:render(Im, raw_bitmap, [{render_engine, alpha}]), - ?line ok = egd:destroy(Im), + ok = egd:destroy(Im), erase(image_size), ok. -image_primitives(suite) -> - []; -image_primitives(doc) -> - ["Image shape api test."]; +%% Image shape API test. image_primitives(Config) when is_list(Config) -> - {W,H} = get_size(?config(max_size, Config)), + {W,H} = get_size(proplists:get_value(max_size, Config)), put(image_size, {W,H}), - ?line Im0 = egd_primitives:create(W, H), - ?line Fgc = egd:color({25,25,255}), - ?line Bgc = egd:color({0,250,25}), + Im0 = egd_primitives:create(W, H), + Fgc = egd:color({25,25,255}), + Bgc = egd:color({0,250,25}), - ?line Im1 = lists:foldl(fun - ({Function, Arguments}, Im) -> - ?line erlang:apply(egd_primitives, Function, [Im|Arguments]) - end, Im0, - [{Fs, [get_point(), get_point(), Bgc]} || Fs <- [line, rectangle, filledEllipse, arc]] ++ - [{pixel, [get_point(), Bgc]}, - {filledTriangle, [get_point(), get_point(), get_point(), Bgc]}]), + Im1 = lists:foldl(fun ({Function, Arguments}, Im) -> + erlang:apply(egd_primitives, Function, [Im|Arguments]) + end, Im0, + [{Fs, [get_point(), get_point(), Bgc]} || Fs <- [line, rectangle, filledEllipse, arc]] ++ + [{pixel, [get_point(), Bgc]}, + {filledTriangle, [get_point(), get_point(), get_point(), Bgc]}]), Pt1 = get_point(), Pt2 = get_point(), - ?line Im2 = egd_primitives:filledRectangle(Im1, Pt1, Pt2, Fgc), + Im2 = egd_primitives:filledRectangle(Im1, Pt1, Pt2, Fgc), - ?line Bitmap = egd_render:binary(Im2, opaque), + Bitmap = egd_render:binary(Im2, opaque), - ?line ok = bitmap_point_has_color(Bitmap, {W,H}, Pt2, Fgc), - ?line ok = bitmap_point_has_color(Bitmap, {W,H}, Pt1, Fgc), + ok = bitmap_point_has_color(Bitmap, {W,H}, Pt2, Fgc), + ok = bitmap_point_has_color(Bitmap, {W,H}, Pt1, Fgc), - ?line <<_/binary>> = egd_render:binary(Im2, alpha), + <<_/binary>> = egd_render:binary(Im2, alpha), erase(image_size), ok. - - - -image_font(suite) -> - []; -image_font(doc) -> - ["Image font test."]; +%% Image font test. image_font(Config) when is_list(Config) -> - {W,H} = get_size(?config(max_size, Config)), + {W,H} = get_size(proplists:get_value(max_size, Config)), put(image_size, {W,H}), - ?line Im = egd:create(W, H), - ?line Fgc = egd:color({0,130,0}), - - ?line Filename = filename:join([code:priv_dir(percept),"fonts","6x11_latin1.wingsfont"]), - ?line Font = egd_font:load(Filename), - + Im = egd:create(W, H), + Fgc = egd:color({0,130,0}), + + Filename = filename:join([code:priv_dir(percept),"fonts","6x11_latin1.wingsfont"]), + Font = egd_font:load(Filename), + % simple text - ?line ok = egd:text(Im, get_point(), Font, "Hello World", Fgc), - ?line <<_/binary>> = egd:render(Im, png), - + ok = egd:text(Im, get_point(), Font, "Hello World", Fgc), + <<_/binary>> = egd:render(Im, png), + GlyphStr1 = " !\"#$%&'()*+,-./", % Codes 32 -> 47 NumericStr = "0123456789", % Codes 48 -> 57 GlyphStr2 = ":;<=>?@", % Codes 58 -> 64 @@ -219,62 +184,59 @@ image_font(Config) when is_list(Config) -> AlphaSmStr = "abcdefghijklmnopqrstuvwxyz", % Codes 97 -> 122 GlyphStr4 = "{|}~", % Codes 123 -> 126 - ?line ok = egd:text(Im, get_point(), Font, GlyphStr1, Fgc), - ?line <<_/binary>> = egd:render(Im, png), + ok = egd:text(Im, get_point(), Font, GlyphStr1, Fgc), + <<_/binary>> = egd:render(Im, png), + + ok = egd:text(Im, get_point(), Font, NumericStr, Fgc), + <<_/binary>> = egd:render(Im, png), + + ok = egd:text(Im, get_point(), Font, GlyphStr2, Fgc), + <<_/binary>> = egd:render(Im, png), + + ok = egd:text(Im, get_point(), Font, AlphaBigStr, Fgc), + <<_/binary>> = egd:render(Im, png), - ?line ok = egd:text(Im, get_point(), Font, NumericStr, Fgc), - ?line <<_/binary>> = egd:render(Im, png), - - ?line ok = egd:text(Im, get_point(), Font, GlyphStr2, Fgc), - ?line <<_/binary>> = egd:render(Im, png), + ok = egd:text(Im, get_point(), Font, GlyphStr3, Fgc), + <<_/binary>> = egd:render(Im, png), - ?line ok = egd:text(Im, get_point(), Font, AlphaBigStr, Fgc), - ?line <<_/binary>> = egd:render(Im, png), - - ?line ok = egd:text(Im, get_point(), Font, GlyphStr3, Fgc), - ?line <<_/binary>> = egd:render(Im, png), + ok = egd:text(Im, get_point(), Font, AlphaSmStr, Fgc), + <<_/binary>> = egd:render(Im, png), - ?line ok = egd:text(Im, get_point(), Font, AlphaSmStr, Fgc), - ?line <<_/binary>> = egd:render(Im, png), - - ?line ok = egd:text(Im, get_point(), Font, GlyphStr4, Fgc), - ?line <<_/binary>> = egd:render(Im, png), + ok = egd:text(Im, get_point(), Font, GlyphStr4, Fgc), + <<_/binary>> = egd:render(Im, png), - ?line ok = egd:destroy(Im), + ok = egd:destroy(Im), erase(image_size), ok. -image_png_compliant(suite) -> - []; -image_png_compliant(doc) -> - ["Image png compliant test."]; +%% Image png compliant test. image_png_compliant(Config) when is_list(Config) -> - {W,H} = get_size(?config(max_size, Config)), + {W,H} = get_size(proplists:get_value(max_size, Config)), put(image_size, {W,H}), - ?line Im = egd:create(W, H), - ?line Fgc = egd:color({0,0,0}), - ?line ok = egd:filledRectangle(Im, get_point(), get_point(), Fgc), - - ?line Bin = egd:render(Im, png), - ?line true = binary_is_png_compliant(Bin), - - ?line ok = egd:destroy(Im), + Im = egd:create(W, H), + Fgc = egd:color({0,0,0}), + ok = egd:filledRectangle(Im, get_point(), get_point(), Fgc), + + Bin = egd:render(Im, png), + true = binary_is_png_compliant(Bin), + + ok = egd:destroy(Im), erase(image_size), ok. %%---------------------------------------------------------------------- %% Auxiliary tests %%---------------------------------------------------------------------- - + bitmap_point_has_color(Bitmap, {W,_}, {X,Y}, C) -> {CR,CG,CB,_} = egd_primitives:rgb_float2byte(C), N = W*Y*3 + X*3, << _:N/binary, R,G,B, _/binary>> = Bitmap, case {R,G,B} of - {CR,CG,CB} -> ok; - Other -> - io:format("bitmap_point_has_color: error color was ~p, should be ~p~n", [Other, {CR,CG,CB}]), - {error, {Other,{CR,CG,CB}}} + {CR,CG,CB} -> ok; + Other -> + io:format("bitmap_point_has_color: error color was ~p, should be ~p~n", [Other, {CR,CG,CB}]), + {error, {Other,{CR,CG,CB}}} end. %% jfif header by specification @@ -283,35 +245,35 @@ bitmap_point_has_color(Bitmap, {W,_}, {X,Y}, C) -> %% 2 bytes, version, (major, minor) %% 1 byte , units %% However, JFIF seems to start at 6 (7 with 1-index)? - + binary_is_jfif_compliant(JpegBin) -> - ?line {Bin, _} = split_binary(JpegBin, 11), + {Bin, _} = split_binary(JpegBin, 11), List = binary_to_list(Bin), case lists:sublist(List, 7, 4) of - "JFIF" -> true; - Other -> - io:format("img -> ~p~n", [Other]), - false + "JFIF" -> true; + Other -> + io:format("img -> ~p~n", [Other]), + false end. binary_is_gif_compliant(GifBin) -> - ?line {Bin, _} = split_binary(GifBin, 10), + {Bin, _} = split_binary(GifBin, 10), List = binary_to_list(Bin), case lists:sublist(List, 1,5) of - "GIF87" -> true; - Other -> - io:format("img -> ~p~n", [Other]), - false + "GIF87" -> true; + Other -> + io:format("img -> ~p~n", [Other]), + false end. binary_is_png_compliant(PngBin) -> - ?line {Bin, _} = split_binary(PngBin, 10), + {Bin, _} = split_binary(PngBin, 10), List = binary_to_list(Bin), case lists:sublist(List, 2,3) of - "PNG" -> true; - Other -> - io:format("img -> ~p~n", [Other]), - false + "PNG" -> true; + Other -> + io:format("img -> ~p~n", [Other]), + false end. %%---------------------------------------------------------------------- @@ -320,20 +282,20 @@ binary_is_png_compliant(PngBin) -> get_rgb() -> - R = random(255), - G = random(255), - B = random(255), - {R,G,B}. + R = random(255), + G = random(255), + B = random(255), + {R,G,B}. get_angle() -> - random(359). + random(359). get_point() -> get_point(get(image_size)). get_point({W,H}) -> - X = random(W - 1), - Y = random(H - 1), - {X,Y}. + X = random(W - 1), + Y = random(H - 1), + {X,Y}. get_size(Max) -> W = trunc(random(Max/2) + Max/2 + 1), @@ -344,7 +306,7 @@ get_size(Max) -> get_points(N) -> get_points(N, []). get_points(0, Out) -> - Out; + Out; get_points(N, Out) -> get_points(N - 1, [get_point() | Out]). diff --git a/lib/percept/test/ipc_tree.erl b/lib/percept/test/ipc_tree.erl index ff1c8d49c1..29da20e83f 100644 --- a/lib/percept/test/ipc_tree.erl +++ b/lib/percept/test/ipc_tree.erl @@ -46,4 +46,4 @@ gather([]) -> ok; gather([_|Pids]) -> receive _ -> gather(Pids) end. workload(0) -> ok; -workload(N) -> math:sin(2), workload(N - 1). +workload(N) -> _ = math:sin(2), workload(N - 1). diff --git a/lib/percept/test/percept_SUITE.erl b/lib/percept/test/percept_SUITE.erl index b771f9ec3a..fbc77302ae 100644 --- a/lib/percept/test/percept_SUITE.erl +++ b/lib/percept/test/percept_SUITE.erl @@ -22,51 +22,23 @@ -include_lib("common_test/include/ct.hrl"). %% Test server specific exports --export([all/0, suite/0,groups/0,init_per_group/2,end_per_group/2]). --export([init_per_suite/1, end_per_suite/1]). --export([init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0]). %% Test cases --export([ - app/1, - appup/1, - profile/1, - analyze/1, - analyze_dist/1, - webserver/1 - ]). - -%% Default timetrap timeout (set in init_per_testcase) --define(default_timeout, ?t:minutes(2)). - -init_per_suite(Config) when is_list(Config) -> - Config. - -end_per_suite(Config) when is_list(Config) -> - Config. - -init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?default_timeout), - [{max_size, 300}, {watchdog,Dog} | Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. +-export([app/1, + appup/1, + profile/1, + analyze/1, + analyze_dist/1, + webserver/1]). -suite() -> [{ct_hooks,[ts_install_cth]}]. +suite() -> + [{ct_hooks,[ts_install_cth]}, + {timetrap, {minutes, 2}}]. all() -> - [app, appup, webserver, profile, analyze, analyze_dist]. - -groups() -> - []. - -init_per_group(_GroupName, Config) -> - Config. - -end_per_group(_GroupName, Config) -> - Config. + [app, appup, webserver, profile, + analyze, analyze_dist]. %%---------------------------------------------------------------------- @@ -75,70 +47,56 @@ end_per_group(_GroupName, Config) -> %% Test that the percept app file is ok app(Config) when is_list(Config) -> - ok = ?t:app_test(percept). + ok = test_server:app_test(percept). %% Test that the percept appup file is ok appup(Config) when is_list(Config) -> - ok = ?t:appup_test(percept). + ok = test_server:appup_test(percept). -webserver(suite) -> - []; -webserver(doc) -> - ["Percept webserver test."]; +%% Percept webserver test. webserver(Config) when is_list(Config) -> % Explicit start inets? - ?line {started, _, Port} = percept:start_webserver(), - ?line ok = percept:stop_webserver(Port), - ?line {started, _, _} = percept:start_webserver(), - ?line ok = percept:stop_webserver(), - ?line {started, _, NewPort} = percept:start_webserver(), - ?line ok = percept:stop_webserver(NewPort), - ?line application:stop(inets), + {started, _, Port} = percept:start_webserver(), + ok = percept:stop_webserver(Port), + {started, _, _} = percept:start_webserver(), + ok = percept:stop_webserver(), + {started, _, NewPort} = percept:start_webserver(), + ok = percept:stop_webserver(NewPort), + application:stop(inets), ok. -profile(suite) -> - []; -profile(doc) -> - ["Percept profile test."]; +%% Percept profile test. profile(Config) when is_list(Config) -> - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), File = filename:join([Path,"profile_test.dat"]), - ?line {ok, _} = percept:profile(File, [procs]), + {ok, _} = percept:profile(File, [procs]), ipc_tree:go(7), - ?line ok = percept:stop_profile(), + ok = percept:stop_profile(), ok. -analyze(suite) -> - []; -analyze(doc) -> - ["Percept analyze test."]; +%% Percept analyze test. analyze(Config) when is_list(Config) -> Begin = processes(), - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), File = filename:join([Path,"profile_test.dat"]), - T0 = erlang:now(), - ?line ok = percept:analyze(File), - T1 = erlang:now(), - Secs = timer:now_diff(T1,T0)/1000000, - io:format("percept:analyze/1 took ~.2f s.~n", [Secs]), - ?line {stopped, _} = percept_db:stop(), + T0 = erlang:monotonic_time(milli_seconds), + ok = percept:analyze(File), + T1 = erlang:monotonic_time(milli_seconds), + io:format("percept:analyze/1 took ~w ms.~n", [T1 - T0]), + {stopped, _} = percept_db:stop(), print_remainers(remainers(Begin, processes())), ok. -analyze_dist(suite) -> - []; -analyze_dist(doc) -> - ["Percept analyze distribution test."]; +%% Percept analyze distribution test. analyze_dist(Config) when is_list(Config) -> Begin = processes(), - Path = ?config(data_dir, Config), + Path = proplists:get_value(data_dir, Config), File = filename:join([Path,"ipc-dist.dat"]), - T0 = erlang:now(), - ?line ok = percept:analyze(File), - T1 = erlang:now(), - Secs = timer:now_diff(T1,T0)/1000000, - io:format("percept:analyze/1 took ~.2f s.~n", [Secs]), - ?line {stopped, _} = percept_db:stop(), + T0 = erlang:monotonic_time(milli_seconds), + ok = percept:analyze(File), + T1 = erlang:monotonic_time(milli_seconds), + io:format("percept:analyze/1 took ~w ms.~n", [T1 - T0]), + {stopped, _} = percept_db:stop(), print_remainers(remainers(Begin, processes())), ok. @@ -166,9 +124,3 @@ remainers(Begin, [Pid|End], Out) -> true -> remainers(Begin, End, Out); false -> remainers(Begin, End, [Pid|Out]) end. - - - - - - diff --git a/lib/percept/test/percept_db_SUITE.erl b/lib/percept/test/percept_db_SUITE.erl index d4f732fb1c..b2827e0e42 100644 --- a/lib/percept/test/percept_db_SUITE.erl +++ b/lib/percept/test/percept_db_SUITE.erl @@ -22,54 +22,32 @@ -include_lib("common_test/include/ct.hrl"). %% Test server specific exports --export([all/1]). --export([init_per_suite/1, end_per_suite/1]). --export([init_per_testcase/2, end_per_testcase/2]). +-export([all/0, suite/0]). %% Test cases --export([ - start/1 - ]). +-export([start/1]). %% Default timetrap timeout (set in init_per_testcase) --define(default_timeout, ?t:minutes(2)). -define(restarts, 10). -define(alive_timeout, 500). -init_per_suite(Config) when is_list(Config) -> - Config. +suite() -> + [{timetrap, {minutes, 2}}]. -end_per_suite(Config) when is_list(Config) -> - Config. - -init_per_testcase(_Case, Config) -> - Dog = ?t:timetrap(?default_timeout), - [{max_size, 300}, {watchdog,Dog} | Config]. - -end_per_testcase(_Case, Config) -> - Dog = ?config(watchdog, Config), - ?t:timetrap_cancel(Dog), - ok. - -all(suite) -> - % Test cases +all() -> [start]. %%---------------------------------------------------------------------- %% Tests %%---------------------------------------------------------------------- -start(suite) -> - []; -start(doc) -> - ["Percept_db start and restart test."]; +%% Percept_db start and restart test. start(Config) when is_list(Config) -> ok = restart(?restarts), {stopped, _DB} = percept_db:stop(), ok. -restart(0)-> - ok; +restart(0)-> ok; restart(N)-> {_, DB} = percept_db:start(), timer:sleep(?alive_timeout), diff --git a/lib/public_key/doc/src/public_key.xml b/lib/public_key/doc/src/public_key.xml index 16a7497a22..6923066da7 100644 --- a/lib/public_key/doc/src/public_key.xml +++ b/lib/public_key/doc/src/public_key.xml @@ -141,7 +141,7 @@ <item><p><c>#'DSAPrivateKey'{}</c></p></item> <tag><c>ec_public_key()</c></tag> - <item><p>= <c>{#'ECPoint'{}, #'EcpkParameters'{} | {namedCurve, oid()}}</c></p></item> + <item><p>= <c>{#'ECPoint'{}, #'ECParameters'{} | {namedCurve, oid()}}</c></p></item> <tag><c>ec_private_key() =</c></tag> <item><p><c>#'ECPrivateKey'{}</c></p></item> @@ -418,13 +418,14 @@ <v>Entity = term()</v> <d>Erlang representation of <c>Asn1Type</c>. If <c>Asn1Type</c> is 'SubjectPublicKeyInfo', - <c>Entity</c> must be either an <c>rsa_public_key()</c> or a - <c>dsa_public_key()</c> and this function creates the appropriate + <c>Entity</c> must be either an <c>rsa_public_key()</c>, + <c>dsa_public_key()</c> or an <c>ec_public_key()</c> + and this function creates the appropriate 'SubjectPublicKeyInfo' entry. </d> <v>CipherInfo = cipher_info()</v> <v>Password = string()</v> - </type> + </type> <desc> <p>Creates a PEM entry that can be feed to <c>pem_encode/1</c>.</p> </desc> diff --git a/lib/public_key/src/public_key.erl b/lib/public_key/src/public_key.erl index a79badef24..a5944bd604 100644 --- a/lib/public_key/src/public_key.erl +++ b/lib/public_key/src/public_key.erl @@ -134,7 +134,8 @@ pem_entry_decode({'SubjectPublicKeyInfo', Der, _}) -> {params, DssParams} = der_decode('DSAParams', Params), {der_decode(KeyType, Key0), DssParams}; 'ECPoint' -> - der_decode(KeyType, Key0) + ECCParams = der_decode('EcpkParameters', Params), + {#'ECPoint'{point = Key0}, ECCParams} end; pem_entry_decode({Asn1Type, Der, not_encrypted}) when is_atom(Asn1Type), is_binary(Der) -> @@ -181,6 +182,13 @@ pem_entry_encode('SubjectPublicKeyInfo', Spki = {'SubjectPublicKeyInfo', {'AlgorithmIdentifier', ?'id-dsa', ParamDer}, KeyDer}, pem_entry_encode('SubjectPublicKeyInfo', Spki); +pem_entry_encode('SubjectPublicKeyInfo', + {#'ECPoint'{point = Key}, ECParam}) when is_binary(Key)-> + Params = der_encode('EcpkParameters',ECParam), + Spki = {'SubjectPublicKeyInfo', + {'AlgorithmIdentifier', ?'id-ecPublicKey', Params}, + Key}, + pem_entry_encode('SubjectPublicKeyInfo', Spki); pem_entry_encode(Asn1Type, Entity) when is_atom(Asn1Type) -> Der = der_encode(Asn1Type, Entity), {Asn1Type, Der, not_encrypted}. diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl index 80dc63dce3..be1a4472e9 100644 --- a/lib/public_key/test/public_key_SUITE.erl +++ b/lib/public_key/test/public_key_SUITE.erl @@ -46,7 +46,7 @@ all() -> pkix_iso_rsa_oid, pkix_iso_dsa_oid, pkix_crl]. groups() -> - [{pem_decode_encode, [], [dsa_pem, rsa_pem, encrypted_pem, + [{pem_decode_encode, [], [dsa_pem, rsa_pem, ec_pem, encrypted_pem, dh_pem, cert_pem, pkcs7_pem, pkcs10_pem]}, {ssh_public_key_decode_encode, [], [ssh_rsa_public_key, ssh_dsa_public_key, ssh_ecdsa_public_key, @@ -123,8 +123,8 @@ dsa_pem(Config) when is_list(Config) -> DSAPubKey = public_key:pem_entry_decode(PubEntry0), true = check_entry_type(DSAPubKey, 'DSAPublicKey'), PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', DSAPubKey), - DSAPubPemNoEndNewLines = strip_ending_newlines(DSAPubPem), - DSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])). + DSAPubPemNoEndNewLines = strip_superfluous_newlines(DSAPubPem), + DSAPubPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PubEntry0])). %%-------------------------------------------------------------------- @@ -151,18 +151,44 @@ rsa_pem(Config) when is_list(Config) -> RSAPubKey = public_key:pem_entry_decode(PubEntry0), true = check_entry_type(RSAPubKey, 'RSAPublicKey'), PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', RSAPubKey), - RSAPubPemNoEndNewLines = strip_ending_newlines(RSAPubPem), - RSAPubPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry0])), + RSAPubPemNoEndNewLines = strip_superfluous_newlines(RSAPubPem), + RSAPubPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PubEntry0])), {ok, RSARawPem} = file:read_file(filename:join(Datadir, "rsa_pub_key.pem")), [{'RSAPublicKey', _, _} = PubEntry1] = public_key:pem_decode(RSARawPem), RSAPubKey = public_key:pem_entry_decode(PubEntry1), - RSARawPemNoEndNewLines = strip_ending_newlines(RSARawPem), - RSARawPemNoEndNewLines = strip_ending_newlines(public_key:pem_encode([PubEntry1])). + RSARawPemNoEndNewLines = strip_superfluous_newlines(RSARawPem), + RSARawPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PubEntry1])). %%-------------------------------------------------------------------- +ec_pem() -> + [{doc, "EC key PEM-file decode/encode"}]. +ec_pem(Config) when is_list(Config) -> + Datadir = ?config(data_dir, Config), + {ok, ECPubPem} = file:read_file(filename:join(Datadir, "ec_pubkey.pem")), + [{'SubjectPublicKeyInfo', _, _} = PubEntry0] = + public_key:pem_decode(ECPubPem), + ECPubKey = public_key:pem_entry_decode(PubEntry0), + true = check_entry_type(ECPubKey, 'ECPoint'), + PubEntry0 = public_key:pem_entry_encode('SubjectPublicKeyInfo', ECPubKey), + ECPubPemNoEndNewLines = strip_superfluous_newlines(ECPubPem), + ECPubPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([PubEntry0])), + + {ok, ECPrivPem} = file:read_file(filename:join(Datadir, "ec_key.pem")), + [{'EcpkParameters', _, not_encrypted} = Entry1, + {'ECPrivateKey', _, not_encrypted} = Entry2] = public_key:pem_decode(ECPrivPem), + + ECParams = public_key:pem_entry_decode(Entry1), + true = check_entry_type(ECParams, 'EcpkParameters'), + ECPrivKey = public_key:pem_entry_decode(Entry2), + true = check_entry_type(ECPrivKey, 'ECPrivateKey'), + ECPemNoEndNewLines = strip_superfluous_newlines(ECPrivPem), + ECPemNoEndNewLines = strip_superfluous_newlines(public_key:pem_encode([Entry1, Entry2])). + +%%-------------------------------------------------------------------- + encrypted_pem() -> [{doc, "Encrypted PEM-file decode/encode"}]. encrypted_pem(Config) when is_list(Config) -> @@ -825,6 +851,14 @@ check_entry_type(#'DHParameter'{}, 'DHParameter') -> true; check_entry_type(#'Certificate'{}, 'Certificate') -> true; +check_entry_type({#'ECPoint'{}, _}, 'ECPoint') -> + true; +check_entry_type(#'ECPrivateKey'{}, 'ECPrivateKey') -> + true; +check_entry_type({namedCurve, _}, 'EcpkParameters') -> + true; +check_entry_type(#'ECParameters'{}, 'EcpkParameters') -> + true; check_entry_type(_,_) -> false. @@ -837,8 +871,9 @@ check_encapsulated_header([ _ | Rest]) -> check_encapsulated_header([]) -> false. -strip_ending_newlines(Bin) -> - string:strip(binary_to_list(Bin), right, 10). +strip_superfluous_newlines(Bin) -> + Str = string:strip(binary_to_list(Bin), right, 10), + re:replace(Str,"\n\n","\n", [{return,list}, global]). incorrect_countryname_pkix_cert() -> <<48,130,5,186,48,130,4,162,160,3,2,1,2,2,7,7,250,61,63,6,140,137,48,13,6,9,42, 134,72,134,247,13,1,1,5,5,0,48,129,220,49,11,48,9,6,3,85,4,6,19,2,85,83,49, 16,48,14,6,3,85,4,8,19,7,65,114,105,122,111,110,97,49,19,48,17,6,3,85,4,7,19, 10,83,99,111,116,116,115,100,97,108,101,49,37,48,35,6,3,85,4,10,19,28,83,116, 97,114,102,105,101,108,100,32,84,101,99,104,110,111,108,111,103,105,101,115, 44,32,73,110,99,46,49,57,48,55,6,3,85,4,11,19,48,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 49,49,48,47,6,3,85,4,3,19,40,83,116,97,114,102,105,101,108,100,32,83,101,99, 117,114,101,32,67,101,114,116,105,102,105,99,97,116,105,111,110,32,65,117, 116,104,111,114,105,116,121,49,17,48,15,6,3,85,4,5,19,8,49,48,54,56,56,52,51, 53,48,30,23,13,49,48,49,48,50,51,48,49,51,50,48,53,90,23,13,49,50,49,48,50, 51,48,49,51,50,48,53,90,48,122,49,11,48,9,6,3,85,4,6,12,2,85,83,49,11,48,9,6, 3,85,4,8,12,2,65,90,49,19,48,17,6,3,85,4,7,12,10,83,99,111,116,116,115,100, 97,108,101,49,38,48,36,6,3,85,4,10,12,29,83,112,101,99,105,97,108,32,68,111, 109,97,105,110,32,83,101,114,118,105,99,101,115,44,32,73,110,99,46,49,33,48, 31,6,3,85,4,3,12,24,42,46,108,111,103,105,110,46,115,101,99,117,114,101,115, 101,114,118,101,114,46,110,101,116,48,130,1,34,48,13,6,9,42,134,72,134,247, 13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,185,136,240,80,141,36,124, 245,182,130,73,19,188,74,166,117,72,228,185,209,43,129,244,40,44,193,231,11, 209,12,234,88,43,142,1,162,48,122,17,95,230,105,171,131,12,147,46,204,36,80, 250,171,33,253,35,62,83,22,71,212,186,141,14,198,89,89,121,204,224,122,246, 127,110,188,229,162,67,95,6,74,231,127,99,131,7,240,85,102,203,251,50,58,58, 104,245,103,181,183,134,32,203,121,232,54,32,188,139,136,112,166,126,14,91, 223,153,172,164,14,61,38,163,208,215,186,210,136,213,143,70,147,173,109,217, 250,169,108,31,211,104,238,103,93,182,59,165,43,196,189,218,241,30,148,240, 109,90,69,176,194,52,116,173,151,135,239,10,209,179,129,192,102,75,11,25,168, 223,32,174,84,223,134,70,167,55,172,143,27,130,123,226,226,7,34,142,166,39, 48,246,96,231,150,84,220,106,133,193,55,95,159,227,24,249,64,36,1,142,171,16, 202,55,126,7,156,15,194,22,116,53,113,174,104,239,203,120,45,131,57,87,84, 163,184,27,83,57,199,91,200,34,43,98,61,180,144,76,65,170,177,2,3,1,0,1,163, 130,1,224,48,130,1,220,48,15,6,3,85,29,19,1,1,255,4,5,48,3,1,1,0,48,29,6,3, 85,29,37,4,22,48,20,6,8,43,6,1,5,5,7,3,1,6,8,43,6,1,5,5,7,3,2,48,14,6,3,85, 29,15,1,1,255,4,4,3,2,5,160,48,56,6,3,85,29,31,4,49,48,47,48,45,160,43,160, 41,134,39,104,116,116,112,58,47,47,99,114,108,46,115,116,97,114,102,105,101, 108,100,116,101,99,104,46,99,111,109,47,115,102,115,50,45,48,46,99,114,108, 48,83,6,3,85,29,32,4,76,48,74,48,72,6,11,96,134,72,1,134,253,110,1,7,23,2,48, 57,48,55,6,8,43,6,1,5,5,7,2,1,22,43,104,116,116,112,115,58,47,47,99,101,114, 116,115,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99,111,109, 47,114,101,112,111,115,105,116,111,114,121,47,48,129,141,6,8,43,6,1,5,5,7,1, 1,4,129,128,48,126,48,42,6,8,43,6,1,5,5,7,48,1,134,30,104,116,116,112,58,47, 47,111,99,115,112,46,115,116,97,114,102,105,101,108,100,116,101,99,104,46,99, 111,109,47,48,80,6,8,43,6,1,5,5,7,48,2,134,68,104,116,116,112,58,47,47,99, 101,114,116,105,102,105,99,97,116,101,115,46,115,116,97,114,102,105,101,108, 100,116,101,99,104,46,99,111,109,47,114,101,112,111,115,105,116,111,114,121, 47,115,102,95,105,110,116,101,114,109,101,100,105,97,116,101,46,99,114,116, 48,31,6,3,85,29,35,4,24,48,22,128,20,73,75,82,39,209,27,188,242,161,33,106, 98,123,81,66,122,138,215,213,86,48,59,6,3,85,29,17,4,52,48,50,130,24,42,46, 108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118,101,114,46,110, 101,116,130,22,108,111,103,105,110,46,115,101,99,117,114,101,115,101,114,118, 101,114,46,110,101,116,48,29,6,3,85,29,14,4,22,4,20,138,233,191,208,157,203, 249,85,242,239,20,195,48,10,148,49,144,101,255,116,48,13,6,9,42,134,72,134, 247,13,1,1,5,5,0,3,130,1,1,0,82,31,121,162,49,50,143,26,167,202,143,61,71, 189,201,199,57,81,122,116,90,192,88,24,102,194,174,48,157,74,27,87,210,223, 253,93,3,91,150,109,120,1,110,27,11,200,198,141,222,246,14,200,71,105,41,138, 13,114,122,106,63,17,197,181,234,121,61,89,74,65,41,231,248,219,129,83,176, 219,55,107,55,211,112,98,38,49,69,77,96,221,108,123,152,12,210,159,157,141, 43,226,55,187,129,3,82,49,136,66,81,196,91,234,196,10,82,48,6,80,163,83,71, 127,102,177,93,209,129,26,104,2,84,24,255,248,161,3,244,169,234,92,122,110, 43,4,17,113,185,235,108,219,210,236,132,216,177,227,17,169,58,162,159,182, 162,93,160,229,200,9,163,229,110,121,240,168,232,14,91,214,188,196,109,210, 164,222,0,109,139,132,113,91,16,118,173,178,176,80,132,34,41,199,51,206,250, 224,132,60,115,192,94,107,163,219,212,226,225,65,169,148,108,213,46,174,173, 103,110,189,229,166,149,254,31,51,44,144,108,187,182,11,251,201,206,86,138, 208,59,51,86,132,235,81,225,88,34,190,8,184>>. diff --git a/lib/public_key/test/public_key_SUITE_data/ec_key.pem b/lib/public_key/test/public_key_SUITE_data/ec_key.pem new file mode 100644 index 0000000000..1bb375d22f --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ec_key.pem @@ -0,0 +1,8 @@ +-----BEGIN EC PARAMETERS----- +BgUrgQQACg== +-----END EC PARAMETERS----- +-----BEGIN EC PRIVATE KEY----- +MHQCAQEEIAd+PV10pm2uQWyU+VLgijqMqDx7MoMup/lsz9SfvHmEoAcGBSuBBAAK +oUQDQgAE0yXQ7YqlfR7O6vmP8mpNc97iabpBUBmJq5Sdos7cX7+289dHiecjPxja +hvJCtMO0iM43nbCJH40Su21+pj+4eA== +-----END EC PRIVATE KEY----- diff --git a/lib/public_key/test/public_key_SUITE_data/ec_pubkey.pem b/lib/public_key/test/public_key_SUITE_data/ec_pubkey.pem new file mode 100644 index 0000000000..186c32bc01 --- /dev/null +++ b/lib/public_key/test/public_key_SUITE_data/ec_pubkey.pem @@ -0,0 +1,4 @@ +-----BEGIN PUBLIC KEY----- +MFYwEAYHKoZIzj0CAQYFK4EEAAoDQgAE0yXQ7YqlfR7O6vmP8mpNc97iabpBUBmJ +q5Sdos7cX7+289dHiecjPxjahvJCtMO0iM43nbCJH40Su21+pj+4eA== +-----END PUBLIC KEY----- diff --git a/lib/reltool/test/reltool_server_SUITE.erl b/lib/reltool/test/reltool_server_SUITE.erl index fb5278b859..e9bd46fb27 100644 --- a/lib/reltool/test/reltool_server_SUITE.erl +++ b/lib/reltool/test/reltool_server_SUITE.erl @@ -2530,13 +2530,54 @@ latest(App) -> rm_missing_app(Apps) -> lists:keydelete(?MISSING_APP_NAME,#app.name,Apps). +%% We will compare the script generated by systools with +%% the script generated by Reltool. +%% +%% The systools script may include additional modules in +%% the first primLoad command (as a pure optimization). +%% Therefore, we cannot compare the primLoad commands +%% directly. Instead we will collect all modules from +%% all primLoad commands in each script. The same +%% modules must be loaded by both scripts. In addition, +%% the error_handler module must be included in the +%% first primLoad in each script. + diff_script(Script, Script) -> equal; diff_script({script, Rel, Commands1}, {script, Rel, Commands2}) -> - diff_cmds(Commands1, Commands2); + case diff_cmds(Commands1, Commands2) of + equal -> + Loaded = diff_get_prim_load(Commands1), + case diff_get_prim_load(Commands2) of + Loaded -> + equal; + Other -> + io:format("Only loaded by systools: ~p", + [Loaded--Other]), + io:format("Only loaded by reltool: ~p", + [Other--Loaded]), + ct:fail(different_prim_loads) + end; + Error -> + Error + end; diff_script({script, Rel1, _}, {script, Rel2, _}) -> {error, {Rel1, Rel2}}. +diff_cmds([{primLoad, Ms1}=Cmd1 | Commands1], + [{primLoad, Ms2}=Cmd2 | Commands2]) -> + case lists:member(error_handler, Ms1) xor + lists:member(error_handler, Ms2) of + false -> + %% error_handler either present in both or + %% absent in both. + diff_cmds(Commands1, Commands2); + true -> + %% error_handler only present in one primLoad. + %% Not OK. + {diff, missing_error_handler, + {expected, Cmd1}, {actual, Cmd2}} + end; diff_cmds([Cmd | Commands1], [Cmd | Commands2]) -> diff_cmds(Commands1, Commands2); diff_cmds([Cmd1 | _Commands1], [Cmd2 | _Commands2]) -> @@ -2544,6 +2585,10 @@ diff_cmds([Cmd1 | _Commands1], [Cmd2 | _Commands2]) -> diff_cmds([], []) -> equal. +diff_get_prim_load(Cmds) -> + L = [Ms || {primLoad, Ms} <- Cmds], + lists:sort(lists:flatten(L)). + os_cmd(Cmd) when is_list(Cmd) -> %% Call the plain os:cmd with an echo command appended to print command status %% io:format("os:cmd(~p).\n", [Cmd]), diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index 9ac175a91c..c23607f8a1 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.erl @@ -1453,16 +1453,37 @@ behave([H|T]) -> behave([]) -> []. -%%______________________________________________________________________ -%% mandatory modules; this modules must be loaded before processes -%% can be started. These are a collection of modules from the kernel -%% and stdlib applications. -%% Nowadays, error_handler dynamically loads almost every module. -%% The error_handler self must still be there though. - mandatory_modules() -> - %% Sorted - [error_handler]. + [error_handler, %Truly mandatory. + + %% Modules that are almost always needed. Listing them here + %% helps the init module to load them faster. Modules not + %% listed here will be loaded by the error_handler module. + %% + %% Keep this list sorted. + application, + application_controller, + application_master, + code, + code_server, + erl_eval, + erl_lint, + erl_parse, + error_logger, + ets, + file, + filename, + file_server, + file_io_server, + gen, + gen_event, + gen_server, + heart, + kernel, + lists, + proc_lib, + supervisor + ]. %%______________________________________________________________________ %% This is the modules that are preloaded into the Erlang system. diff --git a/lib/sasl/test/test_lib.hrl b/lib/sasl/test/test_lib.hrl index b16c4ac34c..2d897e9903 100644 --- a/lib/sasl/test/test_lib.hrl +++ b/lib/sasl/test/test_lib.hrl @@ -1,3 +1,3 @@ -define(ertsvsn,"4.4"). --define(kernelvsn,"3.0"). --define(stdlibvsn,"2.0"). +-define(kernelvsn,"4.0"). +-define(stdlibvsn,"2.5"). diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index ecf165ed23..69d5a47f83 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -54,6 +54,7 @@ MODULES= \ ssh_connection_sup \ ssh_connection \ ssh_connection_handler \ + ssh_dbg \ ssh_shell \ ssh_system_sup \ ssh_subsystem_sup \ diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src index 4a76fd9cd3..c67350bf72 100644 --- a/lib/ssh/src/ssh.app.src +++ b/lib/ssh/src/ssh.app.src @@ -18,6 +18,7 @@ ssh_connection_handler, ssh_connection_sup, ssh_daemon_channel, + ssh_dbg, ssh_shell, sshc_sup, sshd_sup, diff --git a/lib/ssh/src/ssh_dbg.erl b/lib/ssh/src/ssh_dbg.erl new file mode 100644 index 0000000000..fbf85cfcfc --- /dev/null +++ b/lib/ssh/src/ssh_dbg.erl @@ -0,0 +1,140 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. +%% +%% %CopyrightEnd% +%% + +%% + +-module(ssh_dbg). + +-export([messages/0, + messages/1 + ]). + +-include("ssh.hrl"). +-include("ssh_transport.hrl"). +-include("ssh_connect.hrl"). +-include("ssh_auth.hrl"). + +-record(data, { + writer, + acc = []}). +%%%================================================================ +messages() -> messages(fun(String,_D) -> io:format(String) end). +%% messages() -> messages(fun(String,Acc) -> [String|Acc] end) + +messages(Write) when is_function(Write,2) -> + catch dbg:start(), + + Handler = fun msg_formater/2, + InitialData = #data{writer = Write}, + {ok,_} = dbg:tracer(process, {Handler, InitialData}), + + dbg:p(new,c), + dbg:tp(ssh_message,encode,1, x), + dbg:tp(ssh_message,decode,1, x), + dbg:tpl(ssh_transport,select_algorithm,3, x). + +%%%================================================================ +msg_formater({trace,Pid,call,{ssh_message,encode,[Msg]}}, D) -> + fmt("~nSEND ~p ~s~n", [Pid,wr_record(shrink_bin(Msg))], D); + +msg_formater({trace,Pid,return_from,{ssh_message,decode,1},Msg}, D) -> + fmt("~nRECV ~p ~s~n", [Pid,wr_record(shrink_bin(Msg))], D); + +msg_formater({trace,Pid,return_from,{ssh_transport,select_algorithm,3},{ok,Alg}}, D) -> + fmt("~nALGORITHMS ~p~n~s~n", [Pid, wr_record(Alg)], D); + +msg_formater(_, D) -> + D. + + +fmt(Fmt, Args, D=#data{writer=Write,acc=Acc}) -> + D#data{acc = Write(io_lib:format(Fmt, Args), Acc)}. + +%%%---------------------------------------------------------------- +shrink_bin(B) when is_binary(B), size(B)>100 -> {'*** SHRINKED BIN',size(B),element(1,split_binary(B,20)),'***'}; +shrink_bin(L) when is_list(L) -> lists:map(fun shrink_bin/1, L); +shrink_bin(T) when is_tuple(T) -> list_to_tuple(shrink_bin(tuple_to_list(T))); +shrink_bin(X) -> X. + +%%%---------------------------------------------------------------- +-define(wr_record(N,BlackList), wr_record(R=#N{}) -> wr_record(R, record_info(fields,N), BlackList)). + +-define(wr_record(N), ?wr_record(N, [])). + + +?wr_record(alg); + +?wr_record(ssh_msg_disconnect); +?wr_record(ssh_msg_ignore); +?wr_record(ssh_msg_unimplemented); +?wr_record(ssh_msg_debug); +?wr_record(ssh_msg_service_request); +?wr_record(ssh_msg_service_accept); +?wr_record(ssh_msg_kexinit); +?wr_record(ssh_msg_kexdh_init); +?wr_record(ssh_msg_kexdh_reply); +?wr_record(ssh_msg_newkeys); +?wr_record(ssh_msg_kex_dh_gex_request); +?wr_record(ssh_msg_kex_dh_gex_request_old); +?wr_record(ssh_msg_kex_dh_gex_group); +?wr_record(ssh_msg_kex_dh_gex_init); +?wr_record(ssh_msg_kex_dh_gex_reply); +?wr_record(ssh_msg_kex_ecdh_init); +?wr_record(ssh_msg_kex_ecdh_reply); + +?wr_record(ssh_msg_userauth_request); +?wr_record(ssh_msg_userauth_failure); +?wr_record(ssh_msg_userauth_success); +?wr_record(ssh_msg_userauth_banner); +?wr_record(ssh_msg_userauth_passwd_changereq); +?wr_record(ssh_msg_userauth_pk_ok); +?wr_record(ssh_msg_userauth_info_request); +?wr_record(ssh_msg_userauth_info_response); + +?wr_record(ssh_msg_global_request); +?wr_record(ssh_msg_request_success); +?wr_record(ssh_msg_request_failure); +?wr_record(ssh_msg_channel_open); +?wr_record(ssh_msg_channel_open_confirmation); +?wr_record(ssh_msg_channel_open_failure); +?wr_record(ssh_msg_channel_window_adjust); +?wr_record(ssh_msg_channel_data); +?wr_record(ssh_msg_channel_extended_data); +?wr_record(ssh_msg_channel_eof); +?wr_record(ssh_msg_channel_close); +?wr_record(ssh_msg_channel_request); +?wr_record(ssh_msg_channel_success); +?wr_record(ssh_msg_channel_failure); + +wr_record(R) -> io_lib:format('~p~n',[R]). + + +wr_record(T, Fs, BL) when is_tuple(T) -> + wr_record(tuple_to_list(T), Fs, BL); +wr_record([Name|Values], Fields, BlackL) -> + W = case Fields of + [] -> 0; + _ -> lists:max([length(atom_to_list(F)) || F<-Fields]) + end, + [io_lib:format("~p:~n",[string:to_upper(atom_to_list(Name))]) + | [io_lib:format(" ~*p: ~p~n",[W,Tag,Value]) || {Tag,Value} <- lists:zip(Fields,Values), + not lists:member(Tag,BlackL) + ] + ]. diff --git a/lib/ssh/src/ssh_info.erl b/lib/ssh/src/ssh_info.erl index 4e6e25bc70..67130d5eac 100644 --- a/lib/ssh/src/ssh_info.erl +++ b/lib/ssh/src/ssh_info.erl @@ -25,132 +25,174 @@ -module(ssh_info). --compile(export_all). +-export([print/0, + print/1, + string/0, + collect_pids/0 + ]). + +-include("ssh_connect.hrl"). print() -> - print(user). + io:format("~s", [string()]). +print(File) when is_list(File) -> + {ok,D} = file:open(File, write), + print(D), + file:close(D); print(D) -> + io:format(D, "~s", [string()]). + +string() -> try supervisor:which_children(ssh_sup) of _ -> - io:nl(D), - print_general(D), - io:nl(D), - underline(D, "Client part", $=), - print_clients(D), - io:nl(D), - underline(D, "Server part", $=), - print_servers(D), - io:nl(D), - %% case os:type() of - %% {unix,_} -> - %% io:nl(), - %% underline("Linux part", $=), - %% underline("Listening"), - %% catch io:format(os:cmd("netstat -tpln")), - %% io:nl(), - %% underline("Other"), - %% catch io:format(os:cmd("netstat -tpn")); - %% _ -> ok - %% end, - underline(D, "Supervisors", $=), - walk_sups(D, ssh_sup), - io:nl(D) + [io_lib:nl(), + print_general(), + io_lib:nl(), + underline("Client part", $=), + print_clients(), + io_lib:nl(), + underline("Server part", $=), + print_servers(), + io_lib:nl(), + underline("Supervisors", $=), + walk_sups(ssh_sup), + io_lib:nl()] catch _:_ -> - io:format(D,"Ssh not found~n",[]) + io_lib:format("Ssh not found~n",[]) end. + %%%================================================================ -print_general(D) -> +-define(INDENT, " "). + +print_general() -> {_Name, Slogan, Ver} = lists:keyfind(ssh,1,application:which_applications()), - underline(D, io_lib:format("~s ~s", [Slogan, Ver]), $=), - io:format(D, 'This printout is generated ~s. ~n',[datetime()]). + [underline(io_lib:format("~s ~s", [Slogan, Ver]), $=), + io_lib:format('This printout is generated ~s. ~n',[datetime()]) + ]. -%%%================================================================ -print_clients(D) -> - PrintClient = fun(X) -> print_client(D,X) end, +print_clients() -> try - lists:foreach(PrintClient, supervisor:which_children(sshc_sup)) + lists:map(fun print_client/1, + supervisor:which_children(sshc_sup)) catch C:E -> - io:format(D, '***FAILED: ~p:~p~n',[C,E]) + io_lib:format('***print_clients FAILED: ~p:~p~n',[C,E]) end. -print_client(D, {undefined,Pid,supervisor,[ssh_connection_handler]}) -> +print_client({undefined,Pid,supervisor,[ssh_connection_handler]}) -> {{Local,Remote},_Str} = ssh_connection_handler:get_print_info(Pid), - io:format(D, " Local=~s Remote=~s ConnectionRef=~p~n",[fmt_host_port(Local),fmt_host_port(Remote),Pid]); -print_client(D, Other) -> - io:format(D, " [[Other 1: ~p]]~n",[Other]). + [io_lib:format(?INDENT"Local: ~s Remote: ~s ConnectionRef = ~p~n", + [fmt_host_port(Local), fmt_host_port(Remote), Pid]), + case channels(Pid) of + {ok,Channels=[_|_]} -> + [print_ch(ChPid) || #channel{user=ChPid} <- Channels]; + _ -> + io_lib:format(?INDENT?INDENT?INDENT"No channels~n",[]) + end]; + +print_client(Other) -> + io_lib:format(" [[Other 1: ~p]]~n",[Other]). %%%================================================================ -print_servers(D) -> - PrintServer = fun(X) -> print_server(D,X) end, +print_servers() -> try - lists:foreach(PrintServer, supervisor:which_children(sshd_sup)) + lists:map(fun print_server/1, + supervisor:which_children(sshd_sup)) catch C:E -> - io:format(D, '***FAILED: ~p:~p~n',[C,E]) + io_lib:format('***print_servers FAILED: ~p:~p~n',[C,E]) end. -print_server(D, {{server,ssh_system_sup,LocalHost,LocalPort},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) -> - io:format(D, 'Local=~s (~p children)~n',[fmt_host_port({LocalHost,LocalPort}), - ssh_acceptor:number_of_connections(Pid)]), - PrintSystemSup = fun(X) -> print_system_sup(D,X) end, - lists:foreach(PrintSystemSup, supervisor:which_children(Pid)); -print_server(D, Other) -> - io:format(D, " [[Other 2: ~p]]~n",[Other]). - -print_system_sup(D, {Ref,Pid,supervisor,[ssh_subsystem_sup]}) when is_reference(Ref), + +print_server({{server,ssh_system_sup,LocalHost,LocalPort,Profile},Pid,supervisor,[ssh_system_sup]}) when is_pid(Pid) -> + Children = supervisor:which_children(Pid), + [io_lib:format(?INDENT"Listen: ~s (~p children) Profile ~p",[fmt_host_port({LocalHost,LocalPort}), + ssh_acceptor:number_of_connections(Pid), + Profile]), + case [AccPid + || {{ssh_acceptor_sup,_LocalHost,_LocalPort,_Profile}, AccPid, supervisor, [ssh_acceptor_sup]} + <- Children] of + AcceptorPids = [_|_] -> + [io_lib:format(" [Acceptor Pid", []), + [io_lib:format(" ~p",[AccPid]) || AccPid <- AcceptorPids], + io_lib:format("]~n", []) + ]; + [] -> + io_lib:nl() + end, + lists:map(fun print_system_sup/1, + supervisor:which_children(Pid)) + ]. + + +print_system_sup({Ref,Pid,supervisor,[ssh_subsystem_sup]}) when is_reference(Ref), is_pid(Pid) -> - PrintChannels = fun(X) -> print_channels(D,X) end, - lists:foreach(PrintChannels, supervisor:which_children(Pid)); -print_system_sup(D, {{ssh_acceptor_sup,LocalHost,LocalPort}, Pid,supervisor, [ssh_acceptor_sup]}) when is_pid(Pid) -> - io:format(D, " [Acceptor for ~s]~n",[fmt_host_port({LocalHost,LocalPort})]); -print_system_sup(D, Other) -> - io:format(D, " [[Other 3: ~p]]~n",[Other]). - -print_channels(D, {{server,ssh_channel_sup,_,_},Pid,supervisor,[ssh_channel_sup]}) when is_pid(Pid) -> - PrintChannel = fun(X) -> print_channel(D,X) end, - lists:foreach(PrintChannel, supervisor:which_children(Pid)); -print_channels(D, Other) -> - io:format(D, " [[Other 4: ~p]]~n",[Other]). - - -print_channel(D, {Ref,Pid,worker,[ssh_channel]}) when is_reference(Ref), - is_pid(Pid) -> - {{ConnManager,ChannelID}, Str} = ssh_channel:get_print_info(Pid), - {{Local,Remote},StrM} = ssh_connection_handler:get_print_info(ConnManager), - io:format(D, ' ch ~p: ~s ~s',[ChannelID, StrM, Str]), - io:format(D, " Local=~s Remote=~s~n",[fmt_host_port(Local),fmt_host_port(Remote)]); -print_channel(D, Other) -> - io:format(D, " [[Other 5: ~p]]~n",[Other]). - + lists:map(fun print_channels/1, + supervisor:which_children(Pid)); + +print_system_sup({{ssh_acceptor_sup,_LocalHost,_LocalPort,_Profile}, Pid, supervisor, [ssh_acceptor_sup]}) when is_pid(Pid) -> + []. + + + +print_channels({{server,ssh_channel_sup,_,_},Pid,supervisor,[ssh_channel_sup]}) when is_pid(Pid) -> + Children = supervisor:which_children(Pid), + ChannelPids = [P || {R,P,worker,[ssh_channel]} <- Children, + is_pid(P), + is_reference(R)], + case ChannelPids of + [] -> io_lib:format(?INDENT?INDENT"No channels~n",[]); + [Ch1Pid|_] -> + {{ConnManager,_}, _Str} = ssh_channel:get_print_info(Ch1Pid), + {{_,Remote},_} = ssh_connection_handler:get_print_info(ConnManager), + [io_lib:format(?INDENT?INDENT"Remote: ~s ConnectionRef = ~p~n",[fmt_host_port(Remote),ConnManager]), + lists:map(fun print_ch/1, ChannelPids) + ] + end; +print_channels({{server,ssh_connection_sup,_,_},Pid,supervisor,[ssh_connection_sup]}) when is_pid(Pid) -> + []. % The supervisor of the connections socket owning process + +print_ch(Pid) -> + try + {{ConnManager,ChannelID}, Str} = ssh_channel:get_print_info(Pid), + {_LocalRemote,StrM} = ssh_connection_handler:get_print_info(ConnManager), + io_lib:format(?INDENT?INDENT?INDENT"ch ~p ~p: ~s ~s~n",[ChannelID, Pid, StrM, Str]) + catch + C:E -> + io_lib:format('****print_ch FAILED for ChanPid ~p: ~p:~p~n',[Pid, C, E]) + end. + + %%%================================================================ -define(inc(N), (N+4)). -walk_sups(D, StartPid) -> - io:format(D, "Start at ~p, ~s.~n",[StartPid,dead_or_alive(StartPid)]), - walk_sups(D, children(StartPid), _Indent=?inc(0)). +walk_sups(StartPid) -> + io_lib:format("Start at ~p, ~s.~n",[StartPid,dead_or_alive(StartPid)]), + walk_sups(children(StartPid), _Indent=?inc(0)). -walk_sups(D, [H={_,Pid,_,_}|T], Indent) -> - indent(D, Indent), io:format(D, '~200p ~p is ~s~n',[H,Pid,dead_or_alive(Pid)]), - case H of - {_,_,supervisor,[ssh_connection_handler]} -> ok; - {_,Pid,supervisor,_} -> walk_sups(D, children(Pid), ?inc(Indent)); - _ -> ok - end, - walk_sups(D, T, Indent); -walk_sups(_D, [], _) -> - ok. +walk_sups([H={_,Pid,_,_}|T], Indent) -> + [indent(Indent), + io_lib:format('~200p ~p is ~s~n',[H,Pid,dead_or_alive(Pid)]), + case H of + {_,_,supervisor,[ssh_connection_handler]} -> ""; + {_,Pid,supervisor,_} -> walk_sups(children(Pid), ?inc(Indent)); + _ -> "" + end, + walk_sups(T, Indent) + ]; +walk_sups([], _) -> + "". dead_or_alive(Name) when is_atom(Name) -> case whereis(Name) of - undefined -> + undefined -> "**UNDEFINED**"; - Pid -> + Pid -> dead_or_alive(Pid) end; dead_or_alive(Pid) when is_pid(Pid) -> @@ -159,7 +201,8 @@ dead_or_alive(Pid) when is_pid(Pid) -> _ -> "alive" end. -indent(D, I) -> io:format(D,'~*c',[I,$ ]). +indent(I) -> io_lib:format('~*c',[I,$ ]). + children(Pid) -> Parent = self(), @@ -170,23 +213,39 @@ children(Pid) -> {Helper,L} when is_list(L) -> L after - 2000 -> + 2000 -> catch exit(Helper, kill), [] end. -%%%================================================================ -underline(D, Str) -> - underline(D, Str, $-). +is_connection_handler(Pid) -> + try + {ssh_connection_handler,init,_} = + proplists:get_value( + '$initial_call', + proplists:get_value( + dictionary, + process_info(Pid, [dictionary]))) + of + _ -> true -underline(D, Str, LineChar) -> - Len = lists:flatlength(Str), - io:format(D, '~s~n',[Str]), - line(D,Len,LineChar). + catch + _:_ -> + false + end. + +channels(Pid) -> + case is_connection_handler(Pid) of + true -> + ssh_connection_handler:info(Pid,all); + false -> + false + end. + +%%%================================================================ +underline(Str, LineChar) -> + io_lib:format('~s~n~*c~n',[Str, lists:flatlength(Str), LineChar]). -line(D, Len, Char) -> - io:format(D, '~*c~n', [Len,Char]). - datetime() -> {{YYYY,MM,DD}, {H,M,S}} = calendar:now_to_universal_time(erlang:timestamp()), @@ -196,8 +255,82 @@ datetime() -> fmt_host_port({{A,B,C,D},Port}) -> io_lib:format('~p.~p.~p.~p:~p',[A,B,C,D,Port]); fmt_host_port({Host,Port}) -> io_lib:format('~s:~p',[Host,Port]). +%%%################################################################ +collect_pids() -> collect_pids(ssh_sup). + +collect_pids(P) -> + Collector = pcollect_pids(P, spawn(fun init_collector/0)), + Collector ! {get_values,self()}, + receive + {values,Values} -> + Values + end. + +%%%---------------- +pcollect_pids(undefined, Collector) -> + Collector; + +pcollect_pids(A, Collector) when is_atom(A) -> + pcollect_pids(whereis(A), Collector); + +pcollect_pids(Pid, Collector) when is_pid(Pid) -> + Collector ! {expect,Pid}, + spawn(fun() -> + lists:foreach( + fun(P2) -> + pcollect_pids(P2,Collector) + end, children(Pid)), + Collector ! {value,Pid,Pid} + end), + Collector; +pcollect_pids({Ref,Pid,supervisor,_}, Collector) when is_pid(Pid), + is_reference(Ref) -> + pcollect_pids(Pid, Collector); -nyi(D) -> - io:format(D,'Not yet implemented~n',[]), - nyi. +pcollect_pids({sshc_sup,Pid,supervisor,_}, Collector) when is_pid(Pid) -> + pcollect_pids(Pid, Collector); + +pcollect_pids({sshd_sup,Pid,supervisor,_}, Collector) when is_pid(Pid) -> + pcollect_pids(Pid, Collector); + +pcollect_pids({{ssh_acceptor_sup,_,_,_},Pid,supervisor,_}, Collector) when is_pid(Pid) -> + pcollect_pids(Pid, Collector); + +pcollect_pids({{server,_,_,_},Pid,supervisor,_}, Collector) when is_pid(Pid) -> + pcollect_pids(Pid, Collector); + +pcollect_pids({{server,_,_,_,_},Pid,supervisor,_}, Collector) when is_pid(Pid) -> + pcollect_pids(Pid, Collector); + +pcollect_pids({undefined,Pid,supervisor,[ssh_connection_handler]}, Collector) -> + Collector ! {value,Pid,Pid}, + case channels(Pid) of + {ok,L} -> + [Collector!{value,P,P} || #channel{user=P} <- L]; + _ -> + ok + end, + Collector; + +pcollect_pids({_,Pid,_,_}, Collector) when is_pid(Pid) -> + Collector ! {value,Pid,Pid}, + Collector; + +pcollect_pids(_, Collector) -> + Collector. + +%%%---------------- +init_collector() -> + loop_collector([],[]). + +loop_collector(Expects, Values) -> + receive + {expect, Ref} -> + loop_collector([Ref|Expects], Values); + {value, Ref, Val} -> + loop_collector(Expects--[Ref], [Val|Values]); + {get_values, From} when Expects==[] -> +%% Values=/=[] -> + From ! {values,Values} + end. diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl index 74028c20a5..bdc980e65c 100644 --- a/lib/ssh/test/ssh_algorithms_SUITE.erl +++ b/lib/ssh/test/ssh_algorithms_SUITE.erl @@ -28,7 +28,7 @@ %% Note: This directive should only be used in test suites. -compile(export_all). --define(TIMEOUT, 50000). +-define(TIMEOUT, 10000). %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- @@ -36,7 +36,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap,{minutes,10}}]. + {timetrap,{seconds,40}}]. all() -> %% [{group,kex},{group,cipher}... etc @@ -191,6 +191,9 @@ simple_exec_groups_no_match_too_large(Config) -> %%-------------------------------------------------------------------- %% Testing all default groups + +simple_exec_groups() -> [{timetrap,{seconds,180}}]. + simple_exec_groups(Config) -> Sizes = interpolate( public_key:dh_gex_group_sizes() ), lists:foreach( @@ -217,6 +220,7 @@ interpolate(Is) -> %%-------------------------------------------------------------------- %% Use the ssh client of the OS to connect + sshc_simple_exec(Config) -> PrivDir = ?config(priv_dir, Config), KnownHosts = filename:join(PrivDir, "known_hosts"), @@ -227,12 +231,21 @@ sshc_simple_exec(Config) -> ct:log("~p",[Cmd]), SshPort = open_port({spawn, Cmd}, [binary]), Expect = <<"2\n">>, + rcv_expected(SshPort, Expect). + + +rcv_expected(SshPort, Expect) -> receive {SshPort, {data,Expect}} -> ct:log("Got expected ~p from ~p",[Expect,SshPort]), catch port_close(SshPort), - ok + ok; + Other -> + ct:log("Got UNEXPECTED ~p",[Other]), + rcv_expected(SshPort, Expect) + after ?TIMEOUT -> + catch port_close(SshPort), ct:fail("Did not receive answer") end. diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index c70ae083ed..0fa44ded4f 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -79,7 +79,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap,{minutes,10}}]. + {timetrap,{seconds,40}}]. all() -> [app_test, diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl index e08d6047a1..a5f424f863 100644 --- a/lib/ssh/test/ssh_connection_SUITE.erl +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -37,7 +37,7 @@ %% [{ct_hooks,[ts_install_cth]}]. suite() -> - [{timetrap,{minutes,2}}]. + [{timetrap,{seconds,40}}]. all() -> [ @@ -314,11 +314,7 @@ ptty_alloc_pixel(Config) when is_list(Config) -> ssh:close(ConnectionRef). %%-------------------------------------------------------------------- - -interrupted_send() -> - [{doc, "Use a subsystem that echos n char and then sends eof to cause a channel exit partway through a large send."}]. - -interrupted_send(Config) when is_list(Config) -> +interrupted_send(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), diff --git a/lib/ssh/test/ssh_echo_server.erl b/lib/ssh/test/ssh_echo_server.erl index 796a182502..ed9bbe1b67 100644 --- a/lib/ssh/test/ssh_echo_server.erl +++ b/lib/ssh/test/ssh_echo_server.erl @@ -31,6 +31,7 @@ -export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]). init([N]) -> + ct:pal("Echo server: ~p",[self()]), {ok, #state{n = N}}. handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) -> diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl index bb4fbc0a17..1d14a16065 100644 --- a/lib/ssh/test/ssh_options_SUITE.erl +++ b/lib/ssh/test/ssh_options_SUITE.erl @@ -51,8 +51,10 @@ ssh_connect_arg4_timeout/1, ssh_connect_negtimeout_parallel/1, ssh_connect_negtimeout_sequential/1, - ssh_connect_nonegtimeout_connected_parallel/1, - ssh_connect_nonegtimeout_connected_sequential/1, + ssh_connect_nonegtimeout_connected_parallel/0, + ssh_connect_nonegtimeout_connected_parallel/1, + ssh_connect_nonegtimeout_connected_sequential/0, + ssh_connect_nonegtimeout_connected_sequential/1, ssh_connect_timeout/1, connect/4, ssh_daemon_minimal_remote_max_packet_size_option/1, ssh_msg_debug_fun_option_client/1, @@ -80,7 +82,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap,{minutes,6}}]. + {timetrap,{seconds,40}}]. all() -> [connectfun_disconnectfun_server, @@ -980,10 +982,16 @@ ssh_connect_negtimeout(Config, Parallel) -> %%-------------------------------------------------------------------- %%% Test that ssh connection does not timeout if the connection is established (parallel) + +ssh_connect_nonegtimeout_connected_parallel() -> [{timetrap,{seconds,90}}]. + ssh_connect_nonegtimeout_connected_parallel(Config) -> ssh_connect_nonegtimeout_connected(Config, true). %%% Test that ssh connection does not timeout if the connection is established (non-parallel) + +ssh_connect_nonegtimeout_connected_sequential() -> [{timetrap,{seconds,90}}]. + ssh_connect_nonegtimeout_connected_sequential(Config) -> ssh_connect_nonegtimeout_connected(Config, false). diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl index 4e13aeaf24..57404f40db 100644 --- a/lib/ssh/test/ssh_protocol_SUITE.erl +++ b/lib/ssh/test/ssh_protocol_SUITE.erl @@ -43,7 +43,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap,{minutes,2}}]. + {timetrap,{seconds,40}}]. all() -> [{group,tool_tests}, diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl index 94b78fdde2..90132becbd 100644 --- a/lib/ssh/test/ssh_renegotiate_SUITE.erl +++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl @@ -31,7 +31,7 @@ %%-------------------------------------------------------------------- suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap,{minutes,12}}]. + {timetrap,{seconds,40}}]. all() -> [{group,default_algs}, @@ -83,7 +83,8 @@ end_per_testcase(_TestCase, _Config) -> %%-------------------------------------------------------------------- %%% Idle timeout test - +rekey() -> [{timetrap,{seconds,90}}]. + rekey(Config) -> {Pid, Host, Port} = ssh_test_lib:std_daemon(Config, @@ -105,6 +106,8 @@ rekey(Config) -> %%% Test rekeying by data volume +rekey_limit() -> [{timetrap,{seconds,400}}]. + rekey_limit(Config) -> UserDir = ?config(priv_dir, Config), DataFile = filename:join(UserDir, "rekey.data"), diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl index 931bf9c5ff..c4bb02841b 100644 --- a/lib/ssh/test/ssh_sftp_SUITE.erl +++ b/lib/ssh/test/ssh_sftp_SUITE.erl @@ -36,7 +36,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap,{minutes,2}}]. + {timetrap,{seconds,40}}]. all() -> diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl index 1670395880..fb1a9687af 100644 --- a/lib/ssh/test/ssh_sftpd_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_SUITE.erl @@ -45,7 +45,7 @@ %%-------------------------------------------------------------------- suite() -> - [{timetrap,{minutes,3}}]. + [{timetrap,{seconds,40}}]. all() -> [open_close_file, diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl index 992505d955..09bef87148 100644 --- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl +++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl @@ -37,7 +37,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap,{minutes,2}}]. + {timetrap,{seconds,40}}]. all() -> diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl index 9fc41168ee..f800ea806d 100644 --- a/lib/ssh/test/ssh_sup_SUITE.erl +++ b/lib/ssh/test/ssh_sup_SUITE.erl @@ -36,7 +36,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}, - {timetrap,{minutes,1}}]. + {timetrap,{seconds,40}}]. all() -> [default_tree, sshc_subtree, sshd_subtree, sshd_subtree_profile]. diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl index 8e79f80a58..5b65edc32f 100644 --- a/lib/ssh/test/ssh_to_openssh_SUITE.erl +++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl @@ -34,7 +34,7 @@ %%-------------------------------------------------------------------- suite() -> - [{timetrap,{minutes,1}}]. + [{timetrap,{seconds,40}}]. all() -> case os:find_executable("ssh") of diff --git a/lib/ssh/test/ssh_upgrade_SUITE.erl b/lib/ssh/test/ssh_upgrade_SUITE.erl index 4008d94f60..5c7ec17dac 100644 --- a/lib/ssh/test/ssh_upgrade_SUITE.erl +++ b/lib/ssh/test/ssh_upgrade_SUITE.erl @@ -39,7 +39,7 @@ %%% CommonTest callbacks %%% suite() -> - [{timetrap,{minutes,2}}]. + [{timetrap,{seconds,180}}]. all() -> [ diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index f1414783e3..154664d855 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -48,7 +48,7 @@ <item><p><c>true | false</c></p></item> <tag><c>option() =</c></tag> - <item><p><c>socketoption() | ssloption() | transportoption()</c></p> + <item><p><c>socketoption() | ssl_option() | transport_option()</c></p> </item> <tag><c>socketoption() =</c></tag> @@ -60,7 +60,7 @@ <seealso marker="kernel:gen_tcp">gen_tcp(3)</seealso> manual pages in Kernel.</p></item> - <tag><marker id="type-ssloption"/><c>ssloption() =</c></tag> + <tag><marker id="type-ssloption"/><c>ssl_option() =</c></tag> <item> <p><c>{verify, verify_type()}</c></p> <p><c>| {verify_fun, {fun(), term()}}</c></p> @@ -85,11 +85,11 @@ [binary()]} | {client | server, [binary()], binary()}}</c></p> <p><c>| {log_alert, boolean()}</c></p> <p><c>| {server_name_indication, hostname() | disable}</c></p> - <p><c>| {sni_hosts, [{hostname(), ssloptions()}]}</c></p> + <p><c>| {sni_hosts, [{hostname(), [ssl_option()]}]}</c></p> <p><c>| {sni_fun, SNIfun::fun()}</c></p> </item> - <tag><c>transportoption() =</c></tag> + <tag><c>transport_option() =</c></tag> <item><p><c>{cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom(), ErrTag:atom()}}</c></p> @@ -168,7 +168,7 @@ | srp_4096 | srp_6144 | srp_8192</c></p></item> <tag><c>SNIfun::fun()</c></tag> - <item><p><c>= fun(ServerName :: string()) -> ssloptions()</c></p></item> + <item><p><c>= fun(ServerName :: string()) -> [ssl_option()]</c></p></item> </taglist> </section> @@ -421,7 +421,6 @@ fun(srp, Username :: string(), UserState :: term()) -> <warning><p>Using <c>{padding_check, boolean()}</c> makes TLS vulnerable to the Poodle attack.</p></warning> - </section> <section> @@ -522,9 +521,43 @@ fun(srp, Username :: string(), UserState :: term()) -> be supported by the server for the prevention to work. </p></warning> </item> - </taglist> + <tag><marker id="client_signature_algs"/><c>{signature_algs, [{hash(), ecdsa | rsa | dsa}]}</c></tag> + <item> + <p>In addition to the algorithms negotiated by the cipher + suite used for key exchange, payload encryption, message + authentication and pseudo random calculation, the TLS signature + algorithm extension <url + href="http://www.ietf.org/rfc/rfc5246.txt">Section 7.4.1.4.1 in RFC 5246</url> may be + used, from TLS 1.2, to negotiate which signature algorithm to use during the + TLS handshake. If no lower TLS versions than 1.2 are supported, + the client will send a TLS signature algorithm extension + with the algorithms specified by this option. + Defaults to + + <code>[ +%% SHA2 +{sha512, ecdsa}, +{sha512, rsa}, +{sha384, ecdsa}, +{sha384, rsa}, +{sha256, ecdsa}, +{sha256, rsa}, +{sha224, ecdsa}, +{sha224, rsa}, +%% SHA +{sha, ecdsa}, +{sha, rsa}, +{sha, dsa}, +]</code> + + The algorithms should be in the preferred order. + Selected signature algorithm can restrict which hash functions + that may be selected. Default support for {md5, rsa} removed in ssl-8.0 + </p> + </item> + </taglist> </section> - + <section> <title>SSL OPTION DESCRIPTIONS - SERVER SIDE</title> @@ -617,7 +650,7 @@ fun(srp, Username :: string(), UserState :: term()) -> selection. If set to <c>false</c> (the default), use the client preference.</p></item> - <tag><c>{sni_hosts, [{hostname(), ssloptions()}]}</c></tag> + <tag><c>{sni_hosts, [{hostname(), [ssl_option()]}]}</c></tag> <item><p>If the server receives a SNI (Server Name Indication) from the client matching a host listed in the <c>sni_hosts</c> option, the specific options for that host will override previously specified options. @@ -626,11 +659,11 @@ fun(srp, Username :: string(), UserState :: term()) -> <tag><c>{sni_fun, SNIfun::fun()}</c></tag> <item><p>If the server receives a SNI (Server Name Indication) from the client, - the given function will be called to retrieve <c>ssloptions()</c> for the indicated server. - These options will be merged into predefined <c>ssloptions()</c>. + the given function will be called to retrieve <c>[ssl_option()]</c> for the indicated server. + These options will be merged into predefined <c>[ssl_option()]</c>. The function should be defined as: - <c>fun(ServerName :: string()) -> ssloptions()</c> + <c>fun(ServerName :: string()) -> [ssl_option()]</c> and can be specified as a fun or as named <c>fun module:function/1</c> The option <c>sni_fun</c>, and <c>sni_hosts</c> are mutually exclusive.</p></item> @@ -651,6 +684,14 @@ fun(srp, Username :: string(), UserState :: term()) -> <item>If true, use the server's preference for cipher selection. If false (the default), use the client's preference. </item> + + <tag><c>{signature_algs, [{hash(), ecdsa | rsa | dsa}]}</c></tag> + <item><p> The algorithms specified by + this option will be the ones accepted by the server in a signature algorithm + negotiation, introduced in TLS-1.2. The algorithms will also be offered to the client if a + client certificate is requested. For more details see the <seealso marker="#client_signature_algs">corresponding client option</seealso>. + </p> </item> + </taglist> </section> @@ -710,7 +751,7 @@ fun(srp, Username :: string(), UserState :: term()) -> equivalent, connected socket to an SSL socket.</fsummary> <type> <v>Socket = socket()</v> - <v>SslOptions = [ssloption()]</v> + <v>SslOptions = [ssl_option()]</v> <v>Timeout = integer() | infinity</v> <v>SslSocket = sslsocket()</v> <v>Reason = term()</v> @@ -1023,7 +1064,7 @@ fun(srp, Username :: string(), UserState :: term()) -> <fsummary>Performs server-side SSL/TLS handshake.</fsummary> <type> <v>Socket = socket() | sslsocket() </v> - <v>SslOptions = ssloptions()</v> + <v>SslOptions = [ssl_option()]</v> <v>Timeout = integer()</v> <v>Reason = term()</v> </type> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 153d3fef48..e490de7eeb 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -196,8 +196,7 @@ hello(start, #state{host = Host, port = Port, role = client, {Record, State} = next_record(State1), next_state(hello, hello, Record, State); -hello(Hello = #client_hello{client_version = ClientVersion, - extensions = #hello_extensions{hash_signs = HashSigns}}, +hello(Hello = #client_hello{client_version = ClientVersion}, State = #state{connection_states = ConnectionStates0, port = Port, session = #session{own_certificate = Cert} = Session0, renegotiation = {Renegotiation, _}, @@ -209,9 +208,7 @@ hello(Hello = #client_hello{client_version = ClientVersion, {Version, {Type, Session}, ConnectionStates, #hello_extensions{ec_point_formats = EcPointFormats, - elliptic_curves = EllipticCurves} = ServerHelloExt} -> - HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, - dtls_v1:corresponding_tls_version(Version)), + elliptic_curves = EllipticCurves} = ServerHelloExt, HashSign} -> ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign}, State#state{connection_states = ConnectionStates, negotiated_version = Version, diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 85e23e4f17..4f48704cac 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -94,7 +94,10 @@ hello(#server_hello{server_version = Version, random = Random, hello(#client_hello{client_version = ClientVersion}, _Options, {_,_,_,_,ConnectionStates,_}, _Renegotiation) -> %% Return correct typ to make dialyzer happy until we have time to make the real imp. - {ClientVersion, {new, #session{}}, ConnectionStates, #hello_extensions{}}. + HashSigns = tls_v1:default_signature_algs(dtls_v1:corresponding_tls_version(ClientVersion)), + {ClientVersion, {new, #session{}}, ConnectionStates, #hello_extensions{}, + %% Placeholder for real hasign handling + hd(HashSigns)}. %% hello(Address, Port, %% #ssl_tls{epoch = _Epoch, sequence_number = _Seq, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index b565b1fb6b..4bcd6ddb0e 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -700,6 +700,10 @@ handle_options(Opts0, Role) -> srp_identity = handle_option(srp_identity, Opts, undefined), ciphers = handle_cipher_option(proplists:get_value(ciphers, Opts, []), RecordCb:highest_protocol_version(Versions)), + signature_algs = handle_hashsigns_option(proplists:get_value(signature_algs, Opts, + default_option_role(server, + tls_v1:default_signature_algs(Versions), Role)), + RecordCb:highest_protocol_version(Versions)), %% Server side option reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), reuse_sessions = handle_option(reuse_sessions, Opts, true), @@ -749,7 +753,7 @@ handle_options(Opts0, Role) -> alpn_preferred_protocols, next_protocols_advertised, client_preferred_next_protocols, log_alert, server_name_indication, honor_cipher_order, padding_check, crl_check, crl_cache, - fallback], + fallback, signature_algs], SockOpts = lists:foldl(fun(Key, PropList) -> proplists:delete(Key, PropList) @@ -989,6 +993,18 @@ validate_option(crl_cache, {Cb, {_Handle, Options}} = Value) when is_atom(Cb) an validate_option(Opt, Value) -> throw({error, {options, {Opt, Value}}}). +handle_hashsigns_option(Value, {Major, Minor} = Version) when is_list(Value) + andalso Major >= 3 andalso Minor >= 3-> + case tls_v1:signature_algs(Version, Value) of + [] -> + throw({error, {options, no_supported_algorithms, {signature_algs, Value}}}); + _ -> + Value + end; +handle_hashsigns_option(_, {Major, Minor} = Version) when Major >= 3 andalso Minor >= 3-> + handle_hashsigns_option(tls_v1:default_signature_algs(Version), Version); +handle_hashsigns_option(_, _Version) -> + undefined. validate_options([]) -> []; @@ -1285,6 +1301,13 @@ new_ssl_options([{server_name_indication, Value} | Rest], #ssl_options{} = Opts, new_ssl_options(Rest, Opts#ssl_options{server_name_indication = validate_option(server_name_indication, Value)}, RecordCB); new_ssl_options([{honor_cipher_order, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> new_ssl_options(Rest, Opts#ssl_options{honor_cipher_order = validate_option(honor_cipher_order, Value)}, RecordCB); +new_ssl_options([{signature_algs, Value} | Rest], #ssl_options{} = Opts, RecordCB) -> + new_ssl_options(Rest, + Opts#ssl_options{signature_algs = + handle_hashsigns_option(Value, + RecordCB:highest_protocol_version())}, + RecordCB); + new_ssl_options([{Key, Value} | _Rest], #ssl_options{}, _) -> throw({error, {options, {Key, Value}}}). diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index ed7ae90aa5..e66f253a70 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -43,11 +43,12 @@ -export_type([cipher_suite/0, erl_cipher_suite/0, openssl_cipher_suite/0, - key_algo/0]). + hash/0, key_algo/0, sign_algo/0]). -type cipher() :: null |rc4_128 | idea_cbc | des40_cbc | des_cbc | '3des_ede_cbc' | aes_128_cbc | aes_256_cbc | aes_128_gcm | aes_256_gcm | chacha20_poly1305. -type hash() :: null | sha | md5 | sha224 | sha256 | sha384 | sha512. +-type sign_algo() :: rsa | dsa | ecdsa. -type key_algo() :: null | rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa| ecdh_ecdsa | ecdh_rsa| srp_rsa| srp_dss | psk | dhe_psk | rsa_psk | dh_anon | ecdh_anon | srp_anon. -type erl_cipher_suite() :: {key_algo(), cipher(), hash()} % Pre TLS 1.2 %% TLS 1.2, internally PRE TLS 1.2 will use default_prf diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index d9e270ad70..1568e8559f 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -304,13 +304,9 @@ hello(#hello_request{}, #state{role = client} = State0, Connection) -> {Record, State} = Connection:next_record(State0), Connection:next_state(hello, hello, Record, State); -hello({common_client_hello, Type, ServerHelloExt, NegotiatedHashSign}, +hello({common_client_hello, Type, ServerHelloExt}, State, Connection) -> - do_server_hello(Type, ServerHelloExt, - %% Note NegotiatedHashSign is only negotiated for real if - %% if TLS version is at least TLS-1.2 - State#state{hashsign_algorithm = NegotiatedHashSign}, Connection); - + do_server_hello(Type, ServerHelloExt, State, Connection); hello(timeout, State, _) -> {next_state, hello, State, hibernate}; @@ -442,7 +438,8 @@ certify(#server_key_exchange{exchange_keys = Keys}, Alg == srp_dss; Alg == srp_rsa; Alg == srp_anon -> Params = ssl_handshake:decode_server_key(Keys, Alg, Version), - HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, Version), + %% Use negotiated value if TLS-1.2 otherwhise return default + HashSign = negotiated_hashsign(Params#server_key_params.hashsign, Alg, PubKeyInfo, Version), case is_anonymous(Alg) of true -> calculate_secret(Params#server_key_params.params, @@ -464,11 +461,18 @@ certify(#server_key_exchange{} = Msg, certify(#certificate_request{hashsign_algorithms = HashSigns}, #state{session = #session{own_certificate = Cert}, - negotiated_version = Version} = State0, Connection) -> - HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version), - {Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}), - Connection:next_state(certify, certify, Record, - State#state{cert_hashsign_algorithm = HashSign}); + key_algorithm = KeyExAlg, + ssl_options = #ssl_options{signature_algs = SupportedHashSigns}, + negotiated_version = Version} = State0, Connection) -> + + case ssl_handshake:select_hashsign(HashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of + #alert {} = Alert -> + Connection:handle_own_alert(Alert, Version, certify, State0); + NegotiatedHashSign -> + {Record, State} = Connection:next_record(State0#state{client_certificate_requested = true}), + Connection:next_state(certify, certify, Record, + State#state{cert_hashsign_algorithm = NegotiatedHashSign}) + end; %% PSK and RSA_PSK might bypass the Server-Key-Exchange certify(#server_hello_done{}, @@ -576,13 +580,15 @@ cipher(#hello_request{}, State0, Connection) -> cipher(#certificate_verify{signature = Signature, hashsign_algorithm = CertHashSign}, #state{role = server, - public_key_info = {Algo, _, _} =PublicKeyInfo, + key_algorithm = KexAlg, + public_key_info = PublicKeyInfo, negotiated_version = Version, session = #session{master_secret = MasterSecret}, tls_handshake_history = Handshake } = State0, Connection) -> - - HashSign = ssl_handshake:select_hashsign_algs(CertHashSign, Algo, Version), + + %% Use negotiated value if TLS-1.2 otherwhise return default + HashSign = negotiated_hashsign(CertHashSign, KexAlg, PublicKeyInfo, Version), case ssl_handshake:certificate_verify(Signature, PublicKeyInfo, Version, HashSign, MasterSecret, Handshake) of valid -> @@ -1448,7 +1454,8 @@ rsa_psk_key_exchange(Version, PskIdentity, PremasterSecret, PublicKeyInfo = {Alg rsa_psk_key_exchange(_, _, _, _) -> throw (?ALERT_REC(?FATAL,?HANDSHAKE_FAILURE)). -request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, +request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer, + signature_algs = SupportedHashSigns}, connection_states = ConnectionStates0, cert_db = CertDbHandle, cert_db_ref = CertDbRef, @@ -1456,7 +1463,9 @@ request_client_cert(#state{ssl_options = #ssl_options{verify = verify_peer}, #connection_state{security_parameters = #security_parameters{cipher_suite = CipherSuite}} = ssl_record:pending_connection_state(ConnectionStates0, read), - Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef, Version), + HashSigns = ssl_handshake:available_signature_algs(SupportedHashSigns, Version, [Version]), + Msg = ssl_handshake:certificate_request(CipherSuite, CertDbHandle, CertDbRef, + HashSigns, Version), State = Connection:send_handshake(Msg, State0), State#state{client_certificate_requested = true}; @@ -1881,15 +1890,16 @@ make_premaster_secret({MajVer, MinVer}, rsa) -> make_premaster_secret(_, _) -> undefined. -negotiated_hashsign(undefined, Alg, Version) -> +negotiated_hashsign(undefined, KexAlg, PubKeyInfo, Version) -> %% Not negotiated choose default - case is_anonymous(Alg) of + case is_anonymous(KexAlg) of true -> {null, anon}; false -> - ssl_handshake:select_hashsign_algs(Alg, Version) + {PubAlg, _, _} = PubKeyInfo, + ssl_handshake:select_hashsign_algs(undefined, PubAlg, Version) end; -negotiated_hashsign(HashSign = {_, _}, _, _) -> +negotiated_hashsign(HashSign = {_, _}, _, _, _) -> HashSign. ssl_options_list(SslOptions) -> diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index f84958a22e..2a2a7b7d25 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -46,7 +46,7 @@ %% Handshake messages -export([hello_request/0, server_hello/4, server_hello_done/0, - certificate/4, certificate_request/4, key_exchange/3, + certificate/4, certificate_request/5, key_exchange/3, finished/5, next_protocol/1]). %% Handle handshake messages @@ -64,8 +64,8 @@ ]). %% Cipher suites handling --export([available_suites/2, cipher_suites/2, - select_session/10, supported_ecc/1]). +-export([available_suites/2, available_signature_algs/3, cipher_suites/2, + select_session/11, supported_ecc/1]). %% Extensions handling -export([client_hello_extensions/6, @@ -74,8 +74,8 @@ ]). %% MISC --export([select_version/3, prf/5, select_hashsign/3, - select_hashsign_algs/2, select_hashsign_algs/3, +-export([select_version/3, prf/5, select_hashsign/5, + select_hashsign_algs/3, premaster_secret/2, premaster_secret/3, premaster_secret/4]). %%==================================================================== @@ -120,7 +120,8 @@ server_hello(SessionId, Version, ConnectionStates, Extensions) -> server_hello_done() -> #server_hello_done{}. -client_hello_extensions(Host, Version, CipherSuites, SslOpts, ConnectionStates, Renegotiation) -> +client_hello_extensions(Host, Version, CipherSuites, + #ssl_options{signature_algs = SupportedHashSigns, versions = AllVersions} = SslOpts, ConnectionStates, Renegotiation) -> {EcPointFormats, EllipticCurves} = case advertises_ec_ciphers(lists:map(fun ssl_cipher:suite_definition/1, CipherSuites)) of true -> @@ -134,7 +135,7 @@ client_hello_extensions(Host, Version, CipherSuites, SslOpts, ConnectionStates, renegotiation_info = renegotiation_info(tls_record, client, ConnectionStates, Renegotiation), srp = SRP, - hash_signs = advertised_hash_signs(Version), + signature_algs = available_signature_algs(SupportedHashSigns, Version, AllVersions), ec_point_formats = EcPointFormats, elliptic_curves = EllipticCurves, alpn = encode_alpn(SslOpts#ssl_options.alpn_advertised_protocols, Renegotiation), @@ -203,14 +204,14 @@ client_certificate_verify(OwnCert, MasterSecret, Version, end. %%-------------------------------------------------------------------- --spec certificate_request(ssl_cipher:cipher_suite(), db_handle(), certdb_ref(), ssl_record:ssl_version()) -> - #certificate_request{}. +-spec certificate_request(ssl_cipher:cipher_suite(), db_handle(), + certdb_ref(), #hash_sign_algos{}, ssl_record:ssl_version()) -> + #certificate_request{}. %% %% Description: Creates a certificate_request message, called by the server. %%-------------------------------------------------------------------- -certificate_request(CipherSuite, CertDbHandle, CertDbRef, Version) -> +certificate_request(CipherSuite, CertDbHandle, CertDbRef, HashSigns, Version) -> Types = certificate_types(ssl_cipher:suite_definition(CipherSuite), Version), - HashSigns = advertised_hash_signs(Version), Authorities = certificate_authorities(CertDbHandle, CertDbRef), #certificate_request{ certificate_types = Types, @@ -351,6 +352,9 @@ verify_server_key(#server_key_params{params_bin = EncParams, %% %% Description: Checks that the certificate_verify message is valid. %%-------------------------------------------------------------------- +certificate_verify(_, _, _, undefined, _, _) -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE); + certificate_verify(Signature, PublicKeyInfo, Version, HashSign = {HashAlgo, _}, MasterSecret, {_, Handshake}) -> Hash = calc_certificate_verify(Version, HashAlgo, MasterSecret, Handshake), @@ -379,10 +383,11 @@ verify_signature(_Version, Hash, _HashAlgo, Signature, {?rsaEncryption, PubKey, end; verify_signature(_Version, Hash, {HashAlgo, dsa}, Signature, {?'id-dsa', PublicKey, PublicKeyParams}) -> public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}); -verify_signature(_Version, Hash, {HashAlgo, ecdsa}, Signature, +verify_signature(_, Hash, {HashAlgo, _SignAlg}, Signature, {?'id-ecPublicKey', PublicKey, PublicKeyParams}) -> public_key:verify({digest, Hash}, HashAlgo, Signature, {PublicKey, PublicKeyParams}). + %%-------------------------------------------------------------------- -spec certify(#certificate{}, db_handle(), certdb_ref(), integer() | nolimit, verify_peer | verify_none, {fun(), term}, fun(), term(), term(), @@ -573,43 +578,46 @@ prf({3,_N}, Secret, Label, Seed, WantedLength) -> %%-------------------------------------------------------------------- --spec select_hashsign(#hash_sign_algos{}| undefined, undefined | binary(), ssl_record:ssl_version()) -> - {atom(), atom()} | undefined. +-spec select_hashsign(#hash_sign_algos{} | undefined, undefined | binary(), + atom(), [atom()], ssl_record:ssl_version()) -> + {atom(), atom()} | undefined | #alert{}. %% -%% Description: +%% Description: Handles signature_algorithms extension %%-------------------------------------------------------------------- -select_hashsign(_, undefined, _Version) -> +select_hashsign(_, undefined, _, _, _Version) -> {null, anon}; %% The signature_algorithms extension was introduced with TLS 1.2. Ignore it if we have %% negotiated a lower version. -select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, {Major, Minor} = Version) - when Major >= 3 andalso Minor >= 3 -> - #'OTPCertificate'{tbsCertificate = TBSCert} =public_key:pkix_decode_cert(Cert, otp), +select_hashsign(HashSigns, Cert, KeyExAlgo, + undefined, {Major, Minor} = Version) when Major >= 3 andalso Minor >= 3-> + select_hashsign(HashSigns, Cert, KeyExAlgo, tls_v1:default_signature_algs(Version), Version); +select_hashsign(#hash_sign_algos{hash_sign_algos = HashSigns}, Cert, KeyExAlgo, SupportedHashSigns, + {Major, Minor}) when Major >= 3 andalso Minor >= 3 -> + #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, - DefaultHashSign = {_, Sign} = select_hashsign_algs(undefined, Algo, Version), - case lists:filter(fun({sha, dsa}) -> + Sign = cert_sign(Algo), + case lists:filter(fun({sha, dsa = S}) when S == Sign -> true; ({_, dsa}) -> false; - ({Hash, S}) when S == Sign -> - ssl_cipher:is_acceptable_hash(Hash, - proplists:get_value(hashs, crypto:supports())); + ({_, _} = Algos) -> + is_acceptable_hash_sign(Algos, Sign, KeyExAlgo, SupportedHashSigns); (_) -> false end, HashSigns) of [] -> - DefaultHashSign; - [HashSign| _] -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); + [HashSign | _] -> HashSign end; -select_hashsign(_, Cert, Version) -> +select_hashsign(_, Cert, _, _, Version) -> #'OTPCertificate'{tbsCertificate = TBSCert} = public_key:pkix_decode_cert(Cert, otp), #'OTPSubjectPublicKeyInfo'{algorithm = {_,Algo, _}} = TBSCert#'OTPTBSCertificate'.subjectPublicKeyInfo, select_hashsign_algs(undefined, Algo, Version). %%-------------------------------------------------------------------- --spec select_hashsign_algs(#hash_sign_algos{}| undefined, oid(), ssl_record:ssl_version()) -> +-spec select_hashsign_algs({atom(), atom()}| undefined, oid(), ssl_record:ssl_version()) -> {atom(), atom()}. %% Description: For TLS 1.2 hash function and signature algorithm pairs can be @@ -642,24 +650,6 @@ select_hashsign_algs(undefined, ?rsaEncryption, _) -> select_hashsign_algs(undefined, ?'id-dsa', _) -> {sha, dsa}. --spec select_hashsign_algs(atom(), ssl_record:ssl_version()) -> {atom(), atom()}. -%% Wrap function to keep the knowledge of the default values in -%% one place only -select_hashsign_algs(Alg, Version) when (Alg == rsa orelse - Alg == dhe_rsa orelse - Alg == dh_rsa orelse - Alg == ecdhe_rsa orelse - Alg == ecdh_rsa orelse - Alg == srp_rsa) -> - select_hashsign_algs(undefined, ?rsaEncryption, Version); -select_hashsign_algs(Alg, Version) when (Alg == dhe_dss orelse - Alg == dh_dss orelse - Alg == srp_dss) -> - select_hashsign_algs(undefined, ?'id-dsa', Version); -select_hashsign_algs(Alg, Version) when (Alg == ecdhe_ecdsa orelse - Alg == ecdh_ecdsa) -> - select_hashsign_algs(undefined, ?'id-ecPublicKey', Version). - %%-------------------------------------------------------------------- -spec master_secret(atom(), ssl_record:ssl_version(), #session{} | binary(), #connection_states{}, client | server) -> {binary(), #connection_states{}} | #alert{}. @@ -1063,9 +1053,56 @@ available_suites(UserSuites, Version) -> lists:member(Suite, ssl_cipher:all_suites(Version)) end, UserSuites). -available_suites(ServerCert, UserSuites, Version, Curve) -> +available_suites(ServerCert, UserSuites, Version, undefined, Curve) -> ssl_cipher:filter(ServerCert, available_suites(UserSuites, Version)) - -- unavailable_ecc_suites(Curve). + -- unavailable_ecc_suites(Curve); +available_suites(ServerCert, UserSuites, Version, HashSigns, Curve) -> + Suites = available_suites(ServerCert, UserSuites, Version, undefined, Curve), + filter_hashsigns(Suites, [ssl_cipher:suite_definition(Suite) || Suite <- Suites], HashSigns, []). +filter_hashsigns([], [], _, Acc) -> + lists:reverse(Acc); +filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, + Acc) when KeyExchange == dhe_ecdsa; + KeyExchange == ecdhe_ecdsa -> + do_filter_hashsigns(ecdsa, Suite, Suites, Algos, HashSigns, Acc); + +filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, + Acc) when KeyExchange == rsa; + KeyExchange == dhe_rsa; + KeyExchange == ecdhe_rsa; + KeyExchange == srp_rsa; + KeyExchange == rsa_psk -> + do_filter_hashsigns(rsa, Suite, Suites, Algos, HashSigns, Acc); +filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc) when + KeyExchange == dhe_dss; + KeyExchange == srp_dss -> + do_filter_hashsigns(dsa, Suite, Suites, Algos, HashSigns, Acc); +filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc) when + KeyExchange == dh_dss; + KeyExchange == dh_rsa; + KeyExchange == dh_ecdsa; + KeyExchange == ecdh_rsa; + KeyExchange == ecdh_ecdsa -> + %% Fixed DH certificates MAY be signed with any hash/signature + %% algorithm pair appearing in the hash_sign extension. The names + %% DH_DSS, DH_RSA, ECDH_ECDSA, and ECDH_RSA are historical. + filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]); +filter_hashsigns([Suite | Suites], [{KeyExchange,_,_,_} | Algos], HashSigns, Acc) when + KeyExchange == dh_anon; + KeyExchange == ecdh_anon; + KeyExchange == srp_anon; + KeyExchange == psk; + KeyExchange == dhe_psk -> + %% In this case hashsigns is not used as the kexchange is anonaymous + filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]). + +do_filter_hashsigns(SignAlgo, Suite, Suites, Algos, HashSigns, Acc) -> + case lists:keymember(SignAlgo, 2, HashSigns) of + true -> + filter_hashsigns(Suites, Algos, HashSigns, [Suite| Acc]); + false -> + filter_hashsigns(Suites, Algos, HashSigns, Acc) + end. unavailable_ecc_suites(no_curve) -> ssl_cipher:ec_keyed_suites(); @@ -1077,17 +1114,17 @@ cipher_suites(Suites, false) -> cipher_suites(Suites, true) -> Suites. -select_session(SuggestedSessionId, CipherSuites, Compressions, Port, #session{ecc = ECCCurve} = +select_session(SuggestedSessionId, CipherSuites, HashSigns, Compressions, Port, #session{ecc = ECCCurve} = Session, Version, - #ssl_options{ciphers = UserSuites, honor_cipher_order = HCO} = SslOpts, + #ssl_options{ciphers = UserSuites, honor_cipher_order = HonorCipherOrder} = SslOpts, Cache, CacheCb, Cert) -> {SessionId, Resumed} = ssl_session:server_id(Port, SuggestedSessionId, SslOpts, Cert, Cache, CacheCb), case Resumed of undefined -> - Suites = available_suites(Cert, UserSuites, Version, ECCCurve), - CipherSuite = select_cipher_suite(CipherSuites, Suites, HCO), + Suites = available_suites(Cert, UserSuites, Version, HashSigns, ECCCurve), + CipherSuite = select_cipher_suite(CipherSuites, Suites, HonorCipherOrder), Compression = select_compression(Compressions), {new, Session#session{session_id = SessionId, cipher_suite = CipherSuite, @@ -1155,7 +1192,7 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, #hello_extensions{renegotiation_info = Info, srp = SRP, ec_point_formats = ECCFormat, - alpn = ALPN, + alpn = ALPN, next_protocol_negotiation = NextProtocolNegotiation}, Version, #ssl_options{secure_renegotiate = SecureRenegotation, alpn_preferred_protocols = ALPNPreferredProtocols} = Opts, @@ -1324,7 +1361,7 @@ handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) -> hello_extensions_list(#hello_extensions{renegotiation_info = RenegotiationInfo, srp = SRP, - hash_signs = HashSigns, + signature_algs = HashSigns, ec_point_formats = EcPointFormats, elliptic_curves = EllipticCurves, alpn = ALPN, @@ -1799,7 +1836,7 @@ dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), <<?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, Acc#hello_extensions{hash_signs = + dec_hello_extensions(Rest, Acc#hello_extensions{signature_algs = #hash_sign_algos{hash_sign_algos = HashSignAlgos}}); dec_hello_extensions(<<?UINT16(?ELLIPTIC_CURVES_EXT), ?UINT16(Len), @@ -1899,7 +1936,7 @@ from_2bytes(<<?UINT16(N), Rest/binary>>, Acc) -> key_exchange_alg(rsa) -> ?KEY_EXCHANGE_RSA; key_exchange_alg(Alg) when Alg == dhe_rsa; Alg == dhe_dss; - Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon -> + Alg == dh_dss; Alg == dh_rsa; Alg == dh_anon -> ?KEY_EXCHANGE_DIFFIE_HELLMAN; key_exchange_alg(Alg) when Alg == ecdhe_rsa; Alg == ecdh_rsa; Alg == ecdhe_ecdsa; Alg == ecdh_ecdsa; @@ -2008,27 +2045,16 @@ is_member(Suite, SupportedSuites) -> select_compression(_CompressionMetodes) -> ?NULL. --define(TLSEXT_SIGALG_RSA(MD), {MD, rsa}). --define(TLSEXT_SIGALG_DSA(MD), {MD, dsa}). --define(TLSEXT_SIGALG_ECDSA(MD), {MD, ecdsa}). - --define(TLSEXT_SIGALG(MD), ?TLSEXT_SIGALG_ECDSA(MD), ?TLSEXT_SIGALG_RSA(MD)). - -advertised_hash_signs({Major, Minor}) when Major >= 3 andalso Minor >= 3 -> - HashSigns = [?TLSEXT_SIGALG(sha512), - ?TLSEXT_SIGALG(sha384), - ?TLSEXT_SIGALG(sha256), - ?TLSEXT_SIGALG(sha224), - ?TLSEXT_SIGALG(sha), - ?TLSEXT_SIGALG_DSA(sha), - ?TLSEXT_SIGALG_RSA(md5)], - CryptoSupport = crypto:supports(), - HasECC = proplists:get_bool(ecdsa, proplists:get_value(public_keys, CryptoSupport)), - Hashs = proplists:get_value(hashs, CryptoSupport), - #hash_sign_algos{hash_sign_algos = - lists:filter(fun({Hash, ecdsa}) -> HasECC andalso proplists:get_bool(Hash, Hashs); - ({Hash, _}) -> proplists:get_bool(Hash, Hashs) end, HashSigns)}; -advertised_hash_signs(_) -> +available_signature_algs(undefined, _, _) -> + undefined; +available_signature_algs(SupportedHashSigns, {Major, Minor}, AllVersions) when Major >= 3 andalso Minor >= 3 -> + case tls_record:lowest_protocol_version(AllVersions) of + {3, 3} -> + #hash_sign_algos{hash_sign_algos = SupportedHashSigns}; + _ -> + undefined + end; +available_signature_algs(_, _, _) -> undefined. psk_secret(PSKIdentity, PSKLookup) -> @@ -2123,3 +2149,25 @@ distpoints_lookup([DistPoint | Rest], Callback, CRLDbHandle) -> CRLs -> [{DistPoint, {CRL, public_key:der_decode('CertificateList', CRL)}} || CRL <- CRLs] end. + +cert_sign(?rsaEncryption) -> + rsa; +cert_sign(?'id-ecPublicKey') -> + ecdsa; +cert_sign(?'id-dsa') -> + dsa; +cert_sign(Alg) -> + {_, Sign} =public_key:pkix_sign_types(Alg), + Sign. + +is_acceptable_hash_sign({_, Sign} = Algos, Sign, _, SupportedHashSigns) -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign(Algos,_, KeyExAlgo, SupportedHashSigns) when KeyExAlgo == dh_ecdsa; + KeyExAlgo == ecdh_rsa; + KeyExAlgo == ecdh_ecdsa -> + is_acceptable_hash_sign(Algos, SupportedHashSigns); +is_acceptable_hash_sign(_,_,_,_) -> + false. +is_acceptable_hash_sign(Algos, SupportedHashSigns) -> + lists:member(Algos, SupportedHashSigns). + diff --git a/lib/ssl/src/ssl_handshake.hrl b/lib/ssl/src/ssl_handshake.hrl index 4f117903f7..e7b118de10 100644 --- a/lib/ssl/src/ssl_handshake.hrl +++ b/lib/ssl/src/ssl_handshake.hrl @@ -95,7 +95,7 @@ -record(hello_extensions, { renegotiation_info, - hash_signs, % supported combinations of hashes/signature algos + signature_algs, % supported combinations of hashes/signature algos alpn, next_protocol_negotiation = undefined, % [binary()] srp, diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 913746598f..9c52f5a315 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -135,7 +135,8 @@ padding_check = true :: boolean(), fallback = false :: boolean(), crl_check :: boolean() | peer | best_effort, - crl_cache + crl_cache, + signature_algs }). -record(socket_options, diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl index ff81a163b3..4651687fe6 100644 --- a/lib/ssl/src/ssl_tls_dist_proxy.erl +++ b/lib/ssl/src/ssl_tls_dist_proxy.erl @@ -195,6 +195,11 @@ accept_loop(Proxy, erts = Type, Listen, Extra) -> {_Kernel, unsupported_protocol} -> exit(unsupported_protocol) end; + {error, closed} -> + %% The listening socket is closed: the proxy process is + %% shutting down. Exit normally, to avoid generating a + %% spurious error report. + exit(normal); Error -> exit(Error) end, diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index e6a2f3e0d9..a1d13d1850 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -182,8 +182,7 @@ hello(start, #state{host = Host, port = Port, role = client, next_state(hello, hello, Record, State); hello(Hello = #client_hello{client_version = ClientVersion, - extensions = #hello_extensions{hash_signs = HashSigns, - ec_point_formats = EcPointFormats, + extensions = #hello_extensions{ec_point_formats = EcPointFormats, elliptic_curves = EllipticCurves}}, State = #state{connection_states = ConnectionStates0, port = Port, session = #session{own_certificate = Cert} = Session0, @@ -191,27 +190,28 @@ hello(Hello = #client_hello{client_version = ClientVersion, session_cache = Cache, session_cache_cb = CacheCb, negotiated_protocol = CurrentProtocol, + key_algorithm = KeyExAlg, ssl_options = SslOpts}) -> + case tls_handshake:hello(Hello, SslOpts, {Port, Session0, Cache, CacheCb, - ConnectionStates0, Cert}, Renegotiation) of + ConnectionStates0, Cert, KeyExAlg}, Renegotiation) of #alert{} = Alert -> handle_own_alert(Alert, ClientVersion, hello, State); {Version, {Type, Session}, - ConnectionStates, Protocol0, ServerHelloExt} -> - + ConnectionStates, Protocol0, ServerHelloExt, HashSign} -> Protocol = case Protocol0 of - undefined -> CurrentProtocol; - _ -> Protocol0 - end, - - HashSign = ssl_handshake:select_hashsign(HashSigns, Cert, Version), - ssl_connection:hello({common_client_hello, Type, ServerHelloExt, HashSign}, + undefined -> CurrentProtocol; + _ -> Protocol0 + end, + ssl_connection:hello({common_client_hello, Type, ServerHelloExt}, State#state{connection_states = ConnectionStates, negotiated_version = Version, + hashsign_algorithm = HashSign, session = Session, client_ecc = {EllipticCurves, EcPointFormats}, negotiated_protocol = Protocol}, ?MODULE) end; + hello(Hello = #server_hello{}, #state{connection_states = ConnectionStates0, negotiated_version = ReqVersion, @@ -1069,3 +1069,4 @@ handle_sni_extension(#client_hello{extensions = HelloExtensions}, State0) -> end; handle_sni_extension(_, State0) -> State0. + diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 0a6cb9f92d..ef718c13df 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -56,7 +56,7 @@ client_hello(Host, Port, ConnectionStates, Version = tls_record:highest_protocol_version(Versions), Pending = ssl_record:pending_connection_state(ConnectionStates, read), SecParams = Pending#connection_state.security_parameters, - AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version), + AvailableCipherSuites = ssl_handshake:available_suites(UserSuites, Version), Extensions = ssl_handshake:client_hello_extensions(Host, Version, AvailableCipherSuites, SslOpts, ConnectionStates, Renegotiation), @@ -80,13 +80,13 @@ client_hello(Host, Port, ConnectionStates, -spec hello(#server_hello{} | #client_hello{}, #ssl_options{}, #connection_states{} | {inet:port_number(), #session{}, db_handle(), atom(), #connection_states{}, - binary() | undefined}, + binary() | undefined, ssl_cipher:key_algo()}, boolean()) -> {tls_record:tls_version(), session_id(), #connection_states{}, alpn | npn, binary() | undefined}| {tls_record:tls_version(), {resumed | new, #session{}}, #connection_states{}, binary() | undefined, - #hello_extensions{}} | + #hello_extensions{}, {ssl_cipher:hash(), ssl_cipher:sign_algo()} | undefined} | #alert{}. %% %% Description: Handles a recieved hello message @@ -149,26 +149,35 @@ get_tls_handshake(Version, Data, Buffer) -> %%% Internal functions %%-------------------------------------------------------------------- handle_client_hello(Version, #client_hello{session_id = SugesstedId, - cipher_suites = CipherSuites, - compression_methods = Compressions, - random = Random, - extensions = #hello_extensions{elliptic_curves = Curves} = HelloExt}, - #ssl_options{versions = Versions} = SslOpts, - {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert}, Renegotiation) -> + cipher_suites = CipherSuites, + compression_methods = Compressions, + random = Random, + extensions = #hello_extensions{elliptic_curves = Curves, + signature_algs = ClientHashSigns} = HelloExt}, + #ssl_options{versions = Versions, + signature_algs = SupportedHashSigns} = SslOpts, + {Port, Session0, Cache, CacheCb, ConnectionStates0, Cert, _}, Renegotiation) -> case tls_record:is_acceptable_version(Version, Versions) of true -> + AvailableHashSigns = available_signature_algs(ClientHashSigns, SupportedHashSigns, Cert, Version), ECCCurve = ssl_handshake:select_curve(Curves, ssl_handshake:supported_ecc(Version)), {Type, #session{cipher_suite = CipherSuite} = Session1} - = ssl_handshake:select_session(SugesstedId, CipherSuites, Compressions, + = ssl_handshake:select_session(SugesstedId, CipherSuites, AvailableHashSigns, Compressions, Port, Session0#session{ecc = ECCCurve}, Version, SslOpts, Cache, CacheCb, Cert), case CipherSuite of no_suite -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY); _ -> - handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, - SslOpts, Session1, ConnectionStates0, - Renegotiation) + {KeyExAlg,_,_,_} = ssl_cipher:suite_definition(CipherSuite), + case ssl_handshake:select_hashsign(ClientHashSigns, Cert, KeyExAlg, SupportedHashSigns, Version) of + #alert{} = Alert -> + Alert; + HashSign -> + handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, + SslOpts, Session1, ConnectionStates0, + Renegotiation, HashSign) + end end; false -> ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION) @@ -245,14 +254,14 @@ enc_handshake(HandshakeMsg, Version) -> handle_client_hello_extensions(Version, Type, Random, CipherSuites, - HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation) -> + HelloExt, SslOpts, Session0, ConnectionStates0, Renegotiation, HashSign) -> try ssl_handshake:handle_client_hello_extensions(tls_record, Random, CipherSuites, HelloExt, Version, SslOpts, Session0, ConnectionStates0, Renegotiation) of #alert{} = Alert -> Alert; {Session, ConnectionStates, Protocol, ServerHelloExt} -> - {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt} + {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign} catch throw:Alert -> Alert end. @@ -269,3 +278,12 @@ handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, {Version, SessionId, ConnectionStates, ProtoExt, Protocol} end. +available_signature_algs(undefined, SupportedHashSigns, _, {Major, Minor}) when (Major < 3) andalso (Minor < 3) -> + SupportedHashSigns; +available_signature_algs(#hash_sign_algos{hash_sign_algos = ClientHashSigns}, SupportedHashSigns, + _, {Major, Minor}) when (Major < 3) andalso (Minor < 3) -> + ordsets:intersection(ClientHashSigns, SupportedHashSigns); +available_signature_algs(_, _, _, _) -> + undefined. + + diff --git a/lib/ssl/src/tls_v1.erl b/lib/ssl/src/tls_v1.erl index 9aaacc2af7..711db77708 100644 --- a/lib/ssl/src/tls_v1.erl +++ b/lib/ssl/src/tls_v1.erl @@ -31,7 +31,8 @@ -export([master_secret/4, finished/5, certificate_verify/3, mac_hash/7, setup_keys/8, suites/1, prf/5, - ecc_curves/1, oid_to_enum/1, enum_to_oid/1]). + ecc_curves/1, oid_to_enum/1, enum_to_oid/1, + default_signature_algs/1, signature_algs/2]). %%==================================================================== %% Internal application API @@ -256,6 +257,52 @@ suites(3) -> ] ++ suites(2). + +signature_algs({3, 3}, HashSigns) -> + CryptoSupports = crypto:supports(), + Hashes = proplists:get_value(hashs, CryptoSupports), + PubKeys = proplists:get_value(public_keys, CryptoSupports), + Supported = lists:foldl(fun({Hash, dsa = Sign} = Alg, Acc) -> + case proplists:get_bool(dss, PubKeys) + andalso proplists:get_bool(Hash, Hashes) + andalso is_pair(Hash, Sign, Hashes) + of + true -> + [Alg | Acc]; + false -> + Acc + end; + ({Hash, Sign} = Alg, Acc) -> + case proplists:get_bool(Sign, PubKeys) + andalso proplists:get_bool(Hash, Hashes) + andalso is_pair(Hash, Sign, Hashes) + of + true -> + [Alg | Acc]; + false -> + Acc + end + end, [], HashSigns), + lists:reverse(Supported). + +default_signature_algs({3, 3} = Version) -> + Default = [%% SHA2 + {sha512, ecdsa}, + {sha512, rsa}, + {sha384, ecdsa}, + {sha384, rsa}, + {sha256, ecdsa}, + {sha256, rsa}, + {sha224, ecdsa}, + {sha224, rsa}, + %% SHA + {sha, ecdsa}, + {sha, rsa}, + {sha, dsa}], + signature_algs(Version, Default); +default_signature_algs(_) -> + undefined. + %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- @@ -340,6 +387,17 @@ finished_label(client) -> finished_label(server) -> <<"server finished">>. +is_pair(sha, dsa, _) -> + true; +is_pair(_, dsa, _) -> + false; +is_pair(Hash, ecdsa, Hashs) -> + AtLeastSha = Hashs -- [md2,md4,md5], + lists:member(Hash, AtLeastSha); +is_pair(Hash, rsa, Hashs) -> + AtLeastMd5 = Hashs -- [md2,md4], + lists:member(Hash, AtLeastMd5). + %% list ECC curves in prefered order ecc_curves(_Minor) -> TLSCurves = [sect571r1,sect571k1,secp521r1,brainpoolP512r1, diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 4aed615543..50313e6a22 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -58,7 +58,7 @@ all() -> groups() -> [{basic, [], basic_tests()}, {options, [], options_tests()}, - {'tlsv1.2', [], all_versions_groups()}, + {'tlsv1.2', [], all_versions_groups() ++ [conf_signature_algs, no_common_signature_algs]}, {'tlsv1.1', [], all_versions_groups()}, {'tlsv1', [], all_versions_groups() ++ rizzo_tests()}, {'sslv3', [], all_versions_groups() ++ rizzo_tests() ++ [ciphersuite_vs_version]}, @@ -2900,7 +2900,61 @@ ciphersuite_vs_version(Config) when is_list(Config) -> _ -> ct:fail({unexpected_server_hello, ServerHello}) end. - + +%%-------------------------------------------------------------------- +conf_signature_algs() -> + [{doc,"Test to set the signature_algs option on both client and server"}]. +conf_signature_algs(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, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- +no_common_signature_algs() -> + [{doc,"Set the signature_algs option so that there client and server does not share any hash sign algorithms"}]. +no_common_signature_algs(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_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{signature_algs, [{sha256, rsa}]} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{signature_algs, [{sha384, rsa}]} + | ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error, {tls_alert, "insufficient security"}}, + Client, {error, {tls_alert, "insufficient security"}}). + %%-------------------------------------------------------------------- dont_crash_on_handshake_garbage() -> @@ -4096,7 +4150,7 @@ connection_information_result(Socket) -> {ok, Info = [_ | _]} = ssl:connection_information(Socket), case length(Info) > 3 of true -> - %% Atleast one ssloption() is set + %% Atleast one ssl_option() is set ct:log("Info ~p", [Info]), ok; false -> diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index b0bb77c598..d050812208 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -166,10 +166,10 @@ ignore_hassign_extension_pre_tls_1_2(Config) -> CertFile = proplists:get_value(certfile, Opts), [{_, Cert, _}] = ssl_test_lib:pem_to_der(CertFile), HashSigns = #hash_sign_algos{hash_sign_algos = [{sha512, rsa}, {sha, dsa}]}, - {sha512, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, {3,3}), + {sha512, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,3}), {3,3}), %%% Ignore - {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, {3,2}), - {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, {3,0}). + {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,2}), {3,2}), + {md5sha, rsa} = ssl_handshake:select_hashsign(HashSigns, Cert, ecdhe_rsa, tls_v1:default_signature_algs({3,0}), {3,0}). is_supported(Hash) -> Algos = crypto:supports(), diff --git a/lib/stdlib/src/filename.erl b/lib/stdlib/src/filename.erl index ca85fb080d..c4586171ca 100644 --- a/lib/stdlib/src/filename.erl +++ b/lib/stdlib/src/filename.erl @@ -1068,24 +1068,43 @@ basedir_darwin(Type) -> %% user_cache %LOCALAPPDATA%[/$author]/$appname[/$version]/Cache %% user_log %LOCALAPPDATA%[/$author]/$appname[/$version]/Logs +-define(basedir_windows_user_data, "Local"). +-define(basedir_windows_user_config, "Roaming"). +-define(basedir_windows_user_cache, "Local"). %% Cache is added later +-define(basedir_windows_user_log, "Local"). %% Logs is added later + basedir_windows(Type) -> %% If LOCALAPPDATA is not defined we are likely on an %% XP machine. Use APPDATA instead. - AppData = basedir_windows_appdata(), - case Type of - user_data -> getenv("LOCALAPPDATA", AppData); - user_config -> AppData; - user_cache -> getenv("LOCALAPPDATA", AppData); - user_log -> getenv("LOCALAPPDATA", AppData); - site_data -> []; - site_config -> [] + case basedir_windows_appdata() of + noappdata -> + %% No AppData is set + %% Probably running MSYS + case Type of + user_data -> basedir_join_home(?basedir_windows_user_data); + user_config -> basedir_join_home(?basedir_windows_user_config); + user_cache -> basedir_join_home(?basedir_windows_user_cache); + user_log -> basedir_join_home(?basedir_windows_user_log); + site_data -> []; + site_config -> [] + end; + {ok, AppData} -> + case Type of + user_data -> getenv("LOCALAPPDATA", AppData); + user_config -> AppData; + user_cache -> getenv("LOCALAPPDATA", AppData); + user_log -> getenv("LOCALAPPDATA", AppData); + site_data -> []; + site_config -> [] + end end. basedir_windows_appdata() -> case os:getenv("APPDATA") of Invalid when Invalid =:= false orelse Invalid =:= [] -> - erlang:error(noappdata); - Val -> Val + noappdata; + Val -> + {ok, Val} end. %% basedir aux diff --git a/lib/stdlib/src/otp_internal.erl b/lib/stdlib/src/otp_internal.erl index 6f546da7b8..052dffdbfd 100644 --- a/lib/stdlib/src/otp_internal.erl +++ b/lib/stdlib/src/otp_internal.erl @@ -47,18 +47,6 @@ obsolete(Module, Name, Arity) -> obsolete_1(net, _, _) -> {deprecated, "module 'net' obsolete; use 'net_adm'"}; -obsolete_1(erl_internal, builtins, 0) -> - {deprecated, {erl_internal, bif, 2}}; - -obsolete_1(erl_eval, seq, 2) -> - {deprecated, {erl_eval, exprs, 2}}; -obsolete_1(erl_eval, seq, 3) -> - {deprecated, {erl_eval, exprs, 3}}; -obsolete_1(erl_eval, arg_list, 2) -> - {deprecated, {erl_eval, expr_list, 2}}; -obsolete_1(erl_eval, arg_list, 3) -> - {deprecated, {erl_eval, expr_list, 3}}; - obsolete_1(erlang, hash, 2) -> {deprecated, {erlang, phash2, 2}}; @@ -70,10 +58,6 @@ obsolete_1(erlang, now, 0) -> obsolete_1(calendar, local_time_to_universal_time, 1) -> {deprecated, {calendar, local_time_to_universal_time_dst, 1}}; -obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 -> - {deprecated, {rpc, multi_server_call, A}}; - - %% *** CRYPTO add in R16B01 *** obsolete_1(crypto, md4, 1) -> @@ -391,106 +375,10 @@ obsolete_1(http, cookie_header, 2) -> {removed,{httpc,cookie_header,2},"R15B" obsolete_1(http, stream_next, 1) -> {removed,{httpc,stream_next,1},"R15B"}; obsolete_1(http, default_profile, 0) -> {removed,{httpc,default_profile,0},"R15B"}; -obsolete_1(httpd, start, 0) -> {removed,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, start, 1) -> {removed,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, start_link, 0) -> {removed,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, start_link, 1) -> {removed,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, start_child, 0) -> {removed,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, start_child, 1) -> {removed,{inets,start,[2,3]},"R14B"}; -obsolete_1(httpd, stop, 0) -> {removed,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop, 1) -> {removed,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop, 2) -> {removed,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop_child, 0) -> {removed,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop_child, 1) -> {removed,{inets,stop,2},"R14B"}; -obsolete_1(httpd, stop_child, 2) -> {removed,{inets,stop,2},"R14B"}; -obsolete_1(httpd, restart, 0) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, restart, 1) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, restart, 2) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 0) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 1) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 2) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 3) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, block, 4) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, unblock, 0) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, unblock, 1) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd, unblock, 2) -> {removed,{httpd,reload_config,2},"R14B"}; -obsolete_1(httpd_util, key1search, 2) -> {removed,{proplists,get_value,2},"R13B"}; -obsolete_1(httpd_util, key1search, 3) -> {removed,{proplists,get_value,3},"R13B"}; -obsolete_1(ftp, open, 3) -> {removed,{inets,start,[2,3]},"R14B"}; -obsolete_1(ftp, force_active, 1) -> {removed,{inets,start,[2,3]},"R14B"}; - -%% Added in R12B-4. -obsolete_1(ssh_cm, connect, A) when 1 =< A, A =< 3 -> - {removed,{ssh,connect,A},"R14B"}; -obsolete_1(ssh_cm, listen, A) when 2 =< A, A =< 4 -> - {removed,{ssh,daemon,A},"R14B"}; -obsolete_1(ssh_cm, stop_listener, 1) -> - {removed,{ssh,stop_listener,[1,2]},"R14B"}; -obsolete_1(ssh_cm, session_open, A) when A =:= 2; A =:= 4 -> - {removed,{ssh_connection,session_channel,A},"R14B"}; -obsolete_1(ssh_cm, direct_tcpip, A) when A =:= 6; A =:= 8 -> - {removed,{ssh_connection,direct_tcpip,A},"R14B"}; -obsolete_1(ssh_cm, tcpip_forward, 3) -> - {removed,{ssh_connection,tcpip_forward,3},"R14B"}; -obsolete_1(ssh_cm, cancel_tcpip_forward, 3) -> - {removed,{ssh_connection,cancel_tcpip_forward,3},"R14B"}; -obsolete_1(ssh_cm, open_pty, A) when A =:= 3; A =:= 7; A =:= 9 -> - {removed,{ssh_connection,open_pty,A},"R14B"}; -obsolete_1(ssh_cm, setenv, 5) -> - {removed,{ssh_connection,setenv,5},"R14B"}; -obsolete_1(ssh_cm, shell, 2) -> - {removed,{ssh_connection,shell,2},"R14B"}; -obsolete_1(ssh_cm, exec, 4) -> - {removed,{ssh_connection,exec,4},"R14B"}; -obsolete_1(ssh_cm, subsystem, 4) -> - {removed,{ssh_connection,subsystem,4},"R14B"}; -obsolete_1(ssh_cm, winch, A) when A =:= 4; A =:= 6 -> - {removed,{ssh_connection,window_change,A},"R14B"}; -obsolete_1(ssh_cm, signal, 3) -> - {removed,{ssh_connection,signal,3},"R14B"}; -obsolete_1(ssh_cm, attach, A) when A =:= 2; A =:= 3 -> - {removed,"no longer useful; removed in R14B"}; -obsolete_1(ssh_cm, detach, 2) -> - {removed,"no longer useful; removed in R14B"}; -obsolete_1(ssh_cm, set_user_ack, 4) -> - {removed,"no longer useful; removed in R14B"}; -obsolete_1(ssh_cm, adjust_window, 3) -> - {removed,{ssh_connection,adjust_window,3},"R14B"}; -obsolete_1(ssh_cm, close, 2) -> - {removed,{ssh_connection,close,2},"R14B"}; -obsolete_1(ssh_cm, stop, 1) -> - {removed,{ssh,close,1},"R14B"}; -obsolete_1(ssh_cm, send_eof, 2) -> - {removed,{ssh_connection,send_eof,2},"R14B"}; -obsolete_1(ssh_cm, send, A) when A =:= 3; A =:= 4 -> - {removed,{ssh_connection,send,A},"R14B"}; -obsolete_1(ssh_cm, send_ack, A) when 3 =< A, A =< 5 -> - {removed,{ssh_connection,send,[3,4]},"R14B"}; -obsolete_1(ssh_ssh, connect, A) when 1 =< A, A =< 3 -> - {removed,{ssh,shell,A},"R14B"}; -obsolete_1(ssh_sshd, listen, A) when 0 =< A, A =< 3 -> - {removed,{ssh,daemon,[1,2,3]},"R14B"}; -obsolete_1(ssh_sshd, stop, 1) -> - {removed,{ssh,stop_listener,1},"R14B"}; - %% Added in R13A. obsolete_1(regexp, _, _) -> {removed, "removed in R15; use the re module instead"}; -obsolete_1(lists, flat_length, 1) -> - {removed,{lists,flatlength,1},"R14"}; - -obsolete_1(ssh_sftp, connect, A) when 1 =< A, A =< 3 -> - {removed,{ssh_sftp,start_channel,A},"R14B"}; -obsolete_1(ssh_sftp, stop, 1) -> - {removed,{ssh_sftp,stop_channel,1},"R14B"}; - -%% Added in R13B01. -obsolete_1(ssl_pkix, decode_cert_file, A) when A =:= 1; A =:= 2 -> - {removed,"removed in R14A; use public_key:pem_to_der/1 and public_key:pkix_decode_cert/2 instead"}; -obsolete_1(ssl_pkix, decode_cert, A) when A =:= 1; A =:= 2 -> - {removed,{public_key,pkix_decode_cert,2},"R14A"}; - %% Added in R13B04. obsolete_1(erlang, concat_binary, 1) -> {removed,{erlang,list_to_binary,1},"R15B"}; @@ -649,8 +537,12 @@ obsolete_1(random, _, _) -> obsolete_1(code, rehash, 0) -> {deprecated, "deprecated because the code path cache feature has been removed"}; +%% Removed in OTP 19. + obsolete_1(overload, _, _) -> {removed, "removed in OTP 19"}; +obsolete_1(rpc, safe_multi_server_call, A) when A =:= 2; A =:= 3 -> + {removed, {rpc, multi_server_call, A}}; obsolete_1(_, _, _) -> no. diff --git a/lib/stdlib/test/error_logger_h_SUITE.erl b/lib/stdlib/test/error_logger_h_SUITE.erl index 3ae73085e8..2a34c7764f 100644 --- a/lib/stdlib/test/error_logger_h_SUITE.erl +++ b/lib/stdlib/test/error_logger_h_SUITE.erl @@ -65,11 +65,18 @@ logfile(Config) -> error_logger:logfile(close), analyse_events(Log, Ev, [AtNode], unlimited), - [] = [{X, file:pid2name(X)} || X <- processes(), Data <- [process_info(X, [current_function])], - Data =/= undefined, - element(1, element(2, lists:keyfind(current_function, 1, Data))) - =:= file_io_server, - file:pid2name(X) =:= {ok, Log}], + %% Make sure that the file_io_server process has been stopped + [] = lists:filtermap( + fun(X) -> + case {process_info(X, [current_function]), + file:pid2name(X)} of + {[{current_function, {file_io_server, _, _}}], + {ok,P2N = Log}} -> + {true, {X, P2N}}; + _ -> + false + end + end, processes()), test_server:stop_node(Node), @@ -112,7 +119,7 @@ tty(Config) -> do_one_tty(Log, Ev, unlimited), Pa = "-pa " ++ filename:dirname(code:which(?MODULE)), - {ok,Node} = start_node(logfile, Pa), + {ok,Node} = start_node(tty, Pa), tty_log_open(Log), ok = rpc:call(Node, erlang, apply, [fun gen_events/1,[Ev]]), tty_log_close(), diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index 0e3c72b92b..15e3142408 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -2497,9 +2497,10 @@ rename_unnamed_do(Opts) -> %% Rename a table with many fixations, and at the same time delete it. evil_rename(Config) when is_list(Config) -> - evil_rename_1(old_hash, new_hash, [public,named_table]), EtsMem = etsmem(), + evil_rename_1(old_hash, new_hash, [public,named_table]), evil_rename_1(old_tree, new_tree, [public,ordered_set,named_table]), + wait_for_test_procs(true), verify_etsmem(EtsMem). evil_rename_1(Old, New, Flags) -> @@ -2540,7 +2541,8 @@ crazy_fixtable_wait(N, Dracula) -> crazy_fixtable_1(0, _) -> ok; crazy_fixtable_1(N, Fun) -> - spawn_link(Fun), + %%FIXME my_spawn_link(Fun), + my_spawn_link(Fun), crazy_fixtable_1(N-1, Fun). evil_creater_destroyer() -> @@ -5752,10 +5754,11 @@ spawn_logger(Procs, FailedMemchecks) -> after 0 -> case Kill of true -> exit(Proc, kill); - _ -> ok + _ -> + erlang:display({"Waiting for 'DOWN' from", Proc, + process_info(Proc), + pid_status(Proc)}) end, - erlang:display({"Waiting for 'DOWN' from", Proc, - process_info(Proc), pid_status(Proc)}), receive {'DOWN', Mon, _, _, _} -> ok diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl index f5c559eb2c..d6bb002b5f 100644 --- a/lib/stdlib/test/gen_fsm_SUITE.erl +++ b/lib/stdlib/test/gen_fsm_SUITE.erl @@ -391,6 +391,12 @@ abnormal1(Config) when is_list(Config) -> delayed = gen_fsm:sync_send_event(my_fsm, {delayed_answer,1}, 100), {'EXIT',{timeout,_}} = (catch gen_fsm:sync_send_event(my_fsm, {delayed_answer,10}, 1)), + receive + Msg -> + %% Ignore the delayed answer from the server. + io:format("Delayed message: ~p", [Msg]) + end, + [] = get_messages(), ok. @@ -971,7 +977,7 @@ init(stop) -> init(stop_shutdown) -> {stop, shutdown}; init(sleep) -> - ct:sleep(1000), + timer:sleep(1000), {ok, idle, data}; init({timeout, T}) -> {ok, idle, state, T}; @@ -1004,7 +1010,7 @@ idle(_, Data) -> idle({connect, _Pid}, _From, Data) -> {reply, accept, wfor_conf, Data}; idle({delayed_answer, T}, _From, Data) -> - ct:sleep(T), + timer:sleep(T), {reply, delayed, idle, Data}; idle(badreturn, _From, _Data) -> badreturn; diff --git a/lib/wx/api_gen/wx_gen_erl.erl b/lib/wx/api_gen/wx_gen_erl.erl index 118942ba74..2e14fd272d 100644 --- a/lib/wx/api_gen/wx_gen_erl.erl +++ b/lib/wx/api_gen/wx_gen_erl.erl @@ -1089,7 +1089,7 @@ gen_enums_ints() -> %% open_write("../include/wx.hrl"), opened in gen_event_recs w("~n%% Hardcoded Records~n", []), w("-record(wxMouseState, {x :: integer(), y :: integer(),~n" - " leftDown :: boolean(), middleDown :: boolean, rightDown :: boolean, ~n" + " leftDown :: boolean(), middleDown :: boolean(), rightDown :: boolean(), ~n" " controlDown :: boolean(), shiftDown :: boolean(),~n" " altDown :: boolean(), metaDown :: boolean(), cmdDown :: boolean()~n" " }).~n", []), diff --git a/lib/wx/api_gen/wxapi.conf b/lib/wx/api_gen/wxapi.conf index be05610a8a..4f7223e147 100644 --- a/lib/wx/api_gen/wxapi.conf +++ b/lib/wx/api_gen/wxapi.conf @@ -844,7 +844,7 @@ 'GetItemSpacing','GetItemState', 'GetItemText','GetItemTextColour', 'GetNextItem','GetSelectedItemCount','GetTextColour','GetTopItem', - 'GetViewRect',{'HitTest',[{"pSubItem",nowhere}, {"flags", in}]}, + 'GetViewRect',{'HitTest',[{"pSubItem",out}]}, 'InsertColumn','InsertItem', %%'OnGetItemAttr', 'OnGetItemImage','OnGetItemText', 'RefreshItem','RefreshItems','ScrollList', diff --git a/lib/wx/c_src/gen/wxe_funcs.cpp b/lib/wx/c_src/gen/wxe_funcs.cpp index 03b0baf875..059cee59f4 100644 --- a/lib/wx/c_src/gen/wxe_funcs.cpp +++ b/lib/wx/c_src/gen/wxe_funcs.cpp @@ -15631,14 +15631,18 @@ case wxListCtrl_GetViewRect: { // wxListCtrl::GetViewRect break; } case wxListCtrl_HitTest: { // wxListCtrl::HitTest + int flags; + long pSubItem; 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,&pSubItem); rt.addInt(Result); + rt.addInt(flags); + rt.addInt(pSubItem); + rt.addTupleCount(3); break; } case wxListCtrl_InsertColumn_2: { // wxListCtrl::InsertColumn diff --git a/lib/wx/include/wx.hrl b/lib/wx/include/wx.hrl index 333ceca50c..44df57898a 100644 --- a/lib/wx/include/wx.hrl +++ b/lib/wx/include/wx.hrl @@ -353,7 +353,7 @@ %% Hardcoded Records -record(wxMouseState, {x :: integer(), y :: integer(), - leftDown :: boolean(), middleDown :: boolean, rightDown :: boolean, + leftDown :: boolean(), middleDown :: boolean(), rightDown :: boolean(), controlDown :: boolean(), shiftDown :: boolean(), altDown :: boolean(), metaDown :: boolean(), cmdDown :: boolean() }). diff --git a/lib/wx/src/gen/wxListCtrl.erl b/lib/wx/src/gen/wxListCtrl.erl index 6750931fdb..ed997710c8 100644 --- a/lib/wx/src/gen/wxListCtrl.erl +++ b/lib/wx/src/gen/wxListCtrl.erl @@ -38,7 +38,7 @@ 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/3,insertColumn/3,insertColumn/4, + getTopItem/1,getViewRect/1,hitTest/2,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, @@ -516,13 +516,14 @@ getViewRect(#wx_ref{type=ThisT,ref=ThisRef}) -> <<ThisRef:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlistctrl.html#wxlistctrlhittest">external documentation</a>. --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) -> +-spec hitTest(This, Point) -> Result when + Result ::{Res ::integer(), Flags::integer(), PSubItem::integer()}, + This::wxListCtrl(), Point::{X::integer(), Y::integer()}. +hitTest(#wx_ref{type=ThisT,ref=ThisRef},{PointX,PointY}) + when is_integer(PointX),is_integer(PointY) -> ?CLASS(ThisT,wxListCtrl), wxe_util:call(?wxListCtrl_HitTest, - <<ThisRef:32/?UI,PointX:32/?UI,PointY:32/?UI,Flags:32/?UI>>). + <<ThisRef:32/?UI,PointX:32/?UI,PointY:32/?UI>>). %% @doc See <a href="http://www.wxwidgets.org/manuals/2.8.12/wx_wxlistctrl.html#wxlistctrlinsertcolumn">external documentation</a>. %% <br /> Also:<br /> diff --git a/lib/wx/src/gen/wxe_debug.hrl b/lib/wx/src/gen/wxe_debug.hrl index a2462a6fb2..2ec485cd52 100644 --- a/lib/wx/src/gen/wxe_debug.hrl +++ b/lib/wx/src/gen/wxe_debug.hrl @@ -1544,7 +1544,7 @@ wxdebug_table() -> {1695, {wxListCtrl, getTextColour, 0}}, {1696, {wxListCtrl, getTopItem, 0}}, {1697, {wxListCtrl, getViewRect, 0}}, - {1698, {wxListCtrl, hitTest, 2}}, + {1698, {wxListCtrl, hitTest, 3}}, {1699, {wxListCtrl, insertColumn_2, 2}}, {1700, {wxListCtrl, insertColumn_3, 3}}, {1701, {wxListCtrl, insertItem_1, 1}}, |