~rdoering/ubuntu/karmic/erlang/fix-535090

« back to all changes in this revision

Viewing changes to lib/ssh/src/ssh.erl

  • Committer: Bazaar Package Importer
  • Author(s): Sergei Golovan
  • Date: 2009-02-15 16:42:52 UTC
  • mfrom: (3.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090215164252-q5x4rcf8a5pbesb1
Tags: 1:12.b.5-dfsg-2
Upload to unstable after lenny is released.

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
 
21
21
-module(ssh).
22
22
 
23
 
-export([
24
 
         start/0,
25
 
         stop/0
26
 
        ]).
27
 
 
 
23
-include("ssh.hrl").
 
24
-include("ssh_connect.hrl").
 
25
 
 
26
-export([start/0, start/1, stop/0, attach/2, attach/3,
 
27
         connect/3, close/1, daemon/1, daemon/2, daemon/3,
 
28
         stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2,
 
29
         shell/1, shell/2, shell/3]).
 
30
 
 
31
%%--------------------------------------------------------------------
 
32
%% Function: start([, Type]) -> ok
 
33
%%
 
34
%%  Type =  permanent | transient | temporary
 
35
%%
 
36
%% Description: Starts the inets application. Default type
 
37
%% is temporary. see application(3)
 
38
%%--------------------------------------------------------------------
28
39
start() ->
29
40
    application:start(ssh).
30
41
 
 
42
start(Type) ->
 
43
    application:start(ssh, Type).
 
44
 
 
45
%%--------------------------------------------------------------------
 
46
%% Function: stop() -> ok
 
47
%%
 
48
%% Description: Stops the inets application.
 
49
%%--------------------------------------------------------------------
31
50
stop() ->
32
51
    application:stop(ssh).
 
52
 
 
53
%%--------------------------------------------------------------------
 
54
%% Function: connect(Host, Port, Options) -> ConnectionRef
 
55
%% 
 
56
%%      Host - string()
 
57
%%      Port - integer()
 
58
%%      Options - [{Option, Value}]
 
59
%%
 
60
%% Description: Starts an ssh connection.
 
61
%%--------------------------------------------------------------------
 
62
connect(Host, Port, Options) ->
 
63
    {SocketOpts, Opts} = handle_options(Options),
 
64
    Timeout = proplists:get_value(timeout, Opts, ?DEFAULT_TIMEOUT),
 
65
    DisableIpv6 =  proplists:get_value(ip_v6_disabled, Opts, false),
 
66
    {Address, Inet} = ip_address_and_inetopt(Host, DisableIpv6),
 
67
    try sshc_sup:start_child([[{address, Address}, {port, Port}, 
 
68
                              {role, client},
 
69
                              {channel_pid, self()},
 
70
                              {socket_opts, [Inet | SocketOpts]}, 
 
71
                              {ssh_opts, [{host, Host}| Opts]}]]) of 
 
72
        {ok, ConnectionSup} ->
 
73
            {ok, Manager} = 
 
74
                ssh_connection_sup:connection_manager(ConnectionSup),
 
75
            receive 
 
76
                {Manager, is_connected} ->
 
77
                    {ok, Manager};
 
78
                %% When the connection fails 
 
79
                %% ssh_connection_sup:connection_manager
 
80
                %% might return undefined as the connection manager
 
81
                %% could allready have terminated, so we will not
 
82
                %% match the Manager in this case
 
83
                {_, not_connected, {error, Reason}} ->
 
84
                    {error, Reason};
 
85
                {_, not_connected, Other} ->
 
86
                    {error, Other}
 
87
            after 
 
88
                Timeout -> 
 
89
                    exit(ConnectionSup, shutdown),
 
90
                    {error, timeout}
 
91
            end
 
92
    catch 
 
93
        exit:{noproc, _} ->
 
94
            {error, ssh_not_started}
 
95
    end.
 
96
 
 
97
%%--------------------------------------------------------------------
 
98
%% Function: close(ConnectionRef) -> ok
 
99
%%
 
100
%% Description: Closes an ssh connection.
 
101
%%--------------------------------------------------------------------  
 
102
close(ConnectionRef) ->
 
103
    ssh_connection_manager:stop(ConnectionRef).
 
104
 
 
105
%%--------------------------------------------------------------------
 
106
%% Function: daemon(Port) ->
 
107
%%           daemon(Port, Opts) ->
 
108
%%           daemon(Address, Port, Options) -> SshSystemRef
 
109
%%
 
110
%% Description: Starts a server listening for SSH connections 
 
111
%% on the given port.
 
112
%%--------------------------------------------------------------------  
 
113
daemon(Port) ->
 
114
    daemon(Port, []).
 
115
 
 
116
daemon(Port, Opts) ->
 
117
    daemon(any, Port, Opts).
 
118
 
 
119
daemon(HostAddr, Port, Opts) ->
 
120
    Shell = proplists:get_value(shell, Opts, {shell, start, []}),
 
121
    DisableIpv6 =  proplists:get_value(ip_v6_disabled, Opts, false),
 
122
    {Address, Inet} = case HostAddr of
 
123
                          any ->
 
124
                              {ok, Host} = inet:gethostname(), 
 
125
                              ip_address_and_inetopt(Host, DisableIpv6);
 
126
                          Str when is_list(Str) ->
 
127
                              ip_address_and_inetopt(Str, DisableIpv6);
 
128
                          {_,_,_,_} ->
 
129
                              {HostAddr, inet};
 
130
                          {_,_,_,_,_,_,_,_} ->
 
131
                              {HostAddr, inet6}
 
132
                          end,
 
133
    start_daemon(Address, Port, [{role, server}, 
 
134
                                {shell, Shell} | Opts], Inet).
 
135
 
 
136
%%--------------------------------------------------------------------
 
137
%% Function: stop_listener(SysRef) -> ok
 
138
%%           stop_listener(Address, Port) -> ok
 
139
%%
 
140
%%
 
141
%% Description: Stops the listener, but leaves 
 
142
%% existing connections started by the listener up and running.
 
143
%%--------------------------------------------------------------------  
 
144
stop_listener(SysSup) ->
 
145
    ssh_system_sup:stop_listener(SysSup).
 
146
stop_listener(Address, Port) ->
 
147
    ssh_system_sup:stop_listener(Address, Port).
 
148
 
 
149
%%--------------------------------------------------------------------
 
150
%% Function: stop_daemon(SysRef) -> ok
 
151
%%%          stop_daemon(Address, Port) -> ok
 
152
%%
 
153
%%
 
154
%% Description: Stops the listener and all connections started by 
 
155
%% the listener.
 
156
%%--------------------------------------------------------------------  
 
157
stop_daemon(SysSup) ->
 
158
    ssh_system_sup:stop_system(SysSup).
 
159
stop_daemon(Address, Port) ->
 
160
    ssh_system_sup:stop_system(Address, Port).
 
161
 
 
162
%%--------------------------------------------------------------------
 
163
%% Function: shell(Host [,Port,Options]) -> 
 
164
%%
 
165
%%   Host = string()
 
166
%%   Port = integer()
 
167
%%   Options = [{Option, Value}]
 
168
%%
 
169
%% Description: Starts an interactive shell to an SSH server on the
 
170
%% given <Host>. The function waits for user input,
 
171
%% and will not return until the remote shell is ended.(e.g. on
 
172
%% exit from the shell)
 
173
%%--------------------------------------------------------------------
 
174
shell(Host) ->
 
175
    shell(Host, ?SSH_DEFAULT_PORT, []).
 
176
shell(Host, Options) ->
 
177
    shell(Host, ?SSH_DEFAULT_PORT, Options).
 
178
shell(Host, Port, Options) ->
 
179
    ssh_ssh:connect(Host, Port, Options).
 
180
 
 
181
%% TODO: Should this be a supported API function, used by
 
182
%% sftp and ssh_ssh. Does it acctually work? Should be better tested
 
183
%% before made public!
 
184
attach(ConnectionRef, Timeout) ->
 
185
    ssh_connection_manager:attach(ConnectionRef, Timeout).
 
186
 
 
187
attach(ConnectionRef, ChannelPid, Timeout) ->
 
188
    ssh_connection_manager:attach(ConnectionRef, ChannelPid, Timeout).
 
189
 
 
190
%%--------------------------------------------------------------------
 
191
%%% Internal functions
 
192
%%--------------------------------------------------------------------
 
193
start_daemon(Address, Port, Options, Inet) ->
 
194
    {SocketOpts, Opts} = handle_options([{ip, Address} | Options]),
 
195
    case ssh_system_sup:system_supervisor(Address, Port) of
 
196
        undefined ->
 
197
            try sshd_sup:start_child([{address, Address}, 
 
198
                                      {port, Port}, {role, server},
 
199
                                      {socket_opts, [Inet | SocketOpts]}, 
 
200
                                      {ssh_opts, Opts}]) of
 
201
                {ok, SysSup} ->
 
202
                    {ok, SysSup};
 
203
                {error, {already_started, _}} ->
 
204
                    {error, eaddrinuse}
 
205
            catch
 
206
                exit:{noproc, _} ->
 
207
                    {error, ssh_not_started}
 
208
            end;
 
209
        Sup  ->
 
210
            case ssh_system_sup:restart_acceptor(Address, Port) of
 
211
                {ok, _} ->
 
212
                    {ok, Sup};
 
213
                _  ->
 
214
                    {error, eaddrinuse}
 
215
            end
 
216
    end.
 
217
 
 
218
handle_options(Opts) ->
 
219
    handle_options(proplists:unfold(Opts), [], []).
 
220
handle_options([], SockOpts, Opts) ->
 
221
    {SockOpts, Opts};
 
222
%% TODO: Could do some type checks here on plain ssh-opts
 
223
handle_options([{system_dir, _} = Opt | Rest], SockOpts, Opts) -> 
 
224
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
225
handle_options([{user_dir, _} = Opt | Rest], SockOpts, Opts) -> 
 
226
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
227
handle_options([{user_dir_fun, _} = Opt | Rest], SockOpts, Opts) -> 
 
228
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
229
handle_options([{silently_accept_hosts, _} = Opt | Rest], SockOpts, Opts) -> 
 
230
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
231
handle_options([{user_interaction, _} = Opt | Rest], SockOpts, Opts) -> 
 
232
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
233
handle_options([{public_key_alg, _} = Opt | Rest], SockOpts, Opts) -> 
 
234
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
235
handle_options([{connect_timeout, _} = Opt | Rest], SockOpts, Opts) -> 
 
236
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
237
handle_options([{user, _} = Opt | Rest], SockOpts, Opts) -> 
 
238
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
239
handle_options([{password, _} = Opt | Rest], SockOpts, Opts) -> 
 
240
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
241
handle_options([{user_passwords, _} = Opt | Rest], SockOpts, Opts) -> 
 
242
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
243
handle_options([{pwdfun, _} = Opt | Rest], SockOpts, Opts) -> 
 
244
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
245
handle_options([{user_auth, _} = Opt | Rest], SockOpts, Opts) -> 
 
246
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
247
handle_options([{key_cb, _} = Opt | Rest], SockOpts, Opts) -> 
 
248
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
249
handle_options([{role, _} = Opt | Rest], SockOpts, Opts) -> 
 
250
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
251
handle_options([{channel, _} = Opt | Rest], SockOpts, Opts) -> 
 
252
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
253
handle_options([{compression, _} = Opt | Rest], SockOpts, Opts) -> 
 
254
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
255
handle_options([{allow_user_interaction, _} = Opt | Rest], SockOpts, Opts) -> 
 
256
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
257
handle_options([{passive_subsys, _} = Opt | Rest], SockOpts, Opts) -> 
 
258
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
259
handle_options([{infofun, _} = Opt | Rest], SockOpts, Opts) -> 
 
260
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
261
handle_options([{connectfun, _} = Opt | Rest], SockOpts, Opts) -> 
 
262
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
263
handle_options([{disconnectfun , _} = Opt | Rest], SockOpts, Opts) -> 
 
264
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
265
handle_options([{failfun, _} = Opt | Rest], SockOpts, Opts) -> 
 
266
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
267
handle_options([{ip_v6_disabled, _} = Opt | Rest], SockOpts, Opts) -> 
 
268
    handle_options(Rest, SockOpts, [Opt | Opts]);
 
269
handle_options([{ip, _} = Opt | Rest], SockOpts, Opts) -> 
 
270
    handle_options(Rest, [Opt |SockOpts], Opts);
 
271
handle_options([{ifaddr, _} = Opt | Rest], SockOpts, Opts) -> 
 
272
    handle_options(Rest, [Opt |SockOpts], Opts);
 
273
handle_options([{fd, _} = Opt | Rest], SockOpts, Opts) -> 
 
274
    handle_options(Rest, [Opt | SockOpts], Opts);
 
275
handle_options([{nodelay, _} = Opt | Rest], SockOpts, Opts) -> 
 
276
    handle_options(Rest, [Opt | SockOpts], Opts);
 
277
handle_options([Opt | Rest], SockOpts, Opts) ->
 
278
    handle_options(Rest, SockOpts, [Opt | Opts]).
 
279
 
 
280
 
 
281
ip_address_and_inetopt(Host, true) ->
 
282
    {ok, Ip} = inet:getaddr(Host, inet),
 
283
    {Ip, inet};
 
284
 
 
285
ip_address_and_inetopt("localhost", false) ->
 
286
    {ok, Host} = inet:gethostname(), 
 
287
    {_, Inet} = ip_address_and_inetopt(Host, false),
 
288
    {ok, Ip} = inet:getaddr("localhost", Inet),
 
289
    {Ip, Inet};
 
290
 
 
291
ip_address_and_inetopt(Host, false) ->
 
292
    {{ok, Ip}, Inet} =  case (catch inet:getaddr(Host,inet6)) of
 
293
                            {ok, {0, 0, 0, 0, 0, 16#ffff, _, _}} ->
 
294
                                {inet:getaddr(Host, inet), inet};
 
295
                            {ok, IPAddr} ->
 
296
                                {{ok, IPAddr}, inet6};
 
297
                            _ ->
 
298
                                {inet:getaddr(Host, inet), inet} 
 
299
                        end,
 
300
    {Ip, Inet}.