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