diff options
Diffstat (limited to 'p11p-daemon/src/p11p_manager.erl')
-rw-r--r-- | p11p-daemon/src/p11p_manager.erl | 193 |
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}. |