summaryrefslogtreecommitdiff
path: root/p11p-daemon/src/p11p_manager.erl
diff options
context:
space:
mode:
Diffstat (limited to 'p11p-daemon/src/p11p_manager.erl')
-rw-r--r--p11p-daemon/src/p11p_manager.erl193
1 files changed, 118 insertions, 75 deletions
diff --git a/p11p-daemon/src/p11p_manager.erl b/p11p-daemon/src/p11p_manager.erl
index 7c3bdb9..2dbdf6c 100644
--- a/p11p-daemon/src/p11p_manager.erl
+++ b/p11p-daemon/src/p11p_manager.erl
@@ -25,8 +25,8 @@
%% API.
-export([start_link/0]).
--export([client_for_token/1, client_event/2]). % For servers.
--export([server_event/2]). % For clients.
+-export([client_for_token/1, server_event/2]). % For servers.
+-export([client_event/2]). % For clients.
%% Genserver callbacks.
-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,
@@ -43,9 +43,16 @@
}).
-record(vtoken, {
- mode :: p11p_config:token_mode_t(),
- balance_count :: integer(),
- clients :: [#client{}] % Active client in hd().
+ clients :: [#client{}], % Current client in hd().
+
+ %% Invokations left for current client or -1 for no
+ %% balancing.
+ balance_count = -1 :: integer(),
+
+ timeout :: non_neg_integer(),
+ retries :: non_neg_integer(),
+
+ server :: pid() | undefined % Active server, if any.
}).
-record(state, {
@@ -59,23 +66,25 @@ start_link() ->
-spec client_for_token(string()) -> pid().
client_for_token(TokName) ->
- gen_server:call(?MODULE, {client_for_token, TokName}).
-client_event(Event, Args) ->
- gen_server:cast(?MODULE, {client_event, Event, Args}).
+ gen_server:call(?MODULE, {client_for_token, self(), TokName}).
server_event(Event, Args) ->
gen_server:cast(?MODULE, {server_event, Event, Args}).
+client_event(Event, Args) ->
+ gen_server:cast(?MODULE, {client_event, Event, Args}).
+
%% Genserver callbacks.
init([]) ->
{ok, #state{vtokens = init_vtokens(p11p_config:tokens())}}.
-handle_call({client_for_token, TokNameIn}, _, #state{vtokens = Tokens} = S) ->
- #{TokNameIn := TokenIn} = Tokens,
- ClientsIn = TokenIn#vtoken.clients,
+handle_call({client_for_token, Server, TokNameIn}, _From,
+ S = #state{vtokens = VTokensIn}) ->
+ #{TokNameIn := VTokenIn} = VTokensIn,
+ ClientsIn = VTokenIn#vtoken.clients,
lager:debug("all clients: ~p", [ClientsIn]),
{Clients, BalanceCount} =
- case TokenIn#vtoken.balance_count of
+ case VTokenIn#vtoken.balance_count of
0 ->
lager:debug("~p: balancing: next client", [self()]),
Rotated = rotate_clients(ClientsIn),
@@ -87,52 +96,51 @@ handle_call({client_for_token, TokNameIn}, _, #state{vtokens = Tokens} = S) ->
-1 ->
{ClientsIn, -1}
end,
- #client{tokname = TokNameIn,
- servid = ServId,
- modpath = ModPath,
- modenv = ModEnv,
- pid = PidIn} = SelectedClient = hd(Clients),
- case PidIn of
+ Current = hd(Clients),
+ case Current#client.pid of
undefined ->
- {ok, Pid} =
- p11p_client:start_link(ServId, TokNameIn, ModPath, ModEnv),
- Client = SelectedClient#client{pid = Pid},
- Token = TokenIn#vtoken{clients = [Client | tl(Clients)],
- balance_count = BalanceCount},
- {reply, Pid, S#state{vtokens = Tokens#{TokNameIn := Token}}};
- _ ->
- {reply, PidIn, S}
+ Client = start_client(hd(Clients), Server, VTokenIn#vtoken.timeout),
+ VToken = VTokenIn#vtoken{clients = [Client | tl(Clients)],
+ server = Server,
+ balance_count = BalanceCount},
+ {reply, Client#client.pid, S#state{vtokens = VTokensIn#{TokNameIn := VToken}}};
+ Pid ->
+ {reply, Pid, S}
end;
handle_call(Call, _From, State) ->
lager:debug("Unhandled call: ~p~n", [Call]),
{reply, unhandled, State}.
-handle_cast({server_event, timeout, [TokNameIn, Server]},
- #state{vtokens = Tokens} = S) ->
- lager:debug("~p: ~s: timed out, stopping ~p", [self(), TokNameIn, Server]),
- gen_server:stop(Server), % Hang up on p11 client.
- %% TODO: do some code dedup with client_for_token?
+%% Server done with client.
+handle_cast({server_event, server_gone, TokNameIn}, S = #state{vtokens = Tokens}) ->
#{TokNameIn := TokenIn} = Tokens,
- Clients = TokenIn#vtoken.clients,
- SelectedClient = hd(Clients),
- Client = SelectedClient#client{pid = undefined},
- Token = TokenIn#vtoken{clients = tl(Clients) ++ [Client]},
- lager:debug("~p: ~s: updated token: ~p", [self(), TokNameIn, Token]),
- {noreply, S#state{vtokens = Tokens#{TokNameIn := Token}}};
-
-handle_cast({client_event, client_gone, [TokName, Pid]},
- #state{vtokens = Tokens} = S) ->
- lager:debug("~p: asking client ~p to stop", [self(), Pid]),
- p11p_client:stop(Pid, normal),
- #{TokName := TokenIn} = Tokens,
- Clients = lists:map(fun(E) ->
+ CurClient = hd(TokenIn#vtoken.clients),
+ ClientPid = CurClient#client.pid,
+ ok = p11p_client:stop(ClientPid, normal),
+ Clients = lists:map(fun(E) -> % Find and update.
case E#client.pid of
- Pid -> E#client{pid = undefined};
+ ClientPid -> E#client{pid = undefined};
_ -> E
- end
- end, TokenIn#vtoken.clients),
- Token = TokenIn#vtoken{clients = Clients},
- {noreply, S#state{vtokens = Tokens#{TokName := Token}}};
+ end end,
+ TokenIn#vtoken.clients),
+ Token = TokenIn#vtoken{clients = Clients, server = undefined},
+ {noreply, S#state{vtokens = Tokens#{TokNameIn := Token}}};
+
+%% Client reporting that a token has timed out -- mark current client
+%% not running, inform server, rotate client list and start new
+%% client.
+handle_cast({client_event, timeout, TokName}, State) ->
+ #{TokName := VToken} = State#state.vtokens,
+ client_timeout(TokName, VToken, State);
+
+handle_cast({start_client, VTokName}, State = #state{vtokens = VTokens}) ->
+ #{VTokName := VToken} = VTokens,
+ Client = start_client(hd(VToken#vtoken.clients),
+ VToken#vtoken.server,
+ VToken#vtoken.timeout),
+ NewVToken = VToken#vtoken{clients = [Client | tl(VToken#vtoken.clients)]},
+ lager:debug("~p: vtoken updated: ~p", [self(), NewVToken]),
+ {noreply, State#state{vtokens = VTokens#{VTokName := NewVToken}}};
handle_cast(Cast, State) ->
lager:debug("Unhandled cast: ~p~n", [Cast]),
@@ -164,40 +172,40 @@ init_vtokens([H|T], Acc)->
new_vtoken(Conf) ->
Name = p11p_config:nameof(Conf),
- Mode = p11p_config:token_mode(Name),
+ Balances = p11p_config:token_balance(Name),
Clients = clients(Name,
p11p_config:modules_for_token(Name),
- Mode),
- R0 = hd(Clients),
+ Balances),
+ CurrentClient = hd(Clients),
#vtoken{
- mode = p11p_config:token_mode(Name),
- balance_count = R0#client.balance,
- clients = Clients
+ clients = Clients,
+ balance_count = CurrentClient#client.balance,
+ timeout = p11p_config:token_timeout(Name),
+ retries = p11p_config:token_retries(Name)
}.
-clients(TokName, ConfModules, ConfMode) ->
- clients(TokName, ConfModules, ConfMode, []).
+clients(TokName, Modules, []) ->
+ clients(TokName, Modules, [-1 || _ <- lists:seq(1, length(Modules))]);
+clients(TokName, ConfModules, ConfBalance) ->
+ clients(TokName, ConfModules, ConfBalance, []).
+
clients(_, [], _, Acc) ->
Acc;
-clients(TokName, [H|T], ConfMode, Acc) ->
- ModName = p11p_config:nameof(H),
+clients(TokName, [Module|Modules], [Balance|Balances], Acc) ->
+ ModName = p11p_config:nameof(Module),
ServName = "p11p_client:" ++ TokName ++ ":" ++ ModName,
- ModPath = p11p_config:module_path(H),
- ModEnv = p11p_config:module_env(H),
- clients(TokName, T, ConfMode, [#client{
- tokname = TokName,
- servid = list_to_atom(ServName),
- modpath = ModPath,
- modenv = ModEnv,
- balance = balance(ConfMode, length(T) + 1)
- }
- | Acc]).
-
--spec balance(p11p_config:token_mode_t(), non_neg_integer()) -> integer().
-balance({balance, Ratios}, N) ->
- lists:nth(N, Ratios);
-balance(_, _) ->
- -1.
+ clients(TokName, Modules, Balances,
+ [#client{tokname = TokName,
+ servid = list_to_atom(ServName),
+ modpath = p11p_config:module_path(Module),
+ modenv = p11p_config:module_env(Module),
+ balance = Balance} | Acc]).
+
+%% -spec balance(p11p_config:token_mode_t(), non_neg_integer()) -> integer().
+%% balance({balance, Ratios}, N) ->
+%% lists:nth(N, Ratios);
+%% balance(_, _) ->
+%% -1.
%% -spec balance_count(p11p_config:token_mode_t()) -> integer().
%% balance_count(#vtoken{mode = {balance, _}, balance_count = C}) ->
@@ -207,3 +215,38 @@ balance(_, _) ->
rotate_clients(L) ->
lists:reverse([hd(L) | lists:reverse(tl(L))]).
+
+rotate_clients(L, UpdatedCurr) ->
+ lists:reverse([UpdatedCurr | lists:reverse(tl(L))]).
+
+next_client(VToken = #vtoken{clients = Clients}) ->
+ OldC = hd(Clients),
+ NewClients = rotate_clients(Clients, OldC#client{pid = undefined}),
+ gen_server:cast(self(), {start_client, OldC#client.tokname}),
+ VToken#vtoken{clients = NewClients}.
+
+client_timeout(TokName,
+ VToken = #vtoken{retries = Retries},
+ State = #state{vtokens = VTokens})
+ when Retries > 0 ->
+ lager:debug("~p: ~s: token timed out, switching token", [self(), TokName]),
+ p11p_server:token_gone(VToken#vtoken.server, false),
+ NewToken = next_client(VToken),
+ NewVTokens = VTokens#{TokName := NewToken#vtoken{retries = Retries - 1}},
+ {noreply, State#state{vtokens = NewVTokens}};
+
+client_timeout(TokName,
+ VToken,
+ State) ->
+ lager:debug("~p: ~s: token timed out, disconnecting app", [self(), TokName]),
+ p11p_server:token_gone(VToken#vtoken.server, true),
+ {stop, State}.
+
+start_client(Client, Server, Timeout) ->
+ {ok, Pid} = p11p_client:start_link(Client#client.servid,
+ Client#client.tokname,
+ Server,
+ Client#client.modpath,
+ Client#client.modenv,
+ Timeout),
+ Client#client{pid = Pid}.