diff options
Diffstat (limited to 'p11p-daemon/src/p11p_remote.erl')
-rw-r--r-- | p11p-daemon/src/p11p_remote.erl | 97 |
1 files changed, 97 insertions, 0 deletions
diff --git a/p11p-daemon/src/p11p_remote.erl b/p11p-daemon/src/p11p_remote.erl new file mode 100644 index 0000000..e89aa42 --- /dev/null +++ b/p11p-daemon/src/p11p_remote.erl @@ -0,0 +1,97 @@ +%% A remote spawns an Erlang port running the 'remote' program from +%% p11-kit. + +%% Receive p11 requests from p11p_server, forward them to the remote, +%% wait for a reply. If a reply is received within a timeout period, +%% forward the reply to the requesting p11p_server. If the request +%% times out, inform the remote manager (our parent). + +%% TODO: "remote" is not a great name and we shouldn't just inherit it +%% from p11p-kit + +-module(p11p_remote). + +-behaviour(gen_server). + +%% API. +-export([start_link/3]). +-export([send/3]). + +%% Genserver callbacks. +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, + code_change/3]). + +%% Records and types. +-record(state, { + port :: port(), + replyto :: pid() | undefined, + timer :: reference() | undefined, + token :: string() + }). + +-define(P11KITREMOTE_PATH, "/home/linus/usr/libexec/p11-kit/p11-kit-remote"). + +%% API. +-spec start_link(atom(), string(), string()) -> {ok, pid()} | {error, term()}. +start_link(ServName, TokName, ModPath) -> + lager:info("~p: p11p_remote starting for ~s", [ServName, ModPath]), + gen_server:start_link({local, ServName}, ?MODULE, [TokName, ModPath], []). + +-spec send(pid(), pid(), iodata()) -> ok. +send(From, Remote, Data) -> + gen_server:cast(Remote, {send, From, Data}). + +%% Genserver callbacks. +init([TokName, ModPath]) -> + Port = open_port({spawn_executable, ?P11KITREMOTE_PATH}, + [stream, exit_status, {args, [ModPath, "-v"]}]), + lager:debug("~s: New port: ~p", [?P11KITREMOTE_PATH, Port]), + {ok, #state{port = Port, token = TokName}}. + +handle_call(Request, _From, State) -> + lager:debug("Unhandled call: ~p~n", [Request]), + {reply, unhandled, State}. + +handle_cast({send, From, Data}, #state{port = Port} = State) -> + lager:debug("~p: sending ~B octets to remote ~p", + [self(), length(binary_to_list(Data)), Port]), + port_command(Port, Data), + %% TODO: move timing to server, measuring time for a p11 response + %% rather than this + Timer = erlang:start_timer(3000, self(), Port), + NewState = State#state{replyto = From, timer = Timer}, + {noreply, NewState}; +handle_cast(Request, State) -> + lager:debug("Unhandled cast: ~p~n", [Request]), + {noreply, State}. + +handle_info({Port, {data, Data}}, #state{replyto = Pid, timer = Timer} = State) -> + if + Port == State#state.port -> + erlang:cancel_timer(Timer, [{async, true}, {info, false}]), + p11p_server:reply(Pid, Data); + true -> + lager:debug("~p: data from unknown port ~p", [self(), Port]) + end, + {noreply, State}; +handle_info({timeout, Timer, Port}, #state{token = TokName} = State) -> + NewState = + if + Port == State#state.port andalso Timer == State#state.timer -> + p11p_remote_manager:timeout(TokName), + State#state{timer = undefined}; + true -> + lager:debug("~p: unknown timer ~p fired for port ~p", + [self(), Timer, Port]), + State + end, + {noreply, NewState}; +handle_info(Info, State) -> + lager:debug("Unhandled info: ~p~n", [Info]), + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVersion, State, _Extra) -> + {ok, State}. |