all: updated FSF address in GPL text
[sip-router] / modules / ims_qos / cdpeventprocessor.c
1 /*
2  * $Id$
3  *
4  * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
5  * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
6  * 
7  * The initial version of this code was written by Dragos Vingarzan
8  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
9  * Fruanhofer Institute. It was and still is maintained in a separate
10  * branch of the original SER. We are therefore migrating it to
11  * Kamailio/SR and look forward to maintaining it from here on out.
12  * 2011/2012 Smile Communications, Pty. Ltd.
13  * ported/maintained/improved by 
14  * Jason Penton (jason(dot)penton(at)smilecoms.com and
15  * Richard Good (richard(dot)good(at)smilecoms.com) as part of an 
16  * effort to add full IMS support to Kamailio/SR using a new and
17  * improved architecture
18  * 
19  * NB: Alot of this code was originally part of OpenIMSCore,
20  * FhG Fokus. 
21  * Copyright (C) 2004-2006 FhG Fokus
22  * Thanks for great work! This is an effort to 
23  * break apart the various CSCF functions into logically separate
24  * components. We hope this will drive wider use. We also feel
25  * that in this way the architecture is more complete and thereby easier
26  * to manage in the Kamailio/SR environment
27  *
28  * This file is part of Kamailio, a free SIP server.
29  *
30  * Kamailio is free software; you can redistribute it and/or modify
31  * it under the terms of the GNU General Public License as published by
32  * the Free Software Foundation; either version 2 of the License, or
33  * (at your option) any later version
34  *
35  * Kamailio is distributed in the hope that it will be useful,
36  * but WITHOUT ANY WARRANTY; without even the implied warranty of
37  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
38  * GNU General Public License for more details.
39  *
40  * You should have received a copy of the GNU General Public License 
41  * along with this program; if not, write to the Free Software 
42  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
43  * 
44  */
45
46 #include <time.h>
47 #include "sem.h"
48 #include "../ims_usrloc_pcscf/usrloc.h"
49 #include "../dialog_ng/dlg_load.h"
50 #include "../cdp/session.h"
51 #include "mod.h"
52 #include "cdpeventprocessor.h"
53 #include "rx_str.h"
54
55 cdp_cb_event_list_t *cdp_event_list = 0;
56 extern usrloc_api_t ul;
57 extern struct dlg_binds dlgb;
58 extern int cdp_event_latency;
59 extern int cdp_event_threshold;
60 extern int cdp_event_latency_loglevel;
61
62 int init_cdp_cb_event_list() {
63     cdp_event_list = shm_malloc(sizeof (cdp_cb_event_list_t));
64     if (!cdp_event_list) {
65         LM_ERR("No more SHM mem\n");
66         return 0;
67     }
68     memset(cdp_event_list, 0, sizeof (cdp_cb_event_list_t));
69     cdp_event_list->lock = lock_alloc();
70     if (!cdp_event_list->lock) {
71         LM_ERR("failed to create cdp event list lock\n");
72         return 0;
73     }
74     cdp_event_list->lock = lock_init(cdp_event_list->lock);
75
76     sem_new(cdp_event_list->empty, 0); //pre-locked - as we assume list is empty at start
77
78     return 1;
79 }
80
81 void destroy_cdp_cb_event_list() {
82     cdp_cb_event_t *ev, *tmp;
83
84     lock_get(cdp_event_list->lock);
85     ev = cdp_event_list->head;
86     while (ev) {
87         tmp = ev->next;
88         free_cdp_cb_event(ev);
89         ev = tmp;
90     }
91     lock_destroy(cdp_event_list->lock);
92     lock_dealloc(cdp_event_list->lock);
93     shm_free(cdp_event_list);
94 }
95
96 cdp_cb_event_t* new_cdp_cb_event(int event, str *rx_session_id, rx_authsessiondata_t *session_data) {
97     cdp_cb_event_t *new_event = shm_malloc(sizeof (cdp_cb_event_t));
98     if (!new_event) {
99         LM_ERR("no more shm mem\n");
100         return NULL;
101     }
102     memset(new_event, 0, sizeof (cdp_cb_event_t));
103
104     //we have to make a copy of the rx session id because it is not in shm mem
105     if ((rx_session_id->len > 0) && rx_session_id->s) {
106         LM_DBG("Creating new event for rx session [%.*s]\n", rx_session_id->len, rx_session_id->s);
107         new_event->rx_session_id.s = (char*) shm_malloc(rx_session_id->len);
108         if (!new_event->rx_session_id.s) {
109             LM_ERR("no more shm memory\n");
110             shm_free(new_event);
111             return NULL;
112         }
113         memcpy(new_event->rx_session_id.s, rx_session_id->s, rx_session_id->len);
114         new_event->rx_session_id.len = rx_session_id->len;
115     }
116
117     new_event->event = event;
118     new_event->registered = time(NULL);
119     new_event->session_data = session_data; //session_data is already in shm mem
120
121     return new_event;
122 }
123 //add to tail
124
125 void push_cdp_cb_event(cdp_cb_event_t* event) {
126     lock_get(cdp_event_list->lock);
127     if (cdp_event_list->head == 0) { //empty list
128         cdp_event_list->head = cdp_event_list->tail = event;
129     } else {
130         cdp_event_list->tail->next = event;
131         cdp_event_list->tail = event;
132     }
133     sem_release(cdp_event_list->empty);
134     lock_release(cdp_event_list->lock);
135 }
136
137 //pop from head
138
139 cdp_cb_event_t* pop_cdp_cb_event() {
140     cdp_cb_event_t *ev;
141
142     lock_get(cdp_event_list->lock);
143     while (cdp_event_list->head == 0) {
144         lock_release(cdp_event_list->lock);
145         sem_get(cdp_event_list->empty);
146         lock_get(cdp_event_list->lock);
147     }
148
149     ev = cdp_event_list->head;
150     cdp_event_list->head = ev->next;
151
152     if (ev == cdp_event_list->tail) { //list now empty
153         cdp_event_list->tail = 0;
154     }
155     ev->next = 0; //make sure whoever gets this cant access our list
156     lock_release(cdp_event_list->lock);
157
158     return ev;
159 }
160
161 /*main event process function*/
162 void cdp_cb_event_process() {
163     cdp_cb_event_t *ev;
164     udomain_t* domain;
165     pcontact_t* pcontact;
166     str release_reason = {"QoS released", 12}; /* TODO: This could be a module parameter */
167     struct pcontact_info ci;
168     memset(&ci, 0, sizeof (struct pcontact_info));
169
170     for (;;) {
171         ev = pop_cdp_cb_event();
172
173         if (cdp_event_latency) { //track delays
174             unsigned int diff = time(NULL) - ev->registered;
175             if (diff > cdp_event_threshold) {
176                 switch (cdp_event_latency_loglevel) {
177                     case 0:
178                         LM_ERR("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
179                         break;
180                     case 1:
181                         LM_WARN("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
182                         break;
183                     case 2:
184                         LM_INFO("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
185                         break;
186                     case 3:
187                         LM_DBG("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
188                         break;
189                     default:
190                         LM_DBG("Unknown log level....printing as debug\n");
191                         LM_DBG("Took to long to pickup CDP callback event [%d] > [%d]\n", diff, cdp_event_threshold);
192                         break;
193                 }
194             }
195         }
196         LM_DBG("processing event [%d]\n", ev->event);
197         rx_authsessiondata_t *p_session_data = ev->session_data;
198         str *rx_session_id = &ev->rx_session_id;
199
200         switch (ev->event) {
201             case AUTH_EV_SESSION_TIMEOUT:
202             case AUTH_EV_SESSION_GRACE_TIMEOUT:
203             case AUTH_EV_RECV_ASR:
204                 LM_DBG("Received notification of ASR from transport plane or CDP timeout for CDP session with Rx session ID: [%.*s] and associated contact [%.*s]"
205                         " and domain [%.*s]\n",
206                         rx_session_id->len, rx_session_id->s,
207                         p_session_data->registration_aor.len, p_session_data->registration_aor.s,
208                         p_session_data->domain.len, p_session_data->domain.s);
209
210
211                 if (p_session_data->subscribed_to_signaling_path_status) {
212                     LM_DBG("This is a subscription to signalling bearer session");
213                     //nothing to do here - just wait for AUTH_EV_SERVICE_TERMINATED event
214                 } else {
215                     LM_DBG("This is a media bearer session session");
216                     //this is a media bearer session that was terminated from the transport plane - we need to terminate the associated dialog
217                     //so we set p_session_data->must_terminate_dialog to 1 and when we receive AUTH_EV_SERVICE_TERMINATED event we will terminate the dialog
218                     p_session_data->must_terminate_dialog = 1;
219                 }
220                 break;
221
222             case AUTH_EV_SERVICE_TERMINATED:
223                 LM_DBG("Received notification of CDP TERMINATE of CDP session with Rx session ID: [%.*s] and associated contact [%.*s]"
224                         " and domain [%.*s]\n",
225                         rx_session_id->len, rx_session_id->s,
226                         p_session_data->registration_aor.len, p_session_data->registration_aor.s,
227                         p_session_data->domain.len, p_session_data->domain.s);
228
229                 if (p_session_data->subscribed_to_signaling_path_status) {
230                     LM_DBG("This is a subscription to signalling bearer session");
231                     //instead of removing the contact from usrloc_pcscf we just change the state of the contact to TERMINATE_PENDING_NOTIFY
232                     //pcscf_registrar sees this, sends a SIP PUBLISH and on SIP NOTIFY the contact is deleted
233
234                     if (ul.register_udomain(p_session_data->domain.s, &domain)
235                             < 0) {
236                         LM_DBG("Unable to register usrloc domain....aborting\n");
237                         return;
238                     }
239                     ul.lock_udomain(domain, &p_session_data->registration_aor);
240                     if (ul.get_pcontact(domain, &p_session_data->registration_aor,
241                             &pcontact) != 0) {
242                         LM_DBG("no contact found for terminated Rx reg session..... ignoring\n");
243                     } else {
244                         LM_DBG("Updating contact [%.*s] after Rx reg session terminated, setting state to PCONTACT_DEREG_PENDING_PUBLISH\n", pcontact->aor.len, pcontact->aor.s);
245                         ci.reg_state = PCONTACT_DEREG_PENDING_PUBLISH;
246                         ci.num_service_routes = 0;
247                         ul.update_pcontact(domain, &ci, pcontact);
248                     }
249                     ul.unlock_udomain(domain, &p_session_data->registration_aor);
250                 } else {
251                     LM_DBG("This is a media bearer session session");
252                     
253                     //we only terminate the dialog if this was triggered from the transport plane or timeout - i.e. if must_terminate_dialog is set
254                     //if this was triggered from the signalling plane (i.e. someone hanging up) then we don'y need to terminate the dialog
255                     if (p_session_data->must_terminate_dialog) {
256                         LM_DBG("Terminating dialog with callid, ftag, ttag: [%.*s], [%.*s], [%.*s]\n",
257                                 p_session_data->callid.len, p_session_data->callid.s,
258                                 p_session_data->ftag.len, p_session_data->ftag.s,
259                                 p_session_data->ttag.len, p_session_data->ttag.s);
260                         dlgb.terminate_dlg(&p_session_data->callid,
261                                 &p_session_data->ftag, &p_session_data->ttag, NULL,
262                                 &release_reason);
263                     }
264                 }
265
266                 //free callback data
267                 if (p_session_data) {
268                     shm_free(p_session_data);
269                     p_session_data = 0;
270                 }
271                 break;
272             default:
273                 break;
274         }
275
276         free_cdp_cb_event(ev);
277     }
278 }
279
280 void free_cdp_cb_event(cdp_cb_event_t *ev) {
281     if (ev) {
282         LM_DBG("Freeing cdpb CB event structure\n");
283         if (ev->rx_session_id.len > 0 && ev->rx_session_id.s) {
284             LM_DBG("about to free string from cdp CB event [%.*s]\n", ev->rx_session_id.len, ev->rx_session_id.s);
285             shm_free(ev->rx_session_id.s);
286         }
287         shm_free(ev);
288     }
289 }
290
291