summaryrefslogtreecommitdiff
path: root/p11p-daemon/src/p11p_remote.erl
diff options
context:
space:
mode:
Diffstat (limited to 'p11p-daemon/src/p11p_remote.erl')
-rw-r--r--p11p-daemon/src/p11p_remote.erl97
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}.