PageRenderTime 53ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ssh/src/ssh.erl

http://github.com/mfoemmel/erlang-otp
Erlang | 339 lines | 207 code | 25 blank | 107 comment | 0 complexity | 153a04e48bc02924e950e03cacd2d561 MD5 | raw file
Possible License(s): LGPL-2.1, MPL-2.0-no-copyleft-exception
  1. %%
  2. %% %CopyrightBegin%
  3. %%
  4. %% Copyright Ericsson AB 2004-2009. All Rights Reserved.
  5. %%
  6. %% The contents of this file are subject to the Erlang Public License,
  7. %% Version 1.1, (the "License"); you may not use this file except in
  8. %% compliance with the License. You should have received a copy of the
  9. %% Erlang Public License along with this software. If not, it can be
  10. %% retrieved online at http://www.erlang.org/.
  11. %%
  12. %% Software distributed under the License is distributed on an "AS IS"
  13. %% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
  14. %% the License for the specific language governing rights and limitations
  15. %% under the License.
  16. %%
  17. %% %CopyrightEnd%
  18. %%
  19. %%
  20. -module(ssh).
  21. -include("ssh.hrl").
  22. -include("ssh_connect.hrl").
  23. -export([start/0, start/1, stop/0, connect/3, close/1, connection_info/2,
  24. channel_info/3,
  25. daemon/1, daemon/2, daemon/3,
  26. stop_listener/1, stop_listener/2, stop_daemon/1, stop_daemon/2,
  27. shell/1, shell/2, shell/3]).
  28. %%--------------------------------------------------------------------
  29. %% Function: start([, Type]) -> ok
  30. %%
  31. %% Type = permanent | transient | temporary
  32. %%
  33. %% Description: Starts the inets application. Default type
  34. %% is temporary. see application(3)
  35. %%--------------------------------------------------------------------
  36. start() ->
  37. application:start(ssh).
  38. start(Type) ->
  39. application:start(ssh, Type).
  40. %%--------------------------------------------------------------------
  41. %% Function: stop() -> ok
  42. %%
  43. %% Description: Stops the inets application.
  44. %%--------------------------------------------------------------------
  45. stop() ->
  46. application:stop(ssh).
  47. %%--------------------------------------------------------------------
  48. %% Function: connect(Host, Port, Options) ->
  49. %% connect(Host, Port, Options, Timeout -> ConnectionRef | {error, Reason}
  50. %%
  51. %% Host - string()
  52. %% Port - integer()
  53. %% Options - [{Option, Value}]
  54. %% Timeout - infinity | integer().
  55. %%
  56. %% Description: Starts an ssh connection.
  57. %%--------------------------------------------------------------------
  58. connect(Host, Port, Options) ->
  59. connect(Host, Port, Options, infinity).
  60. connect(Host, Port, Options, Timeout) ->
  61. {SocketOpts, Opts} = handle_options(Options),
  62. DisableIpv6 = proplists:get_value(ip_v6_disabled, Opts, false),
  63. Inet = inetopt(DisableIpv6),
  64. try sshc_sup:start_child([[{address, Host}, {port, Port},
  65. {role, client},
  66. {channel_pid, self()},
  67. {socket_opts, [Inet | SocketOpts]},
  68. {ssh_opts, [{host, Host}| Opts]}]]) of
  69. {ok, ConnectionSup} ->
  70. {ok, Manager} =
  71. ssh_connection_sup:connection_manager(ConnectionSup),
  72. MRef = erlang:monitor(process, Manager),
  73. receive
  74. {Manager, is_connected} ->
  75. do_demonitor(MRef, Manager),
  76. {ok, Manager};
  77. %% When the connection fails
  78. %% ssh_connection_sup:connection_manager
  79. %% might return undefined as the connection manager
  80. %% could allready have terminated, so we will not
  81. %% match the Manager in this case
  82. {_, not_connected, {error, Reason}} ->
  83. do_demonitor(MRef, Manager),
  84. {error, Reason};
  85. {_, not_connected, Other} ->
  86. do_demonitor(MRef, Manager),
  87. {error, Other};
  88. {'DOWN', MRef, _, Manager, Reason} when is_pid(Manager) ->
  89. receive %% Clear EXIT message from queue
  90. {'EXIT', Manager, _What} ->
  91. {error, Reason}
  92. after 0 ->
  93. {error, Reason}
  94. end
  95. after Timeout ->
  96. do_demonitor(MRef, Manager),
  97. ssh_connection_manager:stop(Manager),
  98. {error, timeout}
  99. end
  100. catch
  101. exit:{noproc, _} ->
  102. {error, ssh_not_started}
  103. end.
  104. do_demonitor(MRef, Manager) ->
  105. erlang:demonitor(MRef),
  106. receive
  107. {'DOWN', MRef, _, Manager, _} ->
  108. ok
  109. after 0 ->
  110. ok
  111. end.
  112. %%--------------------------------------------------------------------
  113. %% Function: close(ConnectionRef) -> ok
  114. %%
  115. %% Description: Closes an ssh connection.
  116. %%--------------------------------------------------------------------
  117. close(ConnectionRef) ->
  118. ssh_connection_manager:stop(ConnectionRef).
  119. %%--------------------------------------------------------------------
  120. %% Function: connection_info(ConnectionRef) -> [{Option, Value}]
  121. %%
  122. %% Description: Retrieves information about a connection.
  123. %%--------------------------------------------------------------------
  124. connection_info(ConnectionRef, Options) ->
  125. ssh_connection_manager:connection_info(ConnectionRef, Options).
  126. %%--------------------------------------------------------------------
  127. %% Function: channel_info(ConnectionRef) -> [{Option, Value}]
  128. %%
  129. %% Description: Retrieves information about a connection.
  130. %%--------------------------------------------------------------------
  131. channel_info(ConnectionRef, ChannelId, Options) ->
  132. ssh_connection_manager:channel_info(ConnectionRef, ChannelId, Options).
  133. %%--------------------------------------------------------------------
  134. %% Function: daemon(Port) ->
  135. %% daemon(Port, Options) ->
  136. %% daemon(Address, Port, Options) -> SshSystemRef
  137. %%
  138. %% Description: Starts a server listening for SSH connections
  139. %% on the given port.
  140. %%--------------------------------------------------------------------
  141. daemon(Port) ->
  142. daemon(Port, []).
  143. daemon(Port, Options) ->
  144. daemon(any, Port, Options).
  145. daemon(HostAddr, Port, Options0) ->
  146. Options1 = case proplists:get_value(shell, Options0) of
  147. undefined ->
  148. [{shell, {shell, start, []}} | Options0];
  149. _ ->
  150. Options0
  151. end,
  152. DisableIpv6 = proplists:get_value(ip_v6_disabled, Options0, false),
  153. {Host, Inet, Options} = case HostAddr of
  154. any ->
  155. {ok, Host0} = inet:gethostname(),
  156. {Host0, inetopt(DisableIpv6), Options1};
  157. {_,_,_,_} ->
  158. {HostAddr, inet,
  159. [{ip, HostAddr} | Options1]};
  160. {_,_,_,_,_,_,_,_} ->
  161. {HostAddr, inet6,
  162. [{ip, HostAddr} | Options1]}
  163. end,
  164. start_daemon(Host, Port, [{role, server} | Options], Inet).
  165. %%--------------------------------------------------------------------
  166. %% Function: stop_listener(SysRef) -> ok
  167. %% stop_listener(Address, Port) -> ok
  168. %%
  169. %%
  170. %% Description: Stops the listener, but leaves
  171. %% existing connections started by the listener up and running.
  172. %%--------------------------------------------------------------------
  173. stop_listener(SysSup) ->
  174. ssh_system_sup:stop_listener(SysSup).
  175. stop_listener(Address, Port) ->
  176. ssh_system_sup:stop_listener(Address, Port).
  177. %%--------------------------------------------------------------------
  178. %% Function: stop_daemon(SysRef) -> ok
  179. %%% stop_daemon(Address, Port) -> ok
  180. %%
  181. %%
  182. %% Description: Stops the listener and all connections started by
  183. %% the listener.
  184. %%--------------------------------------------------------------------
  185. stop_daemon(SysSup) ->
  186. ssh_system_sup:stop_system(SysSup).
  187. stop_daemon(Address, Port) ->
  188. ssh_system_sup:stop_system(Address, Port).
  189. %%--------------------------------------------------------------------
  190. %% Function: shell(Host [,Port,Options]) -> {ok, ConnectionRef} |
  191. %% {error, Reason}
  192. %%
  193. %% Host = string()
  194. %% Port = integer()
  195. %% Options = [{Option, Value}]
  196. %%
  197. %% Description: Starts an interactive shell to an SSH server on the
  198. %% given <Host>. The function waits for user input,
  199. %% and will not return until the remote shell is ended.(e.g. on
  200. %% exit from the shell)
  201. %%--------------------------------------------------------------------
  202. shell(Host) ->
  203. shell(Host, ?SSH_DEFAULT_PORT, []).
  204. shell(Host, Options) ->
  205. shell(Host, ?SSH_DEFAULT_PORT, Options).
  206. shell(Host, Port, Options) ->
  207. case connect(Host, Port, Options) of
  208. {ok, ConnectionRef} ->
  209. case ssh_connection:session_channel(ConnectionRef, infinity) of
  210. {ok,ChannelId} ->
  211. Args = [{channel_cb, ssh_shell},
  212. {init_args,[ConnectionRef, ChannelId]},
  213. {cm, ConnectionRef}, {channel_id, ChannelId}],
  214. {ok, State} = ssh_channel:init([Args]),
  215. ssh_channel:enter_loop(State);
  216. Error ->
  217. Error
  218. end;
  219. Error ->
  220. Error
  221. end.
  222. %%--------------------------------------------------------------------
  223. %%% Internal functions
  224. %%--------------------------------------------------------------------
  225. start_daemon(Host, Port, Options, Inet) ->
  226. {SocketOpts, Opts} = handle_options(Options),
  227. case ssh_system_sup:system_supervisor(Host, Port) of
  228. undefined ->
  229. %% TODO: It would proably make more sense to call the
  230. %% address option host but that is a too big change at the
  231. %% monent. The name is a legacy name!
  232. try sshd_sup:start_child([{address, Host},
  233. {port, Port}, {role, server},
  234. {socket_opts, [Inet | SocketOpts]},
  235. {ssh_opts, Opts}]) of
  236. {ok, SysSup} ->
  237. {ok, SysSup};
  238. {error, {already_started, _}} ->
  239. {error, eaddrinuse}
  240. catch
  241. exit:{noproc, _} ->
  242. {error, ssh_not_started}
  243. end;
  244. Sup ->
  245. case ssh_system_sup:restart_acceptor(Host, Port) of
  246. {ok, _} ->
  247. {ok, Sup};
  248. _ ->
  249. {error, eaddrinuse}
  250. end
  251. end.
  252. handle_options(Opts) ->
  253. handle_options(proplists:unfold(Opts), [], []).
  254. handle_options([], SockOpts, Opts) ->
  255. {SockOpts, Opts};
  256. %% TODO: Could do some type checks here on plain ssh-opts
  257. handle_options([{system_dir, _} = Opt | Rest], SockOpts, Opts) ->
  258. handle_options(Rest, SockOpts, [Opt | Opts]);
  259. handle_options([{user_dir, _} = Opt | Rest], SockOpts, Opts) ->
  260. handle_options(Rest, SockOpts, [Opt | Opts]);
  261. handle_options([{user_dir_fun, _} = Opt | Rest], SockOpts, Opts) ->
  262. handle_options(Rest, SockOpts, [Opt | Opts]);
  263. handle_options([{silently_accept_hosts, _} = Opt | Rest], SockOpts, Opts) ->
  264. handle_options(Rest, SockOpts, [Opt | Opts]);
  265. handle_options([{user_interaction, _} = Opt | Rest], SockOpts, Opts) ->
  266. handle_options(Rest, SockOpts, [Opt | Opts]);
  267. handle_options([{public_key_alg, _} = Opt | Rest], SockOpts, Opts) ->
  268. handle_options(Rest, SockOpts, [Opt | Opts]);
  269. handle_options([{connect_timeout, _} = Opt | Rest], SockOpts, Opts) ->
  270. handle_options(Rest, SockOpts, [Opt | Opts]);
  271. handle_options([{user, _} = Opt | Rest], SockOpts, Opts) ->
  272. handle_options(Rest, SockOpts, [Opt | Opts]);
  273. handle_options([{password, _} = Opt | Rest], SockOpts, Opts) ->
  274. handle_options(Rest, SockOpts, [Opt | Opts]);
  275. handle_options([{user_passwords, _} = Opt | Rest], SockOpts, Opts) ->
  276. handle_options(Rest, SockOpts, [Opt | Opts]);
  277. handle_options([{pwdfun, _} = Opt | Rest], SockOpts, Opts) ->
  278. handle_options(Rest, SockOpts, [Opt | Opts]);
  279. handle_options([{user_auth, _} = Opt | Rest], SockOpts, Opts) ->
  280. handle_options(Rest, SockOpts, [Opt | Opts]);
  281. handle_options([{key_cb, _} = Opt | Rest], SockOpts, Opts) ->
  282. handle_options(Rest, SockOpts, [Opt | Opts]);
  283. handle_options([{role, _} = Opt | Rest], SockOpts, Opts) ->
  284. handle_options(Rest, SockOpts, [Opt | Opts]);
  285. handle_options([{channel, _} = Opt | Rest], SockOpts, Opts) ->
  286. handle_options(Rest, SockOpts, [Opt | Opts]);
  287. handle_options([{compression, _} = Opt | Rest], SockOpts, Opts) ->
  288. handle_options(Rest, SockOpts, [Opt | Opts]);
  289. handle_options([{allow_user_interaction, _} = Opt | Rest], SockOpts, Opts) ->
  290. handle_options(Rest, SockOpts, [Opt | Opts]);
  291. handle_options([{infofun, _} = Opt | Rest], SockOpts, Opts) ->
  292. handle_options(Rest, SockOpts, [Opt | Opts]);
  293. handle_options([{connectfun, _} = Opt | Rest], SockOpts, Opts) ->
  294. handle_options(Rest, SockOpts, [Opt | Opts]);
  295. handle_options([{disconnectfun , _} = Opt | Rest], SockOpts, Opts) ->
  296. handle_options(Rest, SockOpts, [Opt | Opts]);
  297. handle_options([{failfun, _} = Opt | Rest], SockOpts, Opts) ->
  298. handle_options(Rest, SockOpts, [Opt | Opts]);
  299. handle_options([{ip_v6_disabled, _} = Opt | Rest], SockOpts, Opts) ->
  300. handle_options(Rest, SockOpts, [Opt | Opts]);
  301. handle_options([{ip, _} = Opt | Rest], SockOpts, Opts) ->
  302. handle_options(Rest, [Opt |SockOpts], Opts);
  303. handle_options([{ifaddr, _} = Opt | Rest], SockOpts, Opts) ->
  304. handle_options(Rest, [Opt |SockOpts], Opts);
  305. handle_options([{fd, _} = Opt | Rest], SockOpts, Opts) ->
  306. handle_options(Rest, [Opt | SockOpts], Opts);
  307. handle_options([{nodelay, _} = Opt | Rest], SockOpts, Opts) ->
  308. handle_options(Rest, [Opt | SockOpts], Opts);
  309. handle_options([Opt | Rest], SockOpts, Opts) ->
  310. handle_options(Rest, SockOpts, [Opt | Opts]).
  311. inetopt(true) ->
  312. inet6;
  313. inetopt(false) ->
  314. inet.