%% @doc Sample restricted remote shell module disabling `q/0' and
%% `init:stop/{0,1}' commands. The shell introduces a replacement command
%% to stop remote node: `remote:stop/1' equivalent to `init:stop/1'.
%%
%% To activate restricted shell, run the server node like this:
%% `erl -sname node@host +Bi -shell restricted_shell restrict_remsh_mod'
%%
%% Then you can connect to it with:
%% `erl -sname a@myhost -remsh node@host'
%%
%% See: [http://www.erlang.org/doc/man/shell.html#start_restricted-1]
-module(restrict_remsh_mod).
-author('saleyn@gmail.com').
%% Restricted shell callbacks
-export([local_allowed/3, non_local_allowed/3]).
%% Internal API
-export([remote_node_stop/1]).
%% @private
-spec local_allowed(Func::atom(), Args::list(), State::term()) ->
{boolean(), NewState::term()}.
local_allowed(q, _Args, State) -> {false, State};
local_allowed(halt, _Args, State) -> {false, State};
local_allowed(_Cmd, _Args, State) -> {true, State}.
%% @private
-type funspec() :: {Mod::atom(), Fun::atom()}.
-spec non_local_allowed(FunSpec::funspec(), Args::list(), State::term()) ->
{true,NewState} | {false,NewState} |
{{redirect, NewFuncSpec::funspec(), NewArgs::list()}, NewState::term()}.
non_local_allowed({erlang, halt}, _Args, State) -> {false, State};
non_local_allowed({init, stop}, _Args, State) -> {false, State};
non_local_allowed({remote, stop}, [A], State) -> {{redirect, {?MODULE, remote_node_stop}, [A]}, State};
non_local_allowed({_M, _F}, _Args, State) -> {true, State}.
%% @doc Replaces `init:stop/1' with `remote:stop/1' to avoid accidental
%% exit of remote shell.
-spec remote_node_stop(Status::integer()) -> ok.
remote_node_stop(Status) ->
init:stop(Status).