📄 megaco_sessionfactory_impl.erl
字号:
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%%%% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%%%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%%%% $Id$%%%%----------------------------------------------------------------------%% Purpose : Actual implementation of the Megaco SessionFactory interface.%%-----------------------------------------------------------------------module('Megaco_SessionFactory_impl').%% Genserver callbacks:-export([init/1, terminate/2, code_change/3, handle_info/2]).%% Requests from the (C-) user:-export([startSession/3, startSession/4, sessions/3, stopSession/4]).-export([select_session/2]).-include_lib("megaco/include/megaco.hrl").-include_lib("megaco/include/megaco_message_v1.hrl").-include_lib("megaco_session/include/Megaco.hrl").-include_lib("megaco_session/include/MegacoMessage.hrl").%% -include("megaco_session_et.hrl").%% Records:-record(state, {parent_pid, sessions = [], ref_counter = 0}).-record(session, {local_pid, remote_pid, remote_ref}).%% Defines:-define(EXT(Fun, Value), megaco_session_externalizer:Fun(Value, undefined)).-define(INT(Fun, Value), megaco_session_internalizer:Fun(Value, undefined)).-define(SERVER, megaco_session_factory).-define(d(F,A),dbg(F,A)).select_session(ConnHandle, SessionPid) -> case whereis(?SERVER) of undefined -> no_session; Pid -> Pid ! {self(), {select_session, ConnHandle, SessionPid}}, receive {Pid, Session = {a_session, RemotePid, Ref}} -> Session; {Pid, no_session} -> no_session end end.%% ----------------------------------------------------------------------%% Gen-server callbacks:%% ----------------------------------------------------------------------init([{parent_pid, ParentPid}]) when pid(ParentPid) -> process_flag(trap_exit, true), put(debug, get_debug()), ?d("init -> entry with" "~n ParentPid: ~p", [ParentPid]), {ok, #state{parent_pid = ParentPid}};init(BadEnv) -> {stop, {bad_env, BadEnv}}.terminate(Reason, State) -> ?d("terminate -> entry with" "~n Reason: ~p", [Reason]), ok.code_change({down, Vsn}, State, Extra) -> {ok, State};code_change(Vsn, State, Extra) -> {ok, State}.handle_info({ReplyTo, {select_session, ConnHandle, SessionPid}}, State) -> ?d("handle_info(select_session) -> entry when" "~n ReplyTo: ~p" "~n ConnHandle: ~p" "~n SessionPid: ~p", [ReplyTo, ConnHandle, SessionPid]), case lists:keysearch(SessionPid, #session.local_pid, State#state.sessions) of {value, Session} -> %% BUGBUG: Do we need to sync ref counters between Erlang nodes? ?d("handle_info(select_session) -> Session: ~p", [Session]), RemotePid = Session#session.remote_pid, case State#state.ref_counter + 1 of Ref when Ref > 16#FFFFFFFF -> % Must fit within 32 bits Ref2 = 1, ReplyTo ! {self(), {a_session, RemotePid, Ref2}}, {noreply, State#state{ref_counter = Ref2}}; Ref -> ReplyTo ! {self(), {a_session, RemotePid, Ref}}, {noreply, State#state{ref_counter = Ref}} end; false -> ?d("handle_info(select_session) -> no sessions", []), ReplyTo ! {self(), no_session}, {noreply, State} end; handle_info({'EXIT', Pid, Reason}, State) when Pid == State#state.parent_pid -> ?d("handle_info(EXIT) -> entry when parent (~p) dies" "~n Reason: ~p", [Pid, Reason]), {stop, Reason, State};handle_info({'EXIT', Pid, Reason} = Info, State) -> report("handle_info(~p, ~p)", [Info, State]), Sessions = State#state.sessions, case lists:keysearch(Pid, #session.local_pid, Sessions) of {value, Session} -> %% Local server died. report("local session server (~w) died: ~w~n", [Pid, Reason]), erlang:monitor_node(Session#session.remote_ref, false), %% Sessions = [Session | State#state.sessions], {noreply, State#state{sessions = Sessions -- [Session]}}; false -> {noreply, State} end;handle_info({nodedown, Node}, State) -> ?d("handle_info(nodedown) -> entry when" "~n Node: ~p", [Node]), report("SessionUser node died - ~p", [Node]), Sessions = State#state.sessions, Sessions2 = [Session || #session{remote_ref = N} = Session <- Sessions, N == Node], Fun = fun(Session) -> %% Remote user died. Tell the local server about it. LocalPid = Session#session.local_pid, unlink(LocalPid), exit(LocalPid, shutdown) end, lists:map(Fun, Sessions2), {noreply, State#state{sessions = Sessions -- Sessions2}};handle_info(Info, State) -> report("handle_info(~p, ~p)", [Info, State]), {noreply, State}.%% ----------------------------------------------------------------------%% Requests from the (C-) user:%% ----------------------------------------------------------------------startSession(State, Ref, ReplyTo) -> startSession(State, Ref, ReplyTo, false).startSession(State, Ref, ReplyTo, AutoStopUser) -> ?d("startSession -> entry with" "~n Ref: ~p" "~n ReplyTo: ~p" "~n AutoStopUser: ~p", [Ref, ReplyTo, AutoStopUser]), case 'Megaco_Session':oe_create_link([{parent_pid, self()}, {auto_stop_user, AutoStopUser}]) of {ok, Pid} -> ?d("startSession -> Pid: ~p", [Pid]), Node = node(ReplyTo), ?d("startSession -> Node: ~p", [Node]), erlang:monitor_node(Node, true), Session = #session{local_pid = Pid, remote_pid = ReplyTo, remote_ref = Node}, Status = ?EXT(tr_Status, ok), 'Megaco_SessionUser':startSessionResponse(ReplyTo, Ref, Status, Pid), Sessions = State#state.sessions ++ [Session] , ?d("startSession -> done", []), {noreply, State#state{sessions = Sessions}}; {error, Reason} -> ?d("startSession -> error: ~p", [Reason]), Status = ?EXT(tr_Status, {error, Reason}), Dummy = ReplyTo, 'Megaco_SessionUser':startSessionResponse(ReplyTo, Ref, Status, Dummy), ?d("startSession -> done", []), {noreply, State} end.sessions(State, Ref, ReplyTo) -> Status = ?EXT(tr_Status, ok), SessionPids = [S#session.local_pid || S <- State#state.sessions], 'Megaco_SessionUser':sessionsResponse(ReplyTo, Ref, Status, SessionPids), {noreply, State}.stopSession(State, Ref, ReplyTo, SessionPid) -> ?d("stopSession -> entry when" "~n Ref: ~p" "~n ReplyTo: ~p" "~n SessionPid: ~p", [Ref, ReplyTo, SessionPid]), case lists:keysearch(SessionPid, #session.local_pid, State#state.sessions) of {value, #session{remote_ref = Remote} = Session} -> ?d("stopSession -> demonitor node: ~p", [Remote]), erlang:monitor_node(Remote, false), Status = ?EXT(tr_Status, ok), ?d("stopSession -> reply to session user", []), 'Megaco_SessionUser':stopSessionResponse(ReplyTo, Ref, Status), ?d("stopSession -> unlink from the session process", []), unlink(SessionPid), ?d("stopSession -> stop the session process", []), 'Megaco_Session':stop(SessionPid), Sessions = State#state.sessions, ?d("stopSession -> done", []), {noreply, State#state{sessions = Sessions -- [Session]}}; false -> ?d("stopSession -> session not found", []), Status = ?EXT(tr_Status, {errorString, "No such session"}), 'Megaco_SessionUser':stopSessionResponse(ReplyTo, Ref, Status), ?d("stopSession -> done", []), {noreply, State} end.%% ----------------------------------------------------------------------%% Internal functions%% ----------------------------------------------------------------------report(F, A) -> ok = error_logger:format("~p(~p): " ++ F ++ "~n", [?MODULE, self()|A]).get_debug() -> app_get_env(factory_debug, false).app_get_env(Key, Default) -> case application:get_env(megaco_session, Key) of {ok, Value} -> Value; _ -> Default end.dbg(F, A) -> dbg(get(debug), F, A).dbg(true, F, A) -> io:format("MSFI-DBG:~w: " ++ F ++ "~n", [self()|A]);dbg(_, _, _) -> ok.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -