Skip to content

Instantly share code, notes, and snippets.

@davisp
Last active December 19, 2015 20:58
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 3 You must be signed in to fork a gist
  • Save davisp/6016253 to your computer and use it in GitHub Desktop.
Save davisp/6016253 to your computer and use it in GitHub Desktop.
A remsh that logs every session to ~/.remsh.log
#! /usr/bin/env escript
%%! -hidden -noshell -noinput
-mode(compile).
-export([
init_shell_log/1
]).
main([RemoteNode0]) ->
RemoteNode = list_to_atom(RemoteNode0),
assert_hidden_node(),
case net_kernel:start([local_node(), longnames]) of
{ok, _} -> ok;
{error, {already_started, _}} -> ok;
{error, Reason} -> erlang:error({no_network, Reason})
end,
case net_kernel:connect_node(RemoteNode) of
true -> ok;
_ -> erlang:error({no_connection, RemoteNode})
end,
Shell = user_drv:start(['tty_sl -c -e', {RemoteNode, shell, start, []}]),
log_shell(Shell),
spawn_link(fun() -> monitor_group(RemoteNode, Shell) end),
erlang:monitor(process, Shell),
receive
{'DOWN', _Ref, process, Shell, _Reason} ->
ok
end,
init:halt(0);
main(_) ->
io:format("usage: ~s remote_node~n", [escript:script_name()]).
local_node() ->
list_to_atom("escript_" ++ os:getpid() ++ "@127.0.0.1").
assert_hidden_node() ->
% The list of nodes we want to publish our presence
% on. This is symetrical so if its empty we'll also
% not appear on any node that connects to us.
case global_group:publish_on_nodes() of
[] -> ok;
_ -> erlang:error(not_hidden)
end.
init_shell_log(Shell) ->
spawn_link(?MODULE, log_shell, [Shell]).
log_shell(Shell) ->
Dir = case init:get_argument(home) of
{ok, [[Home]]} ->
Home;
_ ->
"./"
end,
FName = filename:join(Dir, ".remsh.log"),
Fd = case file:open(FName, [append]) of
{ok, Fd0} -> Fd0;
Else -> erlang:exit(Else)
end,
erlang:trace(Shell, true, ['receive']),
log_shell_loop(Fd).
log_shell_loop(Fd) ->
receive
{trace,_,'receive',{_,{requests,Reqs}}} ->
%io:format(standard_error, "~p~n", [Reqs]),
log_requests(Fd, Reqs);
{trace,_,'receive',{_,{put_chars,unicode,_}=Req}} ->
%io:format(standard_error, "~p", [[Req]]),
log_requests(Fd, [Req]);
_Else ->
%io:format(standard_error, "~p~n", [_Else]),
ok
end,
log_shell_loop(Fd).
log_requests(Fd, []) ->
ok;
log_requests(Fd, [{put_chars,unicode,Chars} | Rest]) ->
ok = file:write(Fd, Chars),
log_requests(Fd, Rest);
log_requests(Fd, [{insert_chars,unicode,Chars} | Rest]) ->
{ok, _} = file:position(Fd, {cur, -1}),
ok = file:write(Fd, Chars),
log_requests(Fd, Rest);
log_requests(Fd, [{move_rel,N} | Rest]) ->
{ok, _} = file:position(Fd, {cur, N}),
log_requests(Fd, Rest);
log_requests(Fd, [{delete_chars, N} | Rest]) when N =< 0 ->
{ok, _} = file:position(Fd, {cur, N}),
ok = file:truncate(Fd),
log_requests(Fd, Rest);
log_requests(Fd, [beep | Rest]) ->
log_requests(Fd, Rest);
log_requests(_Fd, [Req | _]) ->
erlang:exit({invalid_request, Req}).
monitor_group(RemoteNode, Shell) ->
timer:sleep(250),
case get_group_leader(Shell) of
Pid when is_pid(Pid) ->
set_expand_fun(RemoteNode, Pid),
erlang:monitor(process, Pid),
receive
{'DOWN', _Ref, process, Pid, _Reason} ->
ok
end;
undefined ->
ok
end,
monitor_group(RemoteNode, Shell).
get_group_leader(Pid) ->
{dictionary, Dict} = process_info(Pid, dictionary),
proplists:get_value(current_group, Dict).
set_expand_fun(RemoteNode, GL) ->
ExpFun = fun(B) ->
rpc:call(RemoteNode, edlin_expand, expand, [B])
end,
ok = io:setopts(GL, [{expand_fun, ExpFun}]).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment