blob: 005cd8b25474500f991af857d2feea3452bdfedc [file] [log] [blame]
Harald Weltef98b8a72012-02-21 00:20:49 +01001-module(map_ss_server).
2-author('Harald Welte <laforge@gnumonks.org>').
3
4-include_lib("TCAP/include/tcap.hrl").
5
6-behaviour(gen_server).
7
8-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
9
10-export([start_link/3, bind_ac/3, unbind_ac/3, dump/0]).
11
12-record(state, {
13 supervisor,
14 tcap_pid,
15 as_tbl,
16 dlg_tbl
17}).
18
19-record(ass_record, {
20 ac,
21 module
22}).
23
24-record(dlg_record, {
25 dialogue_id,
26 pid
27}).
28
29
30% client side
31
32bind_ac(Srv, AcName, Module) when is_list(AcName) ->
33 bind_ac(Srv, list_to_tuple(AcName), Module);
34bind_ac(Srv, AcName, Module) when is_tuple(AcName) ->
35 gen_server:call(Srv, {bind_ac, AcName, Module}).
36
37unbind_ac(Srv, AcName, Module) when is_list(AcName) ->
38 unbind_ac(Srv, list_to_tuple(AcName), Module);
39unbind_ac(Srv, AcName, Module) when is_tuple(AcName) ->
40 gen_server:call(Srv, {unbind_ac, AcName, Module}).
41
42dump() ->
43 fixme.
44
45
46% gen_fsm callbacks
47
48start_link(Supervisor, Ssn, TCO) when is_integer(Ssn) ->
49 ProcName = list_to_atom(?MODULE ++ "_" ++ integer_to_list(Ssn)),
50 gen_server:start_link({local, ProcName}, ?MODULE, [Supervisor, Ssn, TCO], []).
51
52init([Supervisor, Ssn, TCO]) ->
53 AssTbl = ets:new(list_to_atom(?MODULE ++ "_" ++ integer_to_list(Ssn)),
54 [ordered_set, named_table, {keypos, #ass_record.ac}]),
55 DlgTbl = ets:new(list_to_atom(?MODULE ++ "_" ++ integer_to_list(Ssn) ++ "_dlg"),
56 [ordered_set, named_table, {keypos, #dlg_record.dialogue_id}]),
57 {ok, #state{supervisor = Supervisor, tcap_pid = TCO, as_tbl = AssTbl, dlg_tbl = DlgTbl}}.
58
59
60handle_call({bind_ac, Ac, Module}, _From, LoopDat) ->
61 NewRec = #ass_record{ac = Ac, module = Module},
62 case ets:insert_new(LoopDat#state.as_tbl, NewRec) of
63 false ->
64 {reply, {error, ets_insert}, LoopDat};
65 _ ->
66 {reply, ok, LoopDat}
67 end;
68
69handle_call({unbind_ac, Ac, Module}, _From, LoopDat) ->
70 DelRec = #ass_record{ac = Ac, module = Module},
71 ets:delete_object(LoopDat#state.as_tbl, DelRec),
72 {reply, ok, LoopDat}.
73
74handle_cast(W={'TC',_,_,_}, LoopDat) ->
75 handle_tcap(W, LoopDat);
76handle_cast(Info, LoopDat) ->
77 error_logger:error_report(["unknown handle_cast",
78 {module, ?MODULE}, {info, Info},
79 {state, LoopDat}]),
80 {noreply, LoopDat}.
81
82handle_info(Info, LoopDat) ->
83 error_logger:error_report(["unknown handle_info",
84 {module, ?MODULE}, {info, Info},
85 {state, LoopDat}]),
86 {noreply, LoopDat}.
87
88terminate(Reason, _LoopDat) ->
89 io:format("terminating ~p with reason ~p~n", [self(), Reason]),
90 ok.
91
92code_change(_OldVsn, State, _Extra) ->
93 {ok, State}.
94
95% server side
96handle_tcap({'TC','BEGIN',indication,
97 I=#'TC-BEGIN'{appContextName = Ac, dialogueID = DlgId}}, LoopDat) ->
98 case dlg_pid_for_id(DlgId, LoopDat) of
99 {ok, _Pid} ->
100 error_logger:error_report(["TC-BEGIN for existing Dialogue",
101 {dialogue_id, DlgId}]),
102 Abrt = gen_abort(I, applicationContextNotSupported),
103 tcap_user:send_prim(LoopDat#state.tcap_pid, {'TC','U-ABORT',request,Abrt});
104 {error, _} ->
105 case mod_for_ac(Ac, LoopDat) of
106 {ok, Module} ->
107 Args = [LoopDat#state.tcap_pid, Ac, DlgId, Module],
108 ChildName = list_to_atom("dlg_" ++ integer_to_list(DlgId)),
109 Mfa = {gen_server, start_link, [map_dlg_server, Args, []]},
110 ChildSpec = {ChildName, Mfa, temporary, 1000, worker, [map_dlg_server, Module]},
111 {ok, Pid} = supervisor:start_child(LoopDat#state.supervisor, ChildSpec),
112 gen_fsm:send_event(Pid, I),
113 % FIXME: how to safely remove the Pid in all cases of dialogue termination?
114 ets:insert(LoopDat#state.dlg_tbl, #dlg_record{dialogue_id=DlgId, pid=Pid});
115 {error, _Reason} ->
116 error_logger:error_report(["TC-BEGIN for non-existing AC",
117 {application_context, Ac}]),
118 % send a TC-U-ABORT
119 Abrt = gen_abort(I, applicationContextNotSupported),
120 tcap_user:send_prim(LoopDat#state.tcap_pid, {'TC','U-ABORT',request,Abrt})
121 end
122 end;
123handle_tcap({'TC', What, indication, P}, LoopDat) when
124 What == 'CONTINUE'; What == 'END';
125 What == 'U-ABORT'; What == 'P-ABORT';
126 What == 'NOTICE' ->
127 DlgId = tcap_user:get_dialg_id(P),
128 % look up the Pid for the specific Dialogue Handler
129 case dlg_pid_for_id(DlgId, LoopDat) of
130 {ok, Pid} ->
131 gen_fsm:send_event(Pid, P);
132 _ ->
133 error_logger:error_report(["TC-Dialogue non-existing DialogueID",
134 {dialogue_id, DlgId}]),
135 Abrt = gen_abort(P, dialogueRefused),
136 tcap_user:send_prim(LoopDat#state.tcap_pid, {'TC','U-ABORT',request,Abrt})
137 end;
138handle_tcap({'TC', What, indication, P}, LoopDat) when
139 What == 'INVOKE';
140 What == 'RESULT-L'; What == 'RESULT-NL';
141 What == 'U-ERROR'; What == 'L-CANCEL';
142 What == 'L-REJECT'; What == 'U-REJECT';
143 What == 'TIMER-RESET' ->
144 DlgId = tcap_user:get_dialg_id(P),
145 % look up the Pid for the specific Dialogue Handler
146 case dlg_pid_for_id(DlgId, LoopDat) of
147 {ok, Pid} ->
148 gen_fsm:send_event(Pid, P);
149 _ ->
150 error_logger:error_report(["TC-Component non-existing DialogueID",
151 {dialogue_id, DlgId}]),
152 Abrt = gen_abort(P, dialogueRefused),
153 tcap_user:send_prim(LoopDat#state.tcap_pid, {'TC','U-ABORT',request,Abrt})
154 end.
155
156mod_for_ac(Ac, LoopDat) ->
157 case ets:lookup(LoopDat#state.as_tbl, Ac) of
158 [#ass_record{module = Module}] ->
159 {ok, Module};
160 _ ->
161 {error, no_such_ac}
162 end.
163
164dlg_pid_for_id(DlgId, LoopDat) when is_record(LoopDat, state) ->
165 case ets:lookup(LoopDat#state.dlg_tbl, DlgId) of
166 [{DlgId, Pid}] ->
167 {ok, Pid};
168 _ ->
169 {error, notfound}
170 end.
171
172gen_abort(#'TC-BEGIN'{qos = Qos, appContextName = AcName, dialogueID = DlgId},
173 Reason) ->
174 #'TC-U-ABORT'{qos = Qos, appContextName = AcName, dialogueID = DlgId,
175 abortReason = Reason}.
176
177gen_reject(#'TC-INVOKE'{dialogueID = DlgId, invokeID = InvId}, Code) ->
178 #'TC-U-REJECT'{dialogueID = DlgId, invokeID = InvId, problemCode = Code};
179gen_reject(#'TC-RESULT-L'{dialogueID = DlgId, invokeID = InvId}, Code) ->
180 #'TC-U-REJECT'{dialogueID = DlgId, invokeID = InvId, problemCode = Code};
181gen_reject(#'TC-RESULT-NL'{dialogueID = DlgId, invokeID = InvId}, Code) ->
182 #'TC-U-REJECT'{dialogueID = DlgId, invokeID = InvId, problemCode = Code}.
183
184
185