1 /*
2  * Copyright (C) 2012 Smile Communications, jason.penton@smilecoms.com
3  * Copyright (C) 2012 Smile Communications, richard.good@smilecoms.com
4  *
5  * The initial version of this code was written by Dragos Vingarzan
6  * (dragos(dot)vingarzan(at)fokus(dot)fraunhofer(dot)de and the
7  * Fruanhofer Institute. It was and still is maintained in a separate
8  * branch of the original SER. We are therefore migrating it to
9  * Kamailio/SR and look forward to maintaining it from here on out.
10  * 2011/2012 Smile Communications, Pty. Ltd.
11  * ported/maintained/improved by
12  * Jason Penton (jason(dot)penton(at)smilecoms.com and
13  * Richard Good (richard(dot)good(at)smilecoms.com) as part of an
14  * effort to add full IMS support to Kamailio/SR using a new and
15  * improved architecture
16  *
17  * NB: Alot of this code was originally part of OpenIMSCore,
18  * FhG Fokus.
19  * Copyright (C) 2004-2006 FhG Fokus
20  * Thanks for great work! This is an effort to
21  * break apart the various CSCF functions into logically separate
22  * components. We hope this will drive wider use. We also feel
23  * that in this way the architecture is more complete and thereby easier
24  * to manage in the Kamailio/SR environment
25  *
26  * This file is part of Kamailio, a free SIP server.
27  *
28  * Kamailio is free software; you can redistribute it and/or modify
29  * it under the terms of the GNU General Public License as published by
30  * the Free Software Foundation; either version 2 of the License, or
31  * (at your option) any later version
32  *
33  * Kamailio is distributed in the hope that it will be useful,
34  * but WITHOUT ANY WARRANTY; without even the implied warranty of
35  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
36  * GNU General Public License for more details.
37  *
38  * You should have received a copy of the GNU General Public License
39  * along with this program; if not, write to the Free Software
40  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
41  *
42  */
43 
44 #include <stdio.h>
45 #include "ims_usrloc_pcscf_mod.h"
46 #include "../../core/sr_module.h"
47 #include "../../core/dprint.h"
48 #include "../../core/rpc_lookup.h"
49 #include "../../core/timer.h"     /* register_timer */
50 #include "../../core/globals.h"   /* is_main */
51 #include "../../core/ut.h"        /* str_init */
52 #include "dlist.h"           /* register_udomain */
53 #include "udomain.h"         /* {insert,delete,get,release}_urecord */
54 #include "pcontact.h"         /* {insert,delete,get}_ucontact */
55 #include "ul_rpc.h"
56 #include "ul_callback.h"
57 #include "usrloc.h"
58 #include "usrloc_db.h"
59 
60 MODULE_VERSION
61 
62 #define DEFAULT_DBG_FILE "/var/log/usrloc_debug"
63 static FILE *debug_file;
64 
65 static int mod_init(void);                          /*!< Module initialization function */
66 static void destroy(void);                          /*!< Module destroy function */
67 static void timer(unsigned int ticks, void* param); /*!< Timer handler */
68 static int child_init(int rank);                    /*!< Per-child init function */
69 
70 extern int bind_usrloc(usrloc_api_t* api);
71 extern int ul_locks_no;
72 
73 
74 int expires_grace = 3600;   //default is a grace period of 1 hour - after this contact is removed from P
75 
76 /*
77  * Module parameters and their default values
78  */
79 str usrloc_debug_file = str_init(DEFAULT_DBG_FILE);
80 int usrloc_debug 	= 0;
81 int ul_hash_size = 9;
82 int init_flag = 0;
83 str db_url          = str_init(DEFAULT_DB_URL);	/*!< Database URL */
84 int timer_interval  = 60;						/*!< Timer interval in seconds */
85 int db_mode         = 0;						/*!< Database sync scheme: 0-no db, 1-write through, 2-write back, 3-only db */
86 int ul_fetch_rows 	= 2000;
87 int match_contact_host_port = 1;					/*!< Should we match contact just based on rui host and port*/
88 
89 db1_con_t* ul_dbh = 0;
90 db_func_t ul_dbf;
91 
92 /*! \brief
93  * Exported functions
94  */
95 static cmd_export_t cmds[] = {
96 	{"ul_bind_ims_usrloc_pcscf",        (cmd_function)bind_usrloc,        1, 0, 0, 0},
97 	{0, 0, 0, 0, 0, 0}
98 };
99 
100 /*! \brief
101  * Exported parameters
102  */
103 static param_export_t params[] = {
104 	{"hash_size",         INT_PARAM, &ul_hash_size    },
105 	{"timer_interval",    INT_PARAM, &timer_interval  },
106 	{"usrloc_debug_file", PARAM_STR, &usrloc_debug_file },
107 	{"enable_debug_file", INT_PARAM, &usrloc_debug},
108 
109 	{"db_url",              PARAM_STR, &db_url        },
110 	{"timer_interval",      INT_PARAM, &timer_interval  },
111 	{"db_mode",             INT_PARAM, &db_mode         },
112 
113 	{"match_contact_host_port",		INT_PARAM, &match_contact_host_port	},
114         {"expires_grace",		INT_PARAM, &expires_grace	},
115 
116 	{0, 0, 0}
117 };
118 
119 stat_export_t mod_stats[] = {
120 	{"registered_contacts" ,  STAT_IS_FUNC, (stat_var**)get_number_of_contacts  },
121 	{"registered_impus" ,  STAT_IS_FUNC, (stat_var**)get_number_of_impu  },
122 	{"expired_contacts" ,  STAT_IS_FUNC, (stat_var**)get_number_of_expired  },
123 	{0,0,0}
124 };
125 
126 struct module_exports exports = {
127 	"ims_usrloc_pcscf",
128 	DEFAULT_DLFLAGS, /* dlopen flags */
129 	cmds,		/* exported functions */
130 	params,		/* export parameters */
131 	0,		/* exported RPC functions */
132 	0,		/* exported pseudo-variables */
133 	0,		/* response·function */
134 	mod_init,	/* module initialization function */
135 	child_init,	/* per-child·init·function*/
136 	destroy		/* destroy function */
137 };
138 
139 
140 /*! \brief
141  * Module initialization function
142  */
mod_init(void)143 static int mod_init(void) {
144 
145 	if (usrloc_debug){
146 		LM_INFO("Logging usrloc records to %.*s\n", usrloc_debug_file.len, usrloc_debug_file.s);
147 		debug_file = fopen(usrloc_debug_file.s, "a");
148 		fprintf(debug_file, "starting\n");
149 		fflush(debug_file);
150 	}
151 
152 #ifdef STATISTICS
153 	/* register statistics */
154 	if (register_module_stats( exports.name, mod_stats)!=0 ) {
155 		LM_ERR("failed to register core statistics\n");
156 		return -1;
157 	}
158 #endif
159 
160 	if (ul_hash_size <= 1)
161 		ul_hash_size = 512;
162 	else
163 		ul_hash_size = 1 << ul_hash_size;
164 	ul_locks_no = ul_hash_size;
165 
166 	if (ul_init_locks() != 0) {
167 		LM_ERR("locks array initialization failed\n");
168 		return -1;
169 	}
170 
171 	/* Regsiter RPC */
172 	if (rpc_register_array(ul_rpc) != 0) {
173 		LM_ERR("failed to register RPC commands\n");
174 		return -1;
175 	}
176 
177 	/* Register cache timer */
178 	LM_DBG("Registering cache timer");
179 	register_timer(timer, 0, timer_interval);
180 
181 	/* init the callbacks list */
182 	if (init_ulcb_list() < 0) {
183 		LM_ERR("usrloc/callbacks initialization failed\n");
184 		return -1;
185 	}
186 
187 	/* Shall we use database ? */
188 	if (db_mode != NO_DB) { /* Yes */
189 		if(ul_fetch_rows<=0) {
190 			LM_ERR("invalid fetch_rows number '%d'\n", ul_fetch_rows);
191 			return -1;
192 		}
193 
194 		if (init_db(&db_url, timer_interval, ul_fetch_rows) != 0) {
195 			LM_ERR("Error initializing db connection\n");
196 			return -1;
197 		}
198 		LM_DBG("Running in DB mode %i\n", db_mode);
199 	}
200 
201 	init_flag = 1;
202 
203 	return 0;
204 }
205 
child_init(int _rank)206 static int child_init(int _rank)
207 {
208 	dlist_t* ptr;
209 
210 	/* connecting to DB ? */
211 	switch (db_mode) {
212 		case NO_DB:
213 			return 0;
214 		case WRITE_THROUGH:
215 			/* connect to db only from SIP workers, TIMER and MAIN processes */
216 			if (_rank<=0 && _rank!=PROC_TIMER && _rank!=PROC_MAIN)
217 				return 0;
218 			break;
219 		case WRITE_BACK:
220 			/* connect to db only from TIMER (for flush), from MAIN (for
221 			 * final flush() and from child 1 for preload */
222 			if (_rank!=PROC_TIMER && _rank!=PROC_MAIN && _rank!=PROC_SIPINIT)
223 				return 0;
224 			break;
225 	}
226 
227 	LM_DBG("Connecting to usrloc_pcscf DB for rank %d\n", _rank);
228 	if (connect_db(&db_url) != 0) {
229 		LM_ERR("child(%d): failed to connect to database\n", _rank);
230 		return -1;
231 	}
232 	/* _rank==PROC_SIPINIT is used even when fork is disabled */
233 	if (_rank==PROC_SIPINIT && db_mode!=DB_ONLY) {
234 		// if cache is used, populate domains from DB
235 		for( ptr=root ; ptr ; ptr=ptr->next) {
236 			LM_DBG("Preloading domain %.*s\n", ptr->name.len, ptr->name.s);
237 			if (preload_udomain(ul_dbh, ptr->d) < 0) {
238 				LM_ERR("child(%d): failed to preload domain '%.*s'\n",
239 						_rank, ptr->name.len, ZSW(ptr->name.s));
240 				return -1;
241 			}
242 		}
243 	}
244 
245 	return 0;
246 }
247 
248 /*! \brief
249  * Module destroy function
250  */
destroy(void)251 static void destroy(void)
252 {
253 	free_all_udomains();
254 	ul_destroy_locks();
255 
256 	/* free callbacks list */
257 	destroy_ulcb_list();
258 
259 	free_service_route_buf();
260 	free_impu_buf();
261 
262 	if (db_mode)
263 		destroy_db();
264 }
265 
266 
267 /*! \brief
268  * Timer handler
269  */
timer(unsigned int ticks,void * param)270 static void timer(unsigned int ticks, void* param) {
271 	LM_DBG("Syncing cache\n");
272 	if (usrloc_debug) {
273 		print_all_udomains(debug_file);
274 		fflush(debug_file);
275 	}
276 
277 	if (synchronize_all_udomains() != 0) {
278 		LM_ERR("synchronizing cache failed\n");
279 	}
280 }
281 
282