Browse code

cnxcc: added new module for credit control

Carlos Ruiz Díaz authored on 26/03/2013 03:58:28
Showing 16 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,17 @@
0
+# $Id$
1
+#
2
+# example module makefile
3
+#
4
+# 
5
+# WARNING: do not run this directly, it should be run by the master Makefile
6
+
7
+include ../../Makefile.defs
8
+auto_gen=
9
+NAME=cnxcc.so
10
+
11
+DEFS+=-DOPENSER_MOD_INTERFACE
12
+SERLIBPATH=../../lib
13
+SER_LIBS+=$(SERLIBPATH)/kmi/kmi
14
+SER_LIBS+=$(SERLIBPATH)/srdb1/srdb1
15
+SER_LIBS+=$(SERLIBPATH)/kcore/kcore
16
+include ../../Makefile.modules
0 17
new file mode 100644
... ...
@@ -0,0 +1,270 @@
0
+
1
+cnxcc Module
2
+
3
+Carlos Ruiz Diaz
4
+
5
+   ConexionGroup SA
6
+
7
+   Copyright © 2012 Carlos Ruiz Diaz, carlos.ruizdiaz@gmail.com
8
+     __________________________________________________________________
9
+
10
+   Table of Contents
11
+
12
+   1. Admin Guide
13
+
14
+        1. Overview
15
+        2. Dependencies
16
+
17
+              2.1. Modules
18
+
19
+        3. Parameters
20
+
21
+              3.1. dlg_flag (integer)
22
+              3.2. credit_check_period (integer)
23
+
24
+        4. Functions
25
+
26
+              4.1. cnxcc_set_max_credit()
27
+              4.2. cnxcc_set_max_time()
28
+
29
+        5. Exported RPC Commands
30
+
31
+              5.1. cnxcc.active_clients
32
+              5.2. cnxcc.check_client
33
+              5.3. cnxcc.kill_call
34
+
35
+        6. Events
36
+        7. Web Interface
37
+        8. Sample
38
+
39
+   List of Examples
40
+
41
+   1.1. dlg_flag
42
+   1.2. credit_check_period
43
+   1.3. cnxcc_set_max_credit()
44
+   1.4. cnxcc_set_max_time()
45
+   1.5. kamailio-cnxcc.cfg
46
+
47
+Chapter 1. Admin Guide
48
+
49
+   Table of Contents
50
+
51
+   1. Overview
52
+   2. Dependencies
53
+
54
+        2.1. Modules
55
+
56
+   3. Parameters
57
+
58
+        3.1. dlg_flag (integer)
59
+        3.2. credit_check_period (integer)
60
+
61
+   4. Functions
62
+
63
+        4.1. cnxcc_set_max_credit()
64
+        4.2. cnxcc_set_max_time()
65
+
66
+   5. Exported RPC Commands
67
+
68
+        5.1. cnxcc.active_clients
69
+        5.2. cnxcc.check_client
70
+        5.3. cnxcc.kill_call
71
+
72
+   6. Events
73
+   7. Web Interface
74
+   8. Sample
75
+
76
+1. Overview
77
+
78
+   This module was designed to act as a mechanism to limit call duration
79
+   based on credit information parameters. After getting the credit
80
+   information of the call being set up, you can instruct the module to
81
+   start monitoring the consumed credit to shutdown a single call or a
82
+   group of calls in case of credit exhaustion.
83
+
84
+   Every call is associated to an unique client/customer identifier. If a
85
+   credit event occurs, all calls hooked to this identifier are
86
+   automatically shutdown.
87
+
88
+   Cnxcc is dialog-aware so there's no need to explicitly
89
+   allocate/deallocate the monitoring. Only a single function call inside
90
+   the script is needed upon reception of the INVITE.
91
+
92
+   The credit discount rate is proportional to the number of calls grouped
93
+   inside an identifier. Once the setup of the first call is done, the
94
+   information remains while the call is active. If the customer starts a
95
+   new call with the same routing criteria, it will land in the same
96
+   monitoring bag and it will consume the same pool of credit in rates
97
+   that are equal to the cost per second of both calls.
98
+
99
+   If your accounting program does not maintain the state of the call in
100
+   real time, this module can provide you that ability.
101
+
102
+2. Dependencies
103
+
104
+   2.1. Modules
105
+
106
+2.1. Modules
107
+
108
+   The following module must be loaded before this module:
109
+     * dialog
110
+
111
+3. Parameters
112
+
113
+   3.1. dlg_flag (integer)
114
+   3.2. credit_check_period (integer)
115
+
116
+3.1.  dlg_flag (integer)
117
+
118
+   Flag to indicate if the dialog must be monitored or not. Messages are
119
+   flagged with this value if we call one of the monitoring functions.
120
+
121
+   Example 1.1. dlg_flag
122
+...
123
+modparam("cnxcc", "dlg_flag", 29)
124
+...
125
+
126
+3.2. credit_check_period (integer)
127
+
128
+   Indicates how often the credit checking function should be called. It
129
+   is directly related to the precison of the module. The maximum
130
+   precision is 1, which means that every call is checked every one
131
+   second.
132
+
133
+   Values greater than 1 leads to precision lost but less CPU consumption.
134
+
135
+   Example 1.2. credit_check_period
136
+...
137
+modparam("cnxcc", "credit_check_period", 1)
138
+...
139
+
140
+4. Functions
141
+
142
+   4.1. cnxcc_set_max_credit()
143
+   4.2. cnxcc_set_max_time()
144
+
145
+4.1.  cnxcc_set_max_credit()
146
+
147
+   Specifies the initial pulse, final pulse, max credit and cost per
148
+   second of a call. The discount is calculated in pulses (30/6, 1/1, etc)
149
+   and sustracted from the pool of credit.
150
+
151
+   Return code:
152
+     * 1 - successful
153
+     * -1 - failed, error logged
154
+
155
+   Example 1.3. cnxcc_set_max_credit()
156
+...
157
+$var(customer) = "john-doe-123-premium";
158
+$var(credit) = "100";
159
+$var(cps)   = "2.00";         # cost per second
160
+$var(initial_p)   = "030";    # intial pulse
161
+$var(final_p)   = "006";      # final pulse
162
+
163
+cnxcc_set_max_credit("$var(customer)", "$var(credit)", "$var(cps)", "$var(initia
164
+l_p)", "$var(final_p)");
165
+...
166
+
167
+4.2.  cnxcc_set_max_time()
168
+
169
+   Specifies the amount of time the call should last at most.
170
+
171
+   Return code:
172
+     * 1 - successful
173
+     * -1 - failed, error logged
174
+
175
+   Example 1.4. cnxcc_set_max_time()
176
+...
177
+$var(customer) = "john-doe-123-basic";
178
+$var(max_time) = 120;
179
+
180
+cnxcc_set_max_tim ("$var(customer)", "$var(max_time)");
181
+...
182
+
183
+5. Exported RPC Commands
184
+
185
+   5.1. cnxcc.active_clients
186
+   5.2. cnxcc.check_client
187
+   5.3. cnxcc.kill_call
188
+
189
+5.1. cnxcc.active_clients
190
+
191
+   Retrieves all calls grouped by their identifiers.
192
+
193
+   Parameters: none
194
+
195
+   Example:
196
+            sercmd cnxcc.active_clients
197
+
198
+5.2. cnxcc.check_client
199
+
200
+   Retrives all calls from a particular identifier.
201
+
202
+   Parameters: client/customer identifier
203
+
204
+   Example:
205
+            sercmd cnxcc.check_client john-doe-123-premium
206
+
207
+5.3. cnxcc.kill_call
208
+
209
+   Kills an active call using its call ID.
210
+
211
+   Parameters: Call-ID
212
+
213
+   Example:
214
+            sercmd cnxcc.kill_call qumojlaahitafih@carlosrdcnx-laptop.site
215
+
216
+6. Events
217
+
218
+   When a call is forced to end an event route is automatically invoked.
219
+   This route is suited with a fake OPTIONS message containing the call
220
+   ID, ftag and ttag of the original call so it can be located somehow in
221
+   the accounting database.
222
+
223
+   Example:
224
+...
225
+event_route[cnxcc:call-shutdown]
226
+{
227
+        xlog("L_INFO", "[$ci]: call killed");
228
+
229
+        # perform some kind of notification, database update, email sending, etc
230
+.
231
+}
232
+...
233
+
234
+7. Web Interface
235
+
236
+   The module contains a web management interface completely optional.
237
+   With it, you can review your calls in real time and hang them up if
238
+   necessary.
239
+
240
+   Link: https://github.com/caruizdiaz/cnxcc-web
241
+
242
+8. Sample
243
+
244
+   Example 1.5. kamailio-cnxcc.cfg
245
+...
246
+route[CNXCC]
247
+{
248
+        $var(client)              = "test-client-0-123-01";
249
+        $var(credit)              = "50";
250
+        $var(cost_per_sec)        = "0.5";
251
+        $var(i_pulse)             = "30";
252
+        $var(f_pulse)             = "6";
253
+
254
+        if (!cnxcc_set_max_credit("$var(client)",
255
+                          "$var(credit)",
256
+                          "$var(cost_per_sec)",
257
+                          "$var(i_pulse)",
258
+                          "$var(f_pulse)")) {
259
+                 xlog("Error setting up credit control");
260
+        }
261
+}
262
+
263
+event_route[cnxcc:call-shutdown]
264
+{
265
+        xlog("L_INFO", "[$ci]: call killed");
266
+
267
+
268
+}
269
+...
0 270
new file mode 100644
... ...
@@ -0,0 +1,63 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
4
+ *                    ConexionGroup (www.conexiongroup.com)
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ *
22
+ */
23
+#include <time.h>
24
+#include <string.h>
25
+#include <stdlib.h>
26
+#include <sys/time.h>
27
+
28
+#include "cnxcc.h"
29
+
30
+inline void get_datetime(str *dest)
31
+{
32
+	timestamp2isodt(dest, get_current_timestamp());
33
+}
34
+
35
+inline unsigned int get_current_timestamp()
36
+{
37
+	return time(NULL);
38
+}
39
+
40
+inline int timestamp2isodt(str *dest, unsigned int timestamp)
41
+{
42
+	time_t  		tim;
43
+	struct tm 		*tmPtr;
44
+
45
+	tim 		= timestamp;
46
+	tmPtr 		= localtime(&tim);
47
+
48
+	strftime( dest->s, DATETIME_SIZE, "%Y-%m-%d %H:%M:%S", tmPtr);
49
+	dest->len	= DATETIME_LENGTH;
50
+
51
+	return 0;
52
+}
53
+
54
+double str2double(str *string)
55
+{
56
+	char buffer[string->len + 1];
57
+
58
+	buffer[string->len]	= '\0';
59
+	memcpy(buffer, string->s, string->len);
60
+
61
+	return atof(buffer);
62
+}
0 63
new file mode 100644
... ...
@@ -0,0 +1,39 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
4
+ *                    ConexionGroup (www.conexiongroup.com)
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ *
22
+ */
23
+
24
+#ifndef _CNXCC_H
25
+#define _CNXCC_H
26
+
27
+#include "../../str.h"
28
+
29
+#define DATETIME_SIZE		sizeof("0001-01-01 00:00:00")
30
+#define DATETIME_LENGTH		DATETIME_SIZE - 1
31
+
32
+
33
+inline void get_datetime(str *dest);
34
+inline unsigned int get_current_timestamp();
35
+inline int timestamp2isodt(str *dest, unsigned int timestamp);
36
+double str2double(str *string);
37
+
38
+#endif /* _CNXCC_H */
0 39
new file mode 100644
... ...
@@ -0,0 +1,176 @@
0
+/*
1
+ * cnxcc_check.c
2
+ *
3
+ *  Created on: Dec 10, 2012
4
+ *      Author: carlos
5
+ */
6
+
7
+#include <stdio.h>
8
+
9
+#include "../../locking.h"
10
+#include "../../lock_ops.h"
11
+
12
+#include "cnxcc_mod.h"
13
+#include "cnxcc.h"
14
+#include "cnxcc_check.h"
15
+
16
+extern data_t _data;
17
+
18
+void check_calls_by_money(unsigned int ticks, void *param)
19
+{
20
+	struct str_hash_entry *h_entry 	= NULL,
21
+						  *tmp		= NULL;
22
+	call_t *tmp_call				= NULL;
23
+	int i;
24
+
25
+	lock_get(&_data.money.lock);
26
+
27
+	if (_data.money.credit_data_by_client->table)
28
+		for(i = 0; i < _data.money.credit_data_by_client->size; i++)
29
+			clist_foreach_safe(&_data.money.credit_data_by_client->table[i], h_entry, tmp, next)
30
+			{
31
+				credit_data_t *credit_data	= (credit_data_t *) h_entry->u.p;
32
+				call_t *call				= NULL;
33
+				double total_consumed_money	= 0;
34
+
35
+				if (i > SAFE_ITERATION_THRESHOLD)
36
+				{
37
+					LM_ERR("Too many iterations for this loop: %d", i);
38
+					break;
39
+				}
40
+
41
+				lock_get(&credit_data->lock);
42
+
43
+				clist_foreach_safe(credit_data->call_list, call, tmp_call, next)
44
+				{
45
+					int consumed_time = 0;
46
+
47
+					if (!call->confirmed)
48
+						continue;
49
+
50
+					consumed_time 				= get_current_timestamp() - call->start_timestamp;
51
+
52
+					if (consumed_time > call->money_based.initial_pulse)
53
+					{
54
+						call->consumed_amount = (call->money_based.cost_per_second * call->money_based.initial_pulse)
55
+												+
56
+												call->money_based.cost_per_second *
57
+												( (consumed_time - call->money_based.initial_pulse) / call->money_based.final_pulse + 1 ) *
58
+												call->money_based.final_pulse;
59
+					}
60
+
61
+					total_consumed_money	+= call->consumed_amount;
62
+
63
+					if (call->consumed_amount > call->max_amount)
64
+					{
65
+						LM_ALERT("[%.*s] call has exhausted its credit. Breaking the loop\n", call->sip_data.callid.len, call->sip_data.callid.s);
66
+						break;
67
+					}
68
+
69
+					LM_DBG("CID [%.*s], start_timestamp [%d], seconds alive [%d], consumed credit [%f]\n",
70
+																			call->sip_data.callid.len, call->sip_data.callid.s,
71
+																			call->start_timestamp,
72
+																			consumed_time,
73
+																			call->consumed_amount
74
+																			);
75
+				}
76
+
77
+				if (credit_data->concurrent_calls == 0)
78
+				{
79
+					lock_release(&credit_data->lock);
80
+					continue;
81
+				}
82
+
83
+				credit_data->consumed_amount	= credit_data->ended_calls_consumed_amount + total_consumed_money;
84
+
85
+				LM_DBG("Client [%.*s] | Ended-Calls-Credit-Spent: %f  TotalCredit/MaxCredit: %f/%f\n", credit_data->call_list->client_id.len, credit_data->call_list->client_id.s,
86
+																									credit_data->ended_calls_consumed_amount,
87
+																									credit_data->consumed_amount,
88
+																									credit_data->max_amount);
89
+
90
+				if (credit_data->consumed_amount >= credit_data->max_amount)
91
+				{
92
+					terminate_all_calls(credit_data);
93
+					lock_release(&credit_data->lock);
94
+					break;
95
+				}
96
+
97
+				lock_release(&credit_data->lock);
98
+			}
99
+
100
+	lock_release(&_data.money.lock);
101
+}
102
+
103
+void check_calls_by_time(unsigned int ticks, void *param)
104
+{
105
+	struct str_hash_entry *h_entry 	= NULL,
106
+						  *tmp		= NULL;
107
+	call_t *tmp_call				= NULL;
108
+	int i;
109
+
110
+	lock_get(&_data.time.lock);
111
+
112
+	if (_data.time.credit_data_by_client->table)
113
+		for(i = 0; i < _data.time.credit_data_by_client->size; i++)
114
+			clist_foreach_safe(&_data.time.credit_data_by_client->table[i], h_entry, tmp, next)
115
+			{
116
+				credit_data_t *credit_data	= (credit_data_t *) h_entry->u.p;
117
+				call_t *call				= NULL;
118
+				int total_consumed_secs		= 0;
119
+
120
+				lock_get(&credit_data->lock);
121
+
122
+				if (i > SAFE_ITERATION_THRESHOLD)
123
+				{
124
+					LM_ERR("Too many iterations for this loop: %d", i);
125
+					break;
126
+				}
127
+
128
+				LM_DBG("Iterating through calls of client [%.*s]\n", credit_data->call_list->client_id.len, credit_data->call_list->client_id.s);
129
+
130
+				clist_foreach_safe(credit_data->call_list, call, tmp_call, next)
131
+				{
132
+					if (!call->confirmed)
133
+						continue;
134
+
135
+					call->consumed_amount		= get_current_timestamp() - call->start_timestamp;
136
+					total_consumed_secs			+= call->consumed_amount;
137
+
138
+					if (call->consumed_amount > call->max_amount)
139
+					{
140
+						LM_ALERT("[%.*s] call has exhausted its time. Breaking the loop\n", call->sip_data.callid.len, call->sip_data.callid.s);
141
+						break;
142
+					}
143
+
144
+					LM_DBG("CID [%.*s], start_timestamp [%d], seconds alive [%d]\n",
145
+																			call->sip_data.callid.len, call->sip_data.callid.s,
146
+																			call->start_timestamp,
147
+																			(int) call->consumed_amount
148
+																			);
149
+				}
150
+
151
+				if (credit_data->concurrent_calls == 0)
152
+				{
153
+					lock_release(&credit_data->lock);
154
+					continue;
155
+				}
156
+
157
+				credit_data->consumed_amount	= credit_data->ended_calls_consumed_amount + total_consumed_secs;
158
+
159
+				LM_DBG("Client [%.*s] | Ended-Calls-Time: %d  TotalTime/MaxTime: %d/%d\n", credit_data->call_list->client_id.len, credit_data->call_list->client_id.s,
160
+																									(int) credit_data->ended_calls_consumed_amount,
161
+																									(int) credit_data->consumed_amount,
162
+																									(int) credit_data->max_amount);
163
+
164
+				if (credit_data->consumed_amount >= credit_data->max_amount)
165
+				{
166
+					terminate_all_calls(credit_data);
167
+					lock_release(&credit_data->lock);
168
+					break;
169
+				}
170
+
171
+				lock_release(&credit_data->lock);
172
+			}
173
+
174
+	lock_release(&_data.time.lock);
175
+}
0 176
new file mode 100644
... ...
@@ -0,0 +1,16 @@
0
+/*
1
+ * cnxcc_check.h
2
+ *
3
+ *  Created on: Dec 10, 2012
4
+ *      Author: carlos
5
+ */
6
+
7
+#ifndef CNXCC_CHECK_H_
8
+#define CNXCC_CHECK_H_
9
+
10
+#define SAFE_ITERATION_THRESHOLD 5000
11
+
12
+void check_calls_by_time(unsigned int ticks, void *param);
13
+void check_calls_by_money(unsigned int ticks, void *param);
14
+
15
+#endif /* CNXCC_CHECK_H_ */
0 16
new file mode 100644
... ...
@@ -0,0 +1,1526 @@
0
+/*
1
+ * $Id$
2
+ *
3
+ * Copyright (C) 2012 Carlos Ruiz Díaz (caruizdiaz.com),
4
+ *                    ConexionGroup (www.conexiongroup.com)
5
+ *
6
+ * This file is part of Kamailio, a free SIP server.
7
+ *
8
+ * Kamailio is free software; you can redistribute it and/or modify
9
+ * it under the terms of the GNU General Public License as published by
10
+ * the Free Software Foundation; either version 2 of the License, or
11
+ * (at your option) any later version
12
+ *
13
+ * Kamailio is distributed in the hope that it will be useful,
14
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
+ * GNU General Public License for more details.
17
+ *
18
+ * You should have received a copy of the GNU General Public License
19
+ * along with this program; if not, write to the Free Software
20
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21
+ *
22
+ */
23
+
24
+#include <stdio.h>
25
+#include <string.h>
26
+#include <stdlib.h>
27
+#include <sys/types.h>
28
+#include <sys/ipc.h>
29
+#include <unistd.h>
30
+#include <fcntl.h>
31
+#include <time.h>
32
+#include <ctype.h>
33
+
34
+#include "../../sr_module.h"
35
+#include "../../dprint.h"
36
+#include "../../error.h"
37
+#include "../../mem/mem.h"
38
+#include "../../shm_init.h"
39
+#include "../../mem/shm_mem.h"
40
+#include "../../pvar.h"
41
+#include "../../locking.h"
42
+#include "../../lock_ops.h"
43
+#include "../../str_hash.h"
44
+//#include "../../timer.h"
45
+#include "../../timer_proc.h"
46
+#include "../../modules/tm/tm_load.h"
47
+#include "../../parser/parse_from.h"
48
+#include "../../parser/parse_to.h"
49
+#include "../../parser/parse_uri.h"
50
+#include "../../parser/parse_cseq.h"
51
+#include "../../parser/contact/parse_contact.h"
52
+#include "../../parser/contact/contact.h"
53
+#include "../../parser/parse_rr.h"
54
+//#include "../../lib/kcore/parser_helpers.h"
55
+#include "../../mod_fix.h"
56
+#include "../dialog/dlg_load.h"
57
+#include "../dialog/dlg_hash.h"
58
+#include "../../mi/mi_types.h"
59
+#include "../../lib/kcore/faked_msg.h"
60
+#include "../../rpc.h"
61
+#include "../../rpc_lookup.h"
62
+
63
+#include "cnxcc_mod.h"
64
+#include "cnxcc.h"
65
+#include "cnxcc_sip_msg_faker.h"
66
+#include "cnxcc_check.h"
67
+#include "cnxcc_rpc.h"
68
+
69
+MODULE_VERSION
70
+
71
+#define HT_SIZE						229
72
+#define MODULE_NAME					"CNXCC"
73
+#define NUMBER_OF_TIMERS			2
74
+
75
+#define TRUE						1
76
+#define FALSE						0
77
+
78
+data_t _data;
79
+struct dlg_binds _dlgbinds;
80
+
81
+static int fixup_par(void** param, int param_no);
82
+
83
+/*
84
+ *  module core functions
85
+ */
86
+static int mod_init(void);
87
+static int child_init(int);
88
+static int init_hashtable(struct str_hash_table *ht);
89
+
90
+/*
91
+ * Memory management functions
92
+ */
93
+static int shm_str_hash_alloc(struct str_hash_table *ht, int size);
94
+static void free_credit_data_hash_entry(struct str_hash_entry *e);
95
+
96
+/*
97
+ * PV management functions
98
+ */
99
+static int pv_parse_calls_param(pv_spec_p sp, str *in);
100
+static int pv_get_calls(struct sip_msg *msg, pv_param_t *param, pv_value_t *res);
101
+//static int get_str_pv(struct sip_msg* msg, str *pv_name, str *pvvalue);
102
+
103
+/*
104
+ * Billing management functions
105
+ */
106
+static int set_max_time(struct sip_msg* msg, char* number, char* str2);
107
+static int set_max_credit(struct sip_msg* msg, char *str_pv_client, char *str_pv_credit, char *str_pv_cps, char *str_pv_inip, char *str_pv_finp);
108
+static void start_billing(str *callid, str tags[2]);
109
+static void setup_billing(str *callid, unsigned int h_entry, unsigned int h_id);
110
+static void stop_billing(str *callid);
111
+static int add_call_by_cid(str *cid, call_t *call, credit_type_t type);
112
+static credit_data_t *get_or_create_credit_data_entry(str *client_id, credit_type_t type);
113
+static call_t *alloc_new_call_by_time(credit_data_t *credit_data, struct sip_msg *msg, int max_secs);
114
+static call_t *alloc_new_call_by_money(credit_data_t *credit_data, struct sip_msg *msg, double credit, double cost_per_second, int initial_pulse, int final_pulse);
115
+static void notify_call_termination(str *callid, str *from_tag, str *to_tag);
116
+static void free_call(call_t *call);
117
+static int has_to_tag(struct sip_msg *msg);
118
+
119
+/*
120
+ * MI interface
121
+ */
122
+static struct mi_root *mi_credit_control_stats(struct mi_root *tree, void *param);
123
+
124
+/*
125
+ * Dialog management callback functions
126
+ */
127
+static void dialog_terminated_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params);
128
+static void dialog_confirmed_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params);
129
+static void dialog_created_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params);
130
+
131
+static pv_export_t mod_pvs[] =
132
+{
133
+	{ {"cnxcc", sizeof("cnxcc")-1 }, PVT_OTHER, pv_get_calls, 0,
134
+		                pv_parse_calls_param, 0, 0, 0 },
135
+	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
136
+};
137
+
138
+static cmd_export_t cmds[] =
139
+{
140
+	{"cnxcc_set_max_time",   (cmd_function) set_max_time, 2, fixup_pvar_pvar, fixup_free_pvar_pvar, ANY_ROUTE},
141
+	{"cnxcc_set_max_credit",   (cmd_function) set_max_credit, 5, fixup_par, NULL, ANY_ROUTE},
142
+	{0,0,0,0,0,0}
143
+};
144
+
145
+static param_export_t params[] =
146
+{
147
+	{"dlg_flag",  				INT_PARAM,			&_data.ctrl_flag	},
148
+	{"credit_check_period",  	INT_PARAM,			&_data.check_period	},
149
+	{ 0, 0, 0 }
150
+};
151
+
152
+static const char* rpc_active_clients_doc[2] =
153
+{
154
+	"List of clients with active calls",
155
+	0
156
+};
157
+
158
+static const char* rpc_check_client_stats_doc[2] =
159
+{
160
+	"Check specific client calls",
161
+	0
162
+};
163
+
164
+static const char* rpc_kill_call_doc[2] =
165
+{
166
+	"Kill call using its call ID",
167
+	0
168
+};
169
+
170
+rpc_export_t ul_rpc[] =
171
+{
172
+    {"cnxcc.active_clients",	rpc_active_clients,	rpc_active_clients_doc,	0},
173
+    {"cnxcc.check_client",		rpc_check_client_stats,	rpc_check_client_stats_doc,	0},
174
+    {"cnxcc.kill_call",			rpc_kill_call,	rpc_kill_call_doc,	0},
175
+    {0, 0, 0, 0}
176
+};
177
+
178
+/** module exports */
179
+struct module_exports exports =
180
+{
181
+	"cnxcc",
182
+	DEFAULT_DLFLAGS, 	/* dlopen flags */
183
+	cmds,
184
+	params,
185
+	0,          		/* exported statistics */
186
+	0, 		    		/* exported MI functions */
187
+	mod_pvs,  			/* exported pseudo-variables */
188
+	0,          		/* extra processes */
189
+	mod_init,   		/* module initialization function */
190
+	0,
191
+	0,
192
+	child_init          /* per-child init function */
193
+};
194
+
195
+static int fixup_par(void** param, int param_no)
196
+{
197
+	str var;
198
+
199
+	var.s	= (char *) *param;
200
+	var.len = strlen(var.s);
201
+
202
+	if (fixup_pvar_null(param, 1))
203
+	{
204
+		LM_ERR("Invalid PV [%.*s] as parameter\n", var.len, var.s);
205
+		return E_CFG;
206
+	}
207
+/*
208
+	if (((pv_spec_t*)(*param))->setf == NULL)
209
+	{
210
+		LM_ERR("[%.*s] has to be writable\n", var.len, var.s);
211
+		return E_CFG;
212
+	} */
213
+
214
+	return 0;
215
+}
216
+
217
+static int mod_init(void)
218
+{
219
+	LM_ALERT("Loading " MODULE_NAME " module\n");
220
+
221
+	_data.cs_route_number = route_get(&event_rt, "cnxcc:call-shutdown");
222
+
223
+	if (_data.cs_route_number < 0)
224
+		LM_INFO("No cnxcc:call-shutdown event route found");
225
+
226
+	if (_data.cs_route_number > 0 && event_rt.rlist[_data.cs_route_number] == NULL)
227
+	{
228
+		LM_INFO("cnxcc:call-shutdown route is empty");
229
+		_data.cs_route_number	= -1;
230
+	}
231
+
232
+	if (_data.check_period <= 0)
233
+	{
234
+		LM_INFO("credit_check_period cannot be less than 1 second");
235
+		return -1;
236
+	}
237
+
238
+	_data.time.credit_data_by_client	= shm_malloc(sizeof(struct str_hash_table));
239
+	_data.time.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
240
+	_data.money.credit_data_by_client	= shm_malloc(sizeof(struct str_hash_table));
241
+	_data.money.call_data_by_cid 		= shm_malloc(sizeof(struct str_hash_table));
242
+
243
+	_data.stats							= (stats_t *) shm_malloc(sizeof(stats_t));
244
+
245
+	if (!_data.stats)
246
+	{
247
+		LM_ERR("Error allocating shared memory stats\n");
248
+		return -1;
249
+	}
250
+
251
+	_data.stats->active		= 0;
252
+	_data.stats->dropped	= 0;
253
+	_data.stats->total		= 0;
254
+
255
+	if (init_hashtable(_data.time.credit_data_by_client) != 0)
256
+		return -1;
257
+
258
+	if (init_hashtable(_data.time.call_data_by_cid) != 0)
259
+		return -1;
260
+
261
+	if (init_hashtable(_data.money.credit_data_by_client) != 0)
262
+		return -1;
263
+
264
+	if (init_hashtable(_data.money.call_data_by_cid) != 0)
265
+		return -1;
266
+
267
+	lock_init(&_data.lock);
268
+	lock_init(&_data.time.lock);
269
+	lock_init(&_data.money.lock);
270
+
271
+	register_mi_cmd(mi_credit_control_stats, "cnxcc_stats", NULL, NULL, 0);
272
+
273
+	/*
274
+	 * One for time based monitoring
275
+	 * One for money based monitoring
276
+	 */
277
+	register_dummy_timers(NUMBER_OF_TIMERS);
278
+
279
+	if (rpc_register_array(ul_rpc) != 0)
280
+	{
281
+		LM_ERR("Failed registering RPC commands\n");
282
+		return -1;
283
+	}
284
+
285
+	if (load_dlg_api(&_dlgbinds) != 0)
286
+	{
287
+		LM_ERR("Error loading dialog API\n");
288
+	    return -1;
289
+	}
290
+
291
+	_dlgbinds.register_dlgcb(NULL, DLGCB_CREATED, dialog_created_callback, NULL, NULL);
292
+
293
+	return 0;
294
+}
295
+
296
+static int child_init(int rank)
297
+{
298
+	if (rank != PROC_MAIN)
299
+		return 0;
300
+
301
+
302
+	if(fork_dummy_timer(PROC_TIMER, "CNXCC TB TIMER", 1,
303
+			check_calls_by_money, NULL, _data.check_period) < 0)
304
+	{
305
+		LM_ERR("failed to register TB TIMER routine as process\n");
306
+		return -1;
307
+	}
308
+
309
+	if(fork_dummy_timer(PROC_TIMER, "CNXCC MB TIMER", 1,
310
+								check_calls_by_time, NULL, _data.check_period) < 0)
311
+	{
312
+		LM_ERR("failed to register MB TIMER routine as process\n");
313
+		return -1;
314
+	}
315
+
316
+	return 0;
317
+}
318
+
319
+static int init_hashtable(struct str_hash_table *ht)
320
+{
321
+	if (shm_str_hash_alloc(ht, HT_SIZE) != 0)
322
+	{
323
+		LM_ERR("Error allocating shared memory hashtable\n");
324
+		return -1;
325
+	}
326
+
327
+	str_hash_init(ht);
328
+
329
+	return 0;
330
+}
331
+
332
+static void dialog_created_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params)
333
+{
334
+	struct sip_msg *msg	= NULL;
335
+
336
+	msg	= params->direction == SIP_REPLY ? params->rpl : params->req;
337
+
338
+	if (msg == NULL)
339
+	{
340
+		LM_ERR("Error getting direction of SIP msg\n");
341
+		return;
342
+	}
343
+
344
+	if (isflagset(msg, _data.ctrl_flag) == -1)
345
+	{
346
+		LM_DBG("Flag is not set for this message. Ignoring\n");
347
+		return;
348
+	}
349
+
350
+	LM_DBG("Dialog created for CID [%.*s]", cell->callid.len, cell->callid.s);
351
+
352
+	_dlgbinds.register_dlgcb(cell, DLGCB_CONFIRMED, dialog_confirmed_callback, NULL, NULL);
353
+	_dlgbinds.register_dlgcb(cell, DLGCB_TERMINATED|DLGCB_FAILED|DLGCB_EXPIRED, dialog_terminated_callback, NULL, NULL);
354
+
355
+	setup_billing(&cell->callid, cell->h_entry, cell->h_id);
356
+}
357
+
358
+static void dialog_confirmed_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params)
359
+{
360
+	LM_DBG("Dialog confirmed for CID [%.*s]", cell->callid.len, cell->callid.s);
361
+
362
+	start_billing(&cell->callid, cell->tag);
363
+}
364
+
365
+static void dialog_terminated_callback(struct dlg_cell *cell, int type, struct dlg_cb_params *params)
366
+{
367
+	LM_DBG("Dialog terminated for CID [%.*s]", cell->callid.len, cell->callid.s);
368
+
369
+	stop_billing(&cell->callid);
370
+}
371
+
372
+static void notify_call_termination(str *callid, str *from_tag, str *to_tag)
373
+{
374
+	struct run_act_ctx ra_ctx;
375
+	struct sip_msg *msg;
376
+
377
+	if (_data.cs_route_number < 0)
378
+		return;
379
+
380
+	if (faked_msg_init_with_dlg_info(callid, from_tag, to_tag,  &msg) != 0)
381
+	{
382
+		LM_ERR("[%.*s]: error generating faked sip message\n", callid->len, callid->s);
383
+		return;
384
+	}
385
+
386
+	init_run_actions_ctx(&ra_ctx);
387
+	//run_top_route(event_rt.rlist[_data.cs_route_number], msg, &ra_ctx);
388
+
389
+	if (run_actions(&ra_ctx, event_rt.rlist[_data.cs_route_number], msg) < 0)
390
+		LM_ERR("Error executing cnxcc:call-shutdown route");
391
+}
392
+
393
+int try_get_credit_data_entry(str *client_id, credit_data_t **credit_data)
394
+{
395
+	struct str_hash_entry *cd_entry	= NULL;
396
+	hash_tables_t *hts				= NULL;
397
+	*credit_data					= NULL;
398
+
399
+	hts					= &_data.money;
400
+	lock_get(&hts->lock);
401
+
402
+	cd_entry			= str_hash_get(hts->credit_data_by_client, client_id->s, client_id->len);
403
+
404
+	if (cd_entry != NULL)
405
+	{
406
+		*credit_data	= cd_entry->u.p;
407
+		lock_release(&hts->lock);
408
+		return 0;
409
+	}
410
+
411
+	lock_release(&hts->lock);
412
+
413
+	hts					= &_data.time;
414
+	lock_get(&hts->lock);
415
+
416
+	cd_entry			= str_hash_get(hts->call_data_by_cid, client_id->s, client_id->len);
417
+
418
+	if (cd_entry != NULL)
419
+	{
420
+		*credit_data	= cd_entry->u.p;
421
+		lock_release(&hts->lock);
422
+		return 0;
423
+	}
424
+
425
+	lock_release(&hts->lock);
426
+
427
+	return -1;
428
+}
429
+
430
+int try_get_call_entry(str *callid, call_t **call, hash_tables_t **hts)
431
+{
432
+	struct str_hash_entry *call_entry	= NULL;
433
+
434
+	*call					= NULL;
435
+
436
+	*hts					= &_data.money;
437
+	lock_get(&(*hts)->lock);
438
+
439
+	call_entry			= str_hash_get((*hts)->call_data_by_cid, callid->s, callid->len);
440
+
441
+	if (call_entry != NULL)
442
+	{
443
+		*call	= call_entry->u.p;
444
+		lock_release(&(*hts)->lock);
445
+		return 0;
446
+	}
447
+
448
+	lock_release(&(*hts)->lock);
449
+
450
+	*hts				= &_data.time;
451
+	lock_get(&(*hts)->lock);
452
+
453
+	call_entry			= str_hash_get((*hts)->call_data_by_cid, callid->s, callid->len);
454
+
455
+	if (call_entry != NULL)
456
+	{
457
+		*call	= call_entry->u.p;
458
+		lock_release(&(*hts)->lock);
459
+		return 0;
460
+	}
461
+
462
+	lock_release(&(*hts)->lock);
463
+
464
+	return -1;
465
+}
466
+
467
+static void stop_billing(str *callid)
468
+{
469
+	struct str_hash_entry *cd_entry		= NULL;
470
+	call_t *call						= NULL;
471
+	hash_tables_t *hts					= NULL;
472
+	credit_data_t *credit_data			= NULL;
473
+
474
+	/*
475
+	 * Search call data by call-id
476
+	 */
477
+	if (try_get_call_entry(callid, &call, &hts) != 0)
478
+	{
479
+		LM_ERR("Call [%.*s] not found", callid->len, callid->s);
480
+		return;
481
+	}
482
+
483
+	if (call == NULL)
484
+	{
485
+		LM_ERR("[%.*s] call pointer is null", callid->len, callid->s);
486
+		return;
487
+	}
488
+
489
+	if (hts == NULL)
490
+	{
491
+		LM_ERR("[%.*s] result hashtable pointer is null", callid->len, callid->s);
492
+		return;
493
+	}
494
+
495
+	lock_get(&hts->lock);
496
+
497
+	/*
498
+	 * Search credit_data by client_id
499
+	 */
500
+	cd_entry			= str_hash_get(hts->credit_data_by_client, call->client_id.s, call->client_id.len);
501
+
502
+	if (cd_entry == NULL)
503
+	{
504
+		LM_ERR("Credit data not found for CID [%.*s], client-ID [%.*s]\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
505
+		lock_release(&hts->lock);
506
+		return;
507
+	}
508
+
509
+	credit_data	= (credit_data_t *) cd_entry->u.p;
510
+
511
+	if (credit_data == NULL)
512
+	{
513
+		LM_ERR("[%.*s]: credit_data pointer is null", callid->len, callid->s);
514
+		lock_release(&hts->lock);
515
+		return;
516
+	}
517
+
518
+	lock_release(&hts->lock);
519
+
520
+	/*
521
+	 * Update calls statistics
522
+	 */
523
+	lock_get(&_data.lock);
524
+
525
+	_data.stats->active--;
526
+	_data.stats->total--;
527
+
528
+	lock_release(&_data.lock);
529
+
530
+	lock(&credit_data->lock);
531
+
532
+	LM_DBG("Call [%.*s] of client-ID [%.*s], ended\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
533
+
534
+	/*
535
+	 * This call just ended and we need to remove it from the summ.
536
+	 */
537
+	if (call->confirmed)
538
+	{
539
+		credit_data->concurrent_calls--;
540
+		credit_data->ended_calls_consumed_amount += call->consumed_amount;
541
+	}
542
+
543
+	credit_data->number_of_calls--;
544
+
545
+	if (credit_data->concurrent_calls < 0)
546
+	{
547
+		LM_ERR("[BUG]: number of concurrent calls dropped to negative value: %d", credit_data->concurrent_calls);
548
+	}
549
+
550
+	if (credit_data->number_of_calls < 0)
551
+	{
552
+		LM_ERR("[BUG]: number of calls dropped to negative value: %d", credit_data->number_of_calls);
553
+	}
554
+
555
+	/*
556
+	 * Remove (and free) the call from the list of calls of the current credit_data
557
+	 */
558
+	clist_rm(call, next, prev);
559
+	free_call(call);
560
+
561
+	/*
562
+	 * In case there are no active calls for a certain client, we remove the client-id from the hash table.
563
+	 * This way, we can save memory for useful clients.
564
+	 */
565
+	if (credit_data->number_of_calls == 0)
566
+	{
567
+		LM_DBG("Removing client [%.*s] and its calls from the list\n", credit_data->call_list->client_id.len, credit_data->call_list->client_id.s);
568
+
569
+		lock(&hts->lock);
570
+		/*
571
+		 * Remove the credit_data_t from the hash table
572
+		 */
573
+		str_hash_del(cd_entry);
574
+
575
+		lock_release(&hts->lock);
576
+
577
+		/*
578
+		 * Free client_id in list's root
579
+		 */
580
+		shm_free(credit_data->call_list->client_id.s);
581
+		shm_free(credit_data->call_list);
582
+
583
+		/*
584
+		 * Release the lock since we are going to free the entry down below
585
+		 */
586
+		lock_release(&credit_data->lock);
587
+
588
+		/*
589
+		 * Free the whole entry
590
+		 */
591
+		free_credit_data_hash_entry(cd_entry);
592
+
593
+		/*
594
+		 * return without releasing the acquired lock over credit_data. Why? Because we just freed it.
595
+		 */
596
+		return;
597
+	}
598
+
599
+	lock_release(&credit_data->lock);
600
+}
601
+
602
+static void setup_billing(str *callid, unsigned int h_entry, unsigned int h_id)
603
+{
604
+	call_t *call						= NULL;
605
+	hash_tables_t *hts					= NULL;
606
+
607
+	LM_DBG("Creating dialog for [%.*s], h_id [%u], h_entry [%u]", callid->len, callid->s, h_id, h_entry);
608
+
609
+//	lock_get(&_data.lock);
610
+
611
+	/*
612
+	 * Search call data by call-id
613
+	 */
614
+	if (try_get_call_entry(callid, &call, &hts) != 0)
615
+	{
616
+		LM_ERR("Call [%.*s] not found", callid->len, callid->s);
617
+		return;
618
+	}
619
+
620
+	if (call == NULL)
621
+	{
622
+		LM_ERR("[%.*s] call pointer is null", callid->len, callid->s);
623
+		return;
624
+	}
625
+
626
+	if (hts == NULL)
627
+	{
628
+		LM_ERR("[%.*s] result hashtable pointer is null", callid->len, callid->s);
629
+		return;
630
+	}
631
+
632
+	/*
633
+	 * Update calls statistics
634
+	 */
635
+	lock_get(&_data.lock);
636
+
637
+	_data.stats->active++;
638
+	_data.stats->total++;
639
+
640
+	lock_release(&_data.lock);
641
+
642
+	lock_get(&call->lock);
643
+
644
+	call->dlg_h_entry		= h_entry;
645
+	call->dlg_h_id			= h_id;
646
+
647
+	LM_DBG("Call [%.*s] from client [%.*s], created\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
648
+
649
+	lock_release(&call->lock);
650
+}
651
+
652
+static void start_billing(str *callid, str tags[2])
653
+{
654
+	struct str_hash_entry *cd_entry		= NULL;
655
+	call_t *call						= NULL;
656
+	hash_tables_t *hts					= NULL;
657
+	credit_data_t *credit_data			= NULL;
658
+
659
+	LM_DBG("Billing started for call [%.*s]", callid->len, callid->s);
660
+
661
+//	lock_get(&_data.lock);
662
+
663
+	/*
664
+	 * Search call data by call-id
665
+	 */
666
+	if (try_get_call_entry(callid, &call, &hts) != 0)
667
+	{
668
+		LM_ERR("Call [%.*s] not found", callid->len, callid->s);
669
+		return;
670
+	}
671
+
672
+	if (call == NULL)
673
+	{
674
+		LM_ERR("[%.*s] call pointer is null", callid->len, callid->s);
675
+		return;
676
+	}
677
+
678
+	if (hts == NULL)
679
+	{
680
+		LM_ERR("[%.*s] result hashtable pointer is null", callid->len, callid->s);
681
+		return;
682
+	}
683
+
684
+	lock_get(&hts->lock);
685
+
686
+	/*
687
+	 * Search credit_data by client_id
688
+	 */
689
+	cd_entry			= str_hash_get(hts->credit_data_by_client, call->client_id.s, call->client_id.len);
690
+
691
+	if (cd_entry == NULL)
692
+	{
693
+		LM_ERR("Credit data not found for CID [%.*s], client-ID [%.*s]\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
694
+		lock_release(&hts->lock);
695
+		return;
696
+	}
697
+
698
+	credit_data	= (credit_data_t *) cd_entry->u.p;
699
+
700
+	if (credit_data == NULL)
701
+	{
702
+		LM_ERR("[%.*s]: credit_data pointer is null", callid->len, callid->s);
703
+		lock_release(&hts->lock);
704
+		return;
705
+	}
706
+
707
+	lock_release(&hts->lock);
708
+
709
+	lock(&credit_data->lock);
710
+
711
+	/*
712
+	 * Now that the call is confirmed, we can increase the count of "concurrent_calls".
713
+	 * This will impact in the discount rate performed by the check_calls() function.
714
+	 *
715
+	 */
716
+	credit_data->concurrent_calls++;
717
+
718
+	if (credit_data->max_amount == 0)
719
+		credit_data->max_amount	= call->max_amount; // first time setup
720
+
721
+	if (call->max_amount > credit_data->max_amount)
722
+	{
723
+		LM_ALERT("Maximum-speak-time/credit changed, maybe a credit reload? %f > %f. Client [%.*s]", call->max_amount, credit_data->max_amount,
724
+																							call->client_id.len, call->client_id.s);
725
+
726
+		credit_data->max_amount += call->max_amount - credit_data->max_amount;
727
+	}
728
+
729
+	/*
730
+	 * Update max_amount, discounting what was already consumed by other calls of the same client
731
+	 */
732
+
733
+	call->max_amount = credit_data->max_amount - credit_data->consumed_amount;
734
+
735
+	lock_release(&credit_data->lock);
736
+
737
+	lock_get(&call->lock);
738
+
739
+	/*
740
+	 * Store from-tag value
741
+	 */
742
+	if (shm_str_dup(&call->sip_data.from_tag, &tags[0]) != 0)
743
+	{
744
+		LM_ERR("No more pkg memory\n");
745
+		goto exit;
746
+	}
747
+
748
+	/*
749
+	 * Store to-tag value
750
+	 */
751
+	if (shm_str_dup(&call->sip_data.to_tag, &tags[1]) != 0)
752
+	{
753
+		LM_ERR("No more pkg memory\n");
754
+		goto exit;
755
+	}
756
+
757
+	call->start_timestamp	= get_current_timestamp();
758
+	call->confirmed			= TRUE;
759
+
760
+	LM_DBG("Call [%.*s] from client [%.*s], confirmed\n", callid->len, callid->s, call->client_id.len, call->client_id.s);
761
+
762
+exit:
763
+	lock_release(&call->lock);
764
+}
765
+
766
+
767
+void terminate_all_calls(credit_data_t *credit_data)
768
+{
769
+	call_t 	*call 	= NULL,
770
+			*tmp 	= NULL;
771
+
772
+	clist_foreach_safe(credit_data->call_list, call, tmp, next)
773
+	{
774
+		LM_DBG("Killing call with CID [%.*s]\n", call->sip_data.callid.len, call->sip_data.callid.s);
775
+
776
+		/*
777
+		 * Update number of calls forced to end
778
+		 */
779
+		_data.stats->dropped++;
780
+
781
+		terminate_call(call);
782
+	}
783
+}
784
+
785
+/*
786
+ * WARNING: When calling this function, the proper lock should have been acquired
787
+ */
788
+static void free_call(call_t *call)
789
+{
790
+	struct str_hash_entry *e	= NULL;
791
+
792
+	LM_DBG("Freeing call [%.*s]\n", call->sip_data.callid.len, call->sip_data.callid.s);
793
+
794
+	e			= str_hash_get(_data.money.call_data_by_cid, call->sip_data.callid.s, call->sip_data.callid.len);
795
+
796
+	if (e == NULL)
797
+	{
798
+		e			= str_hash_get(_data.time.call_data_by_cid, call->sip_data.callid.s, call->sip_data.callid.len);
799
+
800
+		if (e == NULL)
801
+		{
802
+			LM_ERR("Call [%.*s] not found. Couldn't be able to free it from hashtable", call->sip_data.callid.len, call->sip_data.callid.s);
803
+			return;
804
+		}
805
+	}
806
+
807
+	str_hash_del(e);
808
+
809
+	shm_free(e->key.s);
810
+	shm_free(e);
811
+
812
+	str_shm_free_if_not_null(call->sip_data.callid);
813
+	str_shm_free_if_not_null(call->sip_data.to_tag);
814
+	str_shm_free_if_not_null(call->sip_data.from_tag);
815
+
816
+	shm_free(call);
817
+}
818
+
819
+/*
820
+ * WARNING: When calling this function, the proper lock should have been acquired
821
+ */