1 /*
2  * FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
3  * Copyright (C) 2005-2014, Anthony Minessale II <anthm@freeswitch.org>
4  *
5  * Version: MPL 1.1
6  *
7  * The contents of this file are subject to the Mozilla Public License Version
8  * 1.1 (the "License"); you may not use this file except in compliance with
9  * the License. You may obtain a copy of the License at
10  * http://www.mozilla.org/MPL/
11  *
12  * Software distributed under the License is distributed on an "AS IS" basis,
13  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14  * for the specific language governing rights and limitations under the
15  * License.
16  *
17  * The Original Code is FreeSWITCH Modular Media Switching Software Library / Soft-Switch Application
18  *
19  * The Initial Developer of the Original Code is
20  * Anthony Minessale II <anthm@freeswitch.org>
21  * Portions created by the Initial Developer are Copyright (C)
22  * the Initial Developer. All Rights Reserved.
23  *
24  * Contributor(s):
25  * Daniel Swarbrick <daniel.swarbrick@gmail.com>
26  *
27  * mod_cdr_mongodb.c -- MongoDB CDR Module
28  *
29  * Derived from:
30  * mod_xml_cdr.c -- XML CDR Module to files or curl
31  *
32  */
33 #include <switch.h>
34 #include <mongo.h>
35 
36 #define MONGO_REPLSET_MAX_MEMBERS 12
37 
38 static struct {
39 	switch_memory_pool_t *pool;
40 	int shutdown;
41 	char *mongo_host;
42 	int mongo_port;
43 	char *mongo_namespace;
44 	char *mongo_replset_name;
45 	char *mongo_username;
46 	char *mongo_password;
47 	mongo mongo_conn[1];
48 	switch_mutex_t *mongo_mutex;
49 	switch_bool_t log_b;
50 } globals;
51 
52 static switch_xml_config_item_t config_settings[] = {
53 	/* key, flags, ptr, default_value, syntax, helptext */
54 	SWITCH_CONFIG_ITEM_STRING_STRDUP("host", CONFIG_REQUIRED, &globals.mongo_host, "127.0.0.1", NULL, "MongoDB server host address"),
55 	SWITCH_CONFIG_ITEM_STRING_STRDUP("namespace", CONFIG_REQUIRED, &globals.mongo_namespace, NULL, "database.collection", "MongoDB namespace"),
56 	SWITCH_CONFIG_ITEM_STRING_STRDUP("replica_set_name", CONFIG_RELOADABLE, &globals.mongo_replset_name, "cdr_mongodb", NULL, "MongoDB replica set name"),
57 	SWITCH_CONFIG_ITEM_STRING_STRDUP("username", CONFIG_RELOADABLE, &globals.mongo_username, NULL, NULL, "MongoDB username"),
58 	SWITCH_CONFIG_ITEM_STRING_STRDUP("password", CONFIG_RELOADABLE, &globals.mongo_password, NULL, NULL, "MongoDB password"),
59 
60 	/* key, type, flags, ptr, default_value, data, syntax, helptext */
61 	SWITCH_CONFIG_ITEM("port", SWITCH_CONFIG_INT, CONFIG_REQUIRED, &globals.mongo_port, MONGO_DEFAULT_PORT, NULL, NULL, "MongoDB server TCP port"),
62 	SWITCH_CONFIG_ITEM("log-b-leg", SWITCH_CONFIG_BOOL, CONFIG_RELOADABLE, &globals.log_b, SWITCH_TRUE, NULL, NULL, "Log B-leg in addition to A-leg"),
63 
64 	SWITCH_CONFIG_ITEM_END()
65 };
66 
67 
68 SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load);
69 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown);
70 SWITCH_MODULE_DEFINITION(mod_cdr_mongodb, mod_cdr_mongodb_load, mod_cdr_mongodb_shutdown, NULL);
71 
bson_append_value(bson * cdr,char * name,char * val)72 static void bson_append_value(bson *cdr, char *name, char *val)
73 {
74 		//Check the variable and insert it as int, long int or string depending on it's value
75         char* endptr;
76         long int lintValue = strtol(val, &endptr, 10);
77 
78         if (!*endptr){
79                 int intValue = lintValue;
80                 if(intValue == lintValue){
81                         bson_append_int(cdr, name, intValue);
82                 }else{
83                         bson_append_long(cdr, name, lintValue);
84                 }
85         } else {
86                 bson_append_string(cdr, name, val);
87         }
88 
89 }
90 
91 
set_bson_profile_data(bson * b,switch_caller_profile_t * caller_profile)92 static void set_bson_profile_data(bson *b, switch_caller_profile_t *caller_profile)
93 {
94 	bson_append_string(b, "username", caller_profile->username);
95 	bson_append_string(b, "dialplan", caller_profile->dialplan);
96 	bson_append_string(b, "caller_id_name", caller_profile->caller_id_name);
97 	bson_append_string(b, "ani", caller_profile->ani);
98 	bson_append_string(b, "aniii", caller_profile->aniii);
99 	bson_append_string(b, "caller_id_number", caller_profile->caller_id_number);
100 	bson_append_string(b, "network_addr", caller_profile->network_addr);
101 	bson_append_string(b, "rdnis", caller_profile->rdnis);
102 	bson_append_string(b, "destination_number", caller_profile->destination_number);
103 	bson_append_string(b, "uuid", caller_profile->uuid);
104 	bson_append_string(b, "source", caller_profile->source);
105 	bson_append_string(b, "context", caller_profile->context);
106 	bson_append_string(b, "chan_name", caller_profile->chan_name);
107 }
108 
109 
cdr_mongo_authenticate()110 static switch_status_t cdr_mongo_authenticate() {
111 	switch_status_t status = SWITCH_STATUS_SUCCESS;
112 	mongo_error_t db_status;
113 	char *ns_tmp, *ns_split[2];
114 
115 	/* Split namespace db.collection into separate vars */
116 	switch_strdup(ns_tmp, globals.mongo_namespace);
117 	switch_separate_string(ns_tmp, '.', ns_split, 2);
118 
119 	db_status = mongo_cmd_authenticate(globals.mongo_conn, ns_split[0], globals.mongo_username, globals.mongo_password);
120 
121 	if (db_status != MONGO_OK) {
122 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_cmd_authenticate: authentication failed\n");
123 		status = SWITCH_STATUS_FALSE;
124 	} else {
125 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Successfully authenticated %s@%s\n", globals.mongo_username, ns_split[0]);
126 	}
127 
128 	switch_safe_free(ns_tmp);
129 	return status;
130 }
131 
132 
my_on_reporting(switch_core_session_t * session)133 static switch_status_t my_on_reporting(switch_core_session_t *session)
134 {
135 	switch_status_t status = SWITCH_STATUS_SUCCESS;
136 	switch_channel_t *channel = switch_core_session_get_channel(session);
137 	switch_event_header_t *hi;
138 	switch_caller_profile_t *caller_profile;
139 	switch_hold_record_t *hold_record;
140 	switch_app_log_t *app_log;
141 	bson cdr;
142 	int is_b;
143 	int bson_idx, callflow_idx;
144 	char idx_buffer[12];
145 	char *tmp;
146 
147 	if (globals.shutdown) {
148 		return SWITCH_STATUS_SUCCESS;
149 	}
150 
151 	is_b = channel && switch_channel_get_originator_caller_profile(channel);
152 	if (!globals.log_b && is_b) {
153 		const char *force_cdr = switch_channel_get_variable(channel, SWITCH_FORCE_PROCESS_CDR_VARIABLE);
154 		if (!switch_true(force_cdr)) {
155 			return SWITCH_STATUS_SUCCESS;
156 		}
157 	}
158 
159 	bson_init(&cdr);
160 
161 	/* Channel data */
162 	bson_append_start_object(&cdr, "channel_data");
163 	bson_append_string(&cdr, "state", switch_channel_state_name(switch_channel_get_state(channel)));
164 	bson_append_string(&cdr, "direction", switch_channel_direction(channel) == SWITCH_CALL_DIRECTION_OUTBOUND ? "outbound" : "inbound");
165 	bson_append_int(&cdr, "state_number", switch_channel_get_state(channel));
166 
167 	if ((tmp = switch_channel_get_flag_string(channel))) {
168 		bson_append_string(&cdr, "flags", tmp);
169 		free(tmp);
170 	}
171 
172 	if ((tmp = switch_channel_get_cap_string(channel))) {
173 		bson_append_string(&cdr, "caps", tmp);
174 		free(tmp);
175 	}
176 	bson_append_finish_object(&cdr);				/* channel_data */
177 
178 
179 	/* Channel variables */
180 	bson_append_start_object(&cdr, "variables");
181 
182 	if ((hi = switch_channel_variable_first(channel))) {
183 		for (; hi; hi = hi->next) {
184 			if (!zstr(hi->name) && !zstr(hi->value)) {
185 				bson_append_value(&cdr, hi->name, hi->value);
186 			}
187 		}
188 		switch_channel_variable_last(channel);
189 	}
190 
191 	bson_append_finish_object(&cdr);				/* variables */
192 
193 
194 	/* App log */
195 	if ((app_log = switch_core_session_get_app_log(session))) {
196 		switch_app_log_t *ap;
197 
198 		bson_append_start_array(&cdr, "app_log");
199 
200 		for (ap = app_log, bson_idx = 0; ap; ap = ap->next, bson_idx++) {
201 			switch_snprintf(idx_buffer, sizeof(idx_buffer), "%d", bson_idx);
202 			bson_append_start_object(&cdr, idx_buffer);
203 			bson_append_string(&cdr, "app_name", ap->app);
204 			bson_append_string(&cdr, "app_data", switch_str_nil(ap->arg));
205 			bson_append_long(&cdr, "app_stamp", ap->stamp);
206 			bson_append_finish_object(&cdr);		/* application */
207 		}
208 
209 		bson_append_finish_array(&cdr);				/* app_log */
210 	}
211 
212 
213 	/* Hold */
214 	if ((hold_record = switch_channel_get_hold_record(channel))) {
215 		switch_hold_record_t *hr;
216 
217 		bson_append_start_array(&cdr, "hold_record");
218 
219 		for (hr = hold_record, bson_idx = 0; hr; hr = hr->next, bson_idx++) {
220 			switch_snprintf(idx_buffer, sizeof(idx_buffer), "%d", bson_idx);
221 			bson_append_start_object(&cdr, idx_buffer);
222 			bson_append_long(&cdr, "on", hr->on);
223 			bson_append_long(&cdr, "off", hr->off);
224 			if (hr->uuid) {
225 				bson_append_string(&cdr, "bridged_to", hr->uuid);
226 			}
227 			bson_append_finish_object(&cdr);
228 		}
229 
230 		bson_append_finish_array(&cdr);				/* hold_record */
231 	}
232 
233 
234 	/* Callflow */
235 	caller_profile = switch_channel_get_caller_profile(channel);
236 
237 	/* Start callflow array */
238 	bson_append_start_array(&cdr, "callflow");
239 	callflow_idx = 0;
240 
241 	while (caller_profile) {
242 		snprintf(idx_buffer, sizeof(idx_buffer), "%d", callflow_idx);
243 		bson_append_start_object(&cdr, idx_buffer);
244 
245 		if (!zstr(caller_profile->dialplan)) {
246 			bson_append_string(&cdr, "dialplan", caller_profile->dialplan);
247 		}
248 
249 		if (!zstr(caller_profile->profile_index)) {
250 			bson_append_string(&cdr, "profile_index", caller_profile->profile_index);
251 		}
252 
253 		if (caller_profile->caller_extension) {
254 			switch_caller_application_t *ap;
255 
256 			bson_append_start_object(&cdr, "extension");
257 
258 			bson_append_string(&cdr, "name", switch_str_nil(caller_profile->caller_extension->extension_name));
259 			bson_append_string(&cdr, "number", switch_str_nil(caller_profile->caller_extension->extension_number));
260 
261 			if (caller_profile->caller_extension->current_application) {
262 				bson_append_string(&cdr, "current_app", caller_profile->caller_extension->current_application->application_name);
263 			}
264 
265 			for (ap = caller_profile->caller_extension->applications; ap; ap = ap->next) {
266 				bson_append_start_object(&cdr, "application");
267 				if (ap == caller_profile->caller_extension->current_application) {
268 					bson_append_bool(&cdr, "last_executed", 1);
269 				}
270 				bson_append_string(&cdr, "app_name", ap->application_name);
271 				bson_append_string(&cdr, "app_data", switch_str_nil(ap->application_data));
272 				bson_append_finish_object(&cdr);
273 			}
274 
275 			if (caller_profile->caller_extension->children) {
276 				switch_caller_profile_t *cp = NULL;
277 
278 				for (cp = caller_profile->caller_extension->children; cp; cp = cp->next) {
279 
280 					if (!cp->caller_extension) {
281 						continue;
282 					}
283 
284 					bson_append_start_object(&cdr, "sub_extensions");
285 					bson_append_start_object(&cdr, "extension");
286 
287 					bson_append_string(&cdr, "name", cp->caller_extension->extension_name);
288 					bson_append_string(&cdr, "number", cp->caller_extension->extension_number);
289 					bson_append_string(&cdr, "dialplan", cp->dialplan);
290 					if (cp->caller_extension->current_application) {
291 						bson_append_string(&cdr, "current_app", cp->caller_extension->current_application->application_name);
292 					}
293 
294 					for (ap = cp->caller_extension->applications; ap; ap = ap->next) {
295 						bson_append_start_object(&cdr, "application");
296 						if (ap == cp->caller_extension->current_application) {
297 							bson_append_bool(&cdr, "last_executed", 1);
298 						}
299 						bson_append_string(&cdr, "app_name", ap->application_name);
300 						bson_append_string(&cdr, "app_data", switch_str_nil(ap->application_data));
301 						bson_append_finish_object(&cdr);
302 					}
303 
304 					bson_append_finish_object(&cdr);	/* extension */
305 					bson_append_finish_object(&cdr);	/* sub_extensions */
306 				}
307 			}
308 
309 			bson_append_finish_object(&cdr);			/* extension */
310 		}
311 
312 		bson_append_start_object(&cdr, "caller_profile");
313 		set_bson_profile_data(&cdr, caller_profile);
314 
315 		if (caller_profile->origination_caller_profile) {
316 			switch_caller_profile_t *cp = NULL;
317 
318 			/* Start origination array */
319 			bson_append_start_array(&cdr, "origination");
320 			for (cp = caller_profile->origination_caller_profile, bson_idx = 0; cp; cp = cp->next, bson_idx++) {
321 				snprintf(idx_buffer, sizeof(idx_buffer), "%d", bson_idx);
322 				bson_append_start_object(&cdr, idx_buffer);
323 				set_bson_profile_data(&cdr, cp);
324 				bson_append_finish_object(&cdr);
325 			}
326 			bson_append_finish_object(&cdr);			/* origination */
327 		}
328 
329 		if (caller_profile->originator_caller_profile) {
330 			switch_caller_profile_t *cp = NULL;
331 
332 			/* Start originator array */
333 			bson_append_start_array(&cdr, "originator");
334 			for (cp = caller_profile->originator_caller_profile, bson_idx = 0; cp; cp = cp->next, bson_idx++) {
335 				snprintf(idx_buffer, sizeof(idx_buffer), "%d", bson_idx);
336 				bson_append_start_object(&cdr, idx_buffer);
337 				set_bson_profile_data(&cdr, cp);
338 				bson_append_finish_object(&cdr);
339 			}
340 			bson_append_finish_object(&cdr);			/* originator */
341 		}
342 
343 		if (caller_profile->originatee_caller_profile) {
344 			switch_caller_profile_t *cp = NULL;
345 
346 			/* Start originatee array */
347 			bson_append_start_array(&cdr, "originatee");
348 			for (cp = caller_profile->originatee_caller_profile, bson_idx = 0; cp; cp = cp->next, bson_idx++) {
349 				snprintf(idx_buffer, sizeof(idx_buffer), "%d", bson_idx);
350 				bson_append_start_object(&cdr, idx_buffer);
351 				set_bson_profile_data(&cdr, cp);
352 				bson_append_finish_object(&cdr);
353 			}
354 			bson_append_finish_object(&cdr);			/* originatee */
355 		}
356 
357 		bson_append_finish_object(&cdr);				/* caller_profile */
358 
359 		/* Timestamps */
360 		if (caller_profile->times) {
361 			bson_append_start_object(&cdr, "times");
362 
363 			/* Insert timestamps as long ints (microseconds) to preserve accuracy */
364 			bson_append_long(&cdr, "created_time", caller_profile->times->created);
365 			bson_append_long(&cdr, "profile_created_time", caller_profile->times->profile_created);
366 			bson_append_long(&cdr, "progress_time", caller_profile->times->progress);
367 			bson_append_long(&cdr, "progress_media_time", caller_profile->times->progress_media);
368 			bson_append_long(&cdr, "answered_time", caller_profile->times->answered);
369 			bson_append_long(&cdr, "bridged_time", caller_profile->times->bridged);
370 			bson_append_long(&cdr, "last_hold_time", caller_profile->times->last_hold);
371 			bson_append_long(&cdr, "hold_accum_time", caller_profile->times->hold_accum);
372 			bson_append_long(&cdr, "hangup_time", caller_profile->times->hungup);
373 			bson_append_long(&cdr, "resurrect_time", caller_profile->times->resurrected);
374 			bson_append_long(&cdr, "transfer_time", caller_profile->times->transferred);
375 			bson_append_finish_object(&cdr);			/* times */
376 		}
377 
378 		bson_append_finish_object(&cdr);				/* callflow */
379 		caller_profile = caller_profile->next;
380 		callflow_idx++;
381 	}
382 
383 	bson_append_finish_array(&cdr);
384 
385 	bson_finish(&cdr);
386 
387 	switch_mutex_lock(globals.mongo_mutex);
388 
389 	if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr, NULL) != MONGO_OK) {
390 		if (globals.mongo_conn->err == MONGO_IO_ERROR) {
391 			mongo_error_t db_status;
392 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "MongoDB connection failed; attempting reconnect...\n");
393 			db_status = mongo_reconnect(globals.mongo_conn);
394 
395 			if (db_status != MONGO_OK) {
396 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "MongoDB reconnect failed with error code %d\n", db_status);
397 				status = SWITCH_STATUS_FALSE;
398 			} else {
399 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "MongoDB connection re-established.\n");
400 
401 				/* Re-authentication is necessary after a reconnect */
402 				if (globals.mongo_username && globals.mongo_password) {
403 					status = cdr_mongo_authenticate();
404 				}
405 
406 				if (mongo_insert(globals.mongo_conn, globals.mongo_namespace, &cdr, NULL) != MONGO_OK) {
407 					switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: %s (error code %d)\n", globals.mongo_conn->errstr, globals.mongo_conn->err);
408 					status = SWITCH_STATUS_FALSE;
409 				}
410 			}
411 
412 		} else {
413 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_insert: %s (error code %d)\n", globals.mongo_conn->errstr, globals.mongo_conn->err);
414 			status = SWITCH_STATUS_FALSE;
415 		}
416 	}
417 
418 	switch_mutex_unlock(globals.mongo_mutex);
419 	bson_destroy(&cdr);
420 
421 	return status;
422 }
423 
424 
425 static switch_state_handler_table_t state_handlers = {
426 	/*.on_init */ NULL,
427 	/*.on_routing */ NULL,
428 	/*.on_execute */ NULL,
429 	/*.on_hangup */ NULL,
430 	/*.on_exchange_media */ NULL,
431 	/*.on_soft_execute */ NULL,
432 	/*.on_consume_media */ NULL,
433 	/*.on_hibernate */ NULL,
434 	/*.on_reset */ NULL,
435 	/*.on_park */ NULL,
436 	/*.on_reporting */ my_on_reporting
437 };
438 
439 
load_config(switch_memory_pool_t * pool)440 static switch_status_t load_config(switch_memory_pool_t *pool)
441 {
442 	switch_status_t status = SWITCH_STATUS_SUCCESS;
443 
444 	if (switch_xml_config_parse_module_settings("cdr_mongodb.conf", SWITCH_FALSE, config_settings) != SWITCH_STATUS_SUCCESS) {
445 		return SWITCH_STATUS_FALSE;
446 	}
447 
448 	return status;
449 }
450 
451 
SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load)452 SWITCH_MODULE_LOAD_FUNCTION(mod_cdr_mongodb_load)
453 {
454 	switch_status_t status = SWITCH_STATUS_SUCCESS;
455 	mongo_error_t db_status;
456 	char *repl_hosts[MONGO_REPLSET_MAX_MEMBERS];
457 	char *mongo_host[2];
458 	int num_hosts, mongo_port;
459 
460 	memset(&globals, 0, sizeof(globals));
461 	globals.pool = pool;
462 	if (load_config(pool) != SWITCH_STATUS_SUCCESS) {
463 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Unable to load or parse config!\n");
464 		return SWITCH_STATUS_FALSE;
465 	}
466 
467 	num_hosts = switch_separate_string(globals.mongo_host, ',', repl_hosts, MONGO_REPLSET_MAX_MEMBERS);
468 
469 	if (num_hosts > 1) {
470 		int i;
471 
472 		mongo_replset_init(globals.mongo_conn, globals.mongo_replset_name);
473 
474 		for (i = 0; i < num_hosts; i++) {
475 			switch_separate_string(repl_hosts[i], ':', mongo_host, 2);
476 			mongo_port = mongo_host[1] ? atoi(mongo_host[1]) : globals.mongo_port;
477 			switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Adding MongoDB server %s:%d to replica set\n", mongo_host[0], mongo_port);
478 			mongo_replset_add_seed(globals.mongo_conn, mongo_host[0], mongo_port);
479 		}
480 
481 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connecting to MongoDB replica set %s\n", globals.mongo_replset_name);
482 		db_status = mongo_replset_connect(globals.mongo_conn);
483 	} else {
484 		switch_separate_string(globals.mongo_host, ':', mongo_host, 2);
485 
486 		if (mongo_host[1]) {
487 			globals.mongo_port = atoi(mongo_host[1]);
488 		}
489 
490 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connecting to MongoDB server %s:%d\n", globals.mongo_host, globals.mongo_port);
491 		db_status = mongo_connect(globals.mongo_conn, globals.mongo_host, globals.mongo_port);
492 	}
493 
494 	if (db_status != MONGO_OK) {
495 		switch (globals.mongo_conn->err) {
496 			case MONGO_CONN_NO_SOCKET:
497 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: no socket\n");
498 				break;
499 			case MONGO_CONN_FAIL:
500 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: connection failed\n");
501 				break;
502 			case MONGO_CONN_ADDR_FAIL:
503 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: hostname lookup failed\n");
504 				break;
505 			case MONGO_CONN_NOT_MASTER:
506 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: not master\n");
507 				break;
508 			case MONGO_CONN_BAD_SET_NAME:
509 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_replset_connect: configured replica set name does not match\n");
510 				break;
511 			case MONGO_CONN_NO_PRIMARY:
512 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_replset_connect: cannot find replica set primary member\n");
513 				break;
514 			default:
515 				switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "mongo_connect: unknown error: status code %d, error code %d\n", db_status, globals.mongo_conn->err);
516 		}
517 		return SWITCH_STATUS_FALSE;
518 	} else {
519 		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO, "Connection established\n");
520 	}
521 
522 	if (globals.mongo_username && globals.mongo_password) {
523 		if (cdr_mongo_authenticate() != SWITCH_STATUS_SUCCESS) {
524 			return SWITCH_STATUS_FALSE;
525 		}
526 	}
527 
528 	switch_mutex_init(&globals.mongo_mutex, SWITCH_MUTEX_NESTED, pool);
529 
530 	switch_core_add_state_handler(&state_handlers);
531 	*module_interface = switch_loadable_module_create_module_interface(pool, modname);
532 
533 	return status;
534 }
535 
536 
SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown)537 SWITCH_MODULE_SHUTDOWN_FUNCTION(mod_cdr_mongodb_shutdown)
538 {
539 	globals.shutdown = 1;
540 	switch_core_remove_state_handler(&state_handlers);
541 	switch_mutex_destroy(globals.mongo_mutex);
542 
543 	mongo_destroy(globals.mongo_conn);
544 
545 	return SWITCH_STATUS_SUCCESS;
546 }
547 
548 
549 
550 /* For Emacs:
551  * Local Variables:
552  * mode:c
553  * indent-tabs-mode:t
554  * tab-width:4
555  * c-basic-offset:4
556  * End:
557  * For VIM:
558  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
559  */
560