aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/asn1/test/asn1_SUITE.erl22
-rw-r--r--lib/common_test/src/ct_conn_log_h.erl2
-rw-r--r--lib/common_test/src/ct_framework.erl7
-rw-r--r--lib/common_test/src/ct_netconfc.erl13
-rw-r--r--lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl3
-rw-r--r--lib/diameter/src/base/diameter_service.erl508
-rw-r--r--lib/diameter/src/base/diameter_traffic.erl7
-rw-r--r--lib/eldap/doc/src/eldap.xml100
-rw-r--r--lib/eldap/src/eldap.erl253
-rw-r--r--lib/eldap/test/eldap_basic_SUITE.erl94
-rw-r--r--lib/eldap/vsn.mk2
-rw-r--r--lib/erl_interface/configure.in2
-rw-r--r--lib/erl_interface/doc/src/ei.xml8
-rw-r--r--lib/erl_interface/doc/src/erl_call.xml4
-rw-r--r--lib/erl_interface/doc/src/erl_eterm.xml4
-rw-r--r--lib/hipe/icode/hipe_icode_primops.erl39
-rw-r--r--lib/inets/src/http_server/httpd_request_handler.erl9
-rw-r--r--lib/inets/src/tftp/tftp_engine.erl36
-rw-r--r--lib/inets/test/tftp_SUITE.erl37
-rw-r--r--lib/kernel/doc/src/heart.xml63
-rw-r--r--lib/kernel/examples/uds_dist/c_src/uds_drv.c12
-rw-r--r--lib/kernel/src/erl_epmd.erl18
-rw-r--r--lib/kernel/src/gen_tcp.erl3
-rw-r--r--lib/kernel/src/heart.erl182
-rw-r--r--lib/kernel/src/kernel.appup.src4
-rw-r--r--lib/kernel/test/heart_SUITE.erl67
-rw-r--r--lib/observer/test/crashdump_viewer_SUITE.erl19
-rw-r--r--lib/public_key/src/pubkey_pem.erl2
-rw-r--r--lib/public_key/test/public_key_SUITE.erl11
-rw-r--r--lib/runtime_tools/c_src/trace_file_drv.c4
-rw-r--r--lib/runtime_tools/c_src/trace_ip_drv.c15
-rw-r--r--lib/sasl/src/sasl.appup.src4
-rw-r--r--lib/ssh/doc/src/ssh_sftp.xml6
-rw-r--r--lib/ssh/test/ssh_algorithms_SUITE.erl28
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl27
-rw-r--r--lib/ssh/test/ssh_benchmark_SUITE.erl5
-rw-r--r--lib/ssh/test/ssh_connection_SUITE.erl15
-rw-r--r--lib/ssh/test/ssh_options_SUITE.erl16
-rw-r--r--lib/ssh/test/ssh_protocol_SUITE.erl23
-rw-r--r--lib/ssh/test/ssh_renegotiate_SUITE.erl16
-rw-r--r--lib/ssh/test/ssh_sftp_SUITE.erl24
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl28
-rw-r--r--lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl31
-rw-r--r--lib/ssh/test/ssh_sup_SUITE.erl4
-rw-r--r--lib/ssh/test/ssh_test_lib.erl9
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl20
-rw-r--r--lib/ssh/test/ssh_upgrade_SUITE.erl25
-rw-r--r--lib/ssh/vsn.mk2
-rw-r--r--lib/ssl/doc/src/ssl.xml8
-rw-r--r--lib/ssl/doc/src/ssl_distribution.xml23
-rw-r--r--lib/ssl/examples/src/client_server.erl7
-rw-r--r--lib/ssl/src/Makefile1
-rw-r--r--lib/ssl/src/inet6_tls_dist.erl46
-rw-r--r--lib/ssl/src/inet_tls_dist.erl95
-rw-r--r--lib/ssl/src/ssl.app.src1
-rw-r--r--lib/ssl/src/ssl.erl29
-rw-r--r--lib/ssl/src/ssl_handshake.erl9
-rw-r--r--lib/ssl/src/ssl_tls_dist_proxy.erl30
-rw-r--r--lib/ssl/test/ssl_certificate_verify_SUITE.erl119
-rw-r--r--lib/ssl/test/ssl_crl_SUITE.erl82
-rw-r--r--lib/stdlib/src/maps.erl14
-rw-r--r--lib/stdlib/src/stdlib.appup.src4
-rw-r--r--lib/stdlib/test/win32reg_SUITE.erl49
-rw-r--r--lib/test_server/src/test_server.erl2
-rw-r--r--lib/test_server/src/test_server_internal.hrl1
-rw-r--r--lib/test_server/src/test_server_node.erl13
-rw-r--r--lib/test_server/src/test_server_sup.erl3
-rw-r--r--lib/wx/configure.in8
68 files changed, 1648 insertions, 729 deletions
diff --git a/lib/asn1/test/asn1_SUITE.erl b/lib/asn1/test/asn1_SUITE.erl
index ce87fd3180..6dea1787a8 100644
--- a/lib/asn1/test/asn1_SUITE.erl
+++ b/lib/asn1/test/asn1_SUITE.erl
@@ -628,12 +628,22 @@ ber_other(Config, Rule, Opts) ->
der(Config) ->
asn1_test_lib:compile_all(ber_modules(), Config, [der]).
-module_test(M, Config, Rule, Opts) ->
- asn1_test_lib:compile(M, Config, [Rule|Opts]),
- case asn1ct:test(list_to_atom(M), [{i, ?config(case_dir, Config)}]) of
- ok -> ok;
- Error ->
- erlang:error({test_failed, M, Opts, Error})
+module_test(M0, Config, Rule, Opts) ->
+ asn1_test_lib:compile(M0, Config, [Rule|Opts]),
+ case list_to_atom(M0) of
+ 'LDAP' ->
+ %% Because of the recursive definition of 'Filter' in
+ %% the LDAP module, the construction of a sample
+ %% value for 'Filter' is not guaranteed to terminate.
+ ok;
+ M ->
+ TestOpts = [{i, ?config(case_dir, Config)}],
+ case asn1ct:test(M, TestOpts) of
+ ok ->
+ ok;
+ Error ->
+ erlang:error({test_failed, M, Opts, Error})
+ end
end.
diff --git a/lib/common_test/src/ct_conn_log_h.erl b/lib/common_test/src/ct_conn_log_h.erl
index f7615fdc14..93f358462d 100644
--- a/lib/common_test/src/ct_conn_log_h.erl
+++ b/lib/common_test/src/ct_conn_log_h.erl
@@ -117,7 +117,7 @@ write_report(Time,#conn_log{module=ConnMod}=Info,Data,GL,State) ->
ok;
{LogType,Fd} ->
case format_data(ConnMod,LogType,Data) of
- [] ->
+ [] when Info#conn_log.action==send; Info#conn_log.action==recv ->
ok;
FormattedData ->
io:format(Fd,"~n~ts~ts~ts",[format_head(ConnMod,LogType,Time),
diff --git a/lib/common_test/src/ct_framework.erl b/lib/common_test/src/ct_framework.erl
index f792269c41..8fb7a03bb0 100644
--- a/lib/common_test/src/ct_framework.erl
+++ b/lib/common_test/src/ct_framework.erl
@@ -28,7 +28,7 @@
-export([init_tc/3, end_tc/3, end_tc/4, get_suite/2, get_all_cases/1]).
-export([report/2, warn/1, error_notification/4]).
--export([get_logopts/0, format_comment/1, get_html_wrapper/4]).
+-export([get_log_dir/0, get_logopts/0, format_comment/1, get_html_wrapper/4]).
-export([error_in_suite/1, init_per_suite/1, end_per_suite/1,
init_per_group/2, end_per_group/2]).
@@ -1480,3 +1480,8 @@ get_html_wrapper(TestName, PrintLabel, Cwd, TableCols) ->
get_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding) ->
ct_logs:get_ts_html_wrapper(TestName, PrintLabel, Cwd, TableCols, Encoding).
+
+%%%-----------------------------------------------------------------
+%%% @spec get_log_dir() -> {ok,LogDir}
+get_log_dir() ->
+ ct_logs:get_log_dir(true).
diff --git a/lib/common_test/src/ct_netconfc.erl b/lib/common_test/src/ct_netconfc.erl
index 3da1115c76..8812514ad9 100644
--- a/lib/common_test/src/ct_netconfc.erl
+++ b/lib/common_test/src/ct_netconfc.erl
@@ -234,7 +234,6 @@
%% Internal defines
%%----------------------------------------------------------------------
-define(APPLICATION,?MODULE).
--define(VALID_SSH_OPTS,[user, password, user_dir]).
-define(DEFAULT_STREAM,"NETCONF").
-define(error(ConnName,Report),
@@ -1257,13 +1256,11 @@ check_options([{port,Port}|T], Host, _, #options{} = Options) ->
check_options([{timeout, Timeout}|T], Host, Port, Options)
when is_integer(Timeout); Timeout==infinity ->
check_options(T, Host, Port, Options#options{timeout = Timeout});
-check_options([{X,_}=Opt|T], Host, Port, #options{ssh=SshOpts}=Options) ->
- case lists:member(X,?VALID_SSH_OPTS) of
- true ->
- check_options(T, Host, Port, Options#options{ssh=[Opt|SshOpts]});
- false ->
- {error, {invalid_option, Opt}}
- end.
+check_options([{timeout, _} = Opt|_T], _Host, _Port, _Options) ->
+ {error, {invalid_option, Opt}};
+check_options([Opt|T], Host, Port, #options{ssh=SshOpts}=Options) ->
+ %% Option verified by ssh
+ check_options(T, Host, Port, Options#options{ssh=[Opt|SshOpts]}).
%%%-----------------------------------------------------------------
set_request_timer(infinity) ->
diff --git a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
index 49b02d2bba..9d4c798795 100644
--- a/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
+++ b/lib/common_test/test/ct_netconfc_SUITE_data/netconfc1_SUITE.erl
@@ -332,7 +332,8 @@ invalid_opt(Config) ->
Opts1 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{timeout,invalidvalue}],
{error,{invalid_option,{timeout,invalidvalue}}} = ct_netconfc:open(Opts1),
Opts2 = ?DEFAULT_SSH_OPTS(DataDir) ++ [{some_other_opt,true}],
- {error,{invalid_option,{some_other_opt,true}}} = ct_netconfc:open(Opts2),
+ {error,{ssh,could_not_connect_to_server,{options,_}}} =
+ ct_netconfc:open(Opts2),
ok.
timeout_close_session(Config) ->
diff --git a/lib/diameter/src/base/diameter_service.erl b/lib/diameter/src/base/diameter_service.erl
index d49176ec3e..66c4a7582f 100644
--- a/lib/diameter/src/base/diameter_service.erl
+++ b/lib/diameter/src/base/diameter_service.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2010-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2010-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.
@@ -83,16 +83,8 @@
-define(DEFAULT_TC, 30000). %% RFC 3588 ch 2.1
-define(RESTART_TC, 1000). %% if restart was this recent
-%% Used to be able to swap this with anything else dict-like but now
-%% rely on the fact that a service's #state{} record does not change
-%% in storing in it ?STATE table and not always going through the
-%% service process. In particular, rely on the fact that operations on
-%% a ?Dict don't change the handle to it.
--define(Dict, diameter_dict).
-
-%% Maintains state in a table. In contrast to previously, a service's
-%% stat is not constant and is accessed outside of the service
-%% process.
+%% Maintain state in a table since a service's state is accessed
+%% outside of the service process.
-define(STATE_TABLE, ?MODULE).
%% The default sequence mask.
@@ -117,12 +109,11 @@
service :: #diameter_service{},
watchdogT = ets_new(watchdogs) %% #watchdog{} at start
:: ets:tid(),
- peerT = ets_new(peers) %% #peer{pid = TPid} at okay/reopen
- :: ets:tid(),
- shared_peers = ?Dict:new() %% Alias -> [{TPid, Caps}, ...]
- :: ets:tid(),
- local_peers = ?Dict:new() %% Alias -> [{TPid, Caps}, ...]
- :: ets:tid(),
+ peerT, %% undefined in new code, but remain for upgrade
+ shared_peers, %% reasons. Replaced by local/remote.
+ local_peers, %%
+ local :: {ets:tid(), ets:tid(), ets:tid()},
+ remote :: {ets:tid(), ets:tid(), ets:tid()},
monitor = false :: false | pid(), %% process to die with
options
:: [{sequence, diameter:sequence()} %% sequence mask
@@ -150,10 +141,12 @@
%% diameter_peer_fsm.
-record(peer,
{pid :: pid(),
- apps :: [{0..16#FFFFFFFF, diameter:app_alias()}], %% {Id, Alias}
+ apps :: [{0..16#FFFFFFFF, diameter:app_alias()}] %% {Id, Alias}
+ | [diameter:app_alias()], %% remote
caps :: #diameter_caps{},
- started = diameter_lib:now(), %% at process start
- watchdog :: pid()}). %% key into watchdogT
+ started = diameter_lib:now(), %% at process start or sharing
+ watchdog :: pid() %% key into watchdogT
+ | undefined}). %% undefined if remote
%% ---------------------------------------------------------------------------
%% # start/1
@@ -181,7 +174,7 @@ stop(SvcName) ->
end.
stop(ok, Pid) ->
- MRef = erlang:monitor(process, Pid),
+ MRef = monitor(process, Pid),
receive {'DOWN', MRef, process, _, _} -> ok end;
stop(No, _) ->
No.
@@ -495,6 +488,9 @@ transition({service, Pid}, S) ->
transition({peer, TPid, Aliases, Caps}, S) ->
remote_peer_up(TPid, Aliases, Caps, S),
ok;
+transition({peer, TPid}, S) ->
+ remote_peer_down(TPid, S),
+ ok;
%% Remote peer process has died.
transition({'DOWN', _, process, TPid, _}, S) ->
@@ -514,7 +510,7 @@ transition(Req, S) ->
%% # terminate/2
%% ---------------------------------------------------------------------------
-terminate(Reason, #state{service_name = Name, peerT = PeerT} = S) ->
+terminate(Reason, #state{service_name = Name, local = {PeerT, _, _}} = S) ->
send_event(Name, stop),
ets:delete(?STATE_TABLE, Name),
@@ -536,23 +532,81 @@ terminate(Reason, #state{service_name = Name, peerT = PeerT} = S) ->
%% # code_change/3
%% ---------------------------------------------------------------------------
-code_change(FromVsn,
- #state{service_name = SvcName,
- service = #diameter_service{applications = Apps}}
- = S,
- Extra) ->
- lists:foreach(fun(A) ->
- code_change(FromVsn, SvcName, Extra, A)
- end,
- Apps),
+code_change(_FromVsn, #state{} = S, _Extra) ->
+ {ok, S};
+
+%% Don't support downgrade since we won't in appup.
+code_change({down = T, _}, _, _Extra) ->
+ {error, T};
+
+%% Upgrade local/shared peers dicts populated in old code. Don't
+code_change(_FromVsn, S0, _Extra) ->
+ {state, Id, SvcName, Svc, WT, PeerT, SDict, LDict, Monitor, Opts}
+ = S0,
+
+ init_peers(LT = setelement(1, {PT, _, _} = init_peers(), PeerT),
+ fun({_,A}) -> A end),
+ init_peers(init_peers(RT = init_peers(), SDict),
+ fun(A) -> A end),
+
+ S = #state{id = Id,
+ service_name = SvcName,
+ service = Svc,
+ watchdogT = WT,
+ peerT = PT, %% empty
+ shared_peers = SDict,
+ local_peers = LDict,
+ local = LT,
+ remote = RT,
+ monitor = Monitor,
+ options = Opts},
+
+ %% Replacing the table entry and deleting the old shared tables
+ %% can make outgoing requests return {error, no_connection} until
+ %% everyone is running new code. Don't delete the tables to avoid
+ %% crashing request processes.
+ ets:delete_all_objects(SDict),
+ ets:delete_all_objects(LDict),
+ ets:insert(?STATE_TABLE, S),
{ok, S}.
-code_change(FromVsn, SvcName, Extra, #diameter_app{alias = Alias} = A) ->
- {ok, S} = cb(A, code_change, [FromVsn,
- mod_state(Alias),
- Extra,
- SvcName]),
- mod_state(Alias, S).
+%% init_peers/2
+
+%% Populate app and identity bags from a new-style #peer{} sets.
+init_peers({PeerT, _, _} = T, F)
+ when is_function(F) ->
+ ets:foldl(fun(#peer{pid = P, apps = As, caps = C}, N) ->
+ insert_peer(P, lists:map(F, As), C, T),
+ N+1
+ end,
+ 0,
+ PeerT);
+
+%% Populate #peer{} table given a shared peers dict.
+init_peers({PeerT, _, _}, SDict)
+ when is_integer(SDict) ->
+ dict:fold(fun(P, As, N) ->
+ ets:update_element(PeerT, P, {#peer.apps, As}),
+ N+1
+ end,
+ 0,
+ diameter_dict:fold(fun(A, Ps, D) ->
+ init_peers(A, Ps, PeerT, D)
+ end,
+ dict:new(),
+ SDict)).
+
+%% init_peers/4
+
+init_peers(App, TCs, PeerT, Dict) ->
+ lists:foldl(fun({P,C}, D) ->
+ ets:insert(PeerT, #peer{pid = P,
+ apps = [],
+ caps = C}),
+ dict:append(P, App, D)
+ end,
+ Dict,
+ TCs).
%% ===========================================================================
%% ===========================================================================
@@ -560,9 +614,6 @@ code_change(FromVsn, SvcName, Extra, #diameter_app{alias = Alias} = A) ->
unexpected(F, A, #state{service_name = Name}) ->
?UNEXPECTED(F, A ++ [Name]).
-cb(#diameter_app{module = [_|_] = M}, F, A) ->
- eval(M, F, A).
-
eval([M|X], F, A) ->
apply(M, F, A ++ X).
@@ -582,10 +633,6 @@ choose(false, _, X) -> X.
ets_new(Tbl) ->
ets:new(Tbl, [{keypos, 2}]).
-insert(Tbl, Rec) ->
- ets:insert(Tbl, Rec),
- Rec.
-
%% Using the process dictionary for the callback state was initially
%% just a way to make what was horrendous trace (big state record and
%% much else everywhere) somewhat more readable. There's not as much
@@ -686,6 +733,8 @@ cfg_acc({SvcName, #diameter_service{applications = Apps} = Rec, Opts},
lists:foreach(fun init_mod/1, Apps),
S = #state{service_name = SvcName,
service = Rec#diameter_service{pid = self()},
+ local = init_peers(),
+ remote = init_peers(),
monitor = mref(get_value(monitor, Opts)),
options = service_options(Opts)},
{S, Acc};
@@ -695,6 +744,13 @@ cfg_acc({_Ref, Type, _Opts} = T, {S, Acc})
Type == listen ->
{S, [T | Acc]}.
+init_peers() ->
+ {ets_new(caps), %% #peer{}
+ ets:new(apps, [bag]), %% {Alias, TPid}
+ ets:new(idents, [bag])}. %% {{host, OH} | {realm, OR} | {OR, OH},
+ %% Alias,
+ %% TPid}
+
service_options(Opts) ->
[{sequence, proplists:get_value(sequence, Opts, ?NOMASK)},
{share_peers, get_value(share_peers, Opts)},
@@ -711,7 +767,7 @@ service_options(Opts) ->
mref(false = No) ->
No;
mref(P) ->
- erlang:monitor(process, P).
+ monitor(process, P).
init_shared(#state{options = [_, _, {_,T} | _],
service_name = Svc}) ->
@@ -810,7 +866,7 @@ start(Ref, Type, Opts, State) ->
%% start/5
start(Ref, Type, Opts, N, #state{watchdogT = WatchdogT,
- peerT = PeerT,
+ local = {PeerT, _, _},
options = SvcOpts,
service_name = SvcName,
service = Svc0})
@@ -841,7 +897,7 @@ binary_caps(#diameter_service{capabilities = Caps} = Svc, false) ->
wd(Type, Ref, T, WatchdogT, Rec) ->
Pid = start_watchdog(Type, Ref, T),
- insert(WatchdogT, Rec#watchdog{pid = Pid}),
+ ets:insert(WatchdogT, Rec#watchdog{pid = Pid}),
Pid.
%% Note that the service record passed into the watchdog is the merged
@@ -898,8 +954,8 @@ accepted(Pid, _TPid, #state{watchdogT = WatchdogT} = S) ->
#watchdog{ref = Ref, type = accept = T, peer = false, options = Opts}
= Wd
= fetch(WatchdogT, Pid),
- insert(WatchdogT, Wd#watchdog{peer = true}),%% mark replacement as started
- start(Ref, T, Opts, S). %% start new watchdog
+ ets:insert(WatchdogT, Wd#watchdog{peer = true}),%% mark replacement started
+ start(Ref, T, Opts, S). %% start new watchdog
fetch(Tid, Key) ->
[T] = ets:lookup(Tid, Key),
@@ -925,13 +981,14 @@ watchdog(TPid, [], ?WD_SUSPECT, ?WD_OKAY, Wd, State) ->
#watchdog{peer = TPid} = Wd, %% assert
connection_up(Wd, State);
-%% Watchdog has an unresponsive connection.
+%% Watchdog has an unresponsive connection. Note that the peer table
+%% entry isn't removed until DOWN.
watchdog(TPid, [], ?WD_OKAY, ?WD_SUSPECT = To, Wd, State) ->
#watchdog{peer = TPid} = Wd, %% assert
watchdog_down(Wd, To, State);
%% Watchdog has lost its connection.
-watchdog(TPid, [], _, ?WD_DOWN = To, Wd, #state{peerT = PeerT} = S) ->
+watchdog(TPid, [], _, ?WD_DOWN = To, Wd, #state{local = {PeerT, _, _}} = S) ->
close(Wd),
watchdog_down(Wd, To, S),
ets:delete(PeerT, TPid);
@@ -940,7 +997,7 @@ watchdog(_, [], _, _, _, _) ->
ok.
watchdog_down(Wd, To, #state{watchdogT = WatchdogT} = S) ->
- insert(WatchdogT, Wd#watchdog{state = To}),
+ ets:insert(WatchdogT, Wd#watchdog{state = To}),
connection_down(Wd, To, S).
%% ---------------------------------------------------------------------------
@@ -952,14 +1009,14 @@ watchdog_down(Wd, To, #state{watchdogT = WatchdogT} = S) ->
connection_up({TPid, {Caps, SupportedApps, Pkt}},
#watchdog{pid = Pid}
= Wd,
- #state{peerT = PeerT}
+ #state{local = {PeerT, _, _}}
= S) ->
- Pr = #peer{pid = TPid,
- apps = SupportedApps,
- caps = Caps,
- watchdog = Pid},
- insert(PeerT, Pr),
- connection_up([Pkt], Wd#watchdog{peer = TPid}, Pr, S).
+ Rec = #peer{pid = TPid,
+ apps = SupportedApps,
+ caps = Caps,
+ watchdog = Pid},
+ ets:insert(PeerT, Rec),
+ connection_up([Pkt], Wd#watchdog{peer = TPid}, Rec, S).
%% ---------------------------------------------------------------------------
%% # reopen/3
@@ -969,22 +1026,23 @@ reopen({TPid, {Caps, SupportedApps, _Pkt}},
#watchdog{pid = Pid}
= Wd,
#state{watchdogT = WatchdogT,
- peerT = PeerT}) ->
- insert(PeerT, #peer{pid = TPid,
- apps = SupportedApps,
- caps = Caps,
- watchdog = Pid}),
- insert(WatchdogT, Wd#watchdog{state = ?WD_REOPEN,
- peer = TPid}).
+ local = {PeerT, _, _}}) ->
+ ets:insert(PeerT, #peer{pid = TPid,
+ apps = SupportedApps,
+ caps = Caps,
+ watchdog = Pid}),
+ ets:insert(WatchdogT, Wd#watchdog{state = ?WD_REOPEN,
+ peer = TPid}).
%% ---------------------------------------------------------------------------
%% # connection_up/2
%% ---------------------------------------------------------------------------
-%% Watchdog has recovered as suspect connection. Note that there has
+%% Watchdog has recovered a suspect connection. Note that there has
%% been no new capabilties exchange in this case.
-connection_up(#watchdog{peer = TPid} = Wd, #state{peerT = PeerT} = S) ->
+connection_up(#watchdog{peer = TPid} = Wd, #state{local = {PeerT, _, _}}
+ = S) ->
connection_up([], Wd, fetch(PeerT, TPid), S).
%% connection_up/4
@@ -995,23 +1053,23 @@ connection_up(Extra,
#peer{apps = SApps, caps = Caps}
= Pr,
#state{watchdogT = WatchdogT,
- local_peers = LDict,
+ local = LT,
service_name = SvcName,
service = #diameter_service{applications = Apps}}
= S) ->
- insert(WatchdogT, Wd#watchdog{state = ?WD_OKAY}),
+ ets:insert(WatchdogT, Wd#watchdog{state = ?WD_OKAY}),
diameter_traffic:peer_up(TPid),
- insert_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
+ local_peer_up(SApps, {TPid, Caps}, {SvcName, Apps}, LT),
report_status(up, Wd, Pr, S, Extra).
-insert_local_peer(SApps, T, LDict) ->
- lists:foldl(fun(A,D) -> ilp(A, T, D) end, LDict, SApps).
+local_peer_up(SApps, {TPid, Caps} = TC, SA, LT) ->
+ insert_peer(TPid, [A || {_,A} <- SApps], Caps, LT),
+ lists:foreach(fun(A) -> peer_up(A, TC, SA) end, SApps).
-ilp({Id, Alias}, {TC, SA}, LDict) ->
- init_conn(Id, Alias, TC, SA),
- ?Dict:append(Alias, TC, LDict).
+peer_up({Id, Alias}, TC, SA) ->
+ peer_up(Id, Alias, TC, SA).
-init_conn(Id, Alias, {TPid, _} = TC, {SvcName, Apps}) ->
+peer_up(Id, Alias, {TPid, _} = TC, {SvcName, Apps}) ->
#diameter_app{id = Id} %% assert
= App
= find_app(Alias, Apps),
@@ -1109,17 +1167,17 @@ connection_down(#watchdog{state = ?WD_OKAY,
= Pr,
#state{service_name = SvcName,
service = #diameter_service{applications = Apps},
- local_peers = LDict}
+ local = LT}
= S) ->
report_status(down, Wd, Pr, S, []),
- remove_local_peer(SApps, {{TPid, Caps}, {SvcName, Apps}}, LDict),
+ local_peer_down(SApps, {TPid, Caps}, {SvcName, Apps}, LT),
diameter_traffic:peer_down(TPid);
connection_down(#watchdog{state = ?WD_OKAY,
peer = TPid}
= Wd,
To,
- #state{peerT = PeerT}
+ #state{local = {PeerT, _, _}}
= S)
when is_atom(To) ->
connection_down(Wd, #peer{} = fetch(PeerT, TPid), S);
@@ -1127,15 +1185,14 @@ connection_down(#watchdog{state = ?WD_OKAY,
connection_down(#watchdog{}, _, _) ->
ok.
-remove_local_peer(SApps, T, LDict) ->
- lists:foldl(fun(A,D) -> rlp(A, T, D) end, LDict, SApps).
+local_peer_down(SApps, {TPid, _Caps} = TC, SA, LT) ->
+ delete_peer(TPid, LT),
+ lists:foreach(fun(A) -> peer_down(A, TC, SA) end, SApps).
-rlp({Id, Alias}, {TC, SA}, LDict) ->
- L = ?Dict:fetch(Alias, LDict),
- down_conn(Id, Alias, TC, SA),
- ?Dict:store(Alias, lists:delete(TC, L), LDict).
+peer_down({Id, Alias}, TC, SA) ->
+ peer_down(Id, Alias, TC, SA).
-down_conn(Id, Alias, TC, {SvcName, Apps}) ->
+peer_down(Id, Alias, TC, {SvcName, Apps}) ->
#diameter_app{id = Id} %% assert
= App
= find_app(Alias, Apps),
@@ -1160,7 +1217,7 @@ wd_down(#watchdog{peer = B}, _)
ok;
%% ... or maybe it has.
-wd_down(#watchdog{peer = TPid} = Wd, #state{peerT = PeerT} = S) ->
+wd_down(#watchdog{peer = TPid} = Wd, #state{local = {PeerT, _, _}} = S) ->
connection_down(Wd, ?WD_DOWN, S),
ets:delete(PeerT, TPid).
@@ -1368,19 +1425,37 @@ share_peer(up, Caps, Apps, TPid, #state{options = [_, {_,T} | _],
service_name = Svc}) ->
notify(T, Svc, {peer, TPid, [A || {_,A} <- Apps], Caps});
-share_peer(_, _, _, _, _) ->
- ok.
+share_peer(down, _Caps, _Apps, TPid, #state{options = [_, {_,T} | _],
+ service_name = Svc}) ->
+ notify(T, Svc, {peer, TPid}).
%% ---------------------------------------------------------------------------
%% # share_peers/2
%% ---------------------------------------------------------------------------
-share_peers(Pid, #state{options = [_, {_,T} | _], local_peers = PDict}) ->
- is_remote(Pid, T)
- andalso ?Dict:fold(fun(A,Ps,ok) -> sp(Pid, A, Ps), ok end, ok, PDict).
-
-sp(Pid, Alias, Peers) ->
- lists:foreach(fun({P,C}) -> Pid ! {peer, P, [Alias], C} end, Peers).
+share_peers(Pid, #state{options = [_, {_,SP} | _],
+ local = {PeerT, AppT, _}}) ->
+ is_remote(Pid, SP)
+ andalso ets:foldl(fun(T, N) -> N + sp(Pid, AppT, T) end,
+ 0,
+ PeerT).
+
+%% An entry in the peer table doesn't mean the watchdog state is OKAY,
+%% an entry in the app table does.
+
+sp(Pid, AppT, #peer{pid = TPid,
+ apps = [{_, Alias} | _] = Apps,
+ caps = Caps}) ->
+ Spec = [{{'$1', TPid},
+ [{'==', '$1', {const, Alias}}],
+ ['$_']}],
+ case ets:select(AppT, Spec, 1) of
+ '$end_of_table' ->
+ 0;
+ _ ->
+ Pid ! {peer, TPid, [A || {_,A} <- Apps], Caps},
+ 1
+ end.
is_remote(Pid, T) ->
Node = node(Pid),
@@ -1390,32 +1465,49 @@ is_remote(Pid, T) ->
%% # remote_peer_up/4
%% ---------------------------------------------------------------------------
-remote_peer_up(Pid, Aliases, Caps, #state{options = [_, _, {_,T} | _]} = S) ->
- is_remote(Pid, T)
- andalso rpu(Pid, Aliases, Caps, S).
+remote_peer_up(TPid, Aliases, Caps, #state{options = [_, _, {_,T} | _]} = S) ->
+ is_remote(TPid, T) andalso rpu(TPid, Aliases, Caps, S).
-rpu(Pid, Aliases, Caps, #state{service = Svc, shared_peers = PDict}) ->
+rpu(TPid, Aliases, Caps, #state{service = Svc, remote = RT}) ->
#diameter_service{applications = Apps} = Svc,
Key = #diameter_app.alias,
F = fun(A) -> lists:keymember(A, Key, Apps) end,
- rpu(Pid, lists:filter(F, Aliases), Caps, PDict);
+ rpu(TPid, lists:filter(F, Aliases), Caps, RT);
rpu(_, [] = No, _, _) ->
No;
-rpu(Pid, Aliases, Caps, PDict) ->
- erlang:monitor(process, Pid),
- T = {Pid, Caps},
- lists:foreach(fun(A) -> ?Dict:append(A, T, PDict) end, Aliases).
+rpu(TPid, Aliases, Caps, {PeerT, _, _} = RT) ->
+ monitor(process, TPid),
+ ets:insert(PeerT, #peer{pid = TPid,
+ apps = Aliases,
+ caps = Caps}),
+ insert_peer(TPid, Aliases, Caps, RT).
+
+%% insert_peer/4
+
+insert_peer(TPid, Aliases, Caps, {_PeerT, AppT, IdentT}) ->
+ #diameter_caps{origin_host = {_, OH},
+ origin_realm = {_, OR}}
+ = Caps,
+ ets:insert(AppT, [{A, TPid} || A <- Aliases]),
+ H = iolist_to_binary(OH),
+ R = iolist_to_binary(OR),
+ ets:insert(IdentT, [{T, A, TPid} || T <- [{host, H}, {realm, R}, {R, H}],
+ A <- Aliases]).
%% ---------------------------------------------------------------------------
%% # remote_peer_down/2
%% ---------------------------------------------------------------------------
-remote_peer_down(Pid, #state{shared_peers = PDict}) ->
- lists:foreach(fun(A) -> rpd(Pid, A, PDict) end, ?Dict:fetch_keys(PDict)).
+remote_peer_down(TPid, #state{remote = {PeerT, _, _} = RT}) ->
+ ets:delete(PeerT, TPid),
+ delete_peer(TPid, RT).
+
+%% delete_peer/2
-rpd(Pid, Alias, PDict) ->
- ?Dict:update(Alias, fun(Ps) -> lists:keydelete(Pid, 1, Ps) end, PDict).
+delete_peer(TPid, {_PeerT, AppT, IdentT}) ->
+ ets:select_delete(AppT, [{{'_', TPid}, [], [true]}]),
+ ets:select_delete(IdentT, [{{'_', '_', TPid}, [], [true]}]).
%% ---------------------------------------------------------------------------
%% pick_peer/4
@@ -1425,12 +1517,12 @@ pick_peer(#diameter_app{alias = Alias}
= App,
RealmAndHost,
Filter,
- #state{local_peers = L,
- shared_peers = S,
+ #state{local = LT,
+ remote = RT,
service_name = SvcName,
service = #diameter_service{pid = Pid}}) ->
- pick_peer(peers(Alias, RealmAndHost, Filter, L),
- peers(Alias, RealmAndHost, Filter, S),
+ pick_peer(peers(Alias, RealmAndHost, Filter, LT),
+ peers(Alias, RealmAndHost, Filter, RT),
Pid,
SvcName,
App).
@@ -1495,54 +1587,196 @@ pick_peer(Local,
%% peers/4
-peers(Alias, RH, Filter, Peers) ->
- case ?Dict:find(Alias, Peers) of
- {ok, L} ->
- filter(L, RH, Filter);
- error ->
+peers(Alias, RH, Filter, T) ->
+ filter(Alias, RH, Filter, T, true).
+
+%% filter/5
+%%
+%% Try to limit the peers list by starting with a host/realm lookup.
+
+filter(Alias, RH, {neg, F}, T, B) ->
+ filter(Alias, RH, F, T, not B);
+
+filter(_, _, none, _, false) ->
+ [];
+
+filter(Alias, _, none, T, true) ->
+ all_peers(Alias, T);
+
+filter(Alias, [DR,DH] = RH, K, T, B)
+ when K == realm, DR == undefined;
+ K == host, DH == undefined ->
+ filter(Alias, RH, none, T, B);
+
+filter(Alias, [DR,_] = RH, realm = K, T, B) ->
+ filter(Alias, RH, {K, DR}, T, B);
+
+filter(Alias, [_,DH] = RH, host = K, T, B) ->
+ filter(Alias, RH, {K, DH}, T, B);
+
+filter(Alias, _, {K, D}, {PeerT, _AppT, IdentT}, true)
+ when K == host;
+ K == realm ->
+ try iolist_to_binary(D) of
+ B ->
+ caps(PeerT, ets:select(IdentT, [{{{K, B}, '$1', '$2'},
+ [{'==', '$1', {const, Alias}}],
+ ['$2']}]))
+ catch
+ error:_ ->
[]
- end.
+ end;
-%% filter/3
+filter(Alias, RH, {all, Filters}, T, B)
+ when is_list(Filters) ->
+ fltr_all(Alias, RH, Filters, T, B);
+
+filter(Alias, RH, {first, Filters}, T, B)
+ when is_list(Filters) ->
+ fltr_first(Alias, RH, Filters, T, B);
+
+filter(Alias, RH, Filter, T, B) ->
+ {Ts, Fs} = filter(all_peers(Alias, T), RH, Filter),
+ choose(B, Ts, Fs).
+
+%% fltr_all/5
+
+fltr_all(Alias, RH, [{K, any} | Filters], T, B)
+ when K == host;
+ K == realm ->
+ fltr_all(Alias, RH, Filters, T, B);
+
+fltr_all(Alias, RH, [{host, _} = H, {realm, _} = R | Filters], T, B) ->
+ fltr_all(Alias, RH, [R, H | Filters], T, B);
+
+fltr_all(Alias, RH, [{realm, _} = R, {host, any} | Filters], T, B) ->
+ fltr_all(Alias, RH, [R | Filters], T, B);
+
+fltr_all(Alias, RH, [{realm, OR}, {host, OH} | Filters], T, true) ->
+ {PeerT, _AppT, IdentT} = T,
+ try {iolist_to_binary(OR), iolist_to_binary(OH)} of
+ BT ->
+ Peers = caps(PeerT,
+ ets:select(IdentT, [{{BT, '$1', '$2'},
+ [{'==', '$1', {const, Alias}}],
+ ['$2']}])),
+ {Ts, _} = filter(Peers, RH, {all, Filters}),
+ Ts
+ catch
+ error:_ ->
+ []
+ end;
+
+fltr_all(Alias, [undefined,_] = RH, [realm | Filters], T, B) ->
+ fltr_all(Alias, RH, Filters, T, B);
+
+fltr_all(Alias, [DR,_] = RH, [realm | Filters], T, B) ->
+ fltr_all(Alias, RH, [{realm, DR} | Filters], T, B);
+
+fltr_all(Alias, [_,undefined] = RH, [host | Filters], T, B) ->
+ fltr_all(Alias, RH, Filters, T, B);
+
+fltr_all(Alias, [_,DH] = RH, [host | Filters], T, B) ->
+ fltr_all(Alias, RH, [{host, DH} | Filters], T, B);
+
+fltr_all(Alias, RH, [{K, _} = KT, KA | Filters], T, B)
+ when K == host, KA == realm;
+ K == realm, KA == host ->
+ fltr_all(Alias, RH, [KA, KT | Filters], T, B);
+
+fltr_all(Alias, RH, [F | Filters], T, B) ->
+ {Ts, Fs} = filter(filter(Alias, RH, F, T, B), RH, {all, Filters}),
+ choose(B, Ts, Fs);
+
+fltr_all(Alias, RH, [], T, B) ->
+ filter(Alias, RH, none, T, B).
+
+%% fltr_first/5
%%
-%% Return peers in match order.
+%% Like any, but stop at the first filter with any matches.
+
+fltr_first(Alias, RH, [F | Filters], T, B) ->
+ case filter(Alias, RH, F, T, B) of
+ [] ->
+ fltr_first(Alias, RH, Filters, T, B);
+ [_|_] = Ts ->
+ Ts
+ end;
-filter(Peers, RH, Filter) ->
- {Ts, _} = fltr(Peers, RH, Filter),
- Ts.
+fltr_first(Alias, RH, [], T, B) ->
+ filter(Alias, RH, none, T, not B).
-%% fltr/4
+%% all_peers/2
+
+all_peers(Alias, {PeerT, AppT, _}) ->
+ ets:select(PeerT, [{#peer{pid = P, caps = '$1', _ = '_'},
+ [],
+ [{{P, '$1'}}]}
+ || {_,P} <- ets:lookup(AppT, Alias)]).
-fltr(Peers, _, none) ->
+%% caps/2
+
+caps(PeerT, Pids) ->
+ ets:select(PeerT, [{#peer{pid = P, caps = '$1', _ = '_'},
+ [],
+ [{{P, '$1'}}]}
+ || P <- Pids]).
+
+%% filter/3
+%%
+%% Return peers in match order.
+
+filter(Peers, _, none) ->
{Peers, []};
-fltr(Peers, RH, {neg, F}) ->
- {Ts, Fs} = fltr(Peers, RH, F),
+filter(Peers, RH, {neg, F}) ->
+ {Ts, Fs} = filter(Peers, RH, F),
{Fs, Ts};
-fltr(Peers, RH, {all, L})
+filter(Peers, RH, {all, L})
when is_list(L) ->
lists:foldl(fun(F,A) -> fltr_all(F, A, RH) end,
{Peers, []},
L);
-fltr(Peers, RH, {any, L})
+filter(Peers, RH, {any, L})
when is_list(L) ->
lists:foldl(fun(F,A) -> fltr_any(F, A, RH) end,
{[], Peers},
L);
-fltr(Peers, RH, F) ->
+filter(Peers, RH, {first, L})
+ when is_list(L) ->
+ fltr_first(Peers, RH, L);
+
+filter(Peers, RH, F) ->
lists:partition(fun({_,C}) -> caps_filter(C, RH, F) end, Peers).
+%% fltr_all/3
+
fltr_all(F, {Ts0, Fs0}, RH) ->
- {Ts1, Fs1} = fltr(Ts0, RH, F),
+ {Ts1, Fs1} = filter(Ts0, RH, F),
{Ts1, Fs0 ++ Fs1}.
+%% fltr_any/3
+
fltr_any(F, {Ts0, Fs0}, RH) ->
- {Ts1, Fs1} = fltr(Fs0, RH, F),
+ {Ts1, Fs1} = filter(Fs0, RH, F),
{Ts0 ++ Ts1, Fs1}.
+%% fltr_first/3
+
+fltr_first(Peers, _, []) ->
+ {[], Peers};
+
+fltr_first(Peers, RH, [F | Filters]) ->
+ case filter(Peers, RH, F) of
+ {[], _} ->
+ fltr_first(Peers, RH, Filters);
+ {_, _} = T ->
+ T
+ end.
+
%% caps_filter/3
caps_filter(#diameter_caps{origin_host = {_,OH}}, [_,DH], host) ->
@@ -1586,8 +1820,8 @@ eq(Any, Id, PeerId) ->
transports(#state{watchdogT = WatchdogT}) ->
ets:select(WatchdogT, [{#watchdog{peer = '$1', _ = '_'},
- [{'is_pid', '$1'}],
- ['$1']}]).
+ [{'is_pid', '$1'}],
+ ['$1']}]).
%% ---------------------------------------------------------------------------
%% # service_info/2
@@ -1640,7 +1874,7 @@ tagged_info(Item, S)
undefined
end;
-tagged_info(TPid, #state{watchdogT = WatchdogT, peerT = PeerT})
+tagged_info(TPid, #state{watchdogT = WatchdogT, local = {PeerT, _, _}})
when is_pid(TPid) ->
try
[#peer{watchdog = Pid}] = ets:lookup(PeerT, TPid),
@@ -1790,7 +2024,7 @@ keys(connect = T, Opts) ->
keys(_, _) ->
[{listen, accept}].
-peer_dict(#state{watchdogT = WatchdogT, peerT = PeerT}, Dict0) ->
+peer_dict(#state{watchdogT = WatchdogT, local = {PeerT, _, _}}, Dict0) ->
try ets:tab2list(WatchdogT) of
L -> lists:foldl(fun(T,A) -> peer_acc(PeerT, A, T) end, Dict0, L)
catch
diff --git a/lib/diameter/src/base/diameter_traffic.erl b/lib/diameter/src/base/diameter_traffic.erl
index 07f39c562f..516353219a 100644
--- a/lib/diameter/src/base/diameter_traffic.erl
+++ b/lib/diameter/src/base/diameter_traffic.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2013-2015. All Rights Reserved.
+%% Copyright Ericsson AB 2013-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.
@@ -41,7 +41,6 @@
-export([make_recvdata/1,
peer_up/1,
peer_down/1,
- failover/1,
pending/1]).
%% towards ?MODULE
@@ -1814,7 +1813,7 @@ store_request(T, TPid) ->
ets:member(?REQUEST_TABLE, TPid)
orelse begin
{_Seqs, _Req, TRef} = T,
- (self() ! {failover, TRef}) %% failover/1 may have missed
+ self() ! {failover, TRef} %% failover/1 may have missed
end.
%% lookup_request/2
@@ -1864,7 +1863,7 @@ failover(TPid)
%% notifications are sent here: store_request/2 sends the notification
%% in that case.
-%% Failover as a consequence of request_peer_down/1: inform the
+%% Failover as a consequence of peer_down/1: inform the
%% request process.
failover({_, Req, TRef}) ->
#request{handler = Pid,
diff --git a/lib/eldap/doc/src/eldap.xml b/lib/eldap/doc/src/eldap.xml
index 8f4479a730..43873e44e2 100644
--- a/lib/eldap/doc/src/eldap.xml
+++ b/lib/eldap/doc/src/eldap.xml
@@ -4,7 +4,7 @@
<erlref>
<header>
<copyright>
- <year>2012</year><year>2013</year>
+ <year>2012</year><year>2016</year>
<holder>Ericsson AB. All Rights Reserved.</holder>
</copyright>
<legalnotice>
@@ -29,7 +29,7 @@
<rev>B</rev>
</header>
<module>eldap</module>
- <modulesummary>Eldap Functions</modulesummary>
+ <modulesummary>LDAP Client</modulesummary>
<description>
<p>This module provides a client api to the Lightweight Directory Access Protocol (LDAP).
</p>
@@ -40,20 +40,67 @@
</list>
<p>The above publications can be found at <url href="http://www.ietf.org">IETF</url>.
</p>
- <p><em>Types</em></p>
- <pre>
-handle() Connection handle
-attribute() {Type = string(), Values=[string()]}
-modify_op() See mod_add/2, mod_delete/2, mod_replace/2
-scope() See baseObject/0, singleLevel/0, wholeSubtree/0
-dereference() See neverDerefAliases/0, derefInSearching/0, derefFindingBaseObj/0, derefAlways/0
-filter() See present/1, substrings/2,
- equalityMatch/2, greaterOrEqual/2, lessOrEqual/2,
- approxMatch/2, extensibleMatch/2,
- 'and'/1, 'or'/1, 'not'/1.
- </pre>
- <p></p>
</description>
+
+ <section>
+ <title>DATA TYPES</title>
+ <p>Type definitions that are used more than once in this module:
+ </p>
+ <taglist>
+ <tag><c>handle()</c></tag>
+ <item><p>Connection handle</p></item>
+
+ <tag><c>attribute() =</c></tag>
+ <item><p><c>{Type = string(), Values=[string()]}</c></p></item>
+
+ <tag><c>modify_op()</c></tag>
+ <item><p>See
+ <seealso marker="#mod_add/2">mod_add/2</seealso>,
+ <seealso marker="#mod_delete/2">mod_delete/2</seealso>,
+ <seealso marker="#mod_replace/2">mod_replace/2</seealso>
+ </p></item>
+
+ <tag><c>scope()</c></tag>
+ <item><p>See
+ <seealso marker="#baseObject/0">baseObject/0</seealso>,
+ <seealso marker="#singleLevel/0">singleLevel/0</seealso>,
+ <seealso marker="#wholeSubtree/0">wholeSubtree/0</seealso>
+ </p></item>
+
+ <tag><c>dereference()</c></tag>
+ <item><p>See
+ <seealso marker="#neverDerefAliases/0">neverDerefAliases/0</seealso>,
+ <seealso marker="#derefInSearching/0">derefInSearching/0</seealso>,
+ <seealso marker="#derefFindingBaseObj/0">derefFindingBaseObj/0</seealso>,
+ <seealso marker="#derefAlways/0">derefAlways/0</seealso>
+ </p></item>
+
+ <tag><c>filter()</c></tag>
+ <item><p>See
+ <seealso marker="#present/1">present/1</seealso>,
+ <seealso marker="#substrings/2">substrings/2</seealso>,
+ <seealso marker="#equalityMatch/2">equalityMatch/2</seealso>,
+ <seealso marker="#greaterOrEqual/2">greaterOrEqual/2</seealso>,
+ <seealso marker="#lessOrEqual/2">lessOrEqual/2</seealso>,
+ <seealso marker="#approxMatch/2">approxMatch/2</seealso>,
+ <seealso marker="#extensibleMatch/2">extensibleMatch/2</seealso>,
+ <seealso marker="#'and'/1">'and'/1</seealso>,
+ <seealso marker="#'or'/1">'or'/1</seealso>,
+ <seealso marker="#'not'/1">'not'/1</seealso>
+ </p></item>
+
+ <tag><c>return_value() = </c></tag>
+ <item><p><c>ok | {ok, {referral,referrals()}} | {error,Error}</c>
+ </p></item>
+
+ <tag><c>referrals() =</c></tag>
+ <item><p><c>[Address = string()]</c> The contents of <c>Address</c> is server dependent.
+ </p></item>
+
+ </taglist>
+ </section>
+
+
<funcs>
<func>
<name>open([Host]) -> {ok, Handle} | {error, Reason}</name>
@@ -88,18 +135,19 @@ filter() See present/1, substrings/2,
<v>Handle = handle()</v>
</type>
<desc>
- <p>Shutdown the connection.</p>
+ <p>Shutdown the connection after sending an unbindRequest to the server. If the connection is tls the connection
+ will be closed with <c>ssl:close/1</c>, otherwise with <c>gen_tcp:close/1</c>.</p>
</desc>
</func>
<func>
- <name>start_tls(Handle, Options) -> ok | {error,Error}</name>
+ <name>start_tls(Handle, Options) -> return_value()</name>
<fsummary>Upgrade a connection to TLS.</fsummary>
<desc>
<p>Same as start_tls(Handle, Options, infinity)</p>
</desc>
</func>
<func>
- <name>start_tls(Handle, Options, Timeout) -> ok | {error,Error}</name>
+ <name>start_tls(Handle, Options, Timeout) -> return_value()</name>
<fsummary>Upgrade a connection to TLS.</fsummary>
<type>
<v>Handle = handle()</v>
@@ -128,7 +176,7 @@ filter() See present/1, substrings/2,
</desc>
</func>
<func>
- <name>simple_bind(Handle, Dn, Password) -> ok | {error, Reason}</name>
+ <name>simple_bind(Handle, Dn, Password) -> return_value()</name>
<fsummary>Authenticate the connection.</fsummary>
<type>
<v>Handle = handle()</v>
@@ -140,7 +188,7 @@ filter() See present/1, substrings/2,
</desc>
</func>
<func>
- <name>add(Handle, Dn, [Attribute]) -> ok | {error, Reason}</name>
+ <name>add(Handle, Dn, [Attribute]) -> return_value()</name>
<fsummary>Add an entry.</fsummary>
<type>
<v>Handle = handle()</v>
@@ -161,7 +209,7 @@ filter() See present/1, substrings/2,
</desc>
</func>
<func>
- <name>delete(Handle, Dn) -> ok | {error, Reason}</name>
+ <name>delete(Handle, Dn) -> return_value()</name>
<fsummary>Delete an entry.</fsummary>
<type>
<v>Dn = string()</v>
@@ -203,7 +251,7 @@ filter() See present/1, substrings/2,
</func>
<func>
- <name>modify(Handle, Dn, [ModifyOp]) -> ok | {error, Reason}</name>
+ <name>modify(Handle, Dn, [ModifyOp]) -> return_value()</name>
<fsummary>Modify an entry.</fsummary>
<type>
<v>Dn = string()</v>
@@ -219,7 +267,7 @@ filter() See present/1, substrings/2,
</desc>
</func>
<func>
- <name>modify_password(Handle, Dn, NewPasswd) -> ok | {ok, GenPasswd} | {error, Reason}</name>
+ <name>modify_password(Handle, Dn, NewPasswd) -> return_value() | {ok, GenPasswd}</name>
<fsummary>Modify the password of a user.</fsummary>
<type>
<v>Dn = string()</v>
@@ -230,7 +278,7 @@ filter() See present/1, substrings/2,
</desc>
</func>
<func>
- <name>modify_password(Handle, Dn, NewPasswd, OldPasswd) -> ok | {ok, GenPasswd} | {error, Reason}</name>
+ <name>modify_password(Handle, Dn, NewPasswd, OldPasswd) -> return_value() | {ok, GenPasswd}</name>
<fsummary>Modify the password of a user.</fsummary>
<type>
<v>Dn = string()</v>
@@ -259,7 +307,7 @@ filter() See present/1, substrings/2,
</desc>
</func>
<func>
- <name>modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> ok | {error, Reason}</name>
+ <name>modify_dn(Handle, Dn, NewRDN, DeleteOldRDN, NewSupDN) -> return_value()</name>
<fsummary>Modify the DN of an entry.</fsummary>
<type>
<v>Dn = string()</v>
@@ -279,7 +327,7 @@ filter() See present/1, substrings/2,
</desc>
</func>
<func>
- <name>search(Handle, SearchOptions) -> {ok, #eldap_search_result{}} | {error, Reason}</name>
+ <name>search(Handle, SearchOptions) -> {ok, #eldap_search_result{}} | {ok, {referral,referrals()}} | {error, Reason}</name>
<fsummary>Search the Directory</fsummary>
<type>
<v>SearchOptions = #eldap_search{} | [SearchOption]</v>
diff --git a/lib/eldap/src/eldap.erl b/lib/eldap/src/eldap.erl
index ae47c815c9..0c03021bd0 100644
--- a/lib/eldap/src/eldap.erl
+++ b/lib/eldap/src/eldap.erl
@@ -10,16 +10,23 @@
%%% See MIT-LICENSE at the top dir for licensing information.
%%% --------------------------------------------------------------------
-vc('$Id$ ').
--export([open/1,open/2,simple_bind/3,controlling_process/2,
- start_tls/2, start_tls/3,
- modify_password/3, modify_password/4,
+-export([open/1, open/2,
+ simple_bind/3, simple_bind/4,
+ controlling_process/2,
+ start_tls/2, start_tls/3, start_tls/4,
+ modify_password/3, modify_password/4, modify_password/5,
getopts/2,
baseObject/0,singleLevel/0,wholeSubtree/0,close/1,
equalityMatch/2,greaterOrEqual/2,lessOrEqual/2,
extensibleMatch/2,
- approxMatch/2,search/2,substrings/2,present/1,
- 'and'/1,'or'/1,'not'/1,modify/3, mod_add/2, mod_delete/2,
- mod_replace/2, add/3, delete/2, modify_dn/5,parse_dn/1,
+ search/2, search/3,
+ approxMatch/2,substrings/2,present/1,
+ 'and'/1,'or'/1,'not'/1,mod_add/2, mod_delete/2,
+ mod_replace/2,
+ modify/3, modify/4,
+ add/3, add/4,
+ delete/2, delete/3,
+ modify_dn/5,parse_dn/1,
parse_ldap_url/1]).
-export([neverDerefAliases/0, derefInSearching/0,
@@ -91,7 +98,10 @@ start_tls(Handle, TlsOptions) ->
start_tls(Handle, TlsOptions, infinity).
start_tls(Handle, TlsOptions, Timeout) ->
- send(Handle, {start_tls,TlsOptions,Timeout}),
+ start_tls(Handle, TlsOptions, Timeout, asn1_NOVALUE).
+
+start_tls(Handle, TlsOptions, Timeout, Controls) ->
+ send(Handle, {start_tls,TlsOptions,Timeout,Controls}),
recv(Handle).
%%% --------------------------------------------------------------------
@@ -108,7 +118,11 @@ modify_password(Handle, Dn, NewPasswd) ->
modify_password(Handle, Dn, NewPasswd, OldPasswd)
when is_pid(Handle), is_list(Dn), is_list(NewPasswd), is_list(OldPasswd) ->
- send(Handle, {passwd_modify,optional(Dn),optional(NewPasswd),optional(OldPasswd)}),
+ modify_password(Handle, Dn, NewPasswd, OldPasswd, asn1_NOVALUE).
+
+modify_password(Handle, Dn, NewPasswd, OldPasswd, Controls)
+ when is_pid(Handle), is_list(Dn), is_list(NewPasswd), is_list(OldPasswd) ->
+ send(Handle, {passwd_modify,optional(Dn),optional(NewPasswd),optional(OldPasswd),Controls}),
recv(Handle).
%%% --------------------------------------------------------------------
@@ -147,7 +161,10 @@ controlling_process(Handle, Pid) when is_pid(Handle), is_pid(Pid) ->
%%% Returns: ok | {error, Error}
%%% --------------------------------------------------------------------
simple_bind(Handle, Dn, Passwd) when is_pid(Handle) ->
- send(Handle, {simple_bind, Dn, Passwd}),
+ simple_bind(Handle, Dn, Passwd, asn1_NOVALUE).
+
+simple_bind(Handle, Dn, Passwd, Controls) when is_pid(Handle) ->
+ send(Handle, {simple_bind, Dn, Passwd, Controls}),
recv(Handle).
%%% --------------------------------------------------------------------
@@ -164,7 +181,10 @@ simple_bind(Handle, Dn, Passwd) when is_pid(Handle) ->
%%% )
%%% --------------------------------------------------------------------
add(Handle, Entry, Attributes) when is_pid(Handle),is_list(Entry),is_list(Attributes) ->
- send(Handle, {add, Entry, add_attrs(Attributes)}),
+ add(Handle, Entry, Attributes, asn1_NOVALUE).
+
+add(Handle, Entry, Attributes, Controls) when is_pid(Handle),is_list(Entry),is_list(Attributes) ->
+ send(Handle, {add, Entry, add_attrs(Attributes), Controls}),
recv(Handle).
%%% Do sanity check !
@@ -188,7 +208,10 @@ add_attrs(Attrs) ->
%%% )
%%% --------------------------------------------------------------------
delete(Handle, Entry) when is_pid(Handle), is_list(Entry) ->
- send(Handle, {delete, Entry}),
+ delete(Handle, Entry, asn1_NOVALUE).
+
+delete(Handle, Entry, Controls) when is_pid(Handle), is_list(Entry) ->
+ send(Handle, {delete, Entry, Controls}),
recv(Handle).
%%% --------------------------------------------------------------------
@@ -203,7 +226,10 @@ delete(Handle, Entry) when is_pid(Handle), is_list(Entry) ->
%%% )
%%% --------------------------------------------------------------------
modify(Handle, Object, Mods) when is_pid(Handle), is_list(Object), is_list(Mods) ->
- send(Handle, {modify, Object, Mods}),
+ modify(Handle, Object, Mods, asn1_NOVALUE).
+
+modify(Handle, Object, Mods, Controls) when is_pid(Handle), is_list(Object), is_list(Mods) ->
+ send(Handle, {modify, Object, Mods, Controls}),
recv(Handle).
%%%
@@ -236,8 +262,12 @@ m(Operation, Type, Values) ->
%%% --------------------------------------------------------------------
modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup)
when is_pid(Handle),is_list(Entry),is_list(NewRDN),is_atom(DelOldRDN),is_list(NewSup) ->
+ modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup, asn1_NOVALUE).
+
+modify_dn(Handle, Entry, NewRDN, DelOldRDN, NewSup, Controls)
+ when is_pid(Handle),is_list(Entry),is_list(NewRDN),is_atom(DelOldRDN),is_list(NewSup) ->
send(Handle, {modify_dn, Entry, NewRDN,
- bool_p(DelOldRDN), optional(NewSup)}),
+ bool_p(DelOldRDN), optional(NewSup), Controls}),
recv(Handle).
%%% Sanity checks !
@@ -272,16 +302,19 @@ optional(Value) -> Value.
%%% []}}
%%%
%%% --------------------------------------------------------------------
-search(Handle, A) when is_pid(Handle), is_record(A, eldap_search) ->
- call_search(Handle, A);
-search(Handle, L) when is_pid(Handle), is_list(L) ->
+search(Handle, X) when is_pid(Handle), is_record(X,eldap_search) ; is_list(X) ->
+ search(Handle, X, asn1_NOVALUE).
+
+search(Handle, A, Controls) when is_pid(Handle), is_record(A, eldap_search) ->
+ call_search(Handle, A, Controls);
+search(Handle, L, Controls) when is_pid(Handle), is_list(L) ->
case catch parse_search_args(L) of
{error, Emsg} -> {error, Emsg};
- A when is_record(A, eldap_search) -> call_search(Handle, A)
+ A when is_record(A, eldap_search) -> call_search(Handle, A, Controls)
end.
-call_search(Handle, A) ->
- send(Handle, {search, A}),
+call_search(Handle, A, Controls) ->
+ send(Handle, {search, A, Controls}),
recv(Handle).
parse_search_args(Args) ->
@@ -484,33 +517,33 @@ do_connect(Host, Data, Opts) when Data#eldap.ldaps == true ->
loop(Cpid, Data) ->
receive
- {From, {search, A}} ->
- {Res,NewData} = do_search(Data, A),
+ {From, {search, A, Controls}} ->
+ {Res,NewData} = do_search(Data, A, Controls),
send(From,Res),
?MODULE:loop(Cpid, NewData);
- {From, {modify, Obj, Mod}} ->
- {Res,NewData} = do_modify(Data, Obj, Mod),
+ {From, {modify, Obj, Mod, Controls}} ->
+ {Res,NewData} = do_modify(Data, Obj, Mod, Controls),
send(From,Res),
?MODULE:loop(Cpid, NewData);
- {From, {modify_dn, Obj, NewRDN, DelOldRDN, NewSup}} ->
- {Res,NewData} = do_modify_dn(Data, Obj, NewRDN, DelOldRDN, NewSup),
+ {From, {modify_dn, Obj, NewRDN, DelOldRDN, NewSup, Controls}} ->
+ {Res,NewData} = do_modify_dn(Data, Obj, NewRDN, DelOldRDN, NewSup, Controls),
send(From,Res),
?MODULE:loop(Cpid, NewData);
- {From, {add, Entry, Attrs}} ->
- {Res,NewData} = do_add(Data, Entry, Attrs),
+ {From, {add, Entry, Attrs, Controls}} ->
+ {Res,NewData} = do_add(Data, Entry, Attrs, Controls),
send(From,Res),
?MODULE:loop(Cpid, NewData);
- {From, {delete, Entry}} ->
- {Res,NewData} = do_delete(Data, Entry),
+ {From, {delete, Entry, Controls}} ->
+ {Res,NewData} = do_delete(Data, Entry, Controls),
send(From,Res),
?MODULE:loop(Cpid, NewData);
- {From, {simple_bind, Dn, Passwd}} ->
- {Res,NewData} = do_simple_bind(Data, Dn, Passwd),
+ {From, {simple_bind, Dn, Passwd, Controls}} ->
+ {Res,NewData} = do_simple_bind(Data, Dn, Passwd, Controls),
send(From,Res),
?MODULE:loop(Cpid, NewData);
@@ -520,17 +553,18 @@ loop(Cpid, Data) ->
?PRINT("New Cpid is: ~p~n",[NewCpid]),
?MODULE:loop(NewCpid, Data);
- {From, {start_tls,TlsOptions,Timeout}} ->
- {Res,NewData} = do_start_tls(Data, TlsOptions, Timeout),
+ {From, {start_tls,TlsOptions,Timeout,Controls}} ->
+ {Res,NewData} = do_start_tls(Data, TlsOptions, Timeout, Controls),
send(From,Res),
?MODULE:loop(Cpid, NewData);
- {From, {passwd_modify,Dn,NewPasswd,OldPasswd}} ->
- {Res,NewData} = do_passwd_modify(Data, Dn, NewPasswd, OldPasswd),
+ {From, {passwd_modify,Dn,NewPasswd,OldPasswd,Controls}} ->
+ {Res,NewData} = do_passwd_modify(Data, Dn, NewPasswd, OldPasswd, Controls),
send(From, Res),
?MODULE:loop(Cpid, NewData);
{_From, close} ->
+ {no_reply,_NewData} = do_unbind(Data),
unlink(Cpid),
exit(closed);
@@ -578,11 +612,10 @@ loop(Cpid, Data) ->
%%% --------------------------------------------------------------------
%%% startTLS Request
%%% --------------------------------------------------------------------
-
-do_start_tls(Data=#eldap{using_tls=true}, _, _) ->
+do_start_tls(Data=#eldap{using_tls=true}, _, _, _) ->
{{error,tls_already_started}, Data};
-do_start_tls(Data=#eldap{fd=FD} , TlsOptions, Timeout) ->
- case catch exec_start_tls(Data) of
+do_start_tls(Data=#eldap{fd=FD} , TlsOptions, Timeout, Controls) ->
+ case catch exec_start_tls(Data, Controls) of
{ok,NewData} ->
case ssl:connect(FD,TlsOptions,Timeout) of
{ok, SslSocket} ->
@@ -593,15 +626,16 @@ do_start_tls(Data=#eldap{fd=FD} , TlsOptions, Timeout) ->
{error,Error} ->
{{error,Error}, Data}
end;
- {error,Error} -> {{error,Error},Data};
- Else -> {{error,Else},Data}
+ {{ok,Val},NewData} -> {{ok,Val},NewData};
+ {error,Error} -> {{error,Error},Data};
+ Else -> {{error,Else},Data}
end.
-define(START_TLS_OID, "1.3.6.1.4.1.1466.20037").
-exec_start_tls(Data) ->
+exec_start_tls(Data, Controls) ->
Req = #'ExtendedRequest'{requestName = ?START_TLS_OID},
- Reply = request(Data#eldap.fd, Data, Data#eldap.id, {extendedReq, Req}),
+ Reply = request(Data#eldap.fd, Data, Data#eldap.id, {extendedReq, Req, Controls}),
exec_extended_req_reply(Data, Reply).
exec_extended_req_reply(Data, {ok,Msg}) when
@@ -611,6 +645,8 @@ exec_extended_req_reply(Data, {ok,Msg}) when
case Result#'ExtendedResponse'.resultCode of
success ->
{ok,Data};
+ referral ->
+ {{ok, {referral,Result#'ExtendedResponse'.referral}}, Data};
Error ->
{error, {response,Error}}
end;
@@ -626,30 +662,32 @@ exec_extended_req_reply(_, Error) ->
%%% Authenticate ourselves to the directory using
%%% simple authentication.
-do_simple_bind(Data, anon, anon) -> %% For testing
- do_the_simple_bind(Data, "", "");
-do_simple_bind(Data, Dn, _Passwd) when Dn=="",Data#eldap.anon_auth==false ->
+do_simple_bind(Data, anon, anon, Controls) -> %% For testing
+ do_the_simple_bind(Data, "", "", Controls);
+do_simple_bind(Data, Dn, _Passwd,_) when Dn=="",Data#eldap.anon_auth==false ->
{{error,anonymous_auth},Data};
-do_simple_bind(Data, _Dn, Passwd) when Passwd=="",Data#eldap.anon_auth==false ->
+do_simple_bind(Data, _Dn, Passwd,_) when Passwd=="",Data#eldap.anon_auth==false ->
{{error,anonymous_auth},Data};
-do_simple_bind(Data, Dn, Passwd) ->
- do_the_simple_bind(Data, Dn, Passwd).
+do_simple_bind(Data, Dn, Passwd, Controls) ->
+ do_the_simple_bind(Data, Dn, Passwd, Controls).
-do_the_simple_bind(Data, Dn, Passwd) ->
+do_the_simple_bind(Data, Dn, Passwd, Controls) ->
case catch exec_simple_bind(Data#eldap{binddn = Dn,
passwd = Passwd,
- id = bump_id(Data)}) of
- {ok,NewData} -> {ok,NewData};
- {error,Emsg} -> {{error,Emsg},Data};
- Else -> {{error,Else},Data}
+ id = bump_id(Data)},
+ Controls) of
+ {ok,NewData} -> {ok,NewData};
+ {{ok,Val},NewData} -> {{ok,Val},NewData};
+ {error,Emsg} -> {{error,Emsg},Data};
+ Else -> {{error,Else},Data}
end.
-exec_simple_bind(Data) ->
+exec_simple_bind(Data, Controls) ->
Req = #'BindRequest'{version = Data#eldap.version,
name = Data#eldap.binddn,
authentication = {simple, Data#eldap.passwd}},
log2(Data, "bind request = ~p~n", [Req]),
- Reply = request(Data#eldap.fd, Data, Data#eldap.id, {bindRequest, Req}),
+ Reply = request(Data#eldap.fd, Data, Data#eldap.id, {bindRequest, Req, Controls}),
log2(Data, "bind reply = ~p~n", [Reply]),
exec_simple_bind_reply(Data, Reply).
@@ -659,6 +697,7 @@ exec_simple_bind_reply(Data, {ok,Msg}) when
{bindResponse, Result} ->
case Result#'BindResponse'.resultCode of
success -> {ok,Data};
+ referral -> {{ok, {referral,Result#'BindResponse'.referral}}, Data};
Error -> {error, Error}
end;
Other -> {error, Other}
@@ -671,10 +710,11 @@ exec_simple_bind_reply(_, Error) ->
%%% searchRequest
%%% --------------------------------------------------------------------
-do_search(Data, A) ->
- case catch do_search_0(Data, A) of
+do_search(Data, A, Controls) ->
+ case catch do_search_0(Data, A, Controls) of
{error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
{'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
+ {{ok,Val},NewData} -> {{ok,Val},NewData};
{ok,Res,Ref,NewData} -> {{ok,polish(Res, Ref)},NewData};
{{error,Reason},NewData} -> {{error,Reason},NewData};
Else -> {ldap_closed_p(Data, Else),Data}
@@ -700,7 +740,7 @@ polish_result([H|T]) when is_record(H, 'SearchResultEntry') ->
polish_result([]) ->
[].
-do_search_0(Data, A) ->
+do_search_0(Data, A, Controls) ->
Req = #'SearchRequest'{baseObject = A#eldap_search.base,
scope = v_scope(A#eldap_search.scope),
derefAliases = v_deref(A#eldap_search.deref),
@@ -711,15 +751,15 @@ do_search_0(Data, A) ->
attributes = v_attributes(A#eldap_search.attributes)
},
Id = bump_id(Data),
- collect_search_responses(Data#eldap{id=Id}, Req, Id).
+ collect_search_responses(Data#eldap{id=Id}, Req, Id, Controls).
%%% The returned answers cames in one packet per entry
%%% mixed with possible referals
-collect_search_responses(Data, Req, ID) ->
+collect_search_responses(Data, Req, ID, Controls) ->
S = Data#eldap.fd,
log2(Data, "search request = ~p~n", [Req]),
- send_request(S, Data, ID, {searchRequest, Req}),
+ send_request(S, Data, ID, {searchRequest, Req, Controls}),
Resp = recv_response(S, Data),
log2(Data, "search reply = ~p~n", [Resp]),
collect_search_responses(Data, S, ID, Resp, [], []).
@@ -732,6 +772,8 @@ collect_search_responses(Data, S, ID, {ok,Msg}, Acc, Ref)
success ->
log2(Data, "search reply = searchResDone ~n", []),
{ok,Acc,Ref,Data};
+ referral ->
+ {{ok, {referral,R#'LDAPResult'.referral}}, Data};
Reason ->
{{error,Reason},Data}
end;
@@ -756,21 +798,22 @@ collect_search_responses(_, _, _, Else, _, _) ->
%%% addRequest
%%% --------------------------------------------------------------------
-do_add(Data, Entry, Attrs) ->
- case catch do_add_0(Data, Entry, Attrs) of
+do_add(Data, Entry, Attrs, Controls) ->
+ case catch do_add_0(Data, Entry, Attrs, Controls) of
{error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
{'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
{ok,NewData} -> {ok,NewData};
+ {{ok,Val},NewData} -> {{ok,Val},NewData};
Else -> {ldap_closed_p(Data, Else),Data}
end.
-do_add_0(Data, Entry, Attrs) ->
+do_add_0(Data, Entry, Attrs, Controls) ->
Req = #'AddRequest'{entry = Entry,
attributes = Attrs},
S = Data#eldap.fd,
Id = bump_id(Data),
log2(Data, "add request = ~p~n", [Req]),
- Resp = request(S, Data, Id, {addRequest, Req}),
+ Resp = request(S, Data, Id, {addRequest, Req, Controls}),
log2(Data, "add reply = ~p~n", [Resp]),
check_reply(Data#eldap{id = Id}, Resp, addResponse).
@@ -779,19 +822,20 @@ do_add_0(Data, Entry, Attrs) ->
%%% deleteRequest
%%% --------------------------------------------------------------------
-do_delete(Data, Entry) ->
- case catch do_delete_0(Data, Entry) of
+do_delete(Data, Entry, Controls) ->
+ case catch do_delete_0(Data, Entry, Controls) of
{error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
{'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
{ok,NewData} -> {ok,NewData};
+ {{ok,Val},NewData} -> {{ok,Val},NewData};
Else -> {ldap_closed_p(Data, Else),Data}
end.
-do_delete_0(Data, Entry) ->
+do_delete_0(Data, Entry, Controls) ->
S = Data#eldap.fd,
Id = bump_id(Data),
log2(Data, "del request = ~p~n", [Entry]),
- Resp = request(S, Data, Id, {delRequest, Entry}),
+ Resp = request(S, Data, Id, {delRequest, Entry, Controls}),
log2(Data, "del reply = ~p~n", [Resp]),
check_reply(Data#eldap{id = Id}, Resp, delResponse).
@@ -800,22 +844,23 @@ do_delete_0(Data, Entry) ->
%%% modifyRequest
%%% --------------------------------------------------------------------
-do_modify(Data, Obj, Mod) ->
- case catch do_modify_0(Data, Obj, Mod) of
+do_modify(Data, Obj, Mod, Controls) ->
+ case catch do_modify_0(Data, Obj, Mod, Controls) of
{error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
{'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
{ok,NewData} -> {ok,NewData};
+ {{ok,Val},NewData} -> {{ok,Val},NewData};
Else -> {ldap_closed_p(Data, Else),Data}
end.
-do_modify_0(Data, Obj, Mod) ->
+do_modify_0(Data, Obj, Mod, Controls) ->
v_modifications(Mod),
Req = #'ModifyRequest'{object = Obj,
changes = Mod},
S = Data#eldap.fd,
Id = bump_id(Data),
log2(Data, "modify request = ~p~n", [Req]),
- Resp = request(S, Data, Id, {modifyRequest, Req}),
+ Resp = request(S, Data, Id, {modifyRequest, Req, Controls}),
log2(Data, "modify reply = ~p~n", [Resp]),
check_reply(Data#eldap{id = Id}, Resp, modifyResponse).
@@ -825,16 +870,17 @@ do_modify_0(Data, Obj, Mod) ->
-define(PASSWD_MODIFY_OID, "1.3.6.1.4.1.4203.1.11.1").
-do_passwd_modify(Data, Dn, NewPasswd, OldPasswd) ->
- case catch do_passwd_modify_0(Data, Dn, NewPasswd, OldPasswd) of
+do_passwd_modify(Data, Dn, NewPasswd, OldPasswd, Controls) ->
+ case catch do_passwd_modify_0(Data, Dn, NewPasswd, OldPasswd, Controls) of
{error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
{'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
{ok,NewData} -> {ok,NewData};
+ {{ok,Val},NewData} -> {{ok,Val},NewData};
{ok,Passwd,NewData} -> {{ok, Passwd},NewData};
Else -> {ldap_closed_p(Data, Else),Data}
end.
-do_passwd_modify_0(Data, Dn, NewPasswd, OldPasswd) ->
+do_passwd_modify_0(Data, Dn, NewPasswd, OldPasswd, Controls) ->
Req = #'PasswdModifyRequestValue'{userIdentity = Dn,
oldPasswd = OldPasswd,
newPasswd = NewPasswd},
@@ -844,7 +890,7 @@ do_passwd_modify_0(Data, Dn, NewPasswd, OldPasswd) ->
requestValue = Bytes},
Id = bump_id(Data),
log2(Data, "extended request = ~p~n", [ExtReq]),
- Reply = request(Data#eldap.fd, Data, Id, {extendedReq, ExtReq}),
+ Reply = request(Data#eldap.fd, Data, Id, {extendedReq, ExtReq, Controls}),
log2(Data, "modify password reply = ~p~n", [Reply]),
exec_passwd_modify_reply(Data#eldap{id = Id}, Reply).
@@ -865,6 +911,8 @@ exec_passwd_modify_reply(Data, {ok,Msg}) when
throw(Error)
end
end;
+ referral ->
+ {{ok, {referral,Result#'ExtendedResponse'.referral}}, Data};
Error ->
{error, {response,Error}}
end;
@@ -877,15 +925,16 @@ exec_passwd_modify_reply(_, Error) ->
%%% modifyDNRequest
%%% --------------------------------------------------------------------
-do_modify_dn(Data, Entry, NewRDN, DelOldRDN, NewSup) ->
- case catch do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup) of
+do_modify_dn(Data, Entry, NewRDN, DelOldRDN, NewSup, Controls) ->
+ case catch do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup, Controls) of
{error,Emsg} -> {ldap_closed_p(Data, Emsg),Data};
{'EXIT',Error} -> {ldap_closed_p(Data, Error),Data};
{ok,NewData} -> {ok,NewData};
+ {{ok,Val},NewData} -> {{ok,Val},NewData};
Else -> {ldap_closed_p(Data, Else),Data}
end.
-do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup) ->
+do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup, Controls) ->
Req = #'ModifyDNRequest'{entry = Entry,
newrdn = NewRDN,
deleteoldrdn = DelOldRDN,
@@ -893,22 +942,51 @@ do_modify_dn_0(Data, Entry, NewRDN, DelOldRDN, NewSup) ->
S = Data#eldap.fd,
Id = bump_id(Data),
log2(Data, "modify DN request = ~p~n", [Req]),
- Resp = request(S, Data, Id, {modDNRequest, Req}),
+ Resp = request(S, Data, Id, {modDNRequest, Req, Controls}),
log2(Data, "modify DN reply = ~p~n", [Resp]),
check_reply(Data#eldap{id = Id}, Resp, modDNResponse).
+%%%--------------------------------------------------------------------
+%%% unbindRequest
+%%%--------------------------------------------------------------------
+do_unbind(Data) ->
+ Req = "",
+ log2(Data, "unbind request = ~p (has no reply)~n", [Req]),
+ send_request(Data#eldap.fd, Data, Data#eldap.id, {unbindRequest, Req}),
+ case Data#eldap.using_tls of
+ true -> ssl:close(Data#eldap.fd);
+ false -> gen_tcp:close(Data#eldap.fd)
+ end,
+ {no_reply, Data#eldap{binddn = (#eldap{})#eldap.binddn,
+ passwd = (#eldap{})#eldap.passwd,
+ fd = (#eldap{})#eldap.fd,
+ using_tls = false
+ }}.
+
+
%%% --------------------------------------------------------------------
%%% Send an LDAP request and receive the answer
%%% --------------------------------------------------------------------
-
request(S, Data, ID, Request) ->
send_request(S, Data, ID, Request),
recv_response(S, Data).
-send_request(S, Data, ID, Request) ->
- Message = #'LDAPMessage'{messageID = ID,
- protocolOp = Request},
- {ok,Bytes} = 'ELDAPv3':encode('LDAPMessage', Message),
+send_request(S, Data, Id, {T,P}) ->
+ send_the_LDAPMessage(S, Data, #'LDAPMessage'{messageID = Id,
+ protocolOp = {T,P}});
+send_request(S, Data, Id, {T,P,asn1_NOVALUE}) ->
+ send_the_LDAPMessage(S, Data, #'LDAPMessage'{messageID = Id,
+ protocolOp = {T,P}});
+send_request(S, Data, Id, {T,P,Controls0}) ->
+ Controls = [#'Control'{controlType=F1,
+ criticality=F2,
+ controlValue=F3} || {control,F1,F2,F3} <- Controls0],
+ send_the_LDAPMessage(S, Data, #'LDAPMessage'{messageID = Id,
+ protocolOp = {T,P},
+ controls = Controls}).
+
+send_the_LDAPMessage(S, Data, LDAPMessage) ->
+ {ok,Bytes} = 'ELDAPv3':encode('LDAPMessage', LDAPMessage),
case do_send(S, Data, Bytes) of
{error,Reason} -> throw({gen_tcp_error,Reason});
Else -> Else
@@ -942,6 +1020,7 @@ check_reply(Data, {ok,Msg}, Op) when
{Op, Result} ->
case Result#'LDAPResult'.resultCode of
success -> {ok,Data};
+ referral -> {{ok, {referral,Result#'LDAPResult'.referral}}, Data};
Error -> {error, Error}
end;
Other -> {error, Other}
diff --git a/lib/eldap/test/eldap_basic_SUITE.erl b/lib/eldap/test/eldap_basic_SUITE.erl
index 638a609346..8414ca6e46 100644
--- a/lib/eldap/test/eldap_basic_SUITE.erl
+++ b/lib/eldap/test/eldap_basic_SUITE.erl
@@ -30,6 +30,11 @@
-define(TIMEOUT, 120000). % 2 min
+
+%% Control to delete a referral object:
+-define(manageDsaIT, {control,"2.16.840.1.113730.3.4.2",false,asn1_NOVALUE}).
+
+
all() ->
[app,
appup,
@@ -59,6 +64,7 @@ groups() ->
{api_bound, [], [add_when_bound,
add_already_exists,
more_add,
+ add_referral,
search_filter_equalityMatch,
search_filter_substring_any,
search_filter_initial,
@@ -67,8 +73,11 @@ groups() ->
search_filter_or,
search_filter_and_not,
search_two_hits,
+ search_referral,
modify,
+ modify_referral,
delete,
+ delete_referral,
modify_dn_delete_old,
modify_dn_keep_old]},
{v4_connections, [], connection_tests()},
@@ -92,11 +101,16 @@ connection_tests() ->
init_per_suite(Config) ->
SSL_available = init_ssl_certs_et_al(Config),
- LDAP_server = find_first_server(false, [{config,eldap_server}, {config,ldap_server}, {"localhost",9876}]),
+ LDAP_server = find_first_server(false, [{config,eldap_server},
+ {config,ldap_server},
+ {"localhost",9876},
+ {"aramis.otp.ericsson.se",9876}]),
LDAPS_server =
case SSL_available of
true ->
- find_first_server(true, [{config,ldaps_server}, {"localhost",9877}]);
+ find_first_server(true, [{config,ldaps_server},
+ {"localhost",9877},
+ {"aramis.otp.ericsson.se",9877}]);
false ->
undefined
end,
@@ -454,6 +468,16 @@ more_add(Config) ->
[{"objectclass", ["organizationalUnit"]},
{"ou", ["Team"]}]).
+%%%----------------------------------------------------------------
+add_referral(Config) ->
+ H = ?config(handle, Config),
+ BasePath = ?config(eldap_path, Config),
+ {ok,{referral,["ldap://nowhere.example.com"++_]}} =
+ eldap:add(H, "cn=Foo Bar,dc=notHere," ++ BasePath,
+ [{"objectclass", ["person"]},
+ {"cn", ["Foo Bar"]},
+ {"sn", ["Bar"]},
+ {"telephoneNumber", ["555-1232", "555-5432"]}]).
%%%----------------------------------------------------------------
search_filter_equalityMatch(Config) ->
@@ -569,6 +593,16 @@ search_two_hits(Config) ->
[ok=eldap:delete(H,DN) || DN <- ExpectedDNs].
%%%----------------------------------------------------------------
+search_referral(Config) ->
+ H = ?config(handle, Config),
+ BasePath = ?config(eldap_path, Config),
+ DN = "cn=Santa Claus,dc=notHere," ++ BasePath,
+ {ok,{referral,["ldap://nowhere.example.com"++_]}} =
+ eldap:search(H, #eldap_search{base = DN,
+ filter = eldap:present("description"),
+ scope=eldap:singleLevel()}).
+
+%%%----------------------------------------------------------------
modify(Config) ->
H = ?config(handle, Config),
BasePath = ?config(eldap_path, Config),
@@ -602,6 +636,19 @@ modify(Config) ->
restore_original_object(H, DN, OriginalAttrs).
%%%----------------------------------------------------------------
+modify_referral(Config) ->
+ H = ?config(handle, Config),
+ BasePath = ?config(eldap_path, Config),
+ %% The object to modify
+ DN = "cn=Foo Bar,dc=notHere," ++ BasePath,
+
+ %% Do a change
+ Mod = [eldap:mod_replace("telephoneNumber", ["555-12345"]),
+ eldap:mod_add("description", ["Nice guy"])],
+ {ok,{referral,["ldap://nowhere.example.com"++_]}} =
+ eldap:modify(H, DN, Mod).
+
+%%%----------------------------------------------------------------
delete(Config) ->
H = ?config(handle, Config),
BasePath = ?config(eldap_path, Config),
@@ -620,6 +667,14 @@ delete(Config) ->
restore_original_object(H, DN, OriginalAttrs).
%%%----------------------------------------------------------------
+delete_referral(Config) ->
+ H = ?config(handle, Config),
+ BasePath = ?config(eldap_path, Config),
+ %% The element to play with:
+ DN = "cn=Jonas Jonsson,dc=notHere," ++ BasePath,
+ {ok,{referral,["ldap://nowhere.example.com"++_]}} = eldap:delete(H, DN).
+
+%%%----------------------------------------------------------------
modify_dn_delete_old(Config) ->
H = ?config(handle, Config),
BasePath = ?config(eldap_path, Config),
@@ -817,25 +872,44 @@ delete_old_contents(H, Path) ->
{filter, eldap:present("objectclass")},
{scope, eldap:wholeSubtree()}])
of
- {ok, #eldap_search_result{entries=Entries}} ->
+ {ok, _R=#eldap_search_result{entries=Entries}} ->
+ case eldap:delete(H, "dc=notHere,"++Path, [?manageDsaIT]) of
+ ok -> ok;
+ {error,noSuchObject} -> ok;
+ Other -> ct:fail("eldap:delete notHere ret ~p",[Other])
+ end,
[ok = eldap:delete(H,DN) || #eldap_entry{object_name=DN} <- Entries];
_Res ->
ignore
end.
+
+-define(ok(X), ok(?MODULE,?LINE,X)).
+
add_new_contents(H, Path, MyHost) ->
- ok(eldap:add(H,"dc=ericsson,dc=se",
+ ?ok(eldap:add(H,"dc=ericsson,dc=se",
[{"objectclass", ["dcObject", "organization"]},
{"dc", ["ericsson"]},
{"o", ["Testing"]}])),
- ok(eldap:add(H,Path,
+ ?ok(eldap:add(H,Path,
[{"objectclass", ["dcObject", "organization"]},
{"dc", [MyHost]},
- {"o", ["Test machine"]}])).
-
-
-ok({error,entryAlreadyExists}) -> ok;
-ok(X) -> ok=X.
+ {"o", ["Test machine"]}])),
+ ?ok(eldap:add(H, "dc=notHere,"++Path,
+ [{"objectclass", ["referral",
+ "dcObject"
+ ]},
+ {"ref", ["ldap://nowhere.example.com/notHere,"++Path]},
+ {"dc", ["notHere"]}
+ ])).
+
+
+
+ok(_, _, {error,entryAlreadyExists}) -> ok;
+ok(_, _, ok) -> ok;
+ok(MODULE, LINE, X) ->
+ ct:pal("~p:~p add_new_contents: ret from eldap:add = ~p",[MODULE,LINE,X]),
+ X.
diff --git a/lib/eldap/vsn.mk b/lib/eldap/vsn.mk
index 105a2bcdbb..99c474d588 100644
--- a/lib/eldap/vsn.mk
+++ b/lib/eldap/vsn.mk
@@ -1 +1 @@
-ELDAP_VSN = 1.2
+ELDAP_VSN = 1.2.1
diff --git a/lib/erl_interface/configure.in b/lib/erl_interface/configure.in
index 3ac9212085..7a3b5ce378 100644
--- a/lib/erl_interface/configure.in
+++ b/lib/erl_interface/configure.in
@@ -251,7 +251,7 @@ case "$threads_disabled" in
;;
win32_threads)
EI_THREADS="true"
- THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0500 -DWINVER=0x0500"
+ THR_DEFS="$THR_DEFS -D_WIN32_WINNT=0x0600 -DWINVER=0x0600"
;;
pthread)
EI_THREADS="true"
diff --git a/lib/erl_interface/doc/src/ei.xml b/lib/erl_interface/doc/src/ei.xml
index 6044ac8ef7..8ef433d10f 100644
--- a/lib/erl_interface/doc/src/ei.xml
+++ b/lib/erl_interface/doc/src/ei.xml
@@ -264,9 +264,9 @@ typedef enum {
<fsummary>Encode an atom</fsummary>
<desc>
<p>Encodes an atom in the binary format with character encoding
- <c><seealso marker="#erlang_char_encoding">to_enc</seealso></c> (latin1 or utf8).
+ <seealso marker="#erlang_char_encoding"><c>to_enc</c></seealso> (latin1 or utf8).
The <c>p</c> parameter is the name of the atom with character encoding
- <c><seealso marker="#erlang_char_encoding">from_enc</seealso></c> (ascii, latin1 or utf8).
+ <seealso marker="#erlang_char_encoding"><c>from_enc</c></seealso> (ascii, latin1 or utf8).
The name must either be zero-terminated or a function variant with a <c>len</c>
parameter must be used. If <c>to_enc</c> is set to the bitwise-or'd combination
<c>(ERLANG_LATIN1|ERLANG_UTF8)</c>, utf8 encoding is only used if the atom string
@@ -569,8 +569,8 @@ ei_x_encode_string(&amp;x, "Banana");
<p>This function decodes an atom from the binary format. The
null terminated name of the atom is placed in buffer at <c>p</c> of length
<c>plen</c> bytes.</p>
- <p>The wanted string encoding is specified by <c><seealso marker="#erlang_char_encoding">
- want</seealso></c>. The original encoding used in the
+ <p>The wanted string encoding is specified by <seealso marker="#erlang_char_encoding">
+ <c>want</c></seealso>. The original encoding used in the
binary format (latin1 or utf8) can be obtained from <c>*was</c>. The actual encoding of the resulting string
(7-bit ascii, latin1 or utf8) can be obtained from <c>*result</c>. Both <c>was</c> and <c>result</c> can be <c>NULL</c>.
diff --git a/lib/erl_interface/doc/src/erl_call.xml b/lib/erl_interface/doc/src/erl_call.xml
index aed1ba0ac9..e982bf89e1 100644
--- a/lib/erl_interface/doc/src/erl_call.xml
+++ b/lib/erl_interface/doc/src/erl_call.xml
@@ -178,7 +178,7 @@ erl_call -s -a 'erlang halt' -n madonna
<code type="none"><![CDATA[
erl_call -s -a 'lists map [{math,sqrt},[1,4,9,16,25]]' -n madonna
]]></code>
- <p>Evaluates a couple of expressions. <b>The input ends with EOF (Control-D)</b>.</p>
+ <p>Evaluates a couple of expressions. <em>The input ends with EOF (Control-D)</em>.</p>
<code type="none"><![CDATA[
erl_call -s -e -n madonna
statistics(runtime),
@@ -189,7 +189,7 @@ Y=2,
^D
{ok,{3,0}}
]]></code>
- <p>Compiles a module and runs it. <b>Again, the input ends with EOF (Control-D)</b>. (In the example shown, the output has been formatted afterwards).</p>
+ <p>Compiles a module and runs it. <em>Again, the input ends with EOF (Control-D)</em>. (In the example shown, the output has been formatted afterwards).</p>
<code type="none"><![CDATA[
erl_call -s -m -a lolita -n madonna
-module(lolita).
diff --git a/lib/erl_interface/doc/src/erl_eterm.xml b/lib/erl_interface/doc/src/erl_eterm.xml
index 9a53a137c2..713a90a390 100644
--- a/lib/erl_interface/doc/src/erl_eterm.xml
+++ b/lib/erl_interface/doc/src/erl_eterm.xml
@@ -78,10 +78,12 @@
</p>
<taglist>
<tag><c><![CDATA[char *ERL_ATOM_PTR(t)]]></c></tag>
+ <item/>
<tag><c><![CDATA[char *ERL_ATOM_PTR_UTF8(t)]]></c></tag>
<item>A string representing atom <c><![CDATA[t]]></c>.
</item>
<tag><c><![CDATA[int ERL_ATOM_SIZE(t)]]></c></tag>
+ <item/>
<tag><c><![CDATA[int ERL_ATOM_SIZE_UTF8(t)]]></c></tag>
<item>The length (in bytes) of atom t.</item>
<tag><c><![CDATA[void *ERL_BIN_PTR(t)]]></c></tag>
@@ -95,6 +97,7 @@
<tag><c><![CDATA[double ERL_FLOAT_VALUE(t)]]></c></tag>
<item>The floating point value of <c><![CDATA[t]]></c>.</item>
<tag><c><![CDATA[ETERM *ERL_PID_NODE(t)]]></c></tag>
+ <item/>
<tag><c><![CDATA[ETERM *ERL_PID_NODE_UTF8(t)]]></c></tag>
<item>The Node in pid <c><![CDATA[t]]></c>.</item>
<tag><c><![CDATA[int ERL_PID_NUMBER(t)]]></c></tag>
@@ -108,6 +111,7 @@
<tag><c><![CDATA[int ERL_PORT_CREATION(t)]]></c></tag>
<item>The creation number in port <c><![CDATA[t]]></c>.</item>
<tag><c><![CDATA[ETERM *ERL_PORT_NODE(t)]]></c></tag>
+ <item/>
<tag><c><![CDATA[ETERM *ERL_PORT_NODE_UTF8(t)]]></c></tag>
<item>The node in port <c><![CDATA[t]]></c>.</item>
<tag><c><![CDATA[int ERL_REF_NUMBER(t)]]></c></tag>
diff --git a/lib/hipe/icode/hipe_icode_primops.erl b/lib/hipe/icode/hipe_icode_primops.erl
index 84aae30291..a0deb31c42 100644
--- a/lib/hipe/icode/hipe_icode_primops.erl
+++ b/lib/hipe/icode/hipe_icode_primops.erl
@@ -504,16 +504,16 @@ type(Primop, Args) ->
NewBinType = match_bin(erl_types:t_bitstr(0, Size), BinType),
NewMatchState =
erl_types:t_matchstate_update_present(NewBinType, MatchState),
- if Signed =:= 0 ->
- UpperBound = inf_add(safe_bsl(1, Size), -1),
- erl_types:t_product([erl_types:t_from_range(0, UpperBound),
- NewMatchState]);
- Signed =:= 4 ->
- erl_types:t_product([erl_types:t_from_range(
- inf_inv(safe_bsl(1, Size-1)),
- inf_add(safe_bsl(1, Size-1), -1)),
- NewMatchState])
- end;
+ Range =
+ case Signed of
+ 0 ->
+ UpperBound = inf_add(safe_bsl_1(Size), -1),
+ erl_types:t_from_range(0, UpperBound);
+ 4 ->
+ Bound = safe_bsl_1(Size - 1),
+ erl_types:t_from_range(inf_inv(Bound), inf_add(Bound, -1))
+ end,
+ erl_types:t_product([Range, NewMatchState]);
[_Arg] ->
NewBinType = match_bin(erl_types:t_bitstr(Size, 0), BinType),
NewMatchState =
@@ -969,18 +969,19 @@ check_fun_args(_, _) ->
match_bin(Pattern, Match) ->
erl_types:t_bitstr_match(Pattern, Match).
-safe_bsl(0, _) -> 0;
-safe_bsl(Base, Shift) when Shift =< 128 -> Base bsl Shift;
-safe_bsl(Base, _Shift) when Base > 0 -> pos_inf;
-safe_bsl(Base, _Shift) when Base < 0 -> neg_inf.
+-spec safe_bsl_1(non_neg_integer()) -> non_neg_integer() | 'pos_inf'.
+
+safe_bsl_1(Shift) when Shift =< 128 -> 1 bsl Shift;
+safe_bsl_1(_Shift) -> pos_inf.
+
+%%
+%% The following two functions are stripped-down versions of more
+%% general functions that exist in hipe_icode_range.erl
+%%
inf_inv(pos_inf) -> neg_inf;
-inf_inv(neg_inf) -> pos_inf;
-inf_inv(Number) -> -Number.
+inf_inv(Number) when is_integer(Number) -> -Number.
inf_add(pos_inf, _Number) -> pos_inf;
-inf_add(neg_inf, _Number) -> neg_inf;
-inf_add(_Number, pos_inf) -> pos_inf;
-inf_add(_Number, neg_inf) -> neg_inf;
inf_add(Number1, Number2) when is_integer(Number1), is_integer(Number2) ->
Number1 + Number2.
diff --git a/lib/inets/src/http_server/httpd_request_handler.erl b/lib/inets/src/http_server/httpd_request_handler.erl
index 134576059d..8fae9ac46e 100644
--- a/lib/inets/src/http_server/httpd_request_handler.erl
+++ b/lib/inets/src/http_server/httpd_request_handler.erl
@@ -102,8 +102,8 @@ init([Manager, ConfigDB, AcceptTimeout]) ->
KeepAliveTimeOut = 1000 * httpd_util:lookup(ConfigDB, keep_alive_timeout, 150),
case http_transport:negotiate(SocketType, Socket, ?HANDSHAKE_TIMEOUT) of
- {error, _Error} ->
- exit(shutdown); %% Can be 'normal'.
+ {error, Error} ->
+ exit({shutdown, Error}); %% Can be 'normal'.
ok ->
continue_init(Manager, ConfigDB, SocketType, Socket, KeepAliveTimeOut)
end.
@@ -294,7 +294,10 @@ handle_info(Info, #state{mod = ModData} = State) ->
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
-terminate(normal, State) ->
+terminate(Reason, State) when Reason == normal;
+ Reason == shutdown ->
+ do_terminate(State);
+terminate({shutdown,_}, State) ->
do_terminate(State);
terminate(Reason, #state{response_sent = false, mod = ModData} = State) ->
httpd_response:send_status(ModData, 500, none),
diff --git a/lib/inets/src/tftp/tftp_engine.erl b/lib/inets/src/tftp/tftp_engine.erl
index 8d282a1e9d..493a29a68f 100644
--- a/lib/inets/src/tftp/tftp_engine.erl
+++ b/lib/inets/src/tftp/tftp_engine.erl
@@ -656,22 +656,11 @@ common_read(Config, Callback, Req, _LocalAccess, ExpectedBlockNo, ActualBlockNo,
do_common_read(Config, Callback, Req, LocalAccess, BlockNo, Data, Prepared)
when is_binary(Data), is_record(Prepared, prepared) ->
- NextBlockNo = BlockNo + 1,
- case NextBlockNo =< 65535 of
- true ->
- Reply = #tftp_msg_data{block_no = NextBlockNo, data = Data},
- {Config2, Callback2, TransferRes} =
- transfer(Config, Callback, Req, Reply, LocalAccess, NextBlockNo, Prepared),
- ?MODULE:common_loop(Config2, Callback2, Req, TransferRes, LocalAccess, NextBlockNo);
- false ->
- Code = badblk,
- Text = "Too big transfer ID = " ++
- integer_to_list(NextBlockNo) ++ " > 65535",
- {undefined, Error} =
- callback({abort, {Code, Text}}, Config, Callback, Req),
- send_msg(Config, Req, Error),
- terminate(Config, Req, ?ERROR(read, Code, Text, Req#tftp_msg_req.filename))
- end.
+ NextBlockNo = (BlockNo + 1) rem 65536,
+ Reply = #tftp_msg_data{block_no = NextBlockNo, data = Data},
+ {Config2, Callback2, TransferRes} =
+ transfer(Config, Callback, Req, Reply, LocalAccess, NextBlockNo, Prepared),
+ ?MODULE:common_loop(Config2, Callback2, Req, TransferRes, LocalAccess, NextBlockNo).
-spec common_write(#config{}, #callback{}, _, 'write', integer(), integer(), _, #prepared{}) -> no_return().
@@ -715,21 +704,10 @@ common_write(Config, Callback, Req, _, ExpectedBlockNo, ActualBlockNo, Data, Pre
common_ack(Config, Callback, Req, LocalAccess, BlockNo, Prepared)
when is_record(Prepared, prepared) ->
Reply = #tftp_msg_ack{block_no = BlockNo},
- NextBlockNo = BlockNo + 1,
+ NextBlockNo = (BlockNo + 1) rem 65536,
{Config2, Callback2, TransferRes} =
transfer(Config, Callback, Req, Reply, LocalAccess, NextBlockNo, Prepared),
- case NextBlockNo =< 65535 of
- true ->
- ?MODULE:common_loop(Config2, Callback2, Req, TransferRes, LocalAccess, NextBlockNo);
- false ->
- Code = badblk,
- Text = "Too big transfer ID = " ++
- integer_to_list(NextBlockNo) ++ " > 65535",
- {undefined, Error} =
- callback({abort, {Code, Text}}, Config, Callback2, Req),
- send_msg(Config, Req, Error),
- terminate(Config, Req, ?ERROR(read, Code, Text, Req#tftp_msg_req.filename))
- end.
+ ?MODULE:common_loop(Config2, Callback2, Req, TransferRes, LocalAccess, NextBlockNo).
pre_terminate(Config, Req, Result) ->
if
diff --git a/lib/inets/test/tftp_SUITE.erl b/lib/inets/test/tftp_SUITE.erl
index d29d210d7d..497a50e654 100644
--- a/lib/inets/test/tftp_SUITE.erl
+++ b/lib/inets/test/tftp_SUITE.erl
@@ -76,7 +76,7 @@ suite() -> [{ct_hooks,[ts_install_cth]}].
all() ->
[simple, extra, reuse_connection, resend_client,
- resend_server].
+ resend_server, large_file].
groups() ->
[].
@@ -902,6 +902,41 @@ reuse_connection(Config) when is_list(Config) ->
ok.
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%% Large file: transfer > 65535 blocks
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+large_file(doc) ->
+ ["Start the daemon and test transfer of files greater than 32M."];
+large_file(suite) ->
+ [];
+large_file(Config) when is_list(Config) ->
+ ?VERIFY(ok, application:start(inets)),
+
+ {Port, DaemonPid} = ?IGNORE(?START_DAEMON(0, [{debug, brief}])),
+
+ %% Read fail
+ RemoteFilename = "tftp_temporary_large_file_remote_test_file.txt",
+ LocalFilename = "tftp_temporary_large_file_local_test_file.txt",
+
+ {ok, FH} = file:open(LocalFilename, [write,exclusive]),
+ {ok, Size} = file:position(FH, {eof, 2*512*65535}),
+ ok = file:truncate(FH),
+ ?IGNORE(file:close(FH)),
+
+ %% Write and read
+ ?VERIFY({ok, Size}, tftp:write_file(RemoteFilename, LocalFilename, [{port, Port}])),
+ ?IGNORE(file:delete(LocalFilename)),
+ ?VERIFY({ok, Size}, tftp:read_file(RemoteFilename, LocalFilename, [{port, Port}])),
+
+ %% Cleanup
+ unlink(DaemonPid),
+ exit(DaemonPid, kill),
+ ?VERIFY(ok, file:delete(LocalFilename)),
+ ?VERIFY(ok, file:delete(RemoteFilename)),
+ ?VERIFY(ok, application:stop(inets)),
+ ok.
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%% Goodies
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
diff --git a/lib/kernel/doc/src/heart.xml b/lib/kernel/doc/src/heart.xml
index b9fad17ce1..9da4773f2d 100644
--- a/lib/kernel/doc/src/heart.xml
+++ b/lib/kernel/doc/src/heart.xml
@@ -118,6 +118,13 @@
<p>In the following descriptions, all function fails with reason
<c>badarg</c> if <c>heart</c> is not started.</p>
</description>
+
+ <datatypes>
+ <datatype>
+ <name name="heart_option"/>
+ </datatype>
+ </datatypes>
+
<funcs>
<func>
<name name="set_cmd" arity="1"/>
@@ -154,6 +161,62 @@
the empty string will be returned.</p>
</desc>
</func>
+
+ <func>
+ <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>
+ <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>
+ <name name="clear_callback" arity="0"/>
+ <fsummary>Clear the validation callback</fsummary>
+ <desc>
+ <p>Removes the validation callback call before heartbeats.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="get_callback" arity="0"/>
+ <fsummary>Get the validation callback</fsummary>
+ <desc>
+ <p>Get the validation callback. If the callback is cleared, <c>none</c> will be returned.</p>
+ </desc>
+ </func>
+
+ <func>
+ <name name="set_options" arity="1"/>
+ <fsummary>Set a list of options</fsummary>
+ <desc>
+ <p> Valid options <c>set_options</c> are: </p>
+ <taglist>
+ <tag><c>check_schedulers</c></tag>
+ <item>
+ <p>If enabled, a signal will be sent to each scheduler to check its
+ responsiveness. The system check occurs before any heartbeat sent
+ to the port program. If any scheduler is not responsive enough the
+ heart program will not receive its heartbeat and thus eventually terminate the node.
+ </p>
+ </item>
+ </taglist>
+ <p> Returns with the value <c>ok</c> if the options are valid.</p>
+ </desc>
+ </func>
+ <func>
+ <name name="get_options" arity="0"/>
+ <fsummary>Get the temporary reboot command</fsummary>
+ <desc>
+ <p>Returns <c>{ok, Options}</c> where <c>Options</c> is a list of current options enabled for heart.
+ If the callback is cleared, <c>none</c> will be returned.</p>
+ </desc>
+ </func>
+
+
</funcs>
</erlref>
diff --git a/lib/kernel/examples/uds_dist/c_src/uds_drv.c b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
index e32ad69adf..8c028ba910 100644
--- a/lib/kernel/examples/uds_dist/c_src/uds_drv.c
+++ b/lib/kernel/examples/uds_dist/c_src/uds_drv.c
@@ -957,28 +957,24 @@ static void put_packet_length(char *b, int len)
/*
** Malloc wrappers
-** Note!
-** The function erl_exit is actually not a pert of the
-** driver interface, but it is very nice to use if one wants to halt
-** with a core and an erlang crash dump.
*/
static void *my_malloc(size_t size)
{
- void erl_exit(int, char *, ...);
void *ptr;
if ((ptr = driver_alloc(size)) == NULL) {
- erl_exit(1,"Could not allocate %lu bytes of memory",(unsigned long) size);
+ fprintf(stderr, "Could not allocate %lu bytes of memory",(unsigned long) size);
+ abort();
}
return ptr;
}
static void *my_realloc(void *ptr, size_t size)
{
- void erl_exit(int, char *, ...);
void *nptr;
if ((nptr = driver_realloc(ptr, size)) == NULL) {
- erl_exit(1,"Could not reallocate %lu bytes of memory",(unsigned long) size);
+ fprintf(stderr, "Could not reallocate %lu bytes of memory",(unsigned long) size);
+ abort();
}
return nptr;
}
diff --git a/lib/kernel/src/erl_epmd.erl b/lib/kernel/src/erl_epmd.erl
index 55ce9a7e64..c6202dd796 100644
--- a/lib/kernel/src/erl_epmd.erl
+++ b/lib/kernel/src/erl_epmd.erl
@@ -32,7 +32,7 @@
%% External exports
-export([start/0, start_link/0, stop/0, port_please/2,
port_please/3, names/0, names/1,
- register_node/2, open/0, open/1, open/2]).
+ register_node/2, register_node/3, open/0, open/1, open/2]).
%% gen_server callbacks
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
@@ -102,7 +102,9 @@ names(EpmdAddr) ->
register_node(Name, PortNo) ->
- gen_server:call(erl_epmd, {register, Name, PortNo}, infinity).
+ register_node(Name, PortNo, inet).
+register_node(Name, PortNo, Family) ->
+ gen_server:call(erl_epmd, {register, Name, PortNo, Family}, infinity).
%%%----------------------------------------------------------------------
%%% Callback functions from gen_server
@@ -120,10 +122,10 @@ init(_) ->
-spec handle_call(calls(), term(), state()) ->
{'reply', term(), state()} | {'stop', 'shutdown', 'ok', state()}.
-handle_call({register, Name, PortNo}, _From, State) ->
+handle_call({register, Name, PortNo, Family}, _From, State) ->
case State#state.socket of
P when P < 0 ->
- case do_register_node(Name, PortNo) of
+ case do_register_node(Name, PortNo, Family) of
{alive, Socket, Creation} ->
S = State#state{socket = Socket,
port_no = PortNo,
@@ -206,8 +208,12 @@ open({A,B,C,D,E,F,G,H}=EpmdAddr, Timeout) when ?ip6(A,B,C,D,E,F,G,H) ->
close(Socket) ->
gen_tcp:close(Socket).
-do_register_node(NodeName, TcpPort) ->
- case open() of
+do_register_node(NodeName, TcpPort, Family) ->
+ Localhost = case Family of
+ inet -> open({127,0,0,1});
+ inet6 -> open({0,0,0,0,0,0,0,1})
+ end,
+ case Localhost of
{ok, Socket} ->
Name = to_string(NodeName),
Extra = "",
diff --git a/lib/kernel/src/gen_tcp.erl b/lib/kernel/src/gen_tcp.erl
index d7dba4ac80..8cb2a725e8 100644
--- a/lib/kernel/src/gen_tcp.erl
+++ b/lib/kernel/src/gen_tcp.erl
@@ -114,7 +114,8 @@
option().
-type socket() :: port().
--export_type([option/0, option_name/0, connect_option/0, listen_option/0]).
+-export_type([option/0, option_name/0, connect_option/0, listen_option/0,
+ socket/0]).
%%
%% Connect a socket
diff --git a/lib/kernel/src/heart.erl b/lib/kernel/src/heart.erl
index 137fad706f..eea78aabdf 100644
--- a/lib/kernel/src/heart.erl
+++ b/lib/kernel/src/heart.erl
@@ -34,7 +34,11 @@
%%%
%%% It recognizes the flag '-heart'
%%%--------------------------------------------------------------------
--export([start/0, init/2, set_cmd/1, clear_cmd/0, get_cmd/0, cycle/0]).
+-export([start/0, init/2,
+ set_cmd/1, clear_cmd/0, get_cmd/0,
+ set_callback/2, clear_callback/0, get_callback/0,
+ set_options/1, get_options/0,
+ cycle/0]).
-define(START_ACK, 1).
-define(HEART_BEAT, 2).
@@ -49,6 +53,16 @@
-define(CYCLE_TIMEOUT, 10000).
-define(HEART_PORT_NAME, heart_port).
+%% valid heart options
+-define(SCHEDULER_CHECK_OPT, check_schedulers).
+
+-type heart_option() :: ?SCHEDULER_CHECK_OPT.
+
+-record(state,{port :: port(),
+ cmd :: [] | binary(),
+ options :: [heart_option()],
+ callback :: 'undefined' | {atom(), atom()}}).
+
%%---------------------------------------------------------------------
-spec start() -> 'ignore' | {'error', term()} | {'ok', pid()}.
@@ -81,11 +95,11 @@ wait_for_init_ack(From) ->
init(Starter, Parent) ->
process_flag(trap_exit, true),
process_flag(priority, max),
- register(heart, self()),
+ register(?MODULE, self()),
case catch start_portprogram() of
{ok, Port} ->
Starter ! {ok, self()},
- loop(Parent, Port, "");
+ loop(Parent, #state{port=Port, cmd=[], options=[]});
no_heart ->
Starter ! {no_heart, self()};
error ->
@@ -96,33 +110,68 @@ init(Starter, Parent) ->
Cmd :: string().
set_cmd(Cmd) ->
- heart ! {self(), set_cmd, Cmd},
+ ?MODULE ! {self(), set_cmd, Cmd},
wait().
-spec get_cmd() -> {ok, Cmd} when
Cmd :: string().
get_cmd() ->
- heart ! {self(), get_cmd},
+ ?MODULE ! {self(), get_cmd},
wait().
-spec clear_cmd() -> ok.
clear_cmd() ->
- heart ! {self(), clear_cmd},
+ ?MODULE ! {self(), clear_cmd},
+ wait().
+
+-spec set_callback(Module,Function) -> 'ok' | {'error', {'bad_callback', {Module, Function}}} when
+ Module :: atom(),
+ Function :: atom().
+
+set_callback(Module, Function) ->
+ ?MODULE ! {self(), set_callback, {Module,Function}},
+ wait().
+
+-spec get_callback() -> {'ok', {Module, Function}} | 'none' when
+ Module :: atom(),
+ Function :: atom().
+
+get_callback() ->
+ ?MODULE ! {self(), get_callback},
+ wait().
+
+-spec clear_callback() -> ok.
+
+clear_callback() ->
+ ?MODULE ! {self(), clear_callback},
+ wait().
+
+-spec set_options(Options) -> 'ok' | {'error', {'bad_options', Options}} when
+ Options :: [heart_option()].
+
+set_options(Options) ->
+ ?MODULE ! {self(), set_options, Options},
wait().
+-spec get_options() -> {'ok', Options} | 'none' when
+ Options :: [atom()].
+
+get_options() ->
+ ?MODULE ! {self(), get_options},
+ wait().
%%% Should be used solely by the release handler!!!!!!!
-spec cycle() -> 'ok' | {'error', term()}.
cycle() ->
- heart ! {self(), cycle},
+ ?MODULE ! {self(), cycle},
wait().
wait() ->
receive
- {heart, Res} ->
+ {?MODULE, Res} ->
Res
end.
@@ -182,8 +231,8 @@ wait_ack(Port) ->
{error, Reason}
end.
-loop(Parent, Port, Cmd) ->
- _ = send_heart_beat(Port),
+loop(Parent, #state{port=Port}=S) ->
+ _ = send_heart_beat(S),
receive
{From, set_cmd, NewCmd0} ->
Enc = file:native_name_encoding(),
@@ -191,37 +240,72 @@ loop(Parent, Port, Cmd) ->
NewCmd when is_binary(NewCmd), byte_size(NewCmd) < 2047 ->
_ = send_heart_cmd(Port, NewCmd),
_ = wait_ack(Port),
- From ! {heart, ok},
- loop(Parent, Port, NewCmd);
+ From ! {?MODULE, ok},
+ loop(Parent, S#state{cmd=NewCmd});
_ ->
- From ! {heart, {error, {bad_cmd, NewCmd0}}},
- loop(Parent, Port, Cmd)
+ From ! {?MODULE, {error, {bad_cmd, NewCmd0}}},
+ loop(Parent, S)
end;
{From, clear_cmd} ->
- From ! {heart, ok},
- _ = send_heart_cmd(Port, ""),
+ From ! {?MODULE, ok},
+ _ = send_heart_cmd(Port, []),
_ = wait_ack(Port),
- loop(Parent, Port, "");
+ loop(Parent, S#state{cmd = []});
{From, get_cmd} ->
- From ! {heart, get_heart_cmd(Port)},
- loop(Parent, Port, Cmd);
+ From ! {?MODULE, get_heart_cmd(Port)},
+ loop(Parent, S);
+ {From, set_callback, Callback} ->
+ case Callback of
+ {M,F} when is_atom(M), is_atom(F) ->
+ From ! {?MODULE, ok},
+ loop(Parent, S#state{callback=Callback});
+ _ ->
+ From ! {?MODULE, {error, {bad_callback, Callback}}},
+ loop(Parent, S)
+ end;
+ {From, get_callback} ->
+ Res = case S#state.callback of
+ undefined -> none;
+ Cb -> {ok, Cb}
+ end,
+ From ! {?MODULE, Res},
+ loop(Parent, S);
+ {From, clear_callback} ->
+ From ! {?MODULE, ok},
+ loop(Parent, S#state{callback=undefined});
+ {From, set_options, Options} ->
+ case validate_options(Options) of
+ Validated when is_list(Validated) ->
+ From ! {?MODULE, ok},
+ loop(Parent, S#state{options=Validated});
+ _ ->
+ From ! {?MODULE, {error, {bad_options, Options}}},
+ loop(Parent, S)
+ end;
+ {From, get_options} ->
+ Res = case S#state.options of
+ [] -> none;
+ Cb -> {ok, Cb}
+ end,
+ From ! {?MODULE, Res},
+ loop(Parent, S);
{From, cycle} ->
%% Calls back to loop
- do_cycle_port_program(From, Parent, Port, Cmd);
+ do_cycle_port_program(From, Parent, S);
{'EXIT', Parent, shutdown} ->
no_reboot_shutdown(Port);
{'EXIT', Parent, Reason} ->
exit(Port, Reason),
exit(Reason);
{'EXIT', Port, badsig} -> % we can ignore badsig-messages!
- loop(Parent, Port, Cmd);
+ loop(Parent, S);
{'EXIT', Port, _Reason} ->
- exit({port_terminated, {heart, loop, [Parent, Port, Cmd]}});
+ exit({port_terminated, {?MODULE, loop, [Parent, S]}});
_ ->
- loop(Parent, Port, Cmd)
+ loop(Parent, S)
after
?TIMEOUT ->
- loop(Parent, Port, Cmd)
+ loop(Parent, S)
end.
-spec no_reboot_shutdown(port()) -> no_return().
@@ -233,36 +317,44 @@ no_reboot_shutdown(Port) ->
exit(normal)
end.
-do_cycle_port_program(Caller, Parent, Port, Cmd) ->
+validate_options(Opts) -> validate_options(Opts,[]).
+validate_options([],Res) -> Res;
+validate_options([?SCHEDULER_CHECK_OPT=Opt|Opts],Res) -> validate_options(Opts,[Opt|Res]);
+validate_options(_,_) -> error.
+
+do_cycle_port_program(Caller, Parent, #state{port=Port} = S) ->
unregister(?HEART_PORT_NAME),
case catch start_portprogram() of
{ok, NewPort} ->
_ = send_shutdown(Port),
receive
{'EXIT', Port, _Reason} ->
- _ = send_heart_cmd(NewPort, Cmd),
- Caller ! {heart, ok},
- loop(Parent, NewPort, Cmd)
+ _ = send_heart_cmd(NewPort, S#state.cmd),
+ Caller ! {?MODULE, ok},
+ loop(Parent, S#state{port=NewPort})
after
?CYCLE_TIMEOUT ->
%% Huh! Two heart port programs running...
%% well, the old one has to be sick not to respond
%% so we'll settle for the new one...
- _ = send_heart_cmd(NewPort, Cmd),
- Caller ! {heart, {error, stop_error}},
- loop(Parent, NewPort, Cmd)
+ _ = send_heart_cmd(NewPort, S#state.cmd),
+ Caller ! {?MODULE, {error, stop_error}},
+ loop(Parent, S#state{port=NewPort})
end;
no_heart ->
- Caller ! {heart, {error, no_heart}},
- loop(Parent, Port, Cmd);
+ Caller ! {?MODULE, {error, no_heart}},
+ loop(Parent, S);
error ->
- Caller ! {heart, {error, start_error}},
- loop(Parent, Port, Cmd)
+ Caller ! {?MODULE, {error, start_error}},
+ loop(Parent, S)
end.
%% "Beates" the heart once.
-send_heart_beat(Port) -> Port ! {self(), {command, [?HEART_BEAT]}}.
+send_heart_beat(#state{port=Port, callback=Cb, options=Opts}) ->
+ ok = check_system(Opts),
+ ok = check_callback(Cb),
+ Port ! {self(), {command, [?HEART_BEAT]}}.
%% Set a new HEART_COMMAND.
-dialyzer({no_improper_lists, send_heart_cmd/2}).
@@ -278,6 +370,24 @@ get_heart_cmd(Port) ->
{ok, Cmd}
end.
+check_system([]) -> ok;
+check_system([?SCHEDULER_CHECK_OPT|Opts]) ->
+ ok = erts_internal:system_check(schedulers),
+ check_system(Opts).
+
+%% validate system by performing a check before the heartbeat
+%% return 'ok' if everything is alright.
+%% Terminate if with reason if something is a miss.
+%% It is fine to timeout in the callback, in fact that is the intention
+%% if something goes wront -> no heartbeat.
+
+check_callback(Callback) ->
+ case Callback of
+ undefined -> ok;
+ {M,F} ->
+ erlang:apply(M,F,[])
+ end.
+
%% Sends shutdown command to the port.
send_shutdown(Port) -> Port ! {self(), {command, [?SHUT_DOWN]}}.
diff --git a/lib/kernel/src/kernel.appup.src b/lib/kernel/src/kernel.appup.src
index 860d3640d0..cc9e6f771a 100644
--- a/lib/kernel/src/kernel.appup.src
+++ b/lib/kernel/src/kernel.appup.src
@@ -18,9 +18,9 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"4\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
+ [{<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
{<<"3\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17
%% Down to - max one major revision back
- [{<<"4\\.[0-1](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
+ [{<<"4\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
{<<"3\\.[0-2](\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-17
}.
diff --git a/lib/kernel/test/heart_SUITE.erl b/lib/kernel/test/heart_SUITE.erl
index 83efbb4c35..39cd29cea0 100644
--- a/lib/kernel/test/heart_SUITE.erl
+++ b/lib/kernel/test/heart_SUITE.erl
@@ -27,6 +27,8 @@
node_start_immediately_after_crash/1,
node_start_soon_after_crash/1,
set_cmd/1, clear_cmd/1, get_cmd/1,
+ callback_api/1,
+ options_api/1,
dont_drop/1, kill_pid/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -66,6 +68,8 @@ all() -> [
node_start_immediately_after_crash,
node_start_soon_after_crash,
set_cmd, clear_cmd, get_cmd,
+ callback_api,
+ options_api,
kill_pid
].
@@ -358,6 +362,69 @@ get_cmd(Config) when is_list(Config) ->
stop_node(Node),
ok.
+callback_api(Config) when is_list(Config) ->
+ {ok, Node} = start_check(slave, heart_test),
+ none = rpc:call(Node, heart, get_callback, []),
+ M0 = self(),
+ F0 = ok,
+ {error, {bad_callback, {M0,F0}}} = rpc:call(Node, heart, set_callback, [M0,F0]),
+ none = rpc:call(Node, heart, get_callback, []),
+ M1 = lists:duplicate(28, $a),
+ F1 = lists:duplicate(28, $b),
+ {error, {bad_callback, {M1,F1}}} = rpc:call(Node, heart, set_callback, [M1,F1]),
+ none = rpc:call(Node, heart, get_callback, []),
+
+ M2 = heart_check_module,
+ F2 = cb_ok,
+ F3 = cb_error,
+ Code0 = generate(M2, [], [
+ atom_to_list(F2) ++ "() -> ok.",
+ atom_to_list(F3) ++ "() -> exit(\"callback_error (as intended)\")."
+ ]),
+ {module, M2} = rpc:call(Node, erlang, load_module, [M2, Code0]),
+ ok = rpc:call(Node, M2, F2, []),
+ ok = rpc:call(Node, heart, set_callback, [M2,F2]),
+ {ok, {M2,F2}} = rpc:call(Node, heart, get_callback, []),
+ ok = rpc:call(Node, heart, clear_callback, []),
+ none = rpc:call(Node, heart, get_callback, []),
+ ok = rpc:call(Node, heart, set_callback, [M2,F2]),
+ {ok, {M2,F2}} = rpc:call(Node, heart, get_callback, []),
+ ok = rpc:call(Node, heart, set_callback, [M2,F3]),
+ receive {nodedown, Node} -> ok
+ after 5000 -> test_server:fail(node_not_killed)
+ end,
+ stop_node(Node),
+ ok.
+
+options_api(Config) when is_list(Config) ->
+ {ok, Node} = start_check(slave, heart_test),
+ none = rpc:call(Node, heart, get_options, []),
+ M0 = self(),
+ F0 = ok,
+ {error, {bad_options, {M0,F0}}} = rpc:call(Node, heart, set_options, [{M0,F0}]),
+ none = rpc:call(Node, heart, get_options, []),
+ Ls = lists:duplicate(28, $b),
+ {error, {bad_options, Ls}} = rpc:call(Node, heart, set_options, [Ls]),
+ none = rpc:call(Node, heart, get_options, []),
+
+ ok = rpc:call(Node, heart, set_options, [[check_schedulers]]),
+ {ok, [check_schedulers]} = rpc:call(Node, heart, get_options, []),
+ ok = rpc:call(Node, heart, set_options, [[]]),
+ none = rpc:call(Node, heart, get_options, []),
+
+ ok = rpc:call(Node, heart, set_options, [[check_schedulers]]),
+ {ok, [check_schedulers]} = rpc:call(Node, heart, get_options, []),
+ {error, {bad_options, Ls}} = rpc:call(Node, heart, set_options, [Ls]),
+ {ok, [check_schedulers]} = rpc:call(Node, heart, get_options, []),
+
+ receive after 3000 -> ok end, %% wait 3 secs
+
+ ok = rpc:call(Node, heart, set_options, [[]]),
+ none = rpc:call(Node, heart, get_options, []),
+ stop_node(Node),
+ ok.
+
+
dont_drop(suite) ->
%%% Removed as it may crash epmd/distribution in colourful
%%% ways. While we ARE finding out WHY, it would
diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl
index 84af440245..eae4ee01b9 100644
--- a/lib/observer/test/crashdump_viewer_SUITE.erl
+++ b/lib/observer/test/crashdump_viewer_SUITE.erl
@@ -564,22 +564,11 @@ dump_with_strange_module_name(DataDir,Rel,DumpName) ->
CD.
dump(Node,DataDir,Rel,DumpName) ->
+ Crashdump = filename:join(DataDir, dump_prefix(Rel)++DumpName),
+ rpc:call(Node,os,putenv,["ERL_CRASH_DUMP",Crashdump]),
rpc:call(Node,erlang,halt,[DumpName]),
- Crashdump0 = filename:join(filename:dirname(code:which(?t)),
- "erl_crash_dump.n1"),
- Crashdump1 = filename:join(DataDir, dump_prefix(Rel)++DumpName),
- ok = rename(Crashdump0,Crashdump1),
- Crashdump1.
-
-rename(From,To) ->
- ok = check_complete(From),
- case file:rename(From,To) of
- {error,exdev} ->
- {ok,_} = file:copy(From,To),
- ok = file:delete(From);
- ok ->
- ok
- end.
+ ok = check_complete(Crashdump),
+ Crashdump.
check_complete(File) ->
check_complete1(File,10).
diff --git a/lib/public_key/src/pubkey_pem.erl b/lib/public_key/src/pubkey_pem.erl
index 6a722b0525..d163004c7c 100644
--- a/lib/public_key/src/pubkey_pem.erl
+++ b/lib/public_key/src/pubkey_pem.erl
@@ -103,7 +103,7 @@ encode_pem_entry({'PrivateKeyInfo', Der, EncParams}) ->
[StartStr, "\n", b64encode_and_split(EncDer), "\n", pem_end(StartStr) ,"\n\n"];
encode_pem_entry({Type, Der, {Cipher, Salt}}) ->
StartStr = pem_start(Type),
- [StartStr,"\n", pem_decrypt(),"\n", pem_decrypt_info(Cipher, Salt),"\n",
+ [StartStr,"\n", pem_decrypt(),"\n", pem_decrypt_info(Cipher, Salt),"\n\n",
b64encode_and_split(Der), "\n", pem_end(StartStr) ,"\n\n"].
decode_pem_entries([], Entries) ->
diff --git a/lib/public_key/test/public_key_SUITE.erl b/lib/public_key/test/public_key_SUITE.erl
index 5e677f31d6..ea5e036a7e 100644
--- a/lib/public_key/test/public_key_SUITE.erl
+++ b/lib/public_key/test/public_key_SUITE.erl
@@ -189,6 +189,8 @@ encrypted_pem(Config) when is_list(Config) ->
erl_make_certs:der_to_pem(DesKeyFile, [Entry1]),
[{'RSAPrivateKey', _, {"DES-CBC", Salt1}} =Entry2] =
erl_make_certs:pem_to_der(DesKeyFile),
+ {ok, Pem} = file:read_file(DesKeyFile),
+ check_encapsulated_header(Pem),
true = check_entry_type(public_key:pem_entry_decode(Entry2, "4567efgh"),
'RSAPrivateKey').
@@ -826,6 +828,15 @@ check_entry_type(#'Certificate'{}, 'Certificate') ->
check_entry_type(_,_) ->
false.
+check_encapsulated_header(Pem) when is_binary(Pem)->
+ check_encapsulated_header( binary:split(Pem, <<"\n">>, [global]));
+check_encapsulated_header([<<"DEK-Info: DES-CBC,FB7577791A9056A1">>, <<>> | _]) ->
+ true;
+check_encapsulated_header([ _ | Rest]) ->
+ check_encapsulated_header(Rest);
+check_encapsulated_header([]) ->
+ false.
+
strip_ending_newlines(Bin) ->
string:strip(binary_to_list(Bin), right, 10).
diff --git a/lib/runtime_tools/c_src/trace_file_drv.c b/lib/runtime_tools/c_src/trace_file_drv.c
index a63a7d3ad9..8863b0d6ac 100644
--- a/lib/runtime_tools/c_src/trace_file_drv.c
+++ b/lib/runtime_tools/c_src/trace_file_drv.c
@@ -75,12 +75,8 @@
#ifdef DEBUG
-#ifndef __WIN32__
-#define ASSERT(X) do {if (!(X)) {erl_exit(1,"%s",#X);} } while(0)
-#else
#include <assert.h>
#define ASSERT(X) assert(X)
-#endif
#else
#define ASSERT(X)
#endif
diff --git a/lib/runtime_tools/c_src/trace_ip_drv.c b/lib/runtime_tools/c_src/trace_ip_drv.c
index f7b5ea65cb..5b43f8179e 100644
--- a/lib/runtime_tools/c_src/trace_ip_drv.c
+++ b/lib/runtime_tools/c_src/trace_ip_drv.c
@@ -44,19 +44,8 @@
#endif
#ifdef DEBUG
-# ifndef __WIN32__
- /* erl_exit is not available to dll_drivers on windows. */
- void erl_exit(int, char *, ...);
-# define ASSERT(X) \
- do { \
- if (!(X)) { \
- erl_exit(1,"%s",#X); \
- } \
- } while(0)
-# else
-# include <assert.h>
-# define ASSERT(X) assert(X)
-# endif
+# include <assert.h>
+# define ASSERT(X) assert(X)
#else
# define ASSERT(X)
#endif
diff --git a/lib/sasl/src/sasl.appup.src b/lib/sasl/src/sasl.appup.src
index 8faa0afbd4..e08ae369b8 100644
--- a/lib/sasl/src/sasl.appup.src
+++ b/lib/sasl/src/sasl.appup.src
@@ -18,9 +18,9 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"2\\.[5-6](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
+ [{<<"2\\.[5-7](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}], % OTP-17
%% Down to - max one major revision back
- [{<<"2\\.[5-6](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
+ [{<<"2\\.[5-7](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
{<<"2\\.4(\\.[0-9]+)*">>,[restart_new_emulator]}] % OTP-17
}.
diff --git a/lib/ssh/doc/src/ssh_sftp.xml b/lib/ssh/doc/src/ssh_sftp.xml
index c6ca0f161a..f4b41b74f3 100644
--- a/lib/ssh/doc/src/ssh_sftp.xml
+++ b/lib/ssh/doc/src/ssh_sftp.xml
@@ -333,7 +333,7 @@
<func>
<name>position(ChannelPid, Handle, Location) -></name>
- <name>position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition | {error, Error}</name>
+ <name>position(ChannelPid, Handle, Location, Timeout) -> {ok, NewPosition} | {error, Reason}</name>
<fsummary>Sets the file position of a file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -399,7 +399,7 @@
<func>
<name>pwrite(ChannelPid, Handle, Position, Data) -> ok</name>
- <name>pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | {error, Error}</name>
+ <name>pwrite(ChannelPid, Handle, Position, Data, Timeout) -> ok | {error, Reason}</name>
<fsummary>Writes to an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
@@ -592,7 +592,7 @@
<func>
<name>write(ChannelPid, Handle, Data) -></name>
- <name>write(ChannelPid, Handle, Data, Timeout) -> ok | {error, Error}</name>
+ <name>write(ChannelPid, Handle, Data, Timeout) -> ok | {error, Reason}</name>
<fsummary>Writes to an open file.</fsummary>
<type>
<v>ChannelPid = pid()</v>
diff --git a/lib/ssh/test/ssh_algorithms_SUITE.erl b/lib/ssh/test/ssh_algorithms_SUITE.erl
index f0ac92fef6..49ed15698c 100644
--- a/lib/ssh/test/ssh_algorithms_SUITE.erl
+++ b/lib/ssh/test/ssh_algorithms_SUITE.erl
@@ -35,7 +35,8 @@
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,10}}].
all() ->
%% [{group,kex},{group,cipher}... etc
@@ -90,18 +91,12 @@ init_per_suite(Config) ->
?MAX_NUM_ALGORITHMS
]),
ct:log("all() ->~n ~p.~n~ngroups()->~n ~p.~n",[all(),groups()]),
- catch crypto:stop(),
- case catch crypto:start() of
- ok ->
- ssh:start(),
- [{std_simple_sftp_size,25000} % Sftp transferred data size
- | setup_pubkey(Config)];
- _Else ->
- {skip, "Crypto could not be started!"}
- end.
+ ssh:start(),
+ [{std_simple_sftp_size,25000} % Sftp transferred data size
+ | setup_pubkey(Config)].
+
end_per_suite(_Config) ->
- ssh:stop(),
- crypto:stop().
+ ssh:stop().
init_per_group(Group, Config) ->
@@ -231,8 +226,11 @@ sshc_simple_exec(Config) ->
" ",Host," 1+1."]),
ct:log("~p",[Cmd]),
SshPort = open_port({spawn, Cmd}, [binary]),
+ Expect = <<"2\n">>,
receive
- {SshPort,{data, <<"2\n">>}} ->
+ {SshPort, {data,Expect}} ->
+ ct:log("Got expected ~p from ~p",[Expect,SshPort]),
+ catch port_close(SshPort),
ok
after ?TIMEOUT ->
ct:fail("Did not receive answer")
@@ -273,7 +271,9 @@ sshd_simple_exec(_Config) ->
ConnectionRef, ChannelId1);
Other1 ->
ct:fail(Other1)
- end.
+ end,
+ ssh:close(ConnectionRef).
+
%%%================================================================
%%%
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 4b53f6ec57..094d28e879 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -78,7 +78,8 @@
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,10}}].
all() ->
[app_test,
@@ -129,16 +130,11 @@ basic_tests() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- catch crypto:stop(),
- case catch crypto:start() of
- ok ->
- Config;
- _Else ->
- {skip, "Crypto could not be started!"}
- end.
+ Config.
+
end_per_suite(_Config) ->
- ssh:stop(),
- crypto:stop().
+ ssh:stop().
+
%%--------------------------------------------------------------------
init_per_group(dsa_key, Config) ->
DataDir = ?config(data_dir, Config),
@@ -441,6 +437,7 @@ exec(Config) when is_list(Config) ->
ct:fail(Other1)
end,
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId1),
+ ssh:close(ConnectionRef),
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
@@ -474,6 +471,7 @@ exec_compressed(Config) when is_list(Config) ->
ct:fail(Other)
end,
ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
+ ssh:close(ConnectionRef),
ssh:stop_daemon(Pid)
end.
@@ -979,7 +977,10 @@ shell_no_unicode(Config) ->
new_do_shell(?config(io,Config),
[new_prompt,
{type,"io:format(\"hej ~p~n\",[42])."},
- {expect,"hej 42"}
+ {expect,"hej 42"},
+ {expect,"ok"},
+ new_prompt,
+ {type,"exit()."}
]).
%%--------------------------------------------------------------------
@@ -988,7 +989,9 @@ shell_unicode_string(Config) ->
[new_prompt,
{type,"io:format(\"こにちわ~ts~n\",[\"四二\"])."},
{expect,"こにちわ四二"},
- {expect,"ok"}
+ {expect,"ok"},
+ new_prompt,
+ {type,"exit()."}
]).
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_benchmark_SUITE.erl b/lib/ssh/test/ssh_benchmark_SUITE.erl
index e90bfa3d16..fe90da3028 100644
--- a/lib/ssh/test/ssh_benchmark_SUITE.erl
+++ b/lib/ssh/test/ssh_benchmark_SUITE.erl
@@ -44,9 +44,7 @@ groups() ->
init_per_suite(Config) ->
catch ssh:stop(),
- catch crypto:stop(),
try
- ok = crypto:start(),
report_client_algorithms(),
ok = ssh:start(),
{ok,TracerPid} = erlang_trace(),
@@ -58,7 +56,6 @@ init_per_suite(Config) ->
end_per_suite(_Config) ->
catch ssh:stop(),
- catch crypto:stop(),
ok.
@@ -406,7 +403,7 @@ function_algs_times_sizes(EncDecs, L) ->
end
|| EncDec <- EncDecs,
C = #call{mfa = ED,
- args = Args, %%[S,Data],
+ % args = Args, %%[S,Data],
t_call = T0,
t_return = T1} <- L,
ED == EncDec
diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl
index 1b93cc9c32..6e90faf0e8 100644
--- a/lib/ssh/test/ssh_connection_SUITE.erl
+++ b/lib/ssh/test/ssh_connection_SUITE.erl
@@ -36,6 +36,9 @@
%% suite() ->
%% [{ct_hooks,[ts_install_cth]}].
+suite() ->
+ [{timetrap,{minutes,2}}].
+
all() ->
[
{group, openssh},
@@ -67,16 +70,10 @@ ptty() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- catch crypto:stop(),
- case catch crypto:start() of
- ok ->
- Config;
- _Else ->
- {skip, "Crypto could not be started!"}
- end.
+ Config.
-end_per_suite(_Config) ->
- crypto:stop().
+end_per_suite(Config) ->
+ Config.
%%--------------------------------------------------------------------
init_per_group(openssh, Config) ->
diff --git a/lib/ssh/test/ssh_options_SUITE.erl b/lib/ssh/test/ssh_options_SUITE.erl
index 6a201d401f..ba0107efd6 100644
--- a/lib/ssh/test/ssh_options_SUITE.erl
+++ b/lib/ssh/test/ssh_options_SUITE.erl
@@ -79,7 +79,8 @@
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,6}}].
all() ->
[connectfun_disconnectfun_server,
@@ -125,16 +126,11 @@ groups() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- catch crypto:stop(),
- case catch crypto:start() of
- ok ->
- Config;
- _Else ->
- {skip, "Crypto could not be started!"}
- end.
+ Config.
+
end_per_suite(_Config) ->
- ssh:stop(),
- crypto:stop().
+ ssh:stop().
+
%%--------------------------------------------------------------------
init_per_group(hardening_tests, Config) ->
DataDir = ?config(data_dir, Config),
diff --git a/lib/ssh/test/ssh_protocol_SUITE.erl b/lib/ssh/test/ssh_protocol_SUITE.erl
index fe197f8672..44da0f4d6f 100644
--- a/lib/ssh/test/ssh_protocol_SUITE.erl
+++ b/lib/ssh/test/ssh_protocol_SUITE.erl
@@ -42,7 +42,8 @@
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,2}}].
all() ->
[{group,tool_tests},
@@ -579,23 +580,11 @@ client_handles_keyboard_interactive_0_pwds(Config) ->
%%%---- init_suite and end_suite ---------------------------------------
start_apps(Config) ->
- catch crypto:stop(),
- case catch crypto:start() of
- ok ->
- catch ssh:stop(),
- ok = ssh:start(),
- [{stop_apps,
- fun() ->
- ssh:stop(),
- crypto:stop()
- end} | Config];
- _Else ->
- {skip, "Crypto could not be started!"}
- end.
-
+ catch ssh:stop(),
+ ok = ssh:start(),
+ Config.
-stop_apps(Config) ->
- (?v(stop_apps, Config, fun()-> ok end))(),
+stop_apps(_Config) ->
ssh:stop().
diff --git a/lib/ssh/test/ssh_renegotiate_SUITE.erl b/lib/ssh/test/ssh_renegotiate_SUITE.erl
index e5cfa58bad..6d2c97aa68 100644
--- a/lib/ssh/test/ssh_renegotiate_SUITE.erl
+++ b/lib/ssh/test/ssh_renegotiate_SUITE.erl
@@ -30,7 +30,9 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
-suite() -> [{ct_hooks,[ts_install_cth]}].
+suite() -> [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,12}}].
+
all() -> [{group,default_algs},
{group,aes_gcm}
@@ -44,16 +46,10 @@ tests() -> [rekey, rekey_limit, renegotiate1, renegotiate2].
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- catch crypto:stop(),
- case catch crypto:start() of
- ok ->
- Config;
- _Else ->
- {skip, "Crypto could not be started!"}
- end.
+ Config.
+
end_per_suite(_Config) ->
- ssh:stop(),
- crypto:stop().
+ ssh:stop().
%%--------------------------------------------------------------------
init_per_group(aes_gcm, Config) ->
diff --git a/lib/ssh/test/ssh_sftp_SUITE.erl b/lib/ssh/test/ssh_sftp_SUITE.erl
index 698af259c8..c2b04d7a05 100644
--- a/lib/ssh/test/ssh_sftp_SUITE.erl
+++ b/lib/ssh/test/ssh_sftp_SUITE.erl
@@ -35,7 +35,9 @@
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,2}}].
+
all() ->
[{group, not_unicode},
@@ -44,22 +46,14 @@ all() ->
init_per_suite(Config) ->
- catch crypto:stop(),
- case (catch crypto:start()) of
- ok ->
- ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p",
- [file:native_name_encoding(),io:getopts()]),
- ssh:start(),
- Config;
- _ ->
- {skip,"Could not start crypto!"}
- end.
-
-end_per_suite(Config) ->
- ssh:stop(),
- crypto:stop(),
+ ct:log("file:native_name_encoding() = ~p,~nio:getopts() = ~p",
+ [file:native_name_encoding(),io:getopts()]),
+ ssh:start(),
Config.
+end_per_suite(_onfig) ->
+ ssh:stop().
+
%%--------------------------------------------------------------------
groups() ->
[{not_unicode, [], [{group,erlang_server},
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index 6b03a2b763..45439ce0fa 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -44,6 +44,9 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{minutes,3}}].
+
all() ->
[open_close_file,
open_close_dir,
@@ -69,28 +72,21 @@ groups() ->
%%--------------------------------------------------------------------
init_per_suite(Config) ->
- catch crypto:stop(),
- case (catch crypto:start()) of
- ok ->
- DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
- ssh_test_lib:setup_dsa(DataDir, PrivDir),
- %% to make sure we don't use public-key-auth
- %% this should be tested by other test suites
- UserDir = filename:join(?config(priv_dir, Config), nopubkey),
- file:make_dir(UserDir),
- Config;
- _ ->
- {skip,"Could not start crypto!"}
- end.
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ ssh_test_lib:setup_dsa(DataDir, PrivDir),
+ %% to make sure we don't use public-key-auth
+ %% this should be tested by other test suites
+ UserDir = filename:join(?config(priv_dir, Config), nopubkey),
+ file:make_dir(UserDir),
+ Config.
end_per_suite(Config) ->
SysDir = ?config(priv_dir, Config),
ssh_test_lib:clean_dsa(SysDir),
UserDir = filename:join(?config(priv_dir, Config), nopubkey),
file:del_dir(UserDir),
- ssh:stop(),
- crypto:stop().
+ ssh:stop().
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
index 7a025a6518..02a2ac4cf9 100644
--- a/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_erlclient_SUITE.erl
@@ -36,7 +36,9 @@
%%--------------------------------------------------------------------
suite() ->
- [{ct_hooks,[ts_install_cth]}].
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,2}}].
+
all() ->
[close_file,
@@ -53,29 +55,22 @@ groups() ->
init_per_suite(Config) ->
catch ssh:stop(),
- catch crypto:stop(),
- case catch crypto:start() of
- ok ->
- DataDir = ?config(data_dir, Config),
- PrivDir = ?config(priv_dir, Config),
- FileAlt = filename:join(DataDir, "ssh_sftpd_file_alt.erl"),
- c:c(FileAlt),
- FileName = filename:join(DataDir, "test.txt"),
- {ok, FileInfo} = file:read_file_info(FileName),
- ok = file:write_file_info(FileName,
- FileInfo#file_info{mode = 8#400}),
- ssh_test_lib:setup_dsa(DataDir, PrivDir),
- Config;
- _Else ->
- {skip,"Could not start ssh!"}
- end.
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ FileAlt = filename:join(DataDir, "ssh_sftpd_file_alt.erl"),
+ c:c(FileAlt),
+ FileName = filename:join(DataDir, "test.txt"),
+ {ok, FileInfo} = file:read_file_info(FileName),
+ ok = file:write_file_info(FileName,
+ FileInfo#file_info{mode = 8#400}),
+ ssh_test_lib:setup_dsa(DataDir, PrivDir),
+ Config.
end_per_suite(Config) ->
UserDir = filename:join(?config(priv_dir, Config), nopubkey),
file:del_dir(UserDir),
SysDir = ?config(priv_dir, Config),
ssh_test_lib:clean_dsa(SysDir),
- crypto:stop(),
ok.
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_sup_SUITE.erl b/lib/ssh/test/ssh_sup_SUITE.erl
index 5c77fcf1ef..18e91a9af3 100644
--- a/lib/ssh/test/ssh_sup_SUITE.erl
+++ b/lib/ssh/test/ssh_sup_SUITE.erl
@@ -34,6 +34,10 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+suite() ->
+ [{ct_hooks,[ts_install_cth]},
+ {timetrap,{minutes,1}}].
+
all() ->
[default_tree, sshc_subtree, sshd_subtree, sshd_subtree_profile].
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 2db55b97b4..5f91fb627a 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -120,7 +120,8 @@ std_simple_exec(Host, Port, Config, Opts) ->
Other ->
ct:fail(Other)
end,
- ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId).
+ ssh_test_lib:receive_exec_end(ConnectionRef, ChannelId),
+ ssh:close(ConnectionRef).
start_shell(Port, IOServer, UserDir) ->
@@ -154,14 +155,12 @@ loop_io_server(TestCase, Buff0) ->
{input, TestCase, Line} ->
loop_io_server(TestCase, Buff0 ++ [Line]);
{io_request, From, ReplyAs, Request} ->
-%%ct:log("~p",[{io_request, From, ReplyAs, Request}]),
{ok, Reply, Buff} = io_request(Request, TestCase, From,
ReplyAs, Buff0),
-%%ct:log("io_request(~p)-->~p",[Request,{ok, Reply, Buff}]),
io_reply(From, ReplyAs, Reply),
loop_io_server(TestCase, Buff);
- {'EXIT',_, _} ->
- erlang:display('ssh_test_lib:loop_io_server/2 EXIT'),
+ {'EXIT',_, _} = _Exit ->
+%% ct:log("ssh_test_lib:loop_io_server/2 got ~p",[_Exit]),
ok
after
30000 -> ct:fail("timeout ~p:~p",[?MODULE,?LINE])
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index 67a61d3c11..2788bc6b58 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -33,6 +33,9 @@
%% Common Test interface functions -----------------------------------
%%--------------------------------------------------------------------
+suite() ->
+ [{timetrap,{minutes,1}}].
+
all() ->
case os:find_executable("ssh") of
false ->
@@ -57,21 +60,14 @@ groups() ->
].
init_per_suite(Config) ->
- catch crypto:stop(),
- case catch crypto:start() of
- ok ->
- case gen_tcp:connect("localhost", 22, []) of
- {error,econnrefused} ->
- {skip,"No openssh deamon"};
- _ ->
- ssh_test_lib:openssh_sanity_check(Config)
- end;
- _Else ->
- {skip,"Could not start crypto!"}
+ case gen_tcp:connect("localhost", 22, []) of
+ {error,econnrefused} ->
+ {skip,"No openssh deamon"};
+ _ ->
+ ssh_test_lib:openssh_sanity_check(Config)
end.
end_per_suite(_Config) ->
- crypto:stop(),
ok.
init_per_group(erlang_server, Config) ->
diff --git a/lib/ssh/test/ssh_upgrade_SUITE.erl b/lib/ssh/test/ssh_upgrade_SUITE.erl
index 85f4d36258..bf8874b118 100644
--- a/lib/ssh/test/ssh_upgrade_SUITE.erl
+++ b/lib/ssh/test/ssh_upgrade_SUITE.erl
@@ -38,6 +38,9 @@
%%%
%%% CommonTest callbacks
%%%
+suite() ->
+ [{timetrap,{minutes,2}}].
+
all() ->
[
minor_upgrade,
@@ -45,27 +48,17 @@ all() ->
].
init_per_suite(Config0) ->
- catch crypto:stop(),
- try {crypto:start(), erlang:system_info({wordsize, internal}) ==
- erlang:system_info({wordsize, external})} of
- {ok, true} ->
- case ct_release_test:init(Config0) of
- {skip, Reason} ->
- {skip, Reason};
- Config ->
- ssh:start(),
- Config
- end;
- {ok, false} ->
- {skip, "Test server will not handle halfwordemulator correctly. Skip as halfwordemulator is deprecated"}
- catch _:_ ->
- {skip, "Crypto did not start"}
+ case ct_release_test:init(Config0) of
+ {skip, Reason} ->
+ {skip, Reason};
+ Config ->
+ ssh:start(),
+ Config
end.
end_per_suite(Config) ->
ct_release_test:cleanup(Config),
ssh:stop(),
- crypto:stop(),
UserDir = ?config(priv_dir, Config),
ssh_test_lib:clean_rsa(UserDir).
diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk
index 55d12abffe..41b42d454b 100644
--- a/lib/ssh/vsn.mk
+++ b/lib/ssh/vsn.mk
@@ -1,5 +1,5 @@
#-*-makefile-*- ; force emacs to enter makefile-mode
-SSH_VSN = 4.2.1
+SSH_VSN = 4.2.2
APP_VSN = "ssh-$(SSH_VSN)"
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index d3881ad117..a76d46ee9b 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -271,7 +271,11 @@ atom()}} |
terminate regarding verification failures and the connection is
established.</p></item>
<item><p>If called with an extension unknown to the user application,
- return value <c>{unknown, UserState}</c> is to be used.</p></item>
+ return value <c>{unknown, UserState}</c> is to be used.</p>
+
+ <p>Note that if the fun returns <c>unknown</c> for an extension marked
+ as critical, validation will fail.</p>
+ </item>
</list>
<p>Default option <c>verify_fun</c> in <c>verify_peer mode</c>:</p>
@@ -293,6 +297,8 @@ atom()}} |
<code>
{fun(_,{bad_cert, _}, UserState) ->
{valid, UserState};
+ (_,{extension, #'Extension'{critical = true}}, UserState) ->
+ {valid, UserState};
(_,{extension, _}, UserState) ->
{unknown, UserState};
(_, valid, UserState) ->
diff --git a/lib/ssl/doc/src/ssl_distribution.xml b/lib/ssl/doc/src/ssl_distribution.xml
index a347ce5ae6..dc04d446b0 100644
--- a/lib/ssl/doc/src/ssl_distribution.xml
+++ b/lib/ssl/doc/src/ssl_distribution.xml
@@ -271,4 +271,27 @@ Eshell V5.0 (abort with ^G)
<p>The <c>init:get_arguments()</c> call verifies that the correct
arguments are supplied to the emulator.</p>
</section>
+
+ <section>
+ <title>Using SSL distribution over IPv6</title>
+ <p>It is possible to use SSL distribution over IPv6 instead of
+ IPv4. To do this, pass the option <c>-proto_dist inet6_tls</c>
+ instead of <c>-proto_dist inet_tls</c> when starting Erlang,
+ either on the command line or in the <c>ERL_FLAGS</c> environment
+ variable.</p>
+
+ <p>An example command line with this option would look like this:</p>
+ <code type="none">
+$ erl -boot /home/me/ssl/start_ssl -proto_dist inet6_tls
+ -ssl_dist_opt server_certfile "/home/me/ssl/erlserver.pem"
+ -ssl_dist_opt server_secure_renegotiate true client_secure_renegotiate true
+ -sname ssl_test
+Erlang (BEAM) emulator version 5.0 [source]
+
+Eshell V5.0 (abort with ^G)
+(ssl_test@myhost)1> </code>
+
+ <p>A node started in this way will only be able to communicate with
+ other nodes using SSL distribution over IPv6.</p>
+ </section>
</chapter>
diff --git a/lib/ssl/examples/src/client_server.erl b/lib/ssl/examples/src/client_server.erl
index 799027123f..019b5130d2 100644
--- a/lib/ssl/examples/src/client_server.erl
+++ b/lib/ssl/examples/src/client_server.erl
@@ -26,9 +26,7 @@
start() ->
%% Start ssl application
- application:start(crypto),
- application:start(public_key),
- application:start(ssl),
+ {ok, StartedApps} = application:ensure_all_started(ssl),
%% Let the current process be the server that listens and accepts
%% Listen
@@ -52,7 +50,8 @@ start() ->
ssl:close(ASock),
io:fwrite("Listen: closing and terminating.~n"),
ssl:close(LSock),
- application:stop(ssl).
+
+ lists:foreach(fun application:stop/1, lists:reverse(StartedApps)).
%% Client connect
diff --git a/lib/ssl/src/Makefile b/lib/ssl/src/Makefile
index 790328dc45..7a7a373487 100644
--- a/lib/ssl/src/Makefile
+++ b/lib/ssl/src/Makefile
@@ -51,6 +51,7 @@ MODULES= \
ssl_dist_sup\
ssl_sup \
inet_tls_dist \
+ inet6_tls_dist \
ssl_certificate\
ssl_pkix_db\
ssl_cipher \
diff --git a/lib/ssl/src/inet6_tls_dist.erl b/lib/ssl/src/inet6_tls_dist.erl
new file mode 100644
index 0000000000..ffd7296f93
--- /dev/null
+++ b/lib/ssl/src/inet6_tls_dist.erl
@@ -0,0 +1,46 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2015. 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(inet6_tls_dist).
+
+-export([childspecs/0, listen/1, accept/1, accept_connection/5,
+ setup/5, close/1, select/1]).
+
+childspecs() ->
+ inet_tls_dist:childspecs().
+
+select(Node) ->
+ inet_tls_dist:gen_select(inet6_tcp, Node).
+
+listen(Name) ->
+ inet_tls_dist:gen_listen(inet6_tcp, Name).
+
+accept(Listen) ->
+ inet_tls_dist:gen_accept(inet6_tcp, Listen).
+
+accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+ inet_tls_dist:gen_accept_connection(inet6_tcp, AcceptPid, Socket, MyNode, Allowed, SetupTime).
+
+setup(Node, Type, MyNode, LongOrShortNames,SetupTime) ->
+ inet_tls_dist:gen_setup(inet6_tcp, Node, Type, MyNode, LongOrShortNames,SetupTime).
+
+close(Socket) ->
+ inet_tls_dist:close(Socket).
diff --git a/lib/ssl/src/inet_tls_dist.erl b/lib/ssl/src/inet_tls_dist.erl
index 6fe99a81c5..ec26142a75 100644
--- a/lib/ssl/src/inet_tls_dist.erl
+++ b/lib/ssl/src/inet_tls_dist.erl
@@ -24,6 +24,10 @@
-export([childspecs/0, listen/1, accept/1, accept_connection/5,
setup/5, close/1, select/1, is_node_name/1]).
+%% Generalized dist API
+-export([gen_listen/2, gen_accept/2, gen_accept_connection/6,
+ gen_setup/6, gen_select/2]).
+
-include_lib("kernel/include/net_address.hrl").
-include_lib("kernel/include/dist.hrl").
-include_lib("kernel/include/dist_util.hrl").
@@ -33,9 +37,15 @@ childspecs() ->
permanent, infinity, supervisor, [ssl_dist_sup]}]}.
select(Node) ->
+ gen_select(inet_tcp, Node).
+
+gen_select(Driver, Node) ->
case split_node(atom_to_list(Node), $@, []) of
- [_,_Host] ->
- true;
+ [_, Host] ->
+ case inet:getaddr(Host, Driver:family()) of
+ {ok, _} -> true;
+ _ -> false
+ end;
_ ->
false
end.
@@ -46,23 +56,35 @@ is_node_name(_) ->
false.
listen(Name) ->
- ssl_tls_dist_proxy:listen(Name).
+ gen_listen(inet_tcp, Name).
+
+gen_listen(Driver, Name) ->
+ ssl_tls_dist_proxy:listen(Driver, Name).
accept(Listen) ->
- ssl_tls_dist_proxy:accept(Listen).
+ gen_accept(inet_tcp, Listen).
+
+gen_accept(Driver, Listen) ->
+ ssl_tls_dist_proxy:accept(Driver, Listen).
accept_connection(AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+ gen_accept_connection(inet_tcp, AcceptPid, Socket, MyNode, Allowed, SetupTime).
+
+gen_accept_connection(Driver, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
Kernel = self(),
- spawn_link(fun() -> do_accept(Kernel, AcceptPid, Socket,
+ spawn_link(fun() -> do_accept(Driver, Kernel, AcceptPid, Socket,
MyNode, Allowed, SetupTime) end).
setup(Node, Type, MyNode, LongOrShortNames,SetupTime) ->
+ gen_setup(inet_tcp, Node, Type, MyNode, LongOrShortNames,SetupTime).
+
+gen_setup(Driver, Node, Type, MyNode, LongOrShortNames,SetupTime) ->
Kernel = self(),
- spawn_opt(fun() -> do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) end, [link, {priority, max}]).
+ spawn_opt(fun() -> do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) end, [link, {priority, max}]).
-do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
- [Name, Address] = splitnode(Node, LongOrShortNames),
- case inet:getaddr(Address, inet) of
+do_setup(Driver, Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
+ [Name, Address] = splitnode(Driver, Node, LongOrShortNames),
+ case inet:getaddr(Address, Driver:family()) of
{ok, Ip} ->
Timer = dist_util:start_timer(SetupTime),
case erl_epmd:port_please(Name, Ip) of
@@ -70,7 +92,7 @@ do_setup(Kernel, Node, Type, MyNode, LongOrShortNames, SetupTime) ->
?trace("port_please(~p) -> version ~p~n",
[Node,Version]),
dist_util:reset_timer(Timer),
- case ssl_tls_dist_proxy:connect(Ip, TcpPort) of
+ case ssl_tls_dist_proxy:connect(Driver, Ip, TcpPort) of
{ok, Socket} ->
HSData = connect_hs_data(Kernel, Node, MyNode, Socket,
Timer, Version, Ip, TcpPort, Address,
@@ -99,12 +121,12 @@ close(Socket) ->
gen_tcp:close(Socket),
ok.
-do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
+do_accept(Driver, Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
process_flag(priority, max),
receive
{AcceptPid, controller} ->
Timer = dist_util:start_timer(SetupTime),
- case check_ip(Socket) of
+ case check_ip(Driver, Socket) of
true ->
HSData = accept_hs_data(Kernel, MyNode, Socket, Timer, Allowed),
dist_util:handshake_other_started(HSData);
@@ -118,12 +140,12 @@ do_accept(Kernel, AcceptPid, Socket, MyNode, Allowed, SetupTime) ->
%% Do only accept new connection attempts from nodes at our
%% own LAN, if the check_ip environment parameter is true.
%% ------------------------------------------------------------
-check_ip(Socket) ->
+check_ip(Driver, Socket) ->
case application:get_env(check_ip) of
{ok, true} ->
case get_ifs(Socket) of
{ok, IFs, IP} ->
- check_ip(IFs, IP);
+ check_ip(Driver, IFs, IP);
_ ->
?shutdown(no_node)
end;
@@ -142,37 +164,21 @@ get_ifs(Socket) ->
Error
end.
-check_ip([{OwnIP, _, Netmask}|IFs], PeerIP) ->
- case {mask(Netmask, PeerIP), mask(Netmask, OwnIP)} of
+check_ip(Driver, [{OwnIP, _, Netmask}|IFs], PeerIP) ->
+ case {Driver:mask(Netmask, PeerIP), Driver:mask(Netmask, OwnIP)} of
{M, M} -> true;
_ -> check_ip(IFs, PeerIP)
end;
-check_ip([], PeerIP) ->
+check_ip(_Driver, [], PeerIP) ->
{false, PeerIP}.
-mask({M1,M2,M3,M4}, {IP1,IP2,IP3,IP4}) ->
- {M1 band IP1,
- M2 band IP2,
- M3 band IP3,
- M4 band IP4};
-
-mask({M1,M2,M3,M4, M5, M6, M7, M8}, {IP1,IP2,IP3,IP4, IP5, IP6, IP7, IP8}) ->
- {M1 band IP1,
- M2 band IP2,
- M3 band IP3,
- M4 band IP4,
- M5 band IP5,
- M6 band IP6,
- M7 band IP7,
- M8 band IP8}.
-
%% If Node is illegal terminate the connection setup!!
-splitnode(Node, LongOrShortNames) ->
+splitnode(Driver, Node, LongOrShortNames) ->
case split_node(atom_to_list(Node), $@, []) of
[Name|Tail] when Tail =/= [] ->
Host = lists:append(Tail),
- check_node(Name, Node, Host, LongOrShortNames);
+ check_node(Driver, Name, Node, Host, LongOrShortNames);
[_] ->
error_logger:error_msg("** Nodename ~p illegal, no '@' character **~n",
[Node]),
@@ -182,15 +188,20 @@ splitnode(Node, LongOrShortNames) ->
?shutdown(Node)
end.
-check_node(Name, Node, Host, LongOrShortNames) ->
+check_node(Driver, Name, Node, Host, LongOrShortNames) ->
case split_node(Host, $., []) of
[_] when LongOrShortNames == longnames ->
- error_logger:error_msg("** System running to use "
- "fully qualified "
- "hostnames **~n"
- "** Hostname ~s is illegal **~n",
- [Host]),
- ?shutdown(Node);
+ case Driver:parse_address(Host) of
+ {ok, _} ->
+ [Name, Host];
+ _ ->
+ error_logger:error_msg("** System running to use "
+ "fully qualified "
+ "hostnames **~n"
+ "** Hostname ~s is illegal **~n",
+ [Host]),
+ ?shutdown(Node)
+ end;
[_, _ | _] when LongOrShortNames == shortnames ->
error_logger:error_msg("** System NOT running to use fully qualified "
"hostnames **~n"
diff --git a/lib/ssl/src/ssl.app.src b/lib/ssl/src/ssl.app.src
index 619ab7b610..1a2bf90ccf 100644
--- a/lib/ssl/src/ssl.app.src
+++ b/lib/ssl/src/ssl.app.src
@@ -31,6 +31,7 @@
ssl_listen_tracker_sup,
%% Erlang Distribution over SSL/TLS
inet_tls_dist,
+ inet6_tls_dist,
ssl_tls_dist_proxy,
ssl_dist_sup,
%% SSL/TLS session handling
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index c1bc90559e..780bef5877 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -60,22 +60,19 @@
-spec start() -> ok | {error, reason()}.
-spec start(permanent | transient | temporary) -> ok | {error, reason()}.
%%
-%% Description: Utility function that starts the ssl,
-%% crypto and public_key applications. Default type
-%% is temporary. see application(3)
+%% Description: Utility function that starts the ssl and applications
+%% that it depends on.
+%% see application(3)
%%--------------------------------------------------------------------
start() ->
- application:start(crypto),
- application:start(asn1),
- application:start(public_key),
- application:start(ssl).
-
+ start(temporary).
start(Type) ->
- application:start(crypto, Type),
- application:start(asn1),
- application:start(public_key, Type),
- application:start(ssl, Type).
-
+ case application:ensure_all_started(ssl, Type) of
+ {ok, _} ->
+ ok;
+ Other ->
+ Other
+ end.
%%--------------------------------------------------------------------
-spec stop() -> ok.
%%
@@ -1296,6 +1293,12 @@ handle_verify_options(Opts, CaCerts) ->
DefaultVerifyNoneFun =
{fun(_,{bad_cert, _}, UserState) ->
{valid, UserState};
+ (_,{extension, #'Extension'{critical = true}}, UserState) ->
+ %% This extension is marked as critical, so
+ %% certificate verification should fail if we don't
+ %% understand the extension. However, this is
+ %% `verify_none', so let's accept it anyway.
+ {valid, UserState};
(_,{extension, _}, UserState) ->
{unknown, UserState};
(_, valid, UserState) ->
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index e9e140836b..e98073080a 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -2072,12 +2072,9 @@ crl_check(OtpCert, Check, CertDbHandle, CertDbRef, {Callback, CRLDbHandle}, _) -
],
case dps_and_crls(OtpCert, Callback, CRLDbHandle, ext) of
no_dps ->
- case dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer) of
- [] ->
- valid; %% No relevant CRL existed
- DpsAndCRls ->
- crl_check_same_issuer(OtpCert, Check, DpsAndCRls, Options)
- end;
+ crl_check_same_issuer(OtpCert, Check,
+ dps_and_crls(OtpCert, Callback, CRLDbHandle, same_issuer),
+ Options);
DpsAndCRLs -> %% This DP list may be empty if relevant CRLs existed
%% but could not be retrived, will result in {bad_cert, revocation_status_undetermined}
case public_key:pkix_crls_validate(OtpCert, DpsAndCRLs, Options) of
diff --git a/lib/ssl/src/ssl_tls_dist_proxy.erl b/lib/ssl/src/ssl_tls_dist_proxy.erl
index 211badef56..4c789793ec 100644
--- a/lib/ssl/src/ssl_tls_dist_proxy.erl
+++ b/lib/ssl/src/ssl_tls_dist_proxy.erl
@@ -20,7 +20,7 @@
-module(ssl_tls_dist_proxy).
--export([listen/1, accept/1, connect/2, get_tcp_address/1]).
+-export([listen/2, accept/2, connect/3, get_tcp_address/1]).
-export([init/1, start_link/0, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3, ssl_options/2]).
@@ -39,14 +39,14 @@
%% Internal application API
%%====================================================================
-listen(Name) ->
- gen_server:call(?MODULE, {listen, Name}, infinity).
+listen(Driver, Name) ->
+ gen_server:call(?MODULE, {listen, Driver, Name}, infinity).
-accept(Listen) ->
- gen_server:call(?MODULE, {accept, Listen}, infinity).
+accept(Driver, Listen) ->
+ gen_server:call(?MODULE, {accept, Driver, Listen}, infinity).
-connect(Ip, Port) ->
- gen_server:call(?MODULE, {connect, Ip, Port}, infinity).
+connect(Driver, Ip, Port) ->
+ gen_server:call(?MODULE, {connect, Driver, Ip, Port}, infinity).
do_listen(Options) ->
@@ -108,10 +108,11 @@ init([]) ->
process_flag(priority, max),
{ok, #state{}}.
-handle_call({listen, Name}, _From, State) ->
+handle_call({listen, Driver, Name}, _From, State) ->
case gen_tcp:listen(0, [{active, false}, {packet,?PPRE}, {ip, loopback}]) of
{ok, Socket} ->
- {ok, World} = do_listen([{active, false}, binary, {packet,?PPRE}, {reuseaddr, true}]),
+ {ok, World} = do_listen([{active, false}, binary, {packet,?PPRE}, {reuseaddr, true},
+ Driver:family()]),
{ok, TcpAddress} = get_tcp_address(Socket),
{ok, WorldTcpAddress} = get_tcp_address(World),
{_,Port} = WorldTcpAddress#net_address.address,
@@ -126,15 +127,15 @@ handle_call({listen, Name}, _From, State) ->
{reply, Error, State}
end;
-handle_call({accept, Listen}, {From, _}, State = #state{listen={_, World}}) ->
+handle_call({accept, _Driver, Listen}, {From, _}, State = #state{listen={_, World}}) ->
Self = self(),
ErtsPid = spawn_link(fun() -> accept_loop(Self, erts, Listen, From) end),
WorldPid = spawn_link(fun() -> accept_loop(Self, world, World, Listen) end),
{reply, ErtsPid, State#state{accept_loop={ErtsPid, WorldPid}}};
-handle_call({connect, Ip, Port}, {From, _}, State) ->
+handle_call({connect, Driver, Ip, Port}, {From, _}, State) ->
Me = self(),
- Pid = spawn_link(fun() -> setup_proxy(Ip, Port, Me) end),
+ Pid = spawn_link(fun() -> setup_proxy(Driver, Ip, Port, Me) end),
receive
{Pid, go_ahead, LPort} ->
Res = {ok, Socket} = try_connect(LPort),
@@ -263,10 +264,11 @@ try_connect(Port) ->
try_connect(Port)
end.
-setup_proxy(Ip, Port, Parent) ->
+setup_proxy(Driver, Ip, Port, Parent) ->
process_flag(trap_exit, true),
Opts = connect_options(get_ssl_options(client)),
- case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay()] ++ Opts) of
+ case ssl:connect(Ip, Port, [{active, true}, binary, {packet,?PPRE}, nodelay(),
+ Driver:family()] ++ Opts) of
{ok, World} ->
{ok, ErtsL} = gen_tcp:listen(0, [{active, true}, {ip, loopback}, binary, {packet,?PPRE}]),
{ok, #net_address{address={_,LPort}}} = get_tcp_address(ErtsL),
diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
index 968ef30791..d10506cb69 100644
--- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl
+++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl
@@ -66,7 +66,9 @@ tests() ->
invalid_signature_client,
invalid_signature_server,
extended_key_usage_verify_peer,
- extended_key_usage_verify_none].
+ extended_key_usage_verify_none,
+ critical_extension_verify_peer,
+ critical_extension_verify_none].
error_handling_tests()->
[client_with_cert_cipher_suites_handshake,
@@ -795,6 +797,121 @@ extended_key_usage_verify_none(Config) when is_list(Config) ->
ssl_test_lib:close(Client).
%%--------------------------------------------------------------------
+critical_extension_verify_peer() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_peer mode"}].
+
+critical_extension_verify_peer(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ NewCertName = integer_to_list(erlang:unique_integer()) ++ ".pem",
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join([PrivDir, "server", NewCertName]),
+ add_critical_netscape_cert_type(ServerCertFile, NewServerCertFile, KeyFile),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ NewClientCertFile = filename:join([PrivDir, "client", NewCertName]),
+ add_critical_netscape_cert_type(ClientCertFile, NewClientCertFile, KeyFile),
+ NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
+
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ Server = ssl_test_lib:start_server_error(
+ [{node, ServerNode}, {port, 0},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_peer}, {active, Active} | NewServerOpts]}]),
+ Port = ssl_test_lib:inet_port(Server),
+ Client = ssl_test_lib:start_client_error(
+ [{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {mfa, {ssl_test_lib, ReceiveFunction, []}},
+ {options, [{verify, verify_peer}, {active, Active} | NewClientOpts]}]),
+
+ %% This certificate has a critical extension that we don't
+ %% understand. Therefore, verification should fail.
+ tcp_delivery_workaround(Server, {error, {tls_alert, "unsupported certificate"}},
+ Client, {error, {tls_alert, "unsupported certificate"}}),
+
+ ssl_test_lib:close(Server),
+ ok.
+
+%%--------------------------------------------------------------------
+critical_extension_verify_none() ->
+ [{doc,"Test cert that has a critical unknown extension in verify_none mode"}].
+
+critical_extension_verify_none(Config) when is_list(Config) ->
+ ClientOpts = ?config(client_verification_opts, Config),
+ ServerOpts = ?config(server_verification_opts, Config),
+ PrivDir = ?config(priv_dir, Config),
+ Active = ?config(active, Config),
+ ReceiveFunction = ?config(receive_function, Config),
+
+ KeyFile = filename:join(PrivDir, "otpCA/private/key.pem"),
+ NewCertName = integer_to_list(erlang:unique_integer()) ++ ".pem",
+
+ ServerCertFile = proplists:get_value(certfile, ServerOpts),
+ NewServerCertFile = filename:join([PrivDir, "server", NewCertName]),
+ add_critical_netscape_cert_type(ServerCertFile, NewServerCertFile, KeyFile),
+ NewServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts)],
+
+ ClientCertFile = proplists:get_value(certfile, ClientOpts),
+ NewClientCertFile = filename:join([PrivDir, "client", NewCertName]),
+ add_critical_netscape_cert_type(ClientCertFile, NewClientCertFile, KeyFile),
+ NewClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts)],
+
+ {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, ReceiveFunction, []}},
+ {options, [{verify, verify_none}, {active, Active} | NewServerOpts]}]),
+ 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, ReceiveFunction, []}},
+ {options, [{verify, verify_none}, {active, Active} | NewClientOpts]}]),
+
+ %% This certificate has a critical extension that we don't
+ %% understand. But we're using `verify_none', so verification
+ %% shouldn't fail.
+ ssl_test_lib:check_result(Server, ok, Client, ok),
+
+ ssl_test_lib:close(Server),
+ ssl_test_lib:close(Client),
+ ok.
+
+add_critical_netscape_cert_type(CertFile, NewCertFile, KeyFile) ->
+ [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile),
+ Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)),
+
+ [{'Certificate', DerCert, _}] = ssl_test_lib:pem_to_der(CertFile),
+ OTPCert = public_key:pkix_decode_cert(DerCert, otp),
+ %% This is the "Netscape Cert Type" extension, telling us that the
+ %% certificate can be used for SSL clients and SSL servers.
+ NetscapeCertTypeExt = #'Extension'{
+ extnID = {2,16,840,1,113730,1,1},
+ critical = true,
+ extnValue = <<3,2,6,192>>},
+ OTPTbsCert = OTPCert#'OTPCertificate'.tbsCertificate,
+ Extensions = OTPTbsCert#'OTPTBSCertificate'.extensions,
+ NewOTPTbsCert = OTPTbsCert#'OTPTBSCertificate'{
+ extensions = [NetscapeCertTypeExt] ++ Extensions},
+ NewDerCert = public_key:pkix_sign(NewOTPTbsCert, Key),
+ ssl_test_lib:der_to_pem(NewCertFile, [{'Certificate', NewDerCert, not_encrypted}]),
+ ok.
+
+%%--------------------------------------------------------------------
no_authority_key_identifier() ->
[{doc, "Test cert that does not have authorityKeyIdentifier extension"
" but are present in trusted certs db."}].
diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl
index 44580be1ff..5b86027210 100644
--- a/lib/ssl/test/ssl_crl_SUITE.erl
+++ b/lib/ssl/test/ssl_crl_SUITE.erl
@@ -53,7 +53,7 @@ groups() ->
{idp_crl, [], basic_tests()}].
basic_tests() ->
- [crl_verify_valid, crl_verify_revoked].
+ [crl_verify_valid, crl_verify_revoked, crl_verify_no_crl].
init_per_suite(Config) ->
@@ -186,11 +186,6 @@ crl_verify_revoked(Config) when is_list(Config) ->
{ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
- Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
- {from, self()},
- {options, ServerOpts}]),
- Port = ssl_test_lib:inet_port(Server),
-
ssl_crl_cache:insert({file, filename:join([PrivDir, "erlangCA", "crl.pem"])}),
ssl_crl_cache:insert({file, filename:join([PrivDir, "otpCA", "crl.pem"])}),
@@ -206,16 +201,55 @@ crl_verify_revoked(Config) when is_list(Config) ->
{verify, verify_peer}]
end,
- Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
- {host, Hostname},
- {from, self()},
- {options, ClientOpts}]),
- receive
- {Server, AlertOrColse} ->
- ct:pal("Server Alert or Close ~p", [AlertOrColse])
- end,
- ssl_test_lib:check_result(Client, {error, {tls_alert, "certificate revoked"}}).
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "certificate revoked").
+crl_verify_no_crl() ->
+ [{doc,"Verify a simple CRL chain when the CRL is missing"}].
+crl_verify_no_crl(Config) when is_list(Config) ->
+ PrivDir = ?config(cert_dir, Config),
+ Check = ?config(crl_check, Config),
+ ServerOpts = [{keyfile, filename:join([PrivDir, "server", "key.pem"])},
+ {certfile, filename:join([PrivDir, "server", "cert.pem"])},
+ {cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])}],
+ ClientOpts = case ?config(idp_crl, Config) of
+ true ->
+ [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
+ {crl_check, Check},
+ {crl_cache, {ssl_crl_cache, {internal, [{http, 5000}]}}},
+ {verify, verify_peer}];
+ false ->
+ [{cacertfile, filename:join([PrivDir, "server", "cacerts.pem"])},
+ {crl_check, Check},
+ {verify, verify_peer}]
+ end,
+ {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config),
+
+ %% In case we're running an HTTP server that serves CRLs, let's
+ %% rename those files, so the CRL is absent when we try to verify
+ %% it.
+ %%
+ %% If we're not using an HTTP server, we just need to refrain from
+ %% adding the CRLs to the cache manually.
+ rename_crl(filename:join([PrivDir, "erlangCA", "crl.pem"])),
+ rename_crl(filename:join([PrivDir, "otpCA", "crl.pem"])),
+
+ %% The expected outcome when the CRL is missing depends on the
+ %% crl_check setting.
+ case Check of
+ true ->
+ %% The error "revocation status undetermined" gets turned
+ %% into "bad certificate".
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "bad certificate");
+ peer ->
+ crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts,
+ "bad certificate");
+ best_effort ->
+ %% In "best effort" mode, we consider the certificate not
+ %% to be revoked if we can't find the appropriate CRL.
+ crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts)
+ end.
crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts) ->
Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0},
@@ -236,6 +270,22 @@ crl_verify_valid(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts) ->
ssl_test_lib:close(Server),
ssl_test_lib:close(Client).
+crl_verify_error(Hostname, ServerNode, ServerOpts, ClientNode, ClientOpts, ExpectedAlert) ->
+ Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0},
+ {from, self()},
+ {options, ServerOpts}]),
+ Port = ssl_test_lib:inet_port(Server),
+
+ Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port},
+ {host, Hostname},
+ {from, self()},
+ {options, ClientOpts}]),
+ receive
+ {Server, AlertOrClose} ->
+ ct:pal("Server Alert or Close ~p", [AlertOrClose])
+ end,
+ ssl_test_lib:check_result(Client, {error, {tls_alert, ExpectedAlert}}).
+
%%--------------------------------------------------------------------
%% Internal functions ------------------------------------------------
%%--------------------------------------------------------------------
@@ -259,3 +309,5 @@ make_dir_path(PathComponents) ->
"",
PathComponents).
+rename_crl(Filename) ->
+ file:rename(Filename, Filename ++ ".notfound").
diff --git a/lib/stdlib/src/maps.erl b/lib/stdlib/src/maps.erl
index 3c798b7a04..43d10f4800 100644
--- a/lib/stdlib/src/maps.erl
+++ b/lib/stdlib/src/maps.erl
@@ -205,7 +205,7 @@ size(Val) ->
K :: term().
without(Ks,M) when is_list(Ks), is_map(M) ->
- maps:from_list([{K,V}||{K,V} <- maps:to_list(M), not lists:member(K, Ks)]);
+ lists:foldl(fun(K, M1) -> ?MODULE:remove(K, M1) end, M, Ks);
without(Ks,M) ->
erlang:error(error_type(M),[Ks,M]).
@@ -216,8 +216,16 @@ without(Ks,M) ->
Map2 :: map(),
K :: term().
-with(Ks,M) when is_list(Ks), is_map(M) ->
- maps:from_list([{K,V}||{K,V} <- maps:to_list(M), lists:member(K, Ks)]);
+with(Ks,Map1) when is_list(Ks), is_map(Map1) ->
+ Fun = fun(K, List) ->
+ case ?MODULE:find(K, Map1) of
+ {ok, V} ->
+ [{K, V} | List];
+ error ->
+ List
+ end
+ end,
+ ?MODULE:from_list(lists:foldl(Fun, [], Ks));
with(Ks,M) ->
erlang:error(error_type(M),[Ks,M]).
diff --git a/lib/stdlib/src/stdlib.appup.src b/lib/stdlib/src/stdlib.appup.src
index 04cdf31ada..8a313591a7 100644
--- a/lib/stdlib/src/stdlib.appup.src
+++ b/lib/stdlib/src/stdlib.appup.src
@@ -18,9 +18,9 @@
%% %CopyrightEnd%
{"%VSN%",
%% Up from - max one major revision back
- [{<<"2\\.[5-7](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
+ [{<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
{<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}], % 17.0-17.5
%% Down to - max one major revision back
- [{<<"2\\.[5-7](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
+ [{<<"2\\.[5-8](\\.[0-9]+)*">>,[restart_new_emulator]}, % OTP-18.*
{<<"2\\.[0-4](\\.[0-9]+)*">>,[restart_new_emulator]}] % 17.0-17.5
}.
diff --git a/lib/stdlib/test/win32reg_SUITE.erl b/lib/stdlib/test/win32reg_SUITE.erl
index 82baa43318..6d27ac6387 100644
--- a/lib/stdlib/test/win32reg_SUITE.erl
+++ b/lib/stdlib/test/win32reg_SUITE.erl
@@ -49,48 +49,53 @@ init_per_suite(Config) when is_list(Config) ->
end_per_suite(Config) when is_list(Config) ->
Config.
+
long(doc) -> "Test long keys and entries (OTP-3446).";
long(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ Dog = test_server:timetrap(test_server:seconds(10)),
- ?line LongKey = "software\\" ++
+ LongKey = "software\\" ++
lists:flatten(lists:duplicate(10, "..\\software\\")) ++
"Ericsson\\Erlang",
- ?line {ok,Reg} = win32reg:open([read,write]),
- ?line ok = win32reg:change_key(Reg, "\\hklm"),
- ?line ok = win32reg:change_key(Reg, LongKey),
- ?line {ok,ErlangKey} = win32reg:current_key(Reg),
- io:format("Erlang key: ~s", [ErlangKey]),
+ {ok,Read} = win32reg:open([read]),
+ ok = win32reg:change_key(Read, "\\hklm"),
+
+ ok = win32reg:change_key(Read, LongKey),
+ {ok,ErlangKey} = win32reg:current_key(Read),
+ io:format("Erlang key: ~s~n", [ErlangKey]),
+ ok = win32reg:close(Read),
+ {ok,Reg} = win32reg:open([read, write]),
%% Write a long value and read it back.
- ?line TestKey = "test_key",
- ?line LongValue = lists:concat(["This is a long value generated by the test case ",?MODULE,":long/1. "|lists:duplicate(128, "a")]),
- ?line ok = win32reg:set_value(Reg, TestKey, LongValue),
- ?line {ok,LongValue} = win32reg:value(Reg, TestKey),
+ TestKey = "test_key",
+ LongValue = lists:concat(["This is a long value generated by the test case ",?MODULE,":long/1. "|lists:duplicate(128, "a")]),
+ ok = win32reg:set_value(Reg, TestKey, LongValue),
+ {ok,LongValue} = win32reg:value(Reg, TestKey),
+ io:format("Where ~p Key ~s Value ~s ~n", [win32reg:current_key(Reg), TestKey, LongValue]),
%% Done.
- ?line ok = win32reg:close(Reg),
- ?line test_server:timetrap_cancel(Dog),
+ ok = win32reg:close(Reg),
+ test_server:timetrap_cancel(Dog),
ok.
evil_write(Config) when is_list(Config) ->
- ?line Dog = test_server:timetrap(test_server:seconds(10)),
+ Dog = test_server:timetrap(test_server:seconds(10)),
- ?line Key = "Software\\Ericsson\\Erlang",
- ?line {ok,Reg} = win32reg:open([read,write]),
- ?line ok = win32reg:change_key(Reg, "\\hklm"),
- ?line ok = win32reg:change_key(Reg, Key),
- ?line {ok,ErlangKey} = win32reg:current_key(Reg),
+ Key = "Software\\Ericsson\\Erlang",
+ {ok,Reg} = win32reg:open([read,write]),
+ ok = win32reg:change_key(Reg, "\\hkcu"),
+ ok = win32reg:change_key_create(Reg, Key),
+ {ok,ErlangKey} = win32reg:current_key(Reg),
io:format("Erlang key: ~s", [ErlangKey]),
%% Write keys with different length and read it back.
- ?line TestKey = "test_key " ++ lists:duplicate(128, $a),
+ TestKey = "test_key " ++ lists:duplicate(128, $a),
evil_write_1(Reg, TestKey),
%% Done.
- ?line ok = win32reg:close(Reg),
- ?line test_server:timetrap_cancel(Dog),
+ ok = win32reg:close(Reg),
+ test_server:timetrap_cancel(Dog),
ok.
evil_write_1(Reg, [_|[_|_]=Key]=Key0) ->
diff --git a/lib/test_server/src/test_server.erl b/lib/test_server/src/test_server.erl
index 73803030a3..671674c617 100644
--- a/lib/test_server/src/test_server.erl
+++ b/lib/test_server/src/test_server.erl
@@ -63,13 +63,11 @@
init_target_info() ->
[$.|Emu] = code:objfile_extension(),
{_, OTPRel} = init:script_id(),
- TestServerDir = filename:absname(filename:dirname(code:which(?MODULE))),
#target_info{os_family=test_server_sup:get_os_family(),
os_type=os:type(),
version=erlang:system_info(version),
system_version=erlang:system_info(system_version),
root_dir=code:root_dir(),
- test_server_dir=TestServerDir,
emulator=Emu,
otp_release=OTPRel,
username=test_server_sup:get_username(),
diff --git a/lib/test_server/src/test_server_internal.hrl b/lib/test_server/src/test_server_internal.hrl
index 578f359010..1ec2d83417 100644
--- a/lib/test_server/src/test_server_internal.hrl
+++ b/lib/test_server/src/test_server_internal.hrl
@@ -30,7 +30,6 @@
version, % string()
system_version, % string()
root_dir, % string()
- test_server_dir, % string()
emulator, % string()
otp_release, % string()
username, % string()
diff --git a/lib/test_server/src/test_server_node.erl b/lib/test_server/src/test_server_node.erl
index 70cc2625d4..37f8941d24 100644
--- a/lib/test_server/src/test_server_node.erl
+++ b/lib/test_server/src/test_server_node.erl
@@ -307,11 +307,11 @@ start_node_peer(SlaveName, OptList, From, TI) ->
HostStr, " ", WaitPort]),
% Support for erl_crash_dump files..
- CrashFile = filename:join([TI#target_info.test_server_dir,
+ CrashDir = test_server_sup:crash_dump_dir(),
+ CrashFile = filename:join([CrashDir,
"erl_crash_dump."++cast_to_list(SlaveName)]),
CrashArgs = lists:concat([" -env ERL_CRASH_DUMP \"",CrashFile,"\" "]),
FailOnError = start_node_get_option_value(fail_on_error, OptList, true),
- Pa = TI#target_info.test_server_dir,
Prog0 = start_node_get_option_value(erl, OptList, default),
Prog = quote_progname(pick_erl_program(Prog0)),
Args =
@@ -322,7 +322,6 @@ start_node_peer(SlaveName, OptList, From, TI) ->
Cmd = lists:concat([Prog,
" -detached ",
TI#target_info.naming, " ", SlaveName,
- " -pa \"", Pa,"\"",
NodeStarted,
CrashArgs,
" ", Args]),
@@ -370,15 +369,15 @@ wait_for_node_started_fun(LSock, Tmo, Cleanup, TI, Self) ->
%% Slave nodes are started on a remote host if
%% - the option remote is given when calling test_server:start_node/3
%%
-start_node_slave(SlaveName, OptList, From, TI) ->
+start_node_slave(SlaveName, OptList, From, _TI) ->
SuppliedArgs = start_node_get_option_value(args, OptList, []),
Cleanup = start_node_get_option_value(cleanup, OptList, true),
- CrashFile = filename:join([TI#target_info.test_server_dir,
+ CrashDir = test_server_sup:crash_dump_dir(),
+ CrashFile = filename:join([CrashDir,
"erl_crash_dump."++cast_to_list(SlaveName)]),
CrashArgs = lists:concat([" -env ERL_CRASH_DUMP \"",CrashFile,"\" "]),
- Pa = TI#target_info.test_server_dir,
- Args = lists:concat([" -pa \"", Pa, "\" ", SuppliedArgs, CrashArgs]),
+ Args = lists:concat([" ", SuppliedArgs, CrashArgs]),
Prog0 = start_node_get_option_value(erl, OptList, default),
Prog = pick_erl_program(Prog0),
diff --git a/lib/test_server/src/test_server_sup.erl b/lib/test_server/src/test_server_sup.erl
index fc2cfd57bd..1c0eb18d70 100644
--- a/lib/test_server/src/test_server_sup.erl
+++ b/lib/test_server/src/test_server_sup.erl
@@ -594,7 +594,8 @@ cleanup_crash_dumps() ->
delete_files(Dumps).
crash_dump_dir() ->
- filename:dirname(code:which(?MODULE)).
+ {ok,Dir} = test_server_sup:framework_call(get_log_dir,[]),
+ Dir.
tar_crash_dumps() ->
Dir = crash_dump_dir(),
diff --git a/lib/wx/configure.in b/lib/wx/configure.in
index 48fcca640c..bf27b72aa7 100644
--- a/lib/wx/configure.in
+++ b/lib/wx/configure.in
@@ -164,14 +164,14 @@ case $host_os in
CPPFLAGS="$CPPFLAGS -D_MACOSX $PTHR_CFLAGS"
;;
mingw32)
- CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0500 -D_WINDOWS -D_UNICODE -DUNICODE"
- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500"
+ CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0600 -D_WINDOWS -D_UNICODE -DUNICODE"
+ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600"
AC_MSG_WARN([Reverting to 32-bit time_t])
CPPFLAGS="$CPPFLAGS -D_USE_32BIT_TIME_T"
;;
win32)
- CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0500 -D_WINDOWS -D_UNICODE -DUNICODE"
- CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0500"
+ CFLAGS="$CFLAGS -DWIN32 -DWINVER=0x0600 -D_WINDOWS -D_UNICODE -DUNICODE"
+ CPPFLAGS="$CPPFLAGS -D_WIN32_WINNT=0x0600"
;;
*)
CFLAGS="$CFLAGS -Wno-deprecated-declarations"