diff options
Diffstat (limited to 'lib/ssh/src/ssh_shell.erl')
-rw-r--r-- | lib/ssh/src/ssh_shell.erl | 178 |
1 files changed, 178 insertions, 0 deletions
diff --git a/lib/ssh/src/ssh_shell.erl b/lib/ssh/src/ssh_shell.erl new file mode 100644 index 0000000000..f81b949119 --- /dev/null +++ b/lib/ssh/src/ssh_shell.erl @@ -0,0 +1,178 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009. 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% +%% + +%% + +-module(ssh_shell). + +-include("ssh_connect.hrl"). + +-behaviour(ssh_channel). + +%% ssh_channel callbacks +-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]). + +%% Spawn export +-export([input_loop/2]). + +-record(state, + { + io, %% Io process + channel, %% Id of the ssh channel + cm %% Ssh connection manager + } + ). + +%%==================================================================== +%% ssh_channel callbacks +%%==================================================================== + +%%-------------------------------------------------------------------- +%% Function: init(Args) -> {ok, State} +%% +%% Description: Initiates the CLI +%%-------------------------------------------------------------------- +init([ConnectionManager, ChannelId] = Args) -> + + %% Make sure that we are proclib compatible as + %% this client should be runnable from the + %% erlang shell. + case get('$initial_call') of + undefined -> + Me = get_my_name(), + Ancestors = get_ancestors(), + put('$ancestors', [Me | Ancestors]), + put('$initial_call', {?MODULE, init, Args}); + _ -> + ok + end, + + case ssh_connection:shell(ConnectionManager, ChannelId) of + ok -> + {group_leader, GIO} = + process_info(self(), group_leader), + IoPid = spawn_link(?MODULE, input_loop, + [GIO, self()]), + {ok, #state{io = IoPid, + channel = ChannelId, + cm = ConnectionManager}}; + Error -> + {stop, Error} + end. + +%%-------------------------------------------------------------------- +%% Function: handle_ssh_msg(Args) -> {ok, State} | {stop, ChannelId, State} +%% +%% Description: Handles channel messages received on the ssh-connection. +%%-------------------------------------------------------------------- +handle_ssh_msg({ssh_cm, _, {data, _ChannelId, 0, Data}}, State) -> + %% TODO: When unicode support is ready + %% should we call this function or perhaps a new + %% function. + io:put_chars(Data), + {ok, State}; + +handle_ssh_msg({ssh_cm, _, + {data, _ChannelId, ?SSH_EXTENDED_DATA_STDERR, Data}}, + State) -> + %% TODO: When unicode support is ready + %% should we call this function or perhaps a new + %% function. + io:put_chars(Data), + {ok, State}; + +handle_ssh_msg({ssh_cm, _, {eof, _ChannelId}}, State) -> + {ok, State}; + +handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> + %% Ignore signals according to RFC 4254 section 6.9. + {ok, State}; + +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, Error, _}}, State) -> + io:put_chars("Connection closed by peer"), + %% TODO: When unicode support is ready + %% should we call this function or perhaps a new + %% function. The error is encoded as UTF-8! + io:put_chars(Error), + {stop, ChannelId, State}; + +handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, 0}}, State) -> + io:put_chars("logout"), + io:put_chars("Connection closed"), + {stop, ChannelId, State}; + +handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, Status}}, State) -> + io:put_chars("Connection closed by peer"), + io:put_chars("Status: " ++ integer_to_list(Status)), + {stop, ChannelId, State}. + +%%-------------------------------------------------------------------- +%% Function: handle_ssh_msg(Args) -> {ok, State} | {stop, ChannelId, State} +%% +%% Description: Handles other channel messages +%%-------------------------------------------------------------------- +handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, + #state{channel = ChannelId, + cm = ConnectionManager} = State) -> + {ok, State}; + +handle_msg({input, IoPid, eof}, #state{io = IoPid, channel = ChannelId, + cm = ConnectionManager} = State) -> + ssh_connection:send_eof(ConnectionManager, ChannelId), + {ok, State}; + +handle_msg({input, IoPid, Line}, #state{io = IoPid, + channel = ChannelId, + cm = ConnectionManager} = State) -> + ssh_connection:send(ConnectionManager, ChannelId, Line), + {ok, State}. + +%%-------------------------------------------------------------------- +%% Function: terminate(Reasons, State) -> _ +%% +%% Description: Cleanup when shell channel is terminated +%%-------------------------------------------------------------------- +terminate(_Reason, #state{io = IoPid}) -> + exit(IoPid, kill). + +%%-------------------------------------------------------------------- +%%% Internal functions +%%-------------------------------------------------------------------- + +input_loop(Fd, Pid) -> + case io:get_line(Fd, '>') of + eof -> + Pid ! {input, self(), eof}, + ok; + Line -> + Pid ! {input, self(), Line}, + input_loop (Fd, Pid) + end. + +get_my_name() -> + case process_info(self(),registered_name) of + {registered_name,Name} -> Name; + _ -> self() + end. + +get_ancestors() -> + case get('$ancestors') of + A when is_list(A) -> A; + _ -> [] + end. |