aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/compiler/src/beam_disasm.erl12
-rw-r--r--lib/compiler/test/beam_disasm_SUITE.erl4
-rw-r--r--lib/hipe/main/hipe.app.src1
-rw-r--r--lib/hipe/main/hipe.erl6
-rw-r--r--lib/ssh/src/Makefile2
-rw-r--r--lib/ssh/src/ssh.app.src2
-rw-r--r--lib/ssh/src/ssh.erl246
-rw-r--r--lib/ssh/src/ssh_acceptor.erl8
-rw-r--r--lib/ssh/src/ssh_auth.erl2
-rw-r--r--lib/ssh/src/ssh_connect.hrl6
-rw-r--r--lib/ssh/src/ssh_connection.erl6
-rw-r--r--lib/ssh/src/ssh_connection_handler.erl253
-rw-r--r--lib/ssh/src/ssh_connection_manager.erl145
-rw-r--r--lib/ssh/src/ssh_connection_sup.erl113
-rw-r--r--lib/ssh/src/ssh_subsystem_sup.erl14
-rw-r--r--lib/ssh/src/ssh_system_sup.erl2
-rw-r--r--lib/ssh/src/ssh_transport.erl5
-rw-r--r--lib/ssh/src/sshc_sup.erl26
-rw-r--r--lib/ssh/test/ssh_basic_SUITE.erl95
-rw-r--r--lib/ssh/test/ssh_sftpd_SUITE.erl7
-rw-r--r--lib/ssh/test/ssh_test_lib.erl18
-rw-r--r--lib/ssh/test/ssh_to_openssh_SUITE.erl14
-rw-r--r--lib/stdlib/doc/src/gen_event.xml61
-rw-r--r--lib/stdlib/doc/src/gen_fsm.xml55
-rw-r--r--lib/stdlib/doc/src/gen_server.xml39
-rw-r--r--lib/stdlib/doc/src/supervisor.xml12
-rw-r--r--lib/stdlib/doc/src/supervisor_bridge.xml7
-rw-r--r--lib/stdlib/src/gen.erl22
-rw-r--r--lib/stdlib/src/gen_event.erl10
-rw-r--r--lib/stdlib/src/gen_fsm.erl25
-rw-r--r--lib/stdlib/src/gen_server.erl23
-rw-r--r--lib/stdlib/test/Makefile1
-rw-r--r--lib/stdlib/test/dummy_via.erl94
-rw-r--r--lib/stdlib/test/ets_SUITE.erl18
-rw-r--r--lib/stdlib/test/gen_event_SUITE.erl26
-rw-r--r--lib/stdlib/test/gen_fsm_SUITE.erl71
-rw-r--r--lib/stdlib/test/gen_server_SUITE.erl62
37 files changed, 1086 insertions, 427 deletions
diff --git a/lib/compiler/src/beam_disasm.erl b/lib/compiler/src/beam_disasm.erl
index 7103d2390f..62bdc74cc8 100644
--- a/lib/compiler/src/beam_disasm.erl
+++ b/lib/compiler/src/beam_disasm.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2000-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2000-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -182,10 +182,14 @@ process_chunks(F) ->
Literals = beam_disasm_literals(LiteralBin),
Code = beam_disasm_code(CodeBin, Atoms, mk_imports(ImportsList),
StrBin, Lambdas, Literals, Module),
- Attributes = optional_chunk(F, attributes),
+ Attributes =
+ case optional_chunk(F, attributes) of
+ none -> [];
+ Atts when is_list(Atts) -> Atts
+ end,
CompInfo =
case optional_chunk(F, "CInf") of
- none -> none;
+ none -> [];
CompInfoBin when is_binary(CompInfoBin) ->
binary_to_term(CompInfoBin)
end,
@@ -198,7 +202,7 @@ process_chunks(F) ->
end.
%%-----------------------------------------------------------------------
-%% Retrieve an optional chunk or none if the chunk doesn't exist.
+%% Retrieve an optional chunk or return 'none' if the chunk doesn't exist.
%%-----------------------------------------------------------------------
optional_chunk(F, ChunkTag) ->
diff --git a/lib/compiler/test/beam_disasm_SUITE.erl b/lib/compiler/test/beam_disasm_SUITE.erl
index 44574ae64a..62afc80ca6 100644
--- a/lib/compiler/test/beam_disasm_SUITE.erl
+++ b/lib/compiler/test/beam_disasm_SUITE.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2011. All Rights Reserved.
+%% Copyright Ericsson AB 2011-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -60,6 +60,6 @@ stripped(Config) when is_list(Config) ->
?line true = is_list(Attr),
?line true = is_list(CompileInfo),
?line {ok, {tmp, _}} = beam_lib:strip(BeamName),
- ?line {beam_file, tmp, _, none, none, [_|_]} =
+ ?line {beam_file, tmp, _, [], [], [_|_]} =
beam_disasm:file(BeamName),
ok.
diff --git a/lib/hipe/main/hipe.app.src b/lib/hipe/main/hipe.app.src
index e5d6d1e540..c05aec4c4a 100644
--- a/lib/hipe/main/hipe.app.src
+++ b/lib/hipe/main/hipe.app.src
@@ -74,7 +74,6 @@
hipe_arm_specific,
hipe_bb,
hipe_beam_to_icode,
- hipe_ceach,
hipe_coalescing_regalloc,
hipe_consttab,
hipe_data_pp,
diff --git a/lib/hipe/main/hipe.erl b/lib/hipe/main/hipe.erl
index 309d847374..1be679a13f 100644
--- a/lib/hipe/main/hipe.erl
+++ b/lib/hipe/main/hipe.erl
@@ -574,12 +574,8 @@ file(File, Options) when is_atom(File) ->
disasm(File) ->
case beam_disasm:file(File) of
#beam_file{labeled_exports = LabeledExports,
- compile_info = CompInfo0,
+ compile_info = CompInfo,
code = BeamCode} ->
- CompInfo = case CompInfo0 of
- none -> [];
- _ -> CompInfo0
- end,
CompOpts = proplists:get_value(options, CompInfo, []),
HCompOpts = case lists:keyfind(hipe, 1, CompOpts) of
{hipe, L} when is_list(L) -> L;
diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile
index 7be97abf66..1734ae4df4 100644
--- a/lib/ssh/src/Makefile
+++ b/lib/ssh/src/Makefile
@@ -45,11 +45,11 @@ MODULES= \
ssh_sup \
sshc_sup \
sshd_sup \
+ ssh_connection_sup \
ssh_channel \
ssh_connection \
ssh_connection_handler \
ssh_connection_manager \
- ssh_connection_controler \
ssh_shell \
ssh_system_sup \
ssh_subsystem_sup \
diff --git a/lib/ssh/src/ssh.app.src b/lib/ssh/src/ssh.app.src
index 7a58dbe54f..316c09eb06 100644
--- a/lib/ssh/src/ssh.app.src
+++ b/lib/ssh/src/ssh.app.src
@@ -15,7 +15,7 @@
ssh_connection,
ssh_connection_handler,
ssh_connection_manager,
- ssh_connection_controler,
+ ssh_connection_sup,
ssh_shell,
sshc_sup,
sshd_sup,
diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl
index 5751f2eaa0..39c7fe329e 100644
--- a/lib/ssh/src/ssh.erl
+++ b/lib/ssh/src/ssh.erl
@@ -72,17 +72,25 @@ stop() ->
connect(Host, Port, Options) ->
connect(Host, Port, Options, infinity).
connect(Host, Port, Options, Timeout) ->
- {SocketOpts, Opts} = handle_options(Options),
- DisableIpv6 = proplists:get_value(ip_v6_disabled, Opts, false),
- Inet = inetopt(DisableIpv6),
+ case handle_options(Options) of
+ {error, _Reason} = Error ->
+ Error;
+ {SocketOptions, SshOptions} ->
+ DisableIpv6 = proplists:get_value(ip_v6_disabled, SshOptions, false),
+ Inet = inetopt(DisableIpv6),
+ do_connect(Host, Port, [Inet | SocketOptions],
+ [{host, Host} | SshOptions], Timeout, DisableIpv6)
+ end.
+
+do_connect(Host, Port, SocketOptions, SshOptions, Timeout, DisableIpv6) ->
try sshc_sup:start_child([[{address, Host}, {port, Port},
- {role, client},
- {channel_pid, self()},
- {socket_opts, [Inet | SocketOpts]},
- {ssh_opts, [{host, Host}| Opts]}]]) of
+ {role, client},
+ {channel_pid, self()},
+ {socket_opts, SocketOptions},
+ {ssh_opts, SshOptions}]]) of
{ok, ConnectionSup} ->
{ok, Manager} =
- ssh_connection_controler:connection_manager(ConnectionSup),
+ ssh_connection_sup:connection_manager(ConnectionSup),
MRef = erlang:monitor(process, Manager),
receive
{Manager, is_connected} ->
@@ -95,7 +103,8 @@ connect(Host, Port, Options, Timeout) ->
%% match the Manager in this case
{_, not_connected, {error, econnrefused}} when DisableIpv6 == false ->
do_demonitor(MRef, Manager),
- connect(Host, Port, [{ip_v6_disabled, true} | Options], Timeout);
+ do_connect(Host, Port, proplists:delete(inet6, SocketOptions),
+ SshOptions, Timeout, true);
{_, not_connected, {error, Reason}} ->
do_demonitor(MRef, Manager),
{error, Reason};
@@ -192,7 +201,7 @@ daemon(HostAddr, Port, Options0) ->
{HostAddr, inet6,
[{ip, HostAddr} | Options1]}
end,
- start_daemon(Host, Port, [{role, server} | Options], Inet).
+ start_daemon(Host, Port, Options, Inet).
%%--------------------------------------------------------------------
%% Function: stop_listener(SysRef) -> ok
@@ -258,7 +267,14 @@ shell(Host, Port, Options) ->
%%% Internal functions
%%--------------------------------------------------------------------
start_daemon(Host, Port, Options, Inet) ->
- {SocketOpts, Opts} = handle_options(Options),
+ case handle_options(Options) of
+ {error, _Reason} = Error ->
+ Error;
+ {SocketOptions, SshOptions}->
+ do_start_daemon(Host, Port,[{role, server} |SshOptions] , [Inet | SocketOptions])
+ end.
+
+do_start_daemon(Host, Port, Options, SocketOptions) ->
case ssh_system_sup:system_supervisor(Host, Port) of
undefined ->
%% TODO: It would proably make more sense to call the
@@ -266,8 +282,8 @@ start_daemon(Host, Port, Options, Inet) ->
%% monent. The name is a legacy name!
try sshd_sup:start_child([{address, Host},
{port, Port}, {role, server},
- {socket_opts, [Inet | SocketOpts]},
- {ssh_opts, Opts}]) of
+ {socket_opts, SocketOptions},
+ {ssh_opts, Options}]) of
{ok, SysSup} ->
{ok, SysSup};
{error, {already_started, _}} ->
@@ -286,68 +302,148 @@ start_daemon(Host, Port, Options, Inet) ->
end.
handle_options(Opts) ->
- handle_options(proplists:unfold(Opts), [], []).
-handle_options([], SockOpts, Opts) ->
- {SockOpts, Opts};
-%% TODO: Could do some type checks here on plain ssh-opts
-handle_options([{system_dir, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{user_dir, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{user_dir_fun, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{silently_accept_hosts, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{user_interaction, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{public_key_alg, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{connect_timeout, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{user, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{dsa_pass_phrase, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{rsa_pass_phrase, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{password, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{user_passwords, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{pwdfun, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{user_auth, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{key_cb, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{role, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{channel, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{compression, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{allow_user_interaction, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{infofun, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{connectfun, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{disconnectfun , _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{failfun, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{ip_v6_disabled, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]);
-handle_options([{ip, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, [Opt |SockOpts], Opts);
-handle_options([{ifaddr, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, [Opt |SockOpts], Opts);
-handle_options([{fd, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, [Opt | SockOpts], Opts);
-handle_options([{nodelay, _} = Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, [Opt | SockOpts], Opts);
-handle_options([Opt | Rest], SockOpts, Opts) ->
- handle_options(Rest, SockOpts, [Opt | Opts]).
+ try handle_option(proplists:unfold(Opts), [], []) of
+ {_,_} = Options ->
+ Options
+ catch
+ throw:Error ->
+ Error
+ end.
+
+handle_option([], SocketOptions, SshOptions) ->
+ {SocketOptions, SshOptions};
+handle_option([{system_dir, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{user_dir, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{user_dir_fun, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{silently_accept_hosts, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{public_key_alg, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{connect_timeout, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{user, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{dsa_pass_phrase, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{rsa_pass_phrase, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{password, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{user_passwords, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{pwdfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{user_auth, _} = Opt | Rest],SocketOptions, SshOptions ) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{key_cb, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{role, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{compression, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{allow_user_interaction, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{infofun, _} = Opt | Rest],SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{connectfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{disconnectfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{failfun, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{ip_v6_disabled, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{transport, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{subsystems, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{ssh_cli, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([{shell, _} = Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, SocketOptions, [handle_ssh_option(Opt) | SshOptions]);
+handle_option([Opt | Rest], SocketOptions, SshOptions) ->
+ handle_option(Rest, [handle_inet_option(Opt) | SocketOptions], SshOptions).
+
+handle_ssh_option({system_dir, Value} = Opt) when is_list(Value) ->
+ Opt;
+handle_ssh_option({user_dir, Value} = Opt) when is_list(Value) ->
+ Opt;
+handle_ssh_option({user_dir_fun, Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option({silently_accept_hosts, Value} = Opt) when Value == true; Value == false ->
+ Opt;
+handle_ssh_option({user_interaction, Value} = Opt) when Value == true; Value == false ->
+ Opt;
+handle_ssh_option({public_key_alg, Value} = Opt) when Value == ssh_rsa; Value == ssh_dsa ->
+ Opt;
+handle_ssh_option({connect_timeout, Value} = Opt) when is_integer(Value); Value == infinity ->
+ Opt;
+handle_ssh_option({user, Value} = Opt) when is_list(Value) ->
+ Opt;
+handle_ssh_option({dsa_pass_phrase, Value} = Opt) when is_list(Value) ->
+ Opt;
+handle_ssh_option({rsa_pass_phrase, Value} = Opt) when is_list(Value) ->
+ Opt;
+handle_ssh_option({password, Value} = Opt) when is_list(Value) ->
+ Opt;
+handle_ssh_option({user_passwords, Value} = Opt) when is_list(Value)->
+ Opt;
+handle_ssh_option({pwdfun, Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option({user_auth, Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option({key_cb, Value} = Opt) when is_atom(Value) ->
+ Opt;
+handle_ssh_option({compression, Value} = Opt) when is_atom(Value) ->
+ Opt;
+handle_ssh_option({allow_user_interaction, Value} = Opt) when Value == true;
+ Value == false ->
+ Opt;
+handle_ssh_option({infofun, Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option({connectfun, Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option({disconnectfun , Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option({failfun, Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option({ip_v6_disabled, Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option({transport, {Protocol, Cb, ClosTag}} = Opt) when is_atom(Protocol),
+ is_atom(Cb),
+ is_atom(ClosTag) ->
+ Opt;
+handle_ssh_option({subsystems, Value} = Opt) when is_list(Value) ->
+ Opt;
+handle_ssh_option({ssh_cli, {Cb, _}}= Opt) when is_atom(Cb) ->
+ Opt;
+handle_ssh_option({shell, {Module, Function, _}} = Opt) when is_atom(Module),
+ is_atom(Function) ->
+ Opt;
+handle_ssh_option({shell, Value} = Opt) when is_function(Value) ->
+ Opt;
+handle_ssh_option(Opt) ->
+ throw({error, {eoptions, Opt}}).
+
+handle_inet_option({active, _} = Opt) ->
+ throw({error, {{eoptions, Opt}, "Ssh has built in flow control, "
+ "and activ is handled internaly user is not allowd"
+ "to specify this option"}});
+handle_inet_option({inet, _} = Opt) ->
+ throw({error, {{eoptions, Opt},"Is set internaly use ip_v6_disabled to"
+ " enforce iv4 in the server, client will fallback to ipv4 if"
+ " it can not use ipv6"}});
+handle_inet_option({reuseaddr, _} = Opt) ->
+ throw({error, {{eoptions, Opt},"Is set internaly user is not allowd"
+ "to specify this option"}});
+%% Option verified by inet
+handle_inet_option(Opt) ->
+ Opt.
%% Has IPv6 been disabled?
inetopt(true) ->
diff --git a/lib/ssh/src/ssh_acceptor.erl b/lib/ssh/src/ssh_acceptor.erl
index 59fbd24cf5..d023656c32 100644
--- a/lib/ssh/src/ssh_acceptor.erl
+++ b/lib/ssh/src/ssh_acceptor.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -86,11 +86,11 @@ handle_connection(Callback, Address, Port, Options, Socket) ->
{ok, SubSysSup} = ssh_system_sup:start_subsystem(SystemSup, Options),
ConnectionSup = ssh_system_sup:connection_supervisor(SystemSup),
{ok, Pid} =
- ssh_connection_controler:start_manager_child(ConnectionSup,
- [server, Socket, Options, SubSysSup]),
+ ssh_connection_sup:start_manager_child(ConnectionSup,
+ [server, Socket, Options]),
Callback:controlling_process(Socket, Pid),
SshOpts = proplists:get_value(ssh_opts, Options),
- Pid ! {start_connection, server, [Address, Port, Socket, SshOpts]}.
+ Pid ! {start_connection, server, [Address, Port, Socket, SshOpts, SubSysSup]}.
handle_error(timeout) ->
ok;
diff --git a/lib/ssh/src/ssh_auth.erl b/lib/ssh/src/ssh_auth.erl
index 62d684f4dc..1a4517c689 100644
--- a/lib/ssh/src/ssh_auth.erl
+++ b/lib/ssh/src/ssh_auth.erl
@@ -340,7 +340,7 @@ verify_sig(SessionId, User, Service, Alg, KeyBlob, SigWLen, Opts) ->
?UINT32(SigLen), Sig:SigLen/binary>> = AlgSig,
ssh_transport:verify(PlainText, sha, Sig, Key);
false ->
- {error, key_unacceptable}
+ false
end.
build_sig_data(SessionId, User, Service, KeyBlob, Alg) ->
diff --git a/lib/ssh/src/ssh_connect.hrl b/lib/ssh/src/ssh_connect.hrl
index e06c9ea211..932b0642f1 100644
--- a/lib/ssh/src/ssh_connect.hrl
+++ b/lib/ssh/src/ssh_connect.hrl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2005-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2005-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -253,7 +253,6 @@
-record(connection, {
requests = [], %% [{ChannelId, Pid}...] awaiting reply on request,
channel_cache,
- channel_pids = [],
port_bindings,
channel_id_seed,
cli_spec,
@@ -261,5 +260,6 @@
port,
options,
exec,
- sub_system_supervisor
+ sub_system_supervisor,
+ connection_supervisor
}).
diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl
index cb02d7b824..46f0c7e688 100644
--- a/lib/ssh/src/ssh_connection.erl
+++ b/lib/ssh/src/ssh_connection.erl
@@ -744,8 +744,8 @@ handle_msg(#ssh_msg_global_request{name = _Type,
%%% This transport message will also be handled at the connection level
handle_msg(#ssh_msg_disconnect{code = Code,
- description = Description,
- language = _Lang },
+ description = Description,
+ language = _Lang },
#connection{channel_cache = Cache} = Connection0, _, _) ->
{Connection, Replies} =
ssh_channel:cache_foldl(fun(Channel, {Connection1, Acc}) ->
@@ -779,7 +779,7 @@ handle_cli_msg(#connection{channel_cache = Cache} = Connection0,
handle_cli_msg(Connection0, _, Channel, Reply0) ->
{Reply, Connection} = reply_msg(Channel, Connection0, Reply0),
{{replies, [Reply]}, Connection}.
-
+
channel_eof_msg(ChannelId) ->
#ssh_msg_channel_eof{recipient_channel = ChannelId}.
diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl
index 9079089d5d..5b3d1b8a1b 100644
--- a/lib/ssh/src/ssh_connection_handler.erl
+++ b/lib/ssh/src/ssh_connection_handler.erl
@@ -41,7 +41,7 @@
-export([hello/2, kexinit/2, key_exchange/2, new_keys/2,
userauth/2, connected/2]).
--export([init/1, state_name/3, handle_event/3,
+-export([init/1, handle_event/3,
handle_sync_event/4, handle_info/3, terminate/3, code_change/4]).
%% spawn export
@@ -106,22 +106,28 @@ peer_address(ConnectionHandler) ->
%% initialize.
%%--------------------------------------------------------------------
init([Role, Manager, Socket, SshOpts]) ->
+ process_flag(trap_exit, true),
{NumVsn, StrVsn} = ssh_transport:versions(Role, SshOpts),
ssh_bits:install_messages(ssh_transport:transport_messages(NumVsn)),
{Protocol, Callback, CloseTag} =
proplists:get_value(transport, SshOpts, {tcp, gen_tcp, tcp_closed}),
- Ssh = init_ssh(Role, NumVsn, StrVsn, SshOpts, Socket),
- {ok, hello, #state{ssh_params =
- Ssh#ssh{send_sequence = 0, recv_sequence = 0},
- socket = Socket,
- decoded_data_buffer = <<>>,
- encoded_data_buffer = <<>>,
- transport_protocol = Protocol,
- transport_cb = Callback,
- transport_close_tag = CloseTag,
- manager = Manager,
- opts = SshOpts
- }}.
+ try init_ssh(Role, NumVsn, StrVsn, SshOpts, Socket) of
+ Ssh ->
+ {ok, hello, #state{ssh_params =
+ Ssh#ssh{send_sequence = 0, recv_sequence = 0},
+ socket = Socket,
+ decoded_data_buffer = <<>>,
+ encoded_data_buffer = <<>>,
+ transport_protocol = Protocol,
+ transport_cb = Callback,
+ transport_close_tag = CloseTag,
+ manager = Manager,
+ opts = SshOpts
+ }}
+ catch
+ exit:Reason ->
+ {stop, {shutdown, Reason}}
+ end.
%%--------------------------------------------------------------------
%% Function:
%% state_name(Event, State) -> {next_state, NextStateName, NextState}|
@@ -179,7 +185,12 @@ kexinit({#ssh_msg_kexinit{} = Kex, Payload},
next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = Desc,
+ language = "en"}, State)
end.
key_exchange(#ssh_msg_kexdh_init{} = Msg,
@@ -192,7 +203,12 @@ key_exchange(#ssh_msg_kexdh_init{} = Msg,
{next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = Desc,
+ language = "en"}, State)
end;
key_exchange(#ssh_msg_kexdh_reply{} = Msg,
@@ -203,7 +219,12 @@ key_exchange(#ssh_msg_kexdh_reply{} = Msg,
{next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = Desc,
+ language = "en"}, State)
end;
key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg,
@@ -216,7 +237,12 @@ key_exchange(#ssh_msg_kex_dh_gex_group{} = Msg,
{next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = Desc,
+ language = "en"}, State)
end;
key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg,
@@ -227,7 +253,12 @@ key_exchange(#ssh_msg_kex_dh_gex_request{} = Msg,
{next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = Desc,
+ language = "en"}, State)
end;
key_exchange(#ssh_msg_kex_dh_gex_reply{} = Msg,
#state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
@@ -237,7 +268,12 @@ key_exchange(#ssh_msg_kex_dh_gex_reply{} = Msg,
{next_state, new_keys, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = Desc,
+ language = "en"}, State)
end.
new_keys(#ssh_msg_newkeys{} = Msg, #state{ssh_params = Ssh0} = State0) ->
@@ -248,8 +284,12 @@ new_keys(#ssh_msg_newkeys{} = Msg, #state{ssh_params = Ssh0} = State0) ->
{next_state, NextStateName, next_packet(State)}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State0),
- {stop, normal, State0}
+ handle_disconnect(DisconnectMsg, State0);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_KEY_EXCHANGE_FAILED,
+ description = Desc,
+ language = "en"}, State0)
end.
userauth(#ssh_msg_service_request{name = "ssh-userauth"} = Msg,
@@ -262,7 +302,12 @@ userauth(#ssh_msg_service_request{name = "ssh-userauth"} = Msg,
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = Desc,
+ language = "en"}, State)
end;
userauth(#ssh_msg_service_accept{name = "ssh-userauth"},
@@ -284,7 +329,12 @@ userauth(#ssh_msg_userauth_request{service = "ssh-connection",
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = Desc,
+ language = "en"}, State)
end;
userauth(#ssh_msg_userauth_request{service = "ssh-connection",
@@ -307,7 +357,12 @@ userauth(#ssh_msg_userauth_request{service = "ssh-connection",
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = Desc,
+ language = "en"}, State)
end;
userauth(#ssh_msg_userauth_info_request{} = Msg,
@@ -319,7 +374,12 @@ userauth(#ssh_msg_userauth_info_request{} = Msg,
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = Desc,
+ language = "en"}, State)
end;
userauth(#ssh_msg_userauth_info_response{} = Msg,
@@ -330,7 +390,12 @@ userauth(#ssh_msg_userauth_info_response{} = Msg,
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
catch
#ssh_msg_disconnect{} = DisconnectMsg ->
- handle_disconnect(DisconnectMsg, State)
+ handle_disconnect(DisconnectMsg, State);
+ _:Error ->
+ Desc = log_error(Error),
+ handle_disconnect(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
+ description = Desc,
+ language = "en"}, State)
end;
userauth(#ssh_msg_userauth_success{}, #state{ssh_params = #ssh{role = client},
@@ -361,24 +426,11 @@ userauth(#ssh_msg_userauth_failure{authentications = Methodes},
%% The prefered authentication method failed try next method
userauth(#ssh_msg_userauth_failure{},
- #state{ssh_params = #ssh{role = client} = Ssh0,
- manager = Pid} = State) ->
+ #state{ssh_params = #ssh{role = client} = Ssh0} = State) ->
case ssh_auth:userauth_request_msg(Ssh0) of
- {disconnect, Event, {Msg, _}} ->
- try
- send_msg(Msg, State),
- ssh_connection_manager:event(Pid, Event)
- catch
- exit:{noproc, _Reason} ->
- Report = io_lib:format("Connection Manager terminated: ~p~n",
- [Pid]),
- error_logger:info_report(Report);
- exit:Exit ->
- Report = io_lib:format("Connection Manager returned:~n~p~n~p~n",
- [Msg, Exit]),
- error_logger:info_report(Report)
- end,
- {stop, normal, State};
+ {disconnect, DisconnectMsg,{Msg, Ssh}} ->
+ send_msg(Msg, State),
+ handle_disconnect(DisconnectMsg, State#state{ssh_params = Ssh});
{Msg, Ssh} ->
send_msg(Msg, State),
{next_state, userauth, next_packet(State#state{ssh_params = Ssh})}
@@ -398,25 +450,6 @@ connected({#ssh_msg_kexinit{}, _Payload} = Event, State) ->
kexinit(Event, State#state{renegotiate = true}).
%%--------------------------------------------------------------------
-%% Function:
-%% state_name(Event, From, State) -> {next_state, NextStateName, NextState} |
-%% {next_state, NextStateName,
-%% NextState, Timeout} |
-%% {reply, Reply, NextStateName, NextState}|
-%% {reply, Reply, NextStateName,
-%% NextState, Timeout} |
-%% {stop, Reason, NewState}|
-%% {stop, Reason, Reply, NewState}
-%% Description: There should be one instance of this function for each
-%% possible state name. Whenever a gen_fsm receives an event sent using
-%% gen_fsm:sync_send_event/2,3, the instance of this function with the same
-%% name as the current state name StateName is called to handle the event.
-%%--------------------------------------------------------------------
-state_name(_Event, _From, State) ->
- Reply = ok,
- {reply, Reply, state_name, State}.
-
-%%--------------------------------------------------------------------
%% Function:
%% handle_event(Event, StateName, State) -> {next_state, NextStateName,
%% NextState} |
@@ -566,10 +599,18 @@ handle_info({Protocol, Socket, Data}, Statename,
Statename, State);
handle_info({CloseTag, _Socket}, _StateName,
- #state{transport_close_tag = CloseTag, %%manager = Pid,
+ #state{transport_close_tag = CloseTag,
ssh_params = #ssh{role = _Role, opts = _Opts}} = State) ->
- %%ok = ssh_connection_manager:delivered(Pid),
- {stop, normal, State};
+ DisconnectMsg =
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_CONNECTION_LOST,
+ description = "Connection Lost",
+ language = "en"},
+ {stop, {shutdown, DisconnectMsg}, State};
+
+%%% So that terminate will be run when supervisor is shutdown
+handle_info({'EXIT', _Sup, Reason}, _StateName, State) ->
+ {stop, Reason, State};
+
handle_info(UnexpectedMessage, StateName, #state{ssh_params = SshParams} = State) ->
Msg = lists:flatten(io_lib:format(
"Unexpected message '~p' received in state '~p'\n"
@@ -581,7 +622,6 @@ handle_info(UnexpectedMessage, StateName, #state{ssh_params = SshParams} = State
error_logger:info_report(Msg),
{next_state, StateName, State}.
-
%%--------------------------------------------------------------------
%% Function: terminate(Reason, StateName, State) -> void()
%% Description:This function is called by a gen_fsm when it is about
@@ -596,22 +636,31 @@ terminate(normal, _, #state{transport_cb = Transport,
(catch Transport:close(Socket)),
ok;
-terminate(shutdown, _, State) ->
+%% Terminated as manager terminated
+terminate(shutdown, StateName, #state{ssh_params = Ssh0} = State) ->
DisconnectMsg =
#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Application disconnect",
+ description = "Application shutdown",
language = "en"},
- handle_disconnect(DisconnectMsg, State);
+ {SshPacket, Ssh} = ssh_transport:ssh_packet(DisconnectMsg, Ssh0),
+ send_msg(SshPacket, State),
+ terminate(normal, StateName, State#state{ssh_params = Ssh});
-terminate(Reason, _, State) ->
- Desc = io_lib:format("Erlang ssh connection handler failed with reason: "
- "~p , please report this to [email protected] \n",
- [Reason]),
+terminate({shutdown, #ssh_msg_disconnect{} = Msg}, StateName, #state{ssh_params = Ssh0, manager = Pid} = State) ->
+ {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
+ send_msg(SshPacket, State),
+ ssh_connection_manager:event(Pid, Msg),
+ terminate(normal, StateName, State#state{ssh_params = Ssh});
+terminate(Reason, StateName, #state{ssh_params = Ssh0, manager = Pid} = State) ->
+ log_error(Reason),
DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_CONNECTION_LOST,
- description = Desc,
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Internal error",
language = "en"},
- handle_disconnect(DisconnectMsg, State).
+ {SshPacket, Ssh} = ssh_transport:ssh_packet(DisconnectMsg, Ssh0),
+ ssh_connection_manager:event(Pid, DisconnectMsg),
+ send_msg(SshPacket, State),
+ terminate(normal, StateName, State#state{ssh_params = Ssh}).
%%--------------------------------------------------------------------
%% Function:
@@ -757,11 +806,8 @@ generate_event(<<?BYTE(Byte), _/binary>> = Msg, StateName,
next_packet(State),
{next_state, StateName, State}
catch
- exit:{noproc, _Reason} ->
- Report = io_lib:format("~p Connection Handler terminated: ~p~n",
- [self(), Pid]),
- error_logger:info_report(Report),
- {stop, normal, State0}
+ exit:{noproc, Reason} ->
+ {stop, {shutdown, Reason}, State0}
end;
generate_event(Msg, StateName, State0, EncData) ->
Event = ssh_bits:decode(Msg),
@@ -786,13 +832,18 @@ generate_event_new_state(#state{ssh_params =
next_packet(#state{decoded_data_buffer = <<>>,
encoded_data_buffer = Buff,
+ ssh_params = #ssh{decrypt_block_size = BlockSize},
socket = Socket,
- transport_protocol = Protocol} =
- State) when Buff =/= <<>> andalso size(Buff) >= 8 ->
- %% More data from the next packet has been received
- %% Fake a socket-recive message so that the data will be processed
- inet:setopts(Socket, [{active, once}]),
- self() ! {Protocol, Socket, <<>>},
+ transport_protocol = Protocol} = State) when Buff =/= <<>> ->
+ case size(Buff) >= erlang:max(8, BlockSize) of
+ true ->
+ %% Enough data from the next packet has been received to
+ %% decode the length indicator, fake a socket-recive
+ %% message so that the data will be processed
+ self() ! {Protocol, Socket, <<>>};
+ false ->
+ inet:setopts(Socket, [{active, once}])
+ end,
State;
next_packet(#state{socket = Socket} = State) ->
@@ -853,24 +904,8 @@ handle_ssh_packet(Length, StateName, #state{decoded_data_buffer = DecData0,
handle_disconnect(DisconnectMsg, State0)
end.
-handle_disconnect(#ssh_msg_disconnect{} = Msg,
- #state{ssh_params = Ssh0, manager = Pid} = State) ->
- {SshPacket, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
- try
- send_msg(SshPacket, State),
- ssh_connection_manager:event(Pid, Msg)
- catch
- exit:{noproc, _Reason} ->
- Report = io_lib:format("~p Connection Manager terminated: ~p~n",
- [self(), Pid]),
- error_logger:info_report(Report);
- exit:Exit ->
- Report = io_lib:format("Connection Manager returned:~n~p~n~p~n",
- [Msg, Exit]),
- error_logger:info_report(Report)
- end,
- (catch ssh_userreg:delete_user(Pid)),
- {stop, normal, State#state{ssh_params = Ssh}}.
+handle_disconnect(#ssh_msg_disconnect{} = Msg, State) ->
+ {stop, {shutdown, Msg}, State}.
counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) ->
Ssh#ssh{c_vsn = NumVsn , c_version = StrVsn};
@@ -928,3 +963,11 @@ ssh_info([peer | Rest], #ssh{peer = Peer} = SshParams, Acc) ->
ssh_info([ _ | Rest], SshParams, Acc) ->
ssh_info(Rest, SshParams, Acc).
+
+log_error(Reason) ->
+ Report = io_lib:format("Erlang ssh connection handler failed with reason: "
+ "~p ~n, Stacktace: ~p ~n"
+ "please report this to [email protected] \n",
+ [Reason, erlang:get_stacktrace()]),
+ error_logger:error_report(Report),
+ "Internal error".
diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl
index f729276e65..406a042d72 100644
--- a/lib/ssh/src/ssh_connection_manager.erl
+++ b/lib/ssh/src/ssh_connection_manager.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2011. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -182,17 +182,15 @@ send_eof(ConnectionManager, ChannelId) ->
%% {stop, Reason}
%% Description: Initiates the server
%%--------------------------------------------------------------------
-init([server, _Socket, Opts, SubSysSup]) ->
+init([server, _Socket, Opts]) ->
process_flag(trap_exit, true),
ssh_bits:install_messages(ssh_connection:messages()),
- Cache = ssh_channel:cache_create(),
+ Cache = ssh_channel:cache_create(),
{ok, #state{role = server,
connection_state = #connection{channel_cache = Cache,
channel_id_seed = 0,
port_bindings = [],
- requests = [],
- channel_pids = [],
- sub_system_supervisor = SubSysSup},
+ requests = []},
opts = Opts,
connected = false}};
@@ -207,15 +205,14 @@ init([client, Opts]) ->
Options = proplists:get_value(ssh_opts, Opts),
ChannelPid = proplists:get_value(channel_pid, Opts),
self() !
- {start_connection, client, [Parent, Address, Port,
- ChannelPid, SocketOpts, Options]},
+ {start_connection, client, [Parent, Address, Port, SocketOpts, Options]},
{ok, #state{role = client,
client = ChannelPid,
connection_state = #connection{channel_cache = Cache,
channel_id_seed = 0,
port_bindings = [],
- requests = [],
- channel_pids = []},
+ connection_supervisor = Parent,
+ requests = []},
opts = Opts,
connected = false}}.
@@ -269,29 +266,25 @@ handle_call({ssh_msg, Pid, Msg}, From,
when Role == client andalso (not IsConnected) ->
lists:foreach(fun send_msg/1, Replies),
ClientPid ! {self(), not_connected, Reason},
- {stop, normal, State#state{connection = Connection}};
+ {stop, {shutdown, normal}, State#state{connection = Connection}};
{disconnect, Reason, {{replies, Replies}, Connection}} ->
lists:foreach(fun send_msg/1, Replies),
SSHOpts = proplists:get_value(ssh_opts, Opts),
disconnect_fun(Reason, SSHOpts),
- {stop, normal, State#state{connection_state = Connection}}
+ {stop, {shutdown, normal}, State#state{connection_state = Connection}}
catch
- exit:{noproc, Reason} ->
- Report = io_lib:format("Connection probably terminated:~n~p~n~p~n~p~n",
- [ConnectionMsg, Reason, erlang:get_stacktrace()]),
- error_logger:info_report(Report),
- {noreply, State};
- error:Error ->
- Report = io_lib:format("Connection message returned:~n~p~n~p~n~p~n",
- [ConnectionMsg, Error, erlang:get_stacktrace()]),
- error_logger:info_report(Report),
- {noreply, State};
- exit:Exit ->
- Report = io_lib:format("Connection message returned:~n~p~n~p~n~p~n",
- [ConnectionMsg, Exit, erlang:get_stacktrace()]),
- error_logger:info_report(Report),
- {noreply, State}
- end;
+ _:Reason ->
+ {disconnect, Reason, {{replies, Replies}, Connection}} =
+ ssh_connection:handle_msg(
+ #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "Internal error",
+ language = "en"}, Connection0, undefined,
+ Role),
+ lists:foreach(fun send_msg/1, Replies),
+ SSHOpts = proplists:get_value(ssh_opts, Opts),
+ disconnect_fun(Reason, SSHOpts),
+ {stop, {shutdown, Reason}, State#state{connection_state = Connection}}
+ end;
handle_call({global_request, Pid, _, _, _} = Request, From,
#state{connection_state =
@@ -341,7 +334,8 @@ handle_call({info, ChannelPid}, _From,
handle_call({open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data},
From, #state{connection = Pid,
connection_state =
- #connection{channel_cache = Cache}} = State0) ->
+ #connection{channel_cache = Cache}} = State0) ->
+ erlang:monitor(process, ChannelPid),
{ChannelId, State1} = new_channel_id(State0),
Msg = ssh_connection:channel_open_msg(Type, ChannelId,
InitialWindowSize,
@@ -404,18 +398,18 @@ handle_call({close, ChannelId}, _,
{reply, ok, State}
end;
-handle_call(stop, _, #state{role = _client,
- client = _ChannelPid,
- connection = Pid} = State) ->
- DisconnectMsg =
- #ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
- description = "Application disconnect",
- language = "en"},
- (catch gen_fsm:send_all_state_event(Pid, DisconnectMsg)),
-% ssh_connection_handler:send(Pid, DisconnectMsg),
- {stop, normal, ok, State};
-handle_call(stop, _, State) ->
- {stop, normal, ok, State};
+handle_call(stop, _, #state{connection_state = Connection0,
+ role = Role,
+ opts = Opts} = State) ->
+ {disconnect, Reason, {{replies, Replies}, Connection}} =
+ ssh_connection:handle_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
+ description = "User closed down connection",
+ language = "en"}, Connection0, undefined,
+ Role),
+ lists:foreach(fun send_msg/1, Replies),
+ SSHOpts = proplists:get_value(ssh_opts, Opts),
+ disconnect_fun(Reason, SSHOpts),
+ {stop, normal, ok, State#state{connection_state = Connection}};
%% API violation make it the violaters problem
%% by ignoring it. The violating process will get
@@ -493,31 +487,33 @@ handle_cast({failure, ChannelId}, #state{connection = Pid} = State) ->
%% Description: Handling all non call/cast messages
%%--------------------------------------------------------------------
handle_info({start_connection, server,
- [Address, Port, Socket, Options]},
+ [Address, Port, Socket, Options, SubSysSup]},
#state{connection_state = CState} = State) ->
{ok, Connection} = ssh_transport:accept(Address, Port, Socket, Options),
Shell = proplists:get_value(shell, Options),
Exec = proplists:get_value(exec, Options),
CliSpec = proplists:get_value(ssh_cli, Options, {ssh_cli, [Shell]}),
+ ssh_connection_handler:send_event(Connection, socket_control),
{noreply, State#state{connection = Connection,
connection_state =
CState#connection{address = Address,
port = Port,
cli_spec = CliSpec,
options = Options,
- exec = Exec}}};
+ exec = Exec,
+ sub_system_supervisor = SubSysSup
+ }}};
handle_info({start_connection, client,
- [Parent, Address, Port, ChannelPid, SocketOpts, Options]},
- State) ->
+ [Parent, Address, Port, SocketOpts, Options]},
+ #state{client = Pid} = State) ->
case (catch ssh_transport:connect(Parent, Address,
Port, SocketOpts, Options)) of
{ok, Connection} ->
- erlang:monitor(process, ChannelPid),
{noreply, State#state{connection = Connection}};
Reason ->
- ChannelPid ! {self(), not_connected, Reason},
- {stop, normal, State}
+ Pid ! {self(), not_connected, Reason},
+ {stop, {shutdown, normal}, State}
end;
handle_info({ssh_cm, _Sender, Msg}, State0) ->
@@ -537,20 +533,13 @@ handle_info(ssh_connected, #state{role = client, client = Pid}
handle_info(ssh_connected, #state{role = server} = State) ->
{noreply, State#state{connected = true}};
-handle_info({'DOWN', _Ref, process, ChannelPid, normal}, State0) ->
- handle_down(handle_channel_down(ChannelPid, State0));
-
-handle_info({'DOWN', _Ref, process, ChannelPid, shutdown}, State0) ->
- handle_down(handle_channel_down(ChannelPid, State0));
-
-handle_info({'DOWN', _Ref, process, ChannelPid, Reason}, State0) ->
- Report = io_lib:format("Pid ~p DOWN ~p\n", [ChannelPid, Reason]),
- error_logger:error_report(Report),
- handle_down(handle_channel_down(ChannelPid, State0));
+%%% Handle that ssh channels user process goes down
+handle_info({'DOWN', _Ref, process, ChannelPid, _Reason}, State) ->
+ handle_down(handle_channel_down(ChannelPid, State));
-handle_info({'EXIT', _, _}, State) ->
- %% Handled in 'DOWN'
- {noreply, State}.
+%%% So that terminate will be run when supervisor is shutdown
+handle_info({'EXIT', _Sup, Reason}, State) ->
+ {stop, Reason, State}.
%%--------------------------------------------------------------------
%% Function: terminate(Reason, State) -> void()
@@ -559,20 +548,19 @@ handle_info({'EXIT', _, _}, State) ->
%% cleaning up. When it returns, the gen_server terminates with Reason.
%% The return value is ignored.
%%--------------------------------------------------------------------
-terminate(Reason, #state{connection_state =
- #connection{requests = Requests,
- sub_system_supervisor = SubSysSup},
+terminate(_Reason, #state{role = client,
+ connection_state =
+ #connection{connection_supervisor = Supervisor}}) ->
+ sshc_sup:stop_child(Supervisor);
+
+terminate(_Reason, #state{role = server,
+ connection_state =
+ #connection{sub_system_supervisor = SubSysSup},
opts = Opts}) ->
- SSHOpts = proplists:get_value(ssh_opts, Opts),
- disconnect_fun(Reason, SSHOpts),
- (catch lists:foreach(fun({_, From}) ->
- gen_server:reply(From, {error, connection_closed})
- end, Requests)),
Address = proplists:get_value(address, Opts),
Port = proplists:get_value(port, Opts),
SystemSup = ssh_system_sup:system_supervisor(Address, Port),
- ssh_system_sup:stop_subsystem(SystemSup, SubSysSup),
- ok.
+ ssh_system_sup:stop_subsystem(SystemSup, SubSysSup).
%%--------------------------------------------------------------------
%% Func: code_change(OldVsn, State, Extra) -> {ok, NewState}
@@ -619,16 +607,7 @@ decode_ssh_msg(Msg) ->
send_msg(Msg) ->
- case catch do_send_msg(Msg) of
- {'EXIT', Reason}->
- Report = io_lib:format("Connection Manager fail to send:~n~p~n"
- "Reason why it failed was:~n~p~n",
- [Msg, Reason]),
- error_logger:info_report(Report);
- _ ->
- ok
- end.
-
+ catch do_send_msg(Msg).
do_send_msg({channel_data, Pid, Data}) ->
Pid ! {ssh_cm, self(), Data};
do_send_msg({channel_requst_reply, From, Data}) ->
@@ -676,8 +655,8 @@ handle_down({{replies, Replies}, State}) ->
{noreply, State}.
handle_channel_down(ChannelPid, #state{connection_state =
- #connection{channel_cache = Cache}} =
- State) ->
+ #connection{channel_cache = Cache}} =
+ State) ->
ssh_channel:cache_foldl(
fun(Channel, Acc) when Channel#channel.user == ChannelPid ->
ssh_channel:cache_delete(Cache,
diff --git a/lib/ssh/src/ssh_connection_sup.erl b/lib/ssh/src/ssh_connection_sup.erl
new file mode 100644
index 0000000000..e3544af1c6
--- /dev/null
+++ b/lib/ssh/src/ssh_connection_sup.erl
@@ -0,0 +1,113 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+%%
+%%----------------------------------------------------------------------
+%% Purpose: Ssh connection supervisor.
+%%----------------------------------------------------------------------
+
+-module(ssh_connection_sup).
+
+-behaviour(supervisor).
+
+-export([start_link/1, start_handler_child/2, start_manager_child/2,
+ connection_manager/1]).
+
+%% Supervisor callback
+-export([init/1]).
+
+%%%=========================================================================
+%%% API
+%%%=========================================================================
+start_link(Args) ->
+ supervisor:start_link(?MODULE, [Args]).
+
+%% Will be called from the manager child process
+start_handler_child(Sup, Args) ->
+ [Spec] = child_specs(handler, Args),
+ supervisor:start_child(Sup, Spec).
+
+%% Will be called from the acceptor process
+start_manager_child(Sup, Args) ->
+ [Spec] = child_specs(manager, Args),
+ supervisor:start_child(Sup, Spec).
+
+connection_manager(SupPid) ->
+ Children = supervisor:which_children(SupPid),
+ {ok, ssh_connection_manager(Children)}.
+
+%%%=========================================================================
+%%% Supervisor callback
+%%%=========================================================================
+init([Args]) ->
+ RestartStrategy = one_for_all,
+ MaxR = 0,
+ MaxT = 3600,
+ Children = child_specs(Args),
+ {ok, {{RestartStrategy, MaxR, MaxT}, Children}}.
+
+%%%=========================================================================
+%%% Internal functions
+%%%=========================================================================
+child_specs(Opts) ->
+ case proplists:get_value(role, Opts) of
+ client ->
+ child_specs(manager, [client | Opts]);
+ server ->
+ %% Children started by acceptor process
+ []
+ end.
+
+% The manager process starts the handler process
+child_specs(manager, Opts) ->
+ [manager_spec(Opts)];
+child_specs(handler, Opts) ->
+ [handler_spec(Opts)].
+
+manager_spec([server = Role, Socket, Opts]) ->
+ Name = make_ref(),
+ StartFunc = {ssh_connection_manager, start_link, [[Role, Socket, Opts]]},
+ Restart = temporary,
+ Shutdown = 3600,
+ Modules = [ssh_connection_manager],
+ Type = worker,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules};
+
+manager_spec([client = Role | Opts]) ->
+ Name = make_ref(),
+ StartFunc = {ssh_connection_manager, start_link, [[Role, Opts]]},
+ Restart = temporary,
+ Shutdown = 3600,
+ Modules = [ssh_connection_manager],
+ Type = worker,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+handler_spec([Role, Socket, Opts]) ->
+ Name = make_ref(),
+ StartFunc = {ssh_connection_handler,
+ start_link, [Role, self(), Socket, Opts]},
+ Restart = temporary,
+ Shutdown = 3600,
+ Modules = [ssh_connection_handler],
+ Type = worker,
+ {Name, StartFunc, Restart, Shutdown, Type, Modules}.
+
+ssh_connection_manager([{_, Child, _, [ssh_connection_manager]} | _]) ->
+ Child;
+ssh_connection_manager([_ | Rest]) ->
+ ssh_connection_manager(Rest).
diff --git a/lib/ssh/src/ssh_subsystem_sup.erl b/lib/ssh/src/ssh_subsystem_sup.erl
index d71b6bbc56..cd6defd535 100644
--- a/lib/ssh/src/ssh_subsystem_sup.erl
+++ b/lib/ssh/src/ssh_subsystem_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -70,13 +70,12 @@ ssh_connectinon_child_spec(Opts) ->
Address = proplists:get_value(address, Opts),
Port = proplists:get_value(port, Opts),
Role = proplists:get_value(role, Opts),
- Name = id(Role, ssh_connection_controler, Address, Port),
- StartFunc = {ssh_connection_controler, start_link, [Opts]},
+ Name = id(Role, ssh_connection_sup, Address, Port),
+ StartFunc = {ssh_connection_sup, start_link, [Opts]},
Restart = transient,
-% Restart = permanent,
Shutdown = 5000,
- Modules = [ssh_connection_controler],
- Type = worker,
+ Modules = [ssh_connection_sup],
+ Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
ssh_channel_child_spec(Opts) ->
@@ -86,7 +85,6 @@ ssh_channel_child_spec(Opts) ->
Name = id(Role, ssh_channel_sup, Address, Port),
StartFunc = {ssh_channel_sup, start_link, [Opts]},
Restart = transient,
-% Restart = permanent,
Shutdown = infinity,
Modules = [ssh_channel_sup],
Type = supervisor,
@@ -95,7 +93,7 @@ ssh_channel_child_spec(Opts) ->
id(Role, Sup, Address, Port) ->
{Role, Sup, Address, Port}.
-ssh_connection_sup([{_, Child, _, [ssh_connection_controler]} | _]) ->
+ssh_connection_sup([{_, Child, _, [ssh_connection_sup]} | _]) ->
Child;
ssh_connection_sup([_ | Rest]) ->
ssh_connection_sup(Rest).
diff --git a/lib/ssh/src/ssh_system_sup.erl b/lib/ssh/src/ssh_system_sup.erl
index 920baaadef..23480f5fe3 100644
--- a/lib/ssh/src/ssh_system_sup.erl
+++ b/lib/ssh/src/ssh_system_sup.erl
@@ -146,7 +146,7 @@ ssh_acceptor_child_spec(ServerOpts) ->
ssh_subsystem_child_spec(ServerOpts) ->
Name = make_ref(),
StartFunc = {ssh_subsystem_sup, start_link, [ServerOpts]},
- Restart = temporary,
+ Restart = transient,
Shutdown = infinity,
Modules = [ssh_subsystem_sup],
Type = supervisor,
diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl
index 6140c87f6e..1f912c9bdf 100644
--- a/lib/ssh/src/ssh_transport.erl
+++ b/lib/ssh/src/ssh_transport.erl
@@ -142,7 +142,7 @@ connect(ConnectionSup, Address, Port, SocketOpts, Opts) ->
case do_connect(Callback, Address, Port, SocketOpts, Timeout) of
{ok, Socket} ->
{ok, Pid} =
- ssh_connection_controler:start_handler_child(ConnectionSup,
+ ssh_connection_sup:start_handler_child(ConnectionSup,
[client, Socket,
[{address, Address},
{port, Port} |
@@ -174,12 +174,11 @@ accept(Address, Port, Socket, Options) ->
ssh_system_sup:connection_supervisor(
ssh_system_sup:system_supervisor(Address, Port)),
{ok, Pid} =
- ssh_connection_controler:start_handler_child(ConnectionSup,
+ ssh_connection_sup:start_handler_child(ConnectionSup,
[server, Socket,
[{address, Address},
{port, Port} | Options]]),
Callback:controlling_process(Socket, Pid),
- ssh_connection_handler:send_event(Pid, socket_control),
{ok, Pid}.
format_version({Major,Minor}) ->
diff --git a/lib/ssh/src/sshc_sup.erl b/lib/ssh/src/sshc_sup.erl
index 7c29c669e4..1d2779de23 100644
--- a/lib/ssh/src/sshc_sup.erl
+++ b/lib/ssh/src/sshc_sup.erl
@@ -1,7 +1,7 @@
%%
%% %CopyrightBegin%
%%
-%% Copyright Ericsson AB 2008-2010. All Rights Reserved.
+%% Copyright Ericsson AB 2008-2012. All Rights Reserved.
%%
%% The contents of this file are subject to the Erlang Public License,
%% Version 1.1, (the "License"); you may not use this file except in
@@ -26,7 +26,7 @@
-behaviour(supervisor).
--export([start_link/1, start_child/1]).
+-export([start_link/1, start_child/1, stop_child/1]).
%% Supervisor callback
-export([init/1]).
@@ -40,12 +40,19 @@ start_link(Args) ->
start_child(Args) ->
supervisor:start_child(?MODULE, Args).
+stop_child(Client) ->
+ spawn(fun() ->
+ ClientSup = whereis(?MODULE),
+ supervisor:terminate_child(ClientSup, Client)
+ end),
+ ok.
+
%%%=========================================================================
%%% Supervisor callback
%%%=========================================================================
init(Args) ->
RestartStrategy = simple_one_for_one,
- MaxR = 10,
+ MaxR = 0,
MaxT = 3600,
{ok, {{RestartStrategy, MaxR, MaxT}, [child_spec(Args)]}}.
@@ -54,12 +61,9 @@ init(Args) ->
%%%=========================================================================
child_spec(_) ->
Name = undefined, % As simple_one_for_one is used.
- StartFunc = {ssh_connection_controler, start_link, []},
- Restart = temporary,
-% Shutdown = infinity,
- Shutdown = 5000,
- Modules = [ssh_connection_controler],
-% Type = supervisor,
- Type = worker,
+ StartFunc = {ssh_connection_sup, start_link, []},
+ Restart = temporary,
+ Shutdown = infinity,
+ Modules = [ssh_connection_sup],
+ Type = supervisor,
{Name, StartFunc, Restart, Shutdown, Type, Modules}.
-
diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl
index 9c13180159..012367a6df 100644
--- a/lib/ssh/test/ssh_basic_SUITE.erl
+++ b/lib/ssh/test/ssh_basic_SUITE.erl
@@ -88,7 +88,7 @@ end_per_testcase(TestCase, Config) when TestCase == server_password_option;
UserDir = filename:join(?config(priv_dir, Config), nopubkey),
ssh_test_lib:del_dirs(UserDir),
end_per_testcase(Config);
-end_per_testcase(TestCase, Config) ->
+end_per_testcase(_TestCase, Config) ->
end_per_testcase(Config).
end_per_testcase(_Config) ->
ssh:stop(),
@@ -108,14 +108,16 @@ all() ->
{group, rsa_key},
{group, dsa_pass_key},
{group, rsa_pass_key},
+ {group, internal_error},
daemon_already_started,
server_password_option, server_userpassword_option].
groups() ->
[{dsa_key, [], [exec, exec_compressed, shell, known_hosts]},
- {rsa_key, [], [exec, exec_compressed, shell, known_hosts]},
+ {rsa_key, [], [exec, exec_compressed, shell, known_hosts]},
{dsa_pass_key, [], [pass_phrase]},
- {rsa_pass_key, [], [pass_phrase]}
+ {rsa_pass_key, [], [pass_phrase]},
+ {internal_error, [], [internal_error]}
].
init_per_group(dsa_key, Config) ->
@@ -138,6 +140,12 @@ init_per_group(dsa_pass_key, Config) ->
PrivDir = ?config(priv_dir, Config),
ssh_test_lib:setup_dsa_pass_pharse(DataDir, PrivDir, "Password"),
[{pass_phrase, {dsa_pass_phrase, "Password"}}| Config];
+init_per_group(internal_error, Config) ->
+ DataDir = ?config(data_dir, Config),
+ PrivDir = ?config(priv_dir, Config),
+ ssh_test_lib:setup_dsa(DataDir, PrivDir),
+ file:delete(filename:join(PrivDir, "system/ssh_host_dsa_key")),
+ Config;
init_per_group(_, Config) ->
Config.
@@ -157,6 +165,11 @@ end_per_group(rsa_pass_key, Config) ->
PrivDir = ?config(priv_dir, Config),
ssh_test_lib:clean_rsa(PrivDir),
Config;
+end_per_group(internal_error, Config) ->
+ PrivDir = ?config(priv_dir, Config),
+ ssh_test_lib:clean_dsa(PrivDir),
+ Config;
+
end_per_group(_, Config) ->
Config.
@@ -268,9 +281,14 @@ shell(Config) when is_list(Config) ->
IO = ssh_test_lib:start_io_server(),
Shell = ssh_test_lib:start_shell(Port, IO, UserDir),
receive
+ {'EXIT', _, _} ->
+ test_server:fail(no_ssh_connection);
ErlShellStart ->
- test_server:format("Erlang shell start: ~p~n", [ErlShellStart])
- end,
+ test_server:format("Erlang shell start: ~p~n", [ErlShellStart]),
+ do_shell(IO, Shell)
+ end.
+
+do_shell(IO, Shell) ->
receive
ErlPrompt0 ->
test_server:format("Erlang prompt: ~p~n", [ErlPrompt0])
@@ -361,12 +379,15 @@ server_password_option(Config) when is_list(Config) ->
{password, "morot"},
{user_interaction, false},
{user_dir, UserDir}]),
+
+ Reason = "Unable to connect using the available authentication methods",
+
{error, Reason} =
- ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
- {user, "vego"},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
+ ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "foo"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
test_server:format("Test of wrong password: Error msg: ~p ~n", [Reason]),
@@ -396,25 +417,20 @@ server_userpassword_option(Config) when is_list(Config) ->
{user_dir, UserDir}]),
ssh:close(ConnectionRef),
- {error, Reason0} =
- ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
- {user, "foo"},
- {password, "morot"},
- {user_interaction, false},
- {user_dir, UserDir}]),
-
- test_server:format("Test of user foo that does not exist. "
- "Error msg: ~p ~n", [Reason0]),
+ Reason = "Unable to connect using the available authentication methods",
- {error, Reason1} =
- ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true},
- {user, "vego"},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
- test_server:format("Test of wrong Password. "
- "Error msg: ~p ~n", [Reason1]),
-
+ {error, Reason} =
+ ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "foo"},
+ {password, "morot"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
+ {error, Reason} =
+ ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user, "vego"},
+ {password, "foo"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
@@ -445,6 +461,7 @@ known_hosts(Config) when is_list(Config) ->
[Host, _Ip] = string:tokens(HostAndIp, ","),
"ssh-" ++ _ = Alg,
ssh:stop_daemon(Pid).
+%%--------------------------------------------------------------------
pass_phrase(doc) ->
["Test that we can use keyes protected by pass phrases"];
@@ -470,5 +487,27 @@ pass_phrase(Config) when is_list(Config) ->
ssh:stop_daemon(Pid).
%%--------------------------------------------------------------------
+
+internal_error(doc) ->
+ ["Test that client does not hang if disconnects due to internal error"];
+
+internal_error(suite) ->
+ [];
+
+internal_error(Config) when is_list(Config) ->
+ process_flag(trap_exit, true),
+ SystemDir = filename:join(?config(priv_dir, Config), system),
+ UserDir = ?config(priv_dir, Config),
+
+ {_Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SystemDir},
+ {user_dir, UserDir},
+ {failfun, fun ssh_test_lib:failfun/2}]),
+ {error,"Internal error"} =
+ ssh:connect(Host, Port, [{silently_accept_hosts, true},
+ {user_dir, UserDir},
+ {user_interaction, false}]).
+
+
+%%--------------------------------------------------------------------
%% Internal functions
%%--------------------------------------------------------------------
diff --git a/lib/ssh/test/ssh_sftpd_SUITE.erl b/lib/ssh/test/ssh_sftpd_SUITE.erl
index 6e4480ee9d..de946d4c4c 100644
--- a/lib/ssh/test/ssh_sftpd_SUITE.erl
+++ b/lib/ssh/test/ssh_sftpd_SUITE.erl
@@ -30,7 +30,6 @@
-include_lib("kernel/include/file.hrl").
--define(SFPD_PORT, 9999).
-define(USER, "Alladin").
-define(PASSWD, "Sesame").
-define(XFER_PACKET_SIZE, 32768).
@@ -102,13 +101,15 @@ init_per_testcase(TestCase, Config) ->
ClientUserDir = filename:join(PrivDir, nopubkey),
SystemDir = filename:join(?config(priv_dir, Config), system),
+ Port = ssh_test_lib:inet_port(node()),
+
{ok, Sftpd} =
- ssh_sftpd:listen(?SFPD_PORT, [{system_dir, SystemDir},
+ ssh_sftpd:listen(Port, [{system_dir, SystemDir},
{user_dir, PrivDir},
{user_passwords,[{?USER, ?PASSWD}]},
{pwdfun, fun(_,_) -> true end}]),
- Cm = ssh_test_lib:connect(?SFPD_PORT,
+ Cm = ssh_test_lib:connect(Port,
[{user_dir, ClientUserDir},
{user, ?USER}, {password, ?PASSWD},
{user_interaction, false},
diff --git a/lib/ssh/test/ssh_test_lib.erl b/lib/ssh/test/ssh_test_lib.erl
index 26bbdf5c5c..609663c87a 100644
--- a/lib/ssh/test/ssh_test_lib.erl
+++ b/lib/ssh/test/ssh_test_lib.erl
@@ -43,12 +43,8 @@ connect(Host, Options) ->
connect(any, Port, Options) ->
connect(hostname(), Port, Options);
connect(Host, Port, Options) ->
- case ssh:connect(Host, Port, Options) of
- {ok, ConnectionRef} ->
- ConnectionRef;
- Error ->
- Error
- end.
+ {ok, ConnectionRef} = ssh:connect(Host, Port, Options),
+ ConnectionRef.
daemon(Options) ->
daemon(any, inet_port(), Options).
@@ -338,3 +334,13 @@ del_dirs(Dir) ->
_ ->
ok
end.
+
+inet_port(Node) ->
+ {Port, Socket} = do_inet_port(Node),
+ rpc:call(Node, gen_tcp, close, [Socket]),
+ Port.
+
+do_inet_port(Node) ->
+ {ok, Socket} = rpc:call(Node, gen_tcp, listen, [0, [{reuseaddr, true}]]),
+ {ok, Port} = rpc:call(Node, inet, port, [Socket]),
+ {Port, Socket}.
diff --git a/lib/ssh/test/ssh_to_openssh_SUITE.erl b/lib/ssh/test/ssh_to_openssh_SUITE.erl
index dfe526564d..c337617ee4 100644
--- a/lib/ssh/test/ssh_to_openssh_SUITE.erl
+++ b/lib/ssh/test/ssh_to_openssh_SUITE.erl
@@ -445,7 +445,7 @@ erlang_client_openssh_server_password(Config) when is_list(Config) ->
%% to make sure we don't public-key-auth
UserDir = ?config(data_dir, Config),
{error, Reason0} =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
+ ssh:connect(any, ?SSH_DEFAULT_PORT, [{silently_accept_hosts, true},
{user, "foo"},
{password, "morot"},
{user_interaction, false},
@@ -459,12 +459,12 @@ erlang_client_openssh_server_password(Config) when is_list(Config) ->
case length(string:tokens(User, " ")) of
1 ->
{error, Reason1} =
- ssh_test_lib:connect(?SSH_DEFAULT_PORT,
- [{silently_accept_hosts, true},
- {user, User},
- {password, "foo"},
- {user_interaction, false},
- {user_dir, UserDir}]),
+ ssh:connect(any, ?SSH_DEFAULT_PORT,
+ [{silently_accept_hosts, true},
+ {user, User},
+ {password, "foo"},
+ {user_interaction, false},
+ {user_dir, UserDir}]),
test_server:format("Test of wrong Pasword. "
"Error msg: ~p~n", [Reason1]);
_ ->
diff --git a/lib/stdlib/doc/src/gen_event.xml b/lib/stdlib/doc/src/gen_event.xml
index 79a0c8ad89..ef81b06500 100644
--- a/lib/stdlib/doc/src/gen_event.xml
+++ b/lib/stdlib/doc/src/gen_event.xml
@@ -120,8 +120,10 @@ gen_event:stop -----> Module:terminate/2
<name>start_link(EventMgrName) -> Result</name>
<fsummary>Create a generic event manager process in a supervision tree.</fsummary>
<type>
- <v>EventMgrName = {local,Name} | {global,Name}</v>
+ <v>EventMgrName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Result = {ok,Pid} | {error,{already_started,Pid}}</v>
<v>&nbsp;Pid = pid()</v>
</type>
@@ -132,10 +134,17 @@ gen_event:stop -----> Module:terminate/2
the event manager is linked to the supervisor.</p>
<p>If <c>EventMgrName={local,Name}</c>, the event manager is
registered locally as <c>Name</c> using <c>register/2</c>.
- If <c>EventMgrName={global,Name}</c>, the event manager is
- registered globally as <c>Name</c> using
+ If <c>EventMgrName={global,GlobalName}</c>, the event manager is
+ registered globally as <c>GlobalName</c> using
<c>global:register_name/2</c>. If no name is provided,
- the event manager is not registered.</p>
+ the event manager is not registered.
+ If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will
+ register with the registry represented by <c>Module</c>.
+ The <c>Module</c> callback should export the functions
+ <c>register_name/2</c>, <c>unregister_name/1</c>,
+ <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the
+ corresponding functions in <c>global</c>. Thus,
+ <c>{via,global,GlobalName}</c> is a valid reference.</p>
<p>If the event manager is successfully created the function
returns <c>{ok,Pid}</c>, where <c>Pid</c> is the pid of
the event manager. If there already exists a process with
@@ -149,8 +158,10 @@ gen_event:stop -----> Module:terminate/2
<name>start(EventMgrName) -> Result</name>
<fsummary>Create a stand-alone event manager process.</fsummary>
<type>
- <v>EventMgrName = {local,Name} | {global,Name}</v>
+ <v>EventMgrName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Result = {ok,Pid} | {error,{already_started,Pid}}</v>
<v>&nbsp;Pid = pid()</v>
</type>
@@ -166,8 +177,10 @@ gen_event:stop -----> Module:terminate/2
<name>add_handler(EventMgrRef, Handler, Args) -> Result</name>
<fsummary>Add an event handler to a generic event manager.</fsummary>
<type>
- <v>EventMgr = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgr = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -185,8 +198,10 @@ gen_event:stop -----> Module:terminate/2
<item><c>Name</c>, if the event manager is locally registered,</item>
<item><c>{Name,Node}</c>, if the event manager is locally
registered at another node, or</item>
- <item><c>{global,Name}</c>, if the event manager is globally
+ <item><c>{global,GlobalName}</c>, if the event manager is globally
registered.</item>
+ <item><c>{via,Module,ViaName}</c>, if the event manager is registered
+ through an alternative process registry.</item>
</list>
<p><c>Handler</c> is the name of the callback module <c>Module</c> or
a tuple <c>{Module,Id}</c>, where <c>Id</c> is any term.
@@ -208,8 +223,10 @@ gen_event:stop -----> Module:terminate/2
<name>add_sup_handler(EventMgrRef, Handler, Args) -> Result</name>
<fsummary>Add a supervised event handler to a generic event manager.</fsummary>
<type>
- <v>EventMgr = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgr = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -253,8 +270,10 @@ gen_event:stop -----> Module:terminate/2
<name>sync_notify(EventMgrRef, Event) -> ok</name>
<fsummary>Notify an event manager about an event.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
</type>
<desc>
@@ -278,8 +297,10 @@ gen_event:stop -----> Module:terminate/2
<name>call(EventMgrRef, Handler, Request, Timeout) -> Result</name>
<fsummary>Make a synchronous call to a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -318,8 +339,10 @@ gen_event:stop -----> Module:terminate/2
<name>delete_handler(EventMgrRef, Handler, Args) -> Result</name>
<fsummary>Delete an event handler from a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -346,8 +369,10 @@ gen_event:stop -----> Module:terminate/2
<name>swap_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name>
<fsummary>Replace an event handler in a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler1 = Handler2 = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -390,8 +415,10 @@ gen_event:stop -----> Module:terminate/2
<name>swap_sup_handler(EventMgrRef, {Handler1,Args1}, {Handler2,Args2}) -> Result</name>
<fsummary>Replace an event handler in a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler1 = Handler 2 = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -412,8 +439,10 @@ gen_event:stop -----> Module:terminate/2
<name>which_handlers(EventMgrRef) -> [Handler]</name>
<fsummary>Return all event handlers installed in a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Handler = Module | {Module,Id}</v>
<v>&nbsp;Module = atom()</v>
<v>&nbsp;Id = term()</v>
@@ -429,8 +458,10 @@ gen_event:stop -----> Module:terminate/2
<name>stop(EventMgrRef) -> ok</name>
<fsummary>Terminate a generic event manager.</fsummary>
<type>
- <v>EventMgrRef = Name | {Name,Node} | {global,Name} | pid()</v>
+ <v>EventMgrRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>Name = Node = atom()</v>
+ <v>GlobalName = ViaName = term()</v>
</type>
<desc>
<p>Terminates the event manager <c>EventMgrRef</c>. Before
diff --git a/lib/stdlib/doc/src/gen_fsm.xml b/lib/stdlib/doc/src/gen_fsm.xml
index 421eeb4fd3..73c1911f1e 100644
--- a/lib/stdlib/doc/src/gen_fsm.xml
+++ b/lib/stdlib/doc/src/gen_fsm.xml
@@ -84,9 +84,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>start_link(FsmName, Module, Args, Options) -> Result</name>
<fsummary>Create a gen_fsm process in a supervision tree.</fsummary>
<type>
- <v>FsmName = {local,Name} | {global,GlobalName}</v>
+ <v>FsmName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
@@ -113,8 +114,16 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
locally as <c>Name</c> using <c>register/2</c>.
If <c>FsmName={global,GlobalName}</c>, the gen_fsm is
registered globally as <c>GlobalName</c> using
- <c>global:register_name/2</c>. If no name is provided,
- the gen_fsm is not registered.</p>
+ <c>global:register_name/2</c>.
+ If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will
+ register with the registry represented by <c>Module</c>.
+ The <c>Module</c> callback should export the functions
+ <c>register_name/2</c>, <c>unregister_name/1</c>,
+ <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the
+ corresponding functions in <c>global</c>. Thus,
+ <c>{via,global,GlobalName}</c> is a valid reference.</p>
+ <p>If no name is provided,
+ the gen_fsm is not registered.</p>
<p><c>Module</c> is the name of the callback module.</p>
<p><c>Args</c> is an arbitrary term which is passed as
the argument to <c>Module:init/1</c>.</p>
@@ -154,9 +163,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>start(FsmName, Module, Args, Options) -> Result</name>
<fsummary>Create a stand-alone gen_fsm process.</fsummary>
<type>
- <v>FsmName = {local,Name} | {global,GlobalName}</v>
+ <v>FsmName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
@@ -180,9 +190,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>send_event(FsmRef, Event) -> ok</name>
<fsummary>Send an event asynchronously to a generic FSM.</fsummary>
<type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
</type>
<desc>
@@ -196,9 +207,11 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<item>the pid,</item>
<item><c>Name</c>, if the gen_fsm is locally registered,</item>
<item><c>{Name,Node}</c>, if the gen_fsm is locally
- registered at another node, or</item>
- <item><c>{global,GlobalName}</c>, if the gen_fsm is globally
- registered.</item>
+ registered at another node, or</item>
+ <item><c>{global,GlobalName}</c>, if the gen_fsm is globally
+ registered.</item>
+ <item><c>{via,Module,ViaName}</c>, if the event manager is registered
+ through an alternative process registry.</item>
</list>
<p><c>Event</c> is an arbitrary term which is passed as one of
the arguments to <c>Module:StateName/2</c>.</p>
@@ -208,9 +221,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>send_all_state_event(FsmRef, Event) -> ok</name>
<fsummary>Send an event asynchronously to a generic FSM.</fsummary>
<type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
</type>
<desc>
@@ -232,9 +246,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>sync_send_event(FsmRef, Event, Timeout) -> Reply</name>
<fsummary>Send an event synchronously to a generic FSM.</fsummary>
<type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
<v>Timeout = int()>0 | infinity</v>
<v>Reply = term()</v>
@@ -264,9 +279,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<name>sync_send_all_state_event(FsmRef, Event, Timeout) -> Reply</name>
<fsummary>Send an event synchronously to a generic FSM.</fsummary>
<type>
- <v>FsmRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>FsmRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Name = Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Event = term()</v>
<v>Timeout = int()>0 | infinity</v>
<v>Reply = term()</v>
@@ -388,9 +404,10 @@ gen_fsm:sync_send_all_state_event -----> Module:handle_sync_event/4
<v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
<v>StateName = atom()</v>
<v>StateData = term()</v>
- <v>FsmName = {local,Name} | {global,GlobalName}</v>
+ <v>FsmName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Timeout = int() | infinity</v>
</type>
<desc>
diff --git a/lib/stdlib/doc/src/gen_server.xml b/lib/stdlib/doc/src/gen_server.xml
index edeb7dff91..9edff1b9cf 100644
--- a/lib/stdlib/doc/src/gen_server.xml
+++ b/lib/stdlib/doc/src/gen_server.xml
@@ -83,9 +83,10 @@ gen_server:abcast -----> Module:handle_cast/2
<name>start_link(ServerName, Module, Args, Options) -> Result</name>
<fsummary>Create a gen_server process in a supervision tree.</fsummary>
<type>
- <v>ServerName = {local,Name} | {global,GlobalName}</v>
+ <v>ServerName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
@@ -111,14 +112,22 @@ gen_server:abcast -----> Module:handle_cast/2
If <c>ServerName={global,GlobalName}</c> the gen_server is
registered globally as <c>GlobalName</c> using
<c>global:register_name/2</c>. If no name is provided,
- the gen_server is not registered.</p>
+ the gen_server is not registered.
+ If <c>EventMgrName={via,Module,ViaName}</c>, the event manager will
+ register with the registry represented by <c>Module</c>.
+ The <c>Module</c> callback should export the functions
+ <c>register_name/2</c>, <c>unregister_name/1</c>,
+ <c>whereis_name/1</c> and <c>send/2</c>, which should behave like the
+ corresponding functions in <c>global</c>. Thus,
+ <c>{via,global,GlobalName}</c> is a valid reference.</p>
<p><c>Module</c> is the name of the callback module.</p>
<p><c>Args</c> is an arbitrary term which is passed as
the argument to <c>Module:init/1</c>.</p>
<p>If the option <c>{timeout,Time}</c> is present,
the gen_server is allowed to spend <c>Time</c> milliseconds
initializing or it will be terminated and the start function
- will return <c>{error,timeout}</c>.</p>
+ will return <c>{error,timeout}</c>.
+ </p>
<p>If the option <c>{debug,Dbgs}</c> is present,
the corresponding <c>sys</c> function will be called for each
item in <c>Dbgs</c>. See
@@ -151,9 +160,10 @@ gen_server:abcast -----> Module:handle_cast/2
<name>start(ServerName, Module, Args, Options) -> Result</name>
<fsummary>Create a stand-alone gen_server process.</fsummary>
<type>
- <v>ServerName = {local,Name} | {global,GlobalName}</v>
+ <v>ServerName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Module = atom()</v>
<v>Args = term()</v>
<v>Options = [Option]</v>
@@ -178,9 +188,10 @@ gen_server:abcast -----> Module:handle_cast/2
<name>call(ServerRef, Request, Timeout) -> Reply</name>
<fsummary>Make a synchronous call to a generic server.</fsummary>
<type>
- <v>ServerRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Request = term()</v>
<v>Timeout = int()>0 | infinity</v>
<v>Reply = term()</v>
@@ -198,6 +209,8 @@ gen_server:abcast -----> Module:handle_cast/2
registered at another node, or</item>
<item><c>{global,GlobalName}</c>, if the gen_server is
globally registered.</item>
+ <item><c>{via,Module,ViaName}</c>, if the gen_server is
+ registered through an alternative process registry.</item>
</list>
<p><c>Request</c> is an arbitrary term which is passed as one of
the arguments to <c>Module:handle_call/3</c>.</p>
@@ -281,9 +294,10 @@ gen_server:abcast -----> Module:handle_cast/2
<name>cast(ServerRef, Request) -> ok</name>
<fsummary>Send an asynchronous request to a generic server.</fsummary>
<type>
- <v>ServerRef = Name | {Name,Node} | {global,GlobalName} | pid()</v>
+ <v>ServerRef = Name | {Name,Node} | {global,GlobalName}
+ | {via,Module,ViaName} | pid()</v>
<v>&nbsp;Node = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Request = term()</v>
</type>
<desc>
@@ -355,9 +369,10 @@ gen_server:abcast -----> Module:handle_cast/2
<v>&nbsp;&nbsp;&nbsp;Dbg = trace | log | statistics</v>
<v>&nbsp;&nbsp;&nbsp;&nbsp;| {log_to_file,FileName} | {install,{Func,FuncState}}</v>
<v>State = term()</v>
- <v>ServerName = {local,Name} | {global,GlobalName}</v>
+ <v>ServerName = {local,Name} | {global,GlobalName}
+ | {via,Module,ViaName}</v>
<v>&nbsp;Name = atom()</v>
- <v>&nbsp;GlobalName = term()</v>
+ <v>&nbsp;GlobalName = ViaName = term()</v>
<v>Timeout = int() | infinity</v>
</type>
<desc>
diff --git a/lib/stdlib/doc/src/supervisor.xml b/lib/stdlib/doc/src/supervisor.xml
index 33a7f5bb6a..d1e62230bc 100644
--- a/lib/stdlib/doc/src/supervisor.xml
+++ b/lib/stdlib/doc/src/supervisor.xml
@@ -264,8 +264,14 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
<p>If <c><anno>SupName</anno>={local,Name}</c> the supervisor is registered
locally as <c>Name</c> using <c>register/2</c>. If
<c><anno>SupName</anno>={global,Name}</c> the supervisor is registered
- globally as <c>Name</c> using <c>global:register_name/2</c>.
- If no name is provided, the supervisor is not registered.</p>
+ globally as <c>Name</c> using <c>global:register_name/2</c>. If
+ <c><anno>SupName</anno>={via,Module,Name}</c> the supervisor
+ is registered as <c>Name</c> using the registry represented by
+ <c>Module</c>. The <c>Module</c> callback should export the functions
+ <c>register_name/2</c>, <c>unregister_name/1</c> and <c>send/2</c>,
+ which should behave like the corresponding functions in <c>global</c>.
+ Thus, <c>{via,global,Name}</c> is a valid reference.</p>
+ <p>If no name is provided, the supervisor is not registered.</p>
<p><c><anno>Module</anno></c> is the name of the callback module.</p>
<p><c><anno>Args</anno></c> is an arbitrary term which is passed as
the argument to <c><anno>Module</anno>:init/1</c>.</p>
@@ -308,6 +314,8 @@ child_spec() = {Id,StartFunc,Restart,Shutdown,Type,Modules}
registered at another node, or</item>
<item><c>{global,Name}</c>, if the supervisor is globally
registered.</item>
+ <item><c>{via,Module,Name}</c>, if the supervisor is registered
+ through an alternative process registry.</item>
</list>
<p><c><anno>ChildSpec</anno></c> should be a valid child specification
(unless the supervisor is a <c>simple_one_for_one</c>
diff --git a/lib/stdlib/doc/src/supervisor_bridge.xml b/lib/stdlib/doc/src/supervisor_bridge.xml
index c1a5e7947f..f6712d6c1d 100644
--- a/lib/stdlib/doc/src/supervisor_bridge.xml
+++ b/lib/stdlib/doc/src/supervisor_bridge.xml
@@ -63,6 +63,13 @@
If <c><anno>SupBridgeName</anno>={global,<anno>Name</anno>}</c> the supervisor_bridge is
registered globally as <c><anno>Name</anno></c> using
<c>global:register_name/2</c>.
+ If <c><anno>SupBridgeName</anno>={via,<anno>Module</anno>,<anno>Name</anno>}</c> the supervisor_bridge is
+ registered as <c><anno>Name</anno></c> using a registry represented
+ by <anno>Module</anno>. The <c>Module</c> callback should export
+ the functions <c>register_name/2</c>, <c>unregister_name/1</c>
+ and <c>send/2</c>, which should behave like the
+ corresponding functions in <c>global</c>. Thus,
+ <c>{via,global,GlobalName}</c> is a valid reference.
If no name is provided, the supervisor_bridge is not registered.
If there already exists a process with the specified
<c><anno>SupBridgeName</anno></c> the function returns
diff --git a/lib/stdlib/src/gen.erl b/lib/stdlib/src/gen.erl
index 5d803091b6..42555aedd7 100644
--- a/lib/stdlib/src/gen.erl
+++ b/lib/stdlib/src/gen.erl
@@ -36,7 +36,7 @@
%%-----------------------------------------------------------------
-type linkage() :: 'link' | 'nolink'.
--type emgr_name() :: {'local', atom()} | {'global', term()}.
+-type emgr_name() :: {'local', atom()} | {'global', term()} | {via, atom(), term()}.
-type start_ret() :: {'ok', pid()} | 'ignore' | {'error', term()}.
@@ -53,7 +53,7 @@
%% start(GenMod, LinkP, Name, Mod, Args, Options)
%% GenMod = atom(), callback module implementing the 'real' fsm
%% LinkP = link | nolink
-%% Name = {local, atom()} | {global, term()}
+%% Name = {local, atom()} | {global, term()} | {via, atom(), term()}
%% Args = term(), init arguments (to Mod:init/1)
%% Options = [{timeout, Timeout} | {debug, [Flag]} | {spawn_opt, OptionList}]
%% Flag = trace | log | {logfile, File} | statistics | debug
@@ -158,9 +158,12 @@ call(Name, Label, Request, Timeout)
exit(noproc)
end;
%% Global by name
-call({global, _Name}=Process, Label, Request, Timeout)
- when Timeout =:= infinity;
- is_integer(Timeout), Timeout >= 0 ->
+call(Process, Label, Request, Timeout)
+ when ((tuple_size(Process) == 2 andalso element(1, Process) == global)
+ orelse
+ (tuple_size(Process) == 3 andalso element(1, Process) == via))
+ andalso
+ (Timeout =:= infinity orelse (is_integer(Timeout) andalso Timeout >= 0)) ->
case where(Process) of
Pid when is_pid(Pid) ->
Node = node(Pid),
@@ -274,6 +277,7 @@ reply({To, Tag}, Reply) ->
%%% Misc. functions.
%%%-----------------------------------------------------------------
where({global, Name}) -> global:whereis_name(Name);
+where({via, Module, Name}) -> Module:whereis_name(Name);
where({local, Name}) -> whereis(Name).
name_register({local, Name} = LN) ->
@@ -287,8 +291,16 @@ name_register({global, Name} = GN) ->
case global:register_name(Name, self()) of
yes -> true;
no -> {false, where(GN)}
+ end;
+name_register({via, Module, Name} = GN) ->
+ case Module:register_name(Name, self()) of
+ yes ->
+ true;
+ no ->
+ {false, where(GN)}
end.
+
timeout(Options) ->
case opt(timeout, Options) of
{ok, Time} ->
diff --git a/lib/stdlib/src/gen_event.erl b/lib/stdlib/src/gen_event.erl
index 3317b30e5c..ca05df20c0 100644
--- a/lib/stdlib/src/gen_event.erl
+++ b/lib/stdlib/src/gen_event.erl
@@ -107,8 +107,10 @@
-type add_handler_ret() :: ok | term() | {'EXIT',term()}.
-type del_handler_ret() :: ok | term() | {'EXIT',term()}.
--type emgr_name() :: {'local', atom()} | {'global', atom()}.
--type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()} | pid().
+-type emgr_name() :: {'local', atom()} | {'global', atom()}
+ | {'via', atom(), term()}.
+-type emgr_ref() :: atom() | {atom(), atom()} | {'global', atom()}
+ | {'via', atom(), term()} | pid().
-type start_ret() :: {'ok', pid()} | {'error', term()}.
%%---------------------------------------------------------------------------
@@ -143,6 +145,7 @@ init_it(Starter, Parent, Name0, _, _, Options) ->
name({local,Name}) -> Name;
name({global,Name}) -> Name;
+name({via,_, Name}) -> Name;
name(Pid) when is_pid(Pid) -> Pid.
-spec add_handler(emgr_ref(), handler(), term()) -> term().
@@ -209,6 +212,9 @@ call1(M, Handler, Query, Timeout) ->
send({global, Name}, Cmd) ->
catch global:send(Name, Cmd),
ok;
+send({via, Mod, Name}, Cmd) ->
+ catch Mod:send(Name, Cmd),
+ ok;
send(M, Cmd) ->
M ! Cmd,
ok.
diff --git a/lib/stdlib/src/gen_fsm.erl b/lib/stdlib/src/gen_fsm.erl
index 80866c0806..51dc0bcee2 100644
--- a/lib/stdlib/src/gen_fsm.erl
+++ b/lib/stdlib/src/gen_fsm.erl
@@ -165,7 +165,7 @@
%%% start(Name, Mod, Args, Options)
%%% start_link(Mod, Args, Options)
%%% start_link(Name, Mod, Args, Options) where:
-%%% Name ::= {local, atom()} | {global, atom()}
+%%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()}
%%% Mod ::= atom(), callback module implementing the 'real' fsm
%%% Args ::= term(), init arguments (to Mod:init/1)
%%% Options ::= [{debug, [Flag]}]
@@ -191,6 +191,9 @@ start_link(Name, Mod, Args, Options) ->
send_event({global, Name}, Event) ->
catch global:send(Name, {'$gen_event', Event}),
ok;
+send_event({via, Mod, Name}, Event) ->
+ catch Mod:send(Name, {'$gen_event', Event}),
+ ok;
send_event(Name, Event) ->
Name ! {'$gen_event', Event},
ok.
@@ -214,6 +217,9 @@ sync_send_event(Name, Event, Timeout) ->
send_all_state_event({global, Name}, Event) ->
catch global:send(Name, {'$gen_all_state_event', Event}),
ok;
+send_all_state_event({via, Mod, Name}, Event) ->
+ catch Mod:send(Name, {'$gen_all_state_event', Event}),
+ ok;
send_all_state_event(Name, Event) ->
Name ! {'$gen_all_state_event', Event},
ok.
@@ -273,7 +279,10 @@ cancel_timer(Ref) ->
enter_loop(Mod, Options, StateName, StateData) ->
enter_loop(Mod, Options, StateName, StateData, self(), infinity).
-enter_loop(Mod, Options, StateName, StateData, ServerName = {_,_}) ->
+enter_loop(Mod, Options, StateName, StateData, {Scope,_} = ServerName)
+ when Scope == local; Scope == global ->
+ enter_loop(Mod, Options, StateName, StateData, ServerName,infinity);
+enter_loop(Mod, Options, StateName, StateData, {via,_,_} = ServerName) ->
enter_loop(Mod, Options, StateName, StateData, ServerName,infinity);
enter_loop(Mod, Options, StateName, StateData, Timeout) ->
enter_loop(Mod, Options, StateName, StateData, self(), Timeout).
@@ -303,6 +312,15 @@ get_proc_name({global, Name}) ->
Name;
_Pid ->
exit(process_not_registered_globally)
+ end;
+get_proc_name({via, Mod, Name}) ->
+ case Mod:whereis_name(Name) of
+ undefined ->
+ exit({process_not_registered_via, Mod});
+ Pid when Pid =:= self() ->
+ Name;
+ _Pid ->
+ exit({process_not_registered_via, Mod})
end.
get_parent() ->
@@ -367,12 +385,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
name({local,Name}) -> Name;
name({global,Name}) -> Name;
+name({via,_, Name}) -> Name;
name(Pid) when is_pid(Pid) -> Pid.
unregister_name({local,Name}) ->
_ = (catch unregister(Name));
unregister_name({global,Name}) ->
_ = global:unregister_name(Name);
+unregister_name({via, Mod, Name}) ->
+ _ = Mod:unregister_name(Name);
unregister_name(Pid) when is_pid(Pid) ->
Pid.
diff --git a/lib/stdlib/src/gen_server.erl b/lib/stdlib/src/gen_server.erl
index f720ec15f5..b384e8baf7 100644
--- a/lib/stdlib/src/gen_server.erl
+++ b/lib/stdlib/src/gen_server.erl
@@ -142,7 +142,7 @@
%%% start(Name, Mod, Args, Options)
%%% start_link(Mod, Args, Options)
%%% start_link(Name, Mod, Args, Options) where:
-%%% Name ::= {local, atom()} | {global, atom()}
+%%% Name ::= {local, atom()} | {global, atom()} | {via, atom(), term()}
%%% Mod ::= atom(), callback module implementing the 'real' server
%%% Args ::= term(), init arguments (to Mod:init/1)
%%% Options ::= [{timeout, Timeout} | {debug, [Flag]}]
@@ -194,6 +194,9 @@ call(Name, Request, Timeout) ->
cast({global,Name}, Request) ->
catch global:send(Name, cast_msg(Request)),
ok;
+cast({via, Mod, Name}, Request) ->
+ catch Mod:send(Name, cast_msg(Request)),
+ ok;
cast({Name,Node}=Dest, Request) when is_atom(Name), is_atom(Node) ->
do_cast(Dest, Request);
cast(Dest, Request) when is_atom(Dest) ->
@@ -266,7 +269,11 @@ multi_call(Nodes, Name, Req, Timeout)
enter_loop(Mod, Options, State) ->
enter_loop(Mod, Options, State, self(), infinity).
-enter_loop(Mod, Options, State, ServerName = {_, _}) ->
+enter_loop(Mod, Options, State, ServerName = {Scope, _})
+ when Scope == local; Scope == local ->
+ enter_loop(Mod, Options, State, ServerName, infinity);
+
+enter_loop(Mod, Options, State, ServerName = {via, _, _}) ->
enter_loop(Mod, Options, State, ServerName, infinity);
enter_loop(Mod, Options, State, Timeout) ->
@@ -327,12 +334,15 @@ init_it(Starter, Parent, Name0, Mod, Args, Options) ->
name({local,Name}) -> Name;
name({global,Name}) -> Name;
+name({via,_, Name}) -> Name;
name(Pid) when is_pid(Pid) -> Pid.
unregister_name({local,Name}) ->
_ = (catch unregister(Name));
unregister_name({global,Name}) ->
_ = global:unregister_name(Name);
+unregister_name({via, Mod, Name}) ->
+ _ = Mod:unregister_name(Name);
unregister_name(Pid) when is_pid(Pid) ->
Pid.
@@ -827,6 +837,15 @@ get_proc_name({global, Name}) ->
Name;
_Pid ->
exit(process_not_registered_globally)
+ end;
+get_proc_name({via, Mod, Name}) ->
+ case Mod:whereis_name(Name) of
+ undefined ->
+ exit({process_not_registered_via, Mod});
+ Pid when Pid =:= self() ->
+ Name;
+ _Pid ->
+ exit({process_not_registered_via, Mod})
end.
get_parent() ->
diff --git a/lib/stdlib/test/Makefile b/lib/stdlib/test/Makefile
index aa6a660c34..b36265302c 100644
--- a/lib/stdlib/test/Makefile
+++ b/lib/stdlib/test/Makefile
@@ -20,6 +20,7 @@ MODULES= \
digraph_utils_SUITE \
dummy1_h \
dummy_h \
+ dummy_via \
edlin_expand_SUITE \
epp_SUITE \
erl_eval_helper \
diff --git a/lib/stdlib/test/dummy_via.erl b/lib/stdlib/test/dummy_via.erl
new file mode 100644
index 0000000000..e405811cbe
--- /dev/null
+++ b/lib/stdlib/test/dummy_via.erl
@@ -0,0 +1,94 @@
+-module(dummy_via).
+-export([reset/0,
+ register_name/2,
+ whereis_name/1,
+ unregister_name/1,
+ send/2]).
+
+
+reset() ->
+ P = whereis(?MODULE),
+ catch unlink(P),
+ Ref = erlang:monitor(process, P),
+ catch exit(P, kill),
+ receive {'DOWN',Ref,_,_,_} -> ok end,
+ Me = self(),
+ Pid = spawn_link(fun() ->
+ register(?MODULE, self()),
+ Me ! {self(), started},
+ loop([])
+ end),
+ receive
+ {Pid, started} ->
+ Pid
+ after 10000 ->
+ exit(timeout)
+ end.
+
+register_name(Name, Pid) when is_pid(Pid) ->
+ call({register_name, Name, Pid}).
+
+unregister_name(Name) ->
+ call({unregister_name, Name}).
+
+whereis_name(Name) ->
+ call({whereis_name, Name}).
+
+send(Name, Msg) ->
+ case whereis_name(Name) of
+ undefined ->
+ exit({badarg, {Name, Msg}});
+ Pid when is_pid(Pid) ->
+ Pid ! Msg,
+ Pid
+ end.
+
+call(Req) ->
+ MRef = erlang:monitor(process, ?MODULE),
+ ?MODULE ! {self(), MRef, Req},
+ receive
+ {'DOWN', MRef, _, _, _} ->
+ erlang:error(badarg);
+ {MRef, badarg} ->
+ erlang:error(badarg);
+ {MRef, Reply} ->
+ Reply
+ after 5000 ->
+ erlang:error(timeout)
+ end.
+
+loop(Reg) ->
+ receive
+ {'DOWN', _, _, P, _} when is_pid(P) ->
+ loop([X || {_,Pid,_} = X <- Reg, Pid =/= P]);
+ {From, Ref, Request} when is_pid(From), is_reference(Ref) ->
+ {Reply, NewReg} = handle_request(Request, Reg),
+ From ! {Ref, Reply},
+ loop(NewReg)
+ end.
+
+handle_request({register_name, Name, Pid}, Reg) when is_pid(Pid) ->
+ case lists:keyfind(Name, 1, Reg) of
+ false ->
+ Ref = erlang:monitor(process, Pid),
+ {yes, [{Name, Pid, Ref}|Reg]};
+ _ ->
+ {no, Reg}
+ end;
+handle_request({whereis_name, Name}, Reg) ->
+ case lists:keyfind(Name, 1, Reg) of
+ {_, Pid, _} ->
+ {Pid, Reg};
+ false ->
+ {undefined, Reg}
+ end;
+handle_request({unregister_name, Name}, Reg) ->
+ case lists:keyfind(Name, 1, Reg) of
+ {_, _, Ref} ->
+ catch erlang:demonitor(Ref);
+ _ ->
+ ok
+ end,
+ {ok, lists:keydelete(Name, 1, Reg)};
+handle_request(_, Reg) ->
+ {badarg, Reg}.
diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl
index 101828fdef..59532b65a0 100644
--- a/lib/stdlib/test/ets_SUITE.erl
+++ b/lib/stdlib/test/ets_SUITE.erl
@@ -72,6 +72,7 @@
exit_many_many_tables_owner/1]).
-export([write_concurrency/1, heir/1, give_away/1, setopts/1]).
-export([bad_table/1, types/1]).
+-export([otp_9932/1]).
-export([otp_9423/1]).
-export([init_per_testcase/2, end_per_testcase/2]).
@@ -145,6 +146,7 @@ all() ->
exit_many_large_table_owner, exit_many_tables_owner,
exit_many_many_tables_owner, write_concurrency, heir,
give_away, setopts, bad_table, types,
+ otp_9932,
otp_9423].
groups() ->
@@ -5434,6 +5436,22 @@ types_do(Opts) ->
?line verify_etsmem(EtsMem).
+%% OTP-9932: Memory overwrite when inserting large integers in compressed bag.
+%% Will crash with segv on 64-bit opt if not fixed.
+otp_9932(Config) when is_list(Config) ->
+ T = ets:new(xxx, [bag, compressed]),
+ Fun = fun(N) ->
+ Key = {1316110174588445 bsl N,1316110174588583 bsl N},
+ S = {Key, Key},
+ true = ets:insert(T, S),
+ [S] = ets:lookup(T, Key),
+ true = ets:insert(T, S),
+ [S] = ets:lookup(T, Key)
+ end,
+ lists:foreach(Fun, lists:seq(0, 16)),
+ ets:delete(T).
+
+
otp_9423(doc) -> ["vm-deadlock caused by race between ets:delete and others on write_concurrency table"];
otp_9423(Config) when is_list(Config) ->
InitF = fun(_) -> {0,0} end,
diff --git a/lib/stdlib/test/gen_event_SUITE.erl b/lib/stdlib/test/gen_event_SUITE.erl
index b3a7edc140..5c51e12e35 100644
--- a/lib/stdlib/test/gen_event_SUITE.erl
+++ b/lib/stdlib/test/gen_event_SUITE.erl
@@ -62,6 +62,8 @@ start(suite) -> [];
start(Config) when is_list(Config) ->
OldFl = process_flag(trap_exit, true),
+ ?line dummy_via:reset(),
+
?line {ok, Pid0} = gen_event:start(), %anonymous
?line [] = gen_event:which_handlers(Pid0),
?line ok = gen_event:stop(Pid0),
@@ -85,6 +87,11 @@ start(Config) when is_list(Config) ->
?line [] = gen_event:which_handlers(Pid4),
?line ok = gen_event:stop({global, my_dummy_name}),
+ ?line {ok, Pid5} = gen_event:start_link({via, dummy_via, my_dummy_name}),
+ ?line [] = gen_event:which_handlers({via, dummy_via, my_dummy_name}),
+ ?line [] = gen_event:which_handlers(Pid5),
+ ?line ok = gen_event:stop({via, dummy_via, my_dummy_name}),
+
?line {ok, _} = gen_event:start_link({local, my_dummy_name}),
?line {error, {already_started, _}} =
gen_event:start_link({local, my_dummy_name}),
@@ -92,15 +99,28 @@ start(Config) when is_list(Config) ->
gen_event:start({local, my_dummy_name}),
?line ok = gen_event:stop(my_dummy_name),
- ?line {ok, Pid5} = gen_event:start_link({global, my_dummy_name}),
+ ?line {ok, Pid6} = gen_event:start_link({global, my_dummy_name}),
?line {error, {already_started, _}} =
gen_event:start_link({global, my_dummy_name}),
?line {error, {already_started, _}} =
gen_event:start({global, my_dummy_name}),
- exit(Pid5, shutdown),
+ exit(Pid6, shutdown),
+ receive
+ {'EXIT', Pid6, shutdown} -> ok
+ after 10000 ->
+ ?t:fail(exit_gen_event)
+ end,
+
+ ?line {ok, Pid7} = gen_event:start_link({via, dummy_via, my_dummy_name}),
+ ?line {error, {already_started, _}} =
+ gen_event:start_link({via, dummy_via, my_dummy_name}),
+ ?line {error, {already_started, _}} =
+ gen_event:start({via, dummy_via, my_dummy_name}),
+
+ exit(Pid7, shutdown),
receive
- {'EXIT', Pid5, shutdown} -> ok
+ {'EXIT', Pid7, shutdown} -> ok
after 10000 ->
?t:fail(exit_gen_event)
end,
diff --git a/lib/stdlib/test/gen_fsm_SUITE.erl b/lib/stdlib/test/gen_fsm_SUITE.erl
index d60629d841..bdb4ea65b5 100644
--- a/lib/stdlib/test/gen_fsm_SUITE.erl
+++ b/lib/stdlib/test/gen_fsm_SUITE.erl
@@ -21,11 +21,11 @@
-include_lib("test_server/include/test_server.hrl").
%% Test cases
--export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
init_per_group/2,end_per_group/2]).
--export([ start1/1, start2/1, start3/1, start4/1 , start5/1, start6/1,
- start7/1, start8/1, start9/1, start10/1, start11/1]).
+-export([start1/1, start2/1, start3/1, start4/1, start5/1, start6/1,
+ start7/1, start8/1, start9/1, start10/1, start11/1, start12/1]).
-export([ abnormal1/1, abnormal2/1]).
@@ -56,14 +56,14 @@
suite() -> [{ct_hooks,[ts_install_cth]}].
-all() ->
+all() ->
[{group, start}, {group, abnormal}, shutdown,
{group, sys}, hibernate, enter_loop].
-groups() ->
+groups() ->
[{start, [],
[start1, start2, start3, start4, start5, start6, start7,
- start8, start9, start10, start11]},
+ start8, start9, start10, start11, start12]},
{abnormal, [], [abnormal1, abnormal2]},
{sys, [],
[sys1, call_format_status, error_format_status]}].
@@ -258,6 +258,25 @@ start11(Config) when is_list(Config) ->
test_server:messages_get(),
ok.
+%% Via register linked
+start12(Config) when is_list(Config) ->
+ ?line dummy_via:reset(),
+ ?line {ok, Pid} =
+ gen_fsm:start_link({via, dummy_via, my_fsm}, gen_fsm_SUITE, [], []),
+ ?line {error, {already_started, Pid}} =
+ gen_fsm:start_link({via, dummy_via, my_fsm}, gen_fsm_SUITE, [], []),
+ ?line {error, {already_started, Pid}} =
+ gen_fsm:start({via, dummy_via, my_fsm}, gen_fsm_SUITE, [], []),
+
+ ?line ok = do_func_test(Pid),
+ ?line ok = do_sync_func_test(Pid),
+ ?line ok = do_func_test({via, dummy_via, my_fsm}),
+ ?line ok = do_sync_func_test({via, dummy_via, my_fsm}),
+ ?line stop_it({via, dummy_via, my_fsm}),
+
+ test_server:messages_get(),
+ ok.
+
%% Check that time outs in calls work
abnormal1(suite) -> [];
@@ -362,7 +381,25 @@ call_format_status(Config) when is_list(Config) ->
?line Status4 = sys:get_status(GlobalName2),
?line {status, Pid4, _Mod, [_PDict4, running, _, _, Data4]} = Status4,
?line [format_status_called | _] = lists:reverse(Data4),
- ?line stop_it(Pid4).
+ ?line stop_it(Pid4),
+
+ %% check that format_status can handle a name being a term other than a
+ %% pid or atom
+ ?line dummy_via:reset(),
+ ViaName1 = {via, dummy_via, "CallFormatStatus"},
+ ?line {ok, Pid5} = gen_fsm:start(ViaName1, gen_fsm_SUITE, [], []),
+ ?line Status5 = sys:get_status(ViaName1),
+ ?line {status, Pid5, _Mod, [_PDict5, running, _, _, Data5]} = Status5,
+ ?line [format_status_called | _] = lists:reverse(Data5),
+ ?line stop_it(Pid5),
+ ViaName2 = {via, dummy_via, {name, "term"}},
+ ?line {ok, Pid6} = gen_fsm:start(ViaName2, gen_fsm_SUITE, [], []),
+ ?line Status6 = sys:get_status(ViaName2),
+ ?line {status, Pid6, _Mod, [_PDict6, running, _, _, Data6]} = Status6,
+ ?line [format_status_called | _] = lists:reverse(Data6),
+ ?line stop_it(Pid6).
+
+
error_format_status(Config) when is_list(Config) ->
?line error_logger_forwarder:register(),
@@ -520,6 +557,8 @@ enter_loop(doc) ->
enter_loop(Config) when is_list(Config) ->
OldFlag = process_flag(trap_exit, true),
+ ?line dummy_via:reset(),
+
%% Locally registered process + {local, Name}
?line {ok, Pid1a} =
proc_lib:start_link(?MODULE, enter_loop, [local, local]),
@@ -623,10 +662,22 @@ enter_loop(Config) when is_list(Config) ->
{'EXIT', Pid6b, process_not_registered_globally} ->
ok
after 1000 ->
- ?line test_server:fail(gen_server_started)
+ ?line test_server:fail(gen_fsm_started)
end,
global:unregister_name(armitage),
+ dummy_via:register_name(armitage, self()),
+ ?line {ok, Pid6c} =
+ proc_lib:start_link(?MODULE, enter_loop, [anon, via]),
+ receive
+ {'EXIT', Pid6c, {process_not_registered_via, dummy_via}} ->
+ ok
+ after 1000 ->
+ ?line test_server:fail({gen_fsm_started, process_info(self(),
+ messages)})
+ end,
+ dummy_via:unregister_name(armitage),
+
process_flag(trap_exit, OldFlag),
ok.
@@ -635,6 +686,7 @@ enter_loop(Reg1, Reg2) ->
case Reg1 of
local -> register(armitage, self());
global -> global:register_name(armitage, self());
+ via -> dummy_via:register_name(armitage, self());
anon -> ignore
end,
proc_lib:init_ack({ok, self()}),
@@ -643,6 +695,9 @@ enter_loop(Reg1, Reg2) ->
gen_fsm:enter_loop(?MODULE, [], state0, [], {local,armitage});
global ->
gen_fsm:enter_loop(?MODULE, [], state0, [], {global,armitage});
+ via ->
+ gen_fsm:enter_loop(?MODULE, [], state0, [],
+ {via, dummy_via, armitage});
anon ->
gen_fsm:enter_loop(?MODULE, [], state0, [])
end.
diff --git a/lib/stdlib/test/gen_server_SUITE.erl b/lib/stdlib/test/gen_server_SUITE.erl
index 7fb8d54f2d..cdf15ba017 100644
--- a/lib/stdlib/test/gen_server_SUITE.erl
+++ b/lib/stdlib/test/gen_server_SUITE.erl
@@ -36,7 +36,7 @@
]).
% spawn export
--export([spec_init_local/2, spec_init_global/2,
+-export([spec_init_local/2, spec_init_global/2, spec_init_via/2,
spec_init_default_timeout/2, spec_init_anonymous/1,
spec_init_anonymous_default_timeout/1,
spec_init_not_proc_lib/1, cast_fast_messup/0]).
@@ -199,6 +199,35 @@ start(Config) when is_list(Config) ->
test_server:fail(not_stopped)
end,
+ %% via register
+ ?line dummy_via:reset(),
+ ?line {ok, Pid6} =
+ gen_server:start({via, dummy_via, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({via, dummy_via, my_test_name}, started_p),
+ ?line {error, {already_started, Pid6}} =
+ gen_server:start({via, dummy_via, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({via, dummy_via, my_test_name}, stop),
+ test_server:sleep(1),
+ ?line {'EXIT', {noproc,_}} = (catch gen_server:call(Pid6, started_p, 10)),
+
+ %% via register linked
+ ?line dummy_via:reset(),
+ ?line {ok, Pid7} =
+ gen_server:start_link({via, dummy_via, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({via, dummy_via, my_test_name}, started_p),
+ ?line {error, {already_started, Pid7}} =
+ gen_server:start({via, dummy_via, my_test_name},
+ gen_server_SUITE, [], []),
+ ?line ok = gen_server:call({via, dummy_via, my_test_name}, stop),
+ ?line receive
+ {'EXIT', Pid7, stopped} ->
+ ok
+ after 5000 ->
+ test_server:fail(not_stopped)
+ end,
test_server:messages_get(),
%% Must wait for all error messages before going to next test.
@@ -853,6 +882,8 @@ otp_5854(doc) ->
otp_5854(Config) when is_list(Config) ->
OldFlag = process_flag(trap_exit, true),
+ ?line dummy_via:reset(),
+
%% Make sure gen_server:enter_loop does not accept {local,Name}
%% when it's another process than the calling one which is
%% registered under that name
@@ -881,6 +912,18 @@ otp_5854(Config) when is_list(Config) ->
end,
global:unregister_name(armitage),
+ %% (same for {via, Mod, Name})
+ dummy_via:register_name(armitage, self()),
+ ?line {ok, Pid3} =
+ start_link(spec_init_via, [{not_ok, armitage}, []]),
+ receive
+ {'EXIT', Pid3, {process_not_registered_via, dummy_via}} ->
+ ok
+ after 1000 ->
+ ?line test_server:fail(gen_server_started)
+ end,
+ dummy_via:unregister_name(armitage),
+
process_flag(trap_exit, OldFlag),
ok.
@@ -1060,7 +1103,22 @@ spec_init_global({not_ok, Name}, Options) ->
%% Supervised init can occur here ...
gen_server:enter_loop(?MODULE, Options, {}, {global, Name}, infinity).
-spec_init_default_timeout({ok, Name}, Options) ->
+spec_init_via({ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ dummy_via:register_name(Name, self()),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {},
+ {via, dummy_via, Name}, infinity);
+
+spec_init_via({not_ok, Name}, Options) ->
+ process_flag(trap_exit, true),
+ proc_lib:init_ack({ok, self()}),
+ %% Supervised init can occur here ...
+ gen_server:enter_loop(?MODULE, Options, {},
+ {via, dummy_via, Name}, infinity).
+
+spec_init_default_timeout({ok, Name}, Options) ->
process_flag(trap_exit, true),
register(Name, self()),
proc_lib:init_ack({ok, self()}),