Browse Source

Merge pull request #2107 from lbalaceanu/cdr_json

Adding CDR as JSON functionality
Lucian Balaceanu 5 years ago
parent
commit
ceece0a353

+ 38 - 0
src/modules/acc/acc_api.h

@@ -38,6 +38,7 @@
 #include "../../core/dprint.h"
 #include "../../core/dprint.h"
 #include "../../core/sr_module.h"
 #include "../../core/sr_module.h"
 #include "../../core/mem/mem.h"
 #include "../../core/mem/mem.h"
+#include "../dialog/dlg_load.h"
 
 
 /* param trasnporter */
 /* param trasnporter */
 typedef struct acc_param {
 typedef struct acc_param {
@@ -65,6 +66,10 @@ typedef struct acc_extra {
 	struct acc_extra *next;
 	struct acc_extra *next;
 } acc_extra_t;
 } acc_extra_t;
 
 
+typedef acc_param_t cdr_param_t;
+typedef acc_enviroment_t cdr_enviroment_t;
+typedef acc_extra_t  cdr_extra_t;
+
 typedef int (*core2strar_f)( struct sip_msg *req, str *c_vals,
 typedef int (*core2strar_f)( struct sip_msg *req, str *c_vals,
 		int *i_vals, char *t_vals);
 		int *i_vals, char *t_vals);
 typedef int (*extra2strar_f)(struct acc_extra *extra, struct sip_msg *rq, str *val_arr,
 typedef int (*extra2strar_f)(struct acc_extra *extra, struct sip_msg *rq, str *val_arr,
@@ -73,6 +78,12 @@ typedef int (*legs2strar_f)( struct acc_extra *legs, struct sip_msg *rq, str *va
 		int *int_arr, char *type_arr, int start);
 		int *int_arr, char *type_arr, int start);
 typedef acc_extra_t* (*leg_info_f)(void);
 typedef acc_extra_t* (*leg_info_f)(void);
 
 
+/* cdr related fuctions */
+typedef int (*cdr_core2strar_f)( struct dlg_cell* dlg,
+		str* values, int* unused, char* types);
+typedef int (*extra2strar_dlg_only_f)(struct acc_extra *extra, struct dlg_cell* dlg, str *val_arr,
+		int *int_arr, char *type_arr, const struct dlg_binds* p_dlgb);
+
 /* acc event data structures */
 /* acc event data structures */
 typedef struct acc_info {
 typedef struct acc_info {
 	acc_enviroment_t *env;
 	acc_enviroment_t *env;
@@ -82,6 +93,13 @@ typedef struct acc_info {
 	acc_extra_t *leg_info;
 	acc_extra_t *leg_info;
 } acc_info_t;
 } acc_info_t;
 
 
+/* cdr event data structures */
+typedef struct cdr_info {
+	str *varr;
+	int *iarr;
+	char *tarr;
+} cdr_info_t;
+
 /* acc engine initialization data structures */
 /* acc engine initialization data structures */
 typedef struct acc_init_info {
 typedef struct acc_init_info {
 	acc_extra_t   *leg_info;
 	acc_extra_t   *leg_info;
@@ -90,6 +108,8 @@ typedef struct acc_init_info {
 typedef int (*acc_init_f)(acc_init_info_t *inf);
 typedef int (*acc_init_f)(acc_init_info_t *inf);
 typedef int (*acc_req_f)(struct sip_msg *req, acc_info_t *data);
 typedef int (*acc_req_f)(struct sip_msg *req, acc_info_t *data);
 
 
+typedef int (*cdr_init_f)(void);
+typedef int (*cdr_write_f)(struct dlg_cell *dlg, struct sip_msg *req, cdr_info_t *data);
 /* acc engine structure */
 /* acc engine structure */
 typedef struct acc_engine {
 typedef struct acc_engine {
 	char name[16];
 	char name[16];
@@ -101,6 +121,14 @@ typedef struct acc_engine {
 	struct acc_engine *next;
 	struct acc_engine *next;
 } acc_engine_t;
 } acc_engine_t;
 
 
+/* cdr engine structure */
+typedef struct cdr_engine {
+	char name[16];
+	cdr_init_f   cdr_init;
+	cdr_write_f  cdr_write;
+	struct cdr_engine *next;
+} cdr_engine_t;
+
 #define MAX_ACC_EXTRA 64
 #define MAX_ACC_EXTRA 64
 #define MAX_ACC_LEG   16
 #define MAX_ACC_LEG   16
 #define ACC_CORE_LEN  6
 #define ACC_CORE_LEN  6
@@ -114,6 +142,9 @@ typedef int (*acc_api_exec_f)(struct sip_msg *rq, acc_engine_t *eng,
 		acc_param_t* comment);
 		acc_param_t* comment);
 typedef acc_extra_t* (*parse_extra_f)(char *extra_str);
 typedef acc_extra_t* (*parse_extra_f)(char *extra_str);
 
 
+typedef int (*register_cdr_engine_f)(cdr_engine_t *eng);
+typedef int (*cdr_api_exec_f)(struct dlg_cell *dlg, struct sip_msg *rq, cdr_engine_t *eng, acc_param_t* comment);
+
 /* the acc API */
 /* the acc API */
 typedef struct acc_api {
 typedef struct acc_api {
 	leg_info_f    get_leg_info;
 	leg_info_f    get_leg_info;
@@ -123,6 +154,10 @@ typedef struct acc_api {
 	parse_extra_f parse_extra;
 	parse_extra_f parse_extra;
 	register_engine_f register_engine;
 	register_engine_f register_engine;
 	acc_api_exec_f    exec;
 	acc_api_exec_f    exec;
+	cdr_core2strar_f       get_core_cdr_attrs;
+	extra2strar_dlg_only_f get_extra_dlg_attrs;
+	register_cdr_engine_f  register_cdr_engine;
+	cdr_api_exec_f     exec_cdr;
 } acc_api_t;
 } acc_api_t;
 
 
 typedef int (*bind_acc_f)(acc_api_t* api);
 typedef int (*bind_acc_f)(acc_api_t* api);
@@ -131,6 +166,9 @@ int acc_run_engines(struct sip_msg *msg, int type, int *reset);
 acc_engine_t *acc_api_get_engines(void);
 acc_engine_t *acc_api_get_engines(void);
 void acc_api_set_arrays(acc_info_t *inf);
 void acc_api_set_arrays(acc_info_t *inf);
 
 
+int cdr_run_engines(struct dlg_cell *dlg, struct sip_msg *msg);
+cdr_engine_t *cdr_api_get_engines(void);
+void cdr_api_set_arrays(cdr_info_t *inf);
 
 
 /**
 /**
  * @brief Load the SL API
  * @brief Load the SL API

+ 39 - 2
src/modules/acc/acc_cdr.c

@@ -39,7 +39,6 @@
 #include "../../core/str.h"
 #include "../../core/str.h"
 #include "../dialog/dlg_load.h"
 #include "../dialog/dlg_load.h"
 
 
-#include "acc_api.h"
 #include "acc_cdr.h"
 #include "acc_cdr.h"
 #include "acc_mod.h"
 #include "acc_mod.h"
 #include "acc_extra.h"
 #include "acc_extra.h"
@@ -92,7 +91,7 @@ extern int _acc_cdr_on_failed;
 static int string2time( str* time_str, struct timeval* time_value);
 static int string2time( str* time_str, struct timeval* time_value);
 
 
 /* write all basic information to buffers(e.g. start-time ...) */
 /* write all basic information to buffers(e.g. start-time ...) */
-static int cdr_core2strar( struct dlg_cell* dlg,
+int cdr_core2strar( struct dlg_cell* dlg,
 		str* values,
 		str* values,
 		int* unused,
 		int* unused,
 		char* types)
 		char* types)
@@ -383,6 +382,10 @@ static int write_cdr( struct dlg_cell* dialog,
 		LM_ERR( "dialog is empty!");
 		LM_ERR( "dialog is empty!");
 		return -1;
 		return -1;
 	}
 	}
+
+	/* engines decide if they have cdr_expired_dlg_enable set or not */
+	cdr_run_engines(dialog, message);
+
 	/* message can be null when logging expired dialogs  */
 	/* message can be null when logging expired dialogs  */
 	if ( !cdr_expired_dlg_enable && !message ){
 	if ( !cdr_expired_dlg_enable && !message ){
 		LM_ERR( "message is empty!");
 		LM_ERR( "message is empty!");
@@ -950,3 +953,37 @@ void destroy_cdr_generation( void)
 
 
 	destroy_extras( cdr_extra);
 	destroy_extras( cdr_extra);
 }
 }
+
+/**
+ * @brief execute all acc engines for a SIP request event
+ */
+int cdr_run_engines(struct dlg_cell *dlg, struct sip_msg *msg)
+{
+	cdr_info_t inf;
+	cdr_engine_t *e;
+
+	e = cdr_api_get_engines();
+
+	if(e==NULL)
+		return 0;
+
+	memset(&inf, 0, sizeof(cdr_info_t));
+	inf.varr = cdr_value_array;
+	inf.iarr = cdr_int_array;
+	inf.tarr = cdr_type_array;
+	while(e) {
+		e->cdr_write(dlg, msg, &inf);
+		e = e->next;
+	}
+	return 0;
+}
+
+/**
+ * @brief set hooks to acc_info_t attributes
+ */
+void cdr_api_set_arrays(cdr_info_t *inf)
+{
+	inf->varr = cdr_value_array;
+	inf->iarr = cdr_int_array;
+	inf->tarr = cdr_type_array;
+}

+ 5 - 1
src/modules/acc/acc_cdr.h

@@ -35,6 +35,9 @@
  *  version).
  *  version).
  *
  *
  */
  */
+#ifndef _ACC_CDR_H_
+#define _ACC_CDR_H_
+
 
 
 #define MAX_CDR_CORE 3
 #define MAX_CDR_CORE 3
 #define MAX_CDR_EXTRA 64
 #define MAX_CDR_EXTRA 64
@@ -44,6 +47,7 @@ int set_cdr_extra( char* cdr_extra_value);
 int set_cdr_facility( char* cdr_facility);
 int set_cdr_facility( char* cdr_facility);
 int init_cdr_generation( void);
 int init_cdr_generation( void);
 void destroy_cdr_generation( void);
 void destroy_cdr_generation( void);
+int cdr_core2strar( struct dlg_cell* dlg, str* values, int* unused, char* types);
 
 
 
 
-
+#endif

+ 10 - 0
src/modules/acc/acc_logic.c

@@ -675,6 +675,16 @@ int acc_api_exec(struct sip_msg *rq, acc_engine_t *eng,
 	return eng->acc_req(rq, &inf);
 	return eng->acc_req(rq, &inf);
 }
 }
 
 
+/**
+ * @brief execute an acc event via a specific engine
+ */
+int cdr_api_exec(struct dlg_cell *dlg, struct sip_msg *rq, cdr_engine_t *eng, acc_param_t* comment)
+{
+	cdr_info_t inf;
+	memset(&inf, 0, sizeof(cdr_info_t));
+	cdr_api_set_arrays(&inf);
+	return eng->cdr_write(dlg, rq, &inf);
+}
 
 
 static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps )
 static void tmcb_func( struct cell* t, int type, struct tmcb_params *ps )
 {
 {

+ 2 - 0
src/modules/acc/acc_logic.h

@@ -50,5 +50,7 @@ int ki_acc_request(sip_msg_t *rq, str *comment, str *dbtable);
 
 
 int acc_api_exec(struct sip_msg *rq, acc_engine_t *eng,
 int acc_api_exec(struct sip_msg *rq, acc_engine_t *eng,
 		acc_param_t* comment);
 		acc_param_t* comment);
+int cdr_api_exec(struct dlg_cell *dlg, struct sip_msg *rq, cdr_engine_t *eng,
+		acc_param_t* comment);
 
 
 #endif
 #endif

+ 93 - 0
src/modules/acc/acc_mod.c

@@ -161,6 +161,11 @@ static int acc_init_engines(void);
 static acc_engine_t *_acc_engines=NULL;
 static acc_engine_t *_acc_engines=NULL;
 static int _acc_module_initialized = 0;
 static int _acc_module_initialized = 0;
 
 
+static int cdr_register_engine(cdr_engine_t *eng);
+static int cdr_init_engines(void);
+static cdr_engine_t *_cdr_engines=NULL;
+static int cdr_module_initialized = 0;
+
 /* ------------- fixup function --------------- */
 /* ------------- fixup function --------------- */
 static int acc_fixup(void** param, int param_no);
 static int acc_fixup(void** param, int param_no);
 static int free_acc_fixup(void** param, int param_no);
 static int free_acc_fixup(void** param, int param_no);
@@ -560,6 +565,12 @@ static int mod_init( void )
 		return -1;
 		return -1;
 	}
 	}
 
 
+	cdr_module_initialized = 1;
+	if(cdr_init_engines()<0) {
+		LM_ERR("failed to init extra engines\n");
+		return -1;
+	}
+
 	return 0;
 	return 0;
 }
 }
 
 
@@ -613,6 +624,19 @@ static int bind_acc(acc_api_t* api)
 	api->get_leg_attrs   = legs2strar;
 	api->get_leg_attrs   = legs2strar;
 	api->parse_extra     = parse_acc_extra;
 	api->parse_extra     = parse_acc_extra;
 	api->exec            = acc_api_exec;
 	api->exec            = acc_api_exec;
+
+	if (cdr_enable) {
+		api->register_cdr_engine = cdr_register_engine;
+		api->get_core_cdr_attrs  = cdr_core2strar;
+		api->get_extra_dlg_attrs = extra2strar_dlg_only;
+		api->exec_cdr            = cdr_api_exec;
+	}
+	else {
+		api->register_cdr_engine = NULL;
+		api->get_core_cdr_attrs  = NULL;
+		api->get_extra_dlg_attrs = NULL;
+		api->exec_cdr            = NULL;
+	}
 	return 0;
 	return 0;
 }
 }
 
 
@@ -685,6 +709,67 @@ static int acc_register_engine(acc_engine_t *eng)
 	return 0;
 	return 0;
 }
 }
 
 
+/**
+ * @brief init an acc engine
+ */
+static int cdr_init_engine(cdr_engine_t *e)
+{
+	if(cdr_module_initialized==0)
+		return 0;
+
+	if(e->cdr_init()<0)
+	{
+		LM_ERR("failed to initialize extra cdr engine\n");
+		return -1;
+	}
+	return 0;
+}
+
+/**
+ * @brief init registered acc engines
+ */
+static int cdr_init_engines(void)
+{
+	cdr_engine_t *e;
+	e = _cdr_engines;
+	while(e) {
+		if(cdr_init_engine(e)<0)
+			return -1;
+		e = e->next;
+	}
+	return 0;
+}
+
+/**
+ * @brief register an accounting engine
+ * @return 0 on success, <0 on failure
+ */
+static int cdr_register_engine(cdr_engine_t *eng)
+{
+	cdr_engine_t *e;
+
+	if(eng==NULL)
+		return -1;
+	e = (cdr_engine_t*)pkg_malloc(sizeof(cdr_engine_t));
+	if(e ==NULL)
+	{
+		PKG_MEM_ERROR;
+		return -1;
+	}
+	memcpy(e, eng, sizeof(cdr_engine_t));
+
+	if(cdr_init_engine(e)<0)
+	{
+		pkg_free(e);
+		return -1;
+	}
+
+	e->next = _cdr_engines;
+	_cdr_engines = e;
+	LM_DBG("new acc engine registered: %s\n", e->name);
+	return 0;
+}
+
 /**
 /**
  *
  *
  */
  */
@@ -693,6 +778,14 @@ acc_engine_t *acc_api_get_engines(void)
 	return _acc_engines;
 	return _acc_engines;
 }
 }
 
 
+/**
+ *
+ */
+cdr_engine_t *cdr_api_get_engines(void)
+{
+	return _cdr_engines;
+}
+
 /**
 /**
  *
  *
  */
  */

+ 202 - 24
src/modules/acc_json/acc_json_mod.c

@@ -43,22 +43,36 @@ static int child_init(int rank);
 
 
 int acc_json_init(acc_init_info_t *inf);
 int acc_json_init(acc_init_info_t *inf);
 int acc_json_send_request(struct sip_msg *req, acc_info_t *inf);
 int acc_json_send_request(struct sip_msg *req, acc_info_t *inf);
+int cdr_json_init(void);
+int cdr_json_write(struct dlg_cell *dlg, struct sip_msg *req, cdr_info_t *inf);
 
 
 // acc API
 // acc API
 acc_api_t accb;
 acc_api_t accb;
 acc_engine_t _acc_json_engine;
 acc_engine_t _acc_json_engine;
+cdr_engine_t _cdr_json_engine;
 // mqueue API
 // mqueue API
 mq_api_t mq_api;
 mq_api_t mq_api;
+// dlg API
+struct dlg_binds dlgb;
 
 
 int acc_flag = -1;
 int acc_flag = -1;
 int acc_missed_flag = -1;
 int acc_missed_flag = -1;
 int acc_time_mode = 0;
 int acc_time_mode = 0;
 static char *acc_extra_str = 0;
 static char *acc_extra_str = 0;
 acc_extra_t *acc_extra = 0;
 acc_extra_t *acc_extra = 0;
-int output_syslog = -1;
-char *output_mqueue_str = 0; /* see mqueue module queue name */
-str q_name = {0, 0};
-static char *log_facility_str = 0;
+int acc_output_syslog = -1;
+char *acc_output_mqueue_str = 0; /* see mqueue module queue name */
+str acc_q_name = {0, 0};
+static char *acc_log_facility_str = 0;
+
+int cdr_enable  = 0;
+static char *cdr_extra_str = 0;
+acc_extra_t *cdr_extra = 0;
+int cdr_expired_dlg_enable = 0;
+int cdr_output_syslog = -1;
+char *cdr_output_mqueue_str = 0; /* see mqueue module queue name */
+str cdr_q_name = {0, 0};
+static char *cdr_log_facility_str = 0;
 
 
 static cmd_export_t cmds[] = {{0, 0, 0, 0, 0, 0}};
 static cmd_export_t cmds[] = {{0, 0, 0, 0, 0, 0}};
 
 
@@ -68,10 +82,17 @@ static param_export_t params[] = {{"acc_flag", INT_PARAM, &acc_flag},
 		{"acc_extra", PARAM_STRING, &acc_extra_str},
 		{"acc_extra", PARAM_STRING, &acc_extra_str},
 		{"acc_time_mode", INT_PARAM, &acc_time_mode},
 		{"acc_time_mode", INT_PARAM, &acc_time_mode},
 		{"acc_time_format", PARAM_STRING, &acc_time_format},
 		{"acc_time_format", PARAM_STRING, &acc_time_format},
-		{"log_level", INT_PARAM, &log_level},
-		{"log_facility", PARAM_STRING, &log_facility_str},
-		{"output_mqueue", PARAM_STRING, &output_mqueue_str},
-		{"output_syslog", INT_PARAM, &output_syslog}, {0, 0, 0}};
+		{"acc_log_level", INT_PARAM, &acc_log_level},
+		{"acc_log_facility", PARAM_STRING, &acc_log_facility_str},
+		{"acc_output_mqueue", PARAM_STRING, &acc_output_mqueue_str},
+		{"acc_output_syslog", INT_PARAM, &acc_output_syslog},
+		{"cdr_extra", PARAM_STRING, &cdr_extra_str},
+		{"cdr_enable", INT_PARAM, &cdr_enable},
+		{"cdr_expired_dlg_enable", INT_PARAM, &cdr_expired_dlg_enable},
+		{"cdr_log_level", INT_PARAM, &cdr_log_level},
+		{"cdr_log_facility", PARAM_STRING, &cdr_log_facility_str},
+		{"cdr_output_mqueue", PARAM_STRING, &cdr_output_mqueue_str},
+		{"cdr_output_syslog", INT_PARAM, &cdr_output_syslog}, {0, 0, 0}};
 
 
 
 
 struct module_exports exports = {
 struct module_exports exports = {
@@ -96,15 +117,34 @@ static int mod_init(void)
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if( cdr_enable < 0 || cdr_enable > 1) {
+		LM_ERR("cdr_enable is out of range\n");
+		return -1;
+	}
+	if( cdr_expired_dlg_enable < 0 || cdr_expired_dlg_enable > 1) {
+		LM_ERR("cdr_expired_dlg_enable is out of range\n");
+		return -1;
+	}
+
 	LM_INFO("janson version : %s\n", JANSSON_VERSION);
 	LM_INFO("janson version : %s\n", JANSSON_VERSION);
 #if JANSSON_VERSION_HEX >= 0x010300
 #if JANSSON_VERSION_HEX >= 0x010300
 /* Code specific to version 1.3 and above */
 /* Code specific to version 1.3 and above */
 #endif
 #endif
 
 
-	if(log_facility_str) {
-		int tmp = str2facility(log_facility_str);
+	if(acc_log_facility_str) {
+		int tmp = str2facility(acc_log_facility_str);
 		if(tmp != -1)
 		if(tmp != -1)
-			log_facility = tmp;
+			acc_log_facility = tmp;
+		else {
+			LM_ERR("invalid log facility configured");
+			return -1;
+		}
+	}
+
+	if (cdr_log_facility_str) {
+		int tmp = str2facility(cdr_log_facility_str);
+		if (tmp != -1)
+			cdr_log_facility = tmp;
 		else {
 		else {
 			LM_ERR("invalid log facility configured");
 			LM_ERR("invalid log facility configured");
 			return -1;
 			return -1;
@@ -112,15 +152,18 @@ static int mod_init(void)
 	}
 	}
 
 
 	/* load the MQUEUE API */
 	/* load the MQUEUE API */
-	if(output_mqueue_str && (load_mq_api(&mq_api) != 0)) {
+	if((acc_output_mqueue_str || cdr_output_mqueue_str) && (load_mq_api(&mq_api) != 0)) {
 		LM_ERR("can't load mqueue module API, disabling json acc to mqueue\n");
 		LM_ERR("can't load mqueue module API, disabling json acc to mqueue\n");
-		output_mqueue_str = NULL;
+		acc_output_mqueue_str = NULL;
 	}
 	}
-	if(output_mqueue_str) {
-		q_name.s = output_mqueue_str;
-		q_name.len = strlen(output_mqueue_str);
+	if(acc_output_mqueue_str) {
+		acc_q_name.s = acc_output_mqueue_str;
+		acc_q_name.len = strlen(acc_output_mqueue_str);
+	}
+	if(cdr_output_mqueue_str) {
+		cdr_q_name.s = cdr_output_mqueue_str;
+		cdr_q_name.len = strlen(cdr_output_mqueue_str);
 	}
 	}
-
 	/* parse the extra string, if any */
 	/* parse the extra string, if any */
 	if(acc_extra_str && (acc_extra = accb.parse_extra(acc_extra_str)) == 0) {
 	if(acc_extra_str && (acc_extra = accb.parse_extra(acc_extra_str)) == 0) {
 		LM_ERR("failed to parse acc_extra param\n");
 		LM_ERR("failed to parse acc_extra param\n");
@@ -141,6 +184,29 @@ static int mod_init(void)
 		return -1;
 		return -1;
 	}
 	}
 
 
+	if (cdr_enable) {
+		if(load_dlg_api( &dlgb) != 0) {
+			LM_ERR("can't load dialog API\n");
+			return -1;
+		}
+		/* parse the extra string, if any */
+		if(cdr_extra_str && (cdr_extra = accb.parse_extra(cdr_extra_str)) == 0) {
+			LM_ERR("failed to parse cdr_extra param\n");
+			return -1;
+		}
+		memset(&_cdr_json_engine, 0, sizeof(cdr_engine_t));
+
+		_cdr_json_engine.cdr_write = cdr_json_write;
+		_cdr_json_engine.cdr_init = cdr_json_init;
+		memcpy(_cdr_json_engine.name, "json", 4);
+
+		if (!accb.register_cdr_engine
+				|| (accb.register_cdr_engine
+						&& (accb.register_cdr_engine(&_cdr_json_engine) < 0))) {
+			LM_ERR("cannot register ACC CDR JSON engine\n");
+			return -1;
+		}
+	}
 	return 0;
 	return 0;
 }
 }
 
 
@@ -166,11 +232,20 @@ int acc_json_init(acc_init_info_t *inf)
 }
 }
 
 
 
 
-void syslog_write(const char *acc)
+void acc_syslog_write(const char *acc)
 {
 {
 	//setlogmask(LOG_UPTO (LOG_NOTICE));
 	//setlogmask(LOG_UPTO (LOG_NOTICE));
-	openlog("json_acc", LOG_CONS | LOG_PID | LOG_NDELAY, log_facility);
-	syslog(log_level, "%s", acc);
+	openlog("json_acc", LOG_CONS | LOG_PID | LOG_NDELAY, acc_log_facility);
+	syslog(acc_log_level, "%s", acc);
+	closelog();
+}
+
+
+void cdr_syslog_write(const char *cdr)
+{
+	//setlogmask(LOG_UPTO (LOG_NOTICE));
+	openlog("json_acc", LOG_CONS | LOG_PID | LOG_NDELAY, cdr_log_facility);
+	syslog(cdr_log_level, "%s", cdr);
 	closelog();
 	closelog();
 }
 }
 
 
@@ -279,9 +354,9 @@ int acc_json_send_request(struct sip_msg *req, acc_info_t *inf)
 		str acc_str = {json_string, strlen(json_string)};
 		str acc_str = {json_string, strlen(json_string)};
 
 
 		// json acc output to mqueue
 		// json acc output to mqueue
-		if(output_mqueue_str) {
+		if(acc_output_mqueue_str) {
 			str key = str_init("acc");
 			str key = str_init("acc");
-			if(mq_api.add(&q_name, &key, &acc_str)) {
+			if(mq_api.add(&acc_q_name, &key, &acc_str)) {
 				LM_DBG("ACC queued [%d][%s]\n", acc_str.len, acc_str.s);
 				LM_DBG("ACC queued [%d][%s]\n", acc_str.len, acc_str.s);
 			} else {
 			} else {
 				LM_DBG("ACC mqueue add error [%d][%s]\n", acc_str.len,
 				LM_DBG("ACC mqueue add error [%d][%s]\n", acc_str.len,
@@ -289,8 +364,8 @@ int acc_json_send_request(struct sip_msg *req, acc_info_t *inf)
 			}
 			}
 		}
 		}
 		// json acc output to syslog
 		// json acc output to syslog
-		if(output_syslog)
-			syslog_write(json_string);
+		if(acc_output_syslog)
+			acc_syslog_write(json_string);
 		free(json_string);
 		free(json_string);
 		json_object_clear(object);
 		json_object_clear(object);
 		json_decref(object);
 		json_decref(object);
@@ -299,3 +374,106 @@ int acc_json_send_request(struct sip_msg *req, acc_info_t *inf)
 	free_strar_mem(&(inf->tarr[m - o]), &(inf->varr[m - o]), o, m);
 	free_strar_mem(&(inf->tarr[m - o]), &(inf->varr[m - o]), o, m);
 	return 1;
 	return 1;
 }
 }
+
+
+int cdr_json_init(void)
+{
+	LM_DBG(" init ...\n");
+	return 0;
+}
+
+
+int cdr_json_write(struct dlg_cell *dlg, struct sip_msg *req, cdr_info_t *inf)
+{
+	int attr_cnt = 0;
+	int i;
+	int extra_cnt = 0;
+	int core_cnt = 0;
+
+	json_t *object = json_object();
+
+	/* get default values */
+	core_cnt = accb.get_core_cdr_attrs( dlg, inf->varr, inf->iarr, inf->tarr);
+	attr_cnt += core_cnt;
+
+	for(i = 0; i < attr_cnt; i++) {
+		LM_DBG("[%d][%.*s]\n", i, inf->varr[i].len, inf->varr[i].s);
+		char *tmp = strndup(inf->varr[i].s, inf->varr[i].len);
+		json_t *value = json_string(tmp);
+		if(!value)
+			value = json_string("NON-UTF8");
+		if(i == 0) {
+			json_object_set_new(object, cdr_start_str.s, value);
+		} else if(i == 1) {
+			json_object_set_new(object, cdr_end_str.s, value);
+		} else if(i == 2) {
+			json_object_set_new(object, cdr_duration_str.s, value);
+		}
+		free(tmp);
+	}
+
+	/* get extra values */
+	if (req)
+	{
+		/* free memory allocated by get_extra_attrs */
+		extra_cnt += accb.get_extra_attrs( cdr_extra,
+				req,
+				inf->varr + attr_cnt,
+				inf->iarr + attr_cnt,
+				inf->tarr + attr_cnt);
+		attr_cnt += extra_cnt;
+	} else if (cdr_expired_dlg_enable){
+		int dlg_index = 0;
+		dlg_index += accb.get_extra_dlg_attrs( cdr_extra,
+				dlg,
+				inf->varr + attr_cnt,
+				inf->iarr + attr_cnt,
+				inf->tarr + attr_cnt,
+				&dlgb);
+		attr_cnt += dlg_index;
+	}
+
+	struct acc_extra *extra = cdr_extra;
+	for( ; i < attr_cnt; i++)
+	{
+		LM_DBG("[%d][%s][%.*s]\n", i, extra->name.s, inf->varr[i].len,
+						inf->varr[i].s);
+		char *tmp = strndup(inf->varr[i].s, inf->varr[i].len);
+		json_t *value = json_string(tmp);
+		if(!value)
+			value = json_string("NON-UTF8");
+		json_object_set_new(object, extra->name.s, value);
+		free(tmp);
+		extra = extra->next;
+	}
+
+	if(object) {
+		if(json_object_size(object) == 0) {
+			LM_ERR("json object empty\n");
+			json_decref(object);
+			return 0;
+		}
+		char *json_string = json_dumps(object, JSON_ENSURE_ASCII);
+		str cdr_str = {json_string, strlen(json_string)};
+
+		// json acc output to mqueue
+		if (cdr_output_mqueue_str) {
+			str key = str_init("cdr");
+			if (mq_api.add(&cdr_q_name, &key, &cdr_str)) {
+				LM_DBG("CDR queued [%d][%s]\n", cdr_str.len, cdr_str.s);
+			} else {
+				LM_DBG("CDR mqueue add error [%d][%s]\n", cdr_str.len,
+						cdr_str.s);
+			}
+		}
+		// json acc output to syslog
+		if(cdr_output_syslog)
+			cdr_syslog_write(json_string);
+		free(json_string);
+		json_object_clear(object);
+		json_decref(object);
+	}
+	/* free memory allocated by get_extra_attrs */
+	free_strar_mem(&(inf->tarr[core_cnt]), &(inf->varr[core_cnt]), extra_cnt, attr_cnt);
+	return 1;
+}

+ 8 - 2
src/modules/acc_json/acc_json_mod.h

@@ -32,11 +32,17 @@ str acc_sipcode_key = str_init("sip_code");
 str acc_sipreason_key = str_init("sip_reason");
 str acc_sipreason_key = str_init("sip_reason");
 str acc_time_key = str_init("time");
 str acc_time_key = str_init("time");
 
 
+str cdr_start_str = str_init("start_time");
+str cdr_end_str = str_init("end_time");
+str cdr_duration_str = str_init("duration");
+
 #define ACC_TIME_FORMAT_SIZE 128
 #define ACC_TIME_FORMAT_SIZE 128
 static char acc_time_format_buf[ACC_TIME_FORMAT_SIZE];
 static char acc_time_format_buf[ACC_TIME_FORMAT_SIZE];
 char *acc_time_format = "%Y-%m-%d %H:%M:%S";
 char *acc_time_format = "%Y-%m-%d %H:%M:%S";
 
 
-int log_level = L_NOTICE;
-int log_facility = LOG_DAEMON;
+int acc_log_level = L_NOTICE;
+int acc_log_facility = LOG_DAEMON;
+int cdr_log_level = L_NOTICE;
+int cdr_log_facility = LOG_DAEMON;
 
 
 #endif
 #endif

+ 164 - 11
src/modules/acc_json/doc/acc_json_admin.xml

@@ -232,7 +232,8 @@ route[RUN_ACC_PUBLISH] {
 ...
 ...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
-	</section>
+	</section>	
+	
 	<section id="acc_json.p.output_syslog">
 	<section id="acc_json.p.output_syslog">
 		<title><varname>output_syslog</varname> (integer)</title>
 		<title><varname>output_syslog</varname> (integer)</title>
 		<para>
 		<para>
@@ -249,15 +250,15 @@ route[RUN_ACC_PUBLISH] {
 		<programlisting format="linespecific">
 		<programlisting format="linespecific">
 ...
 ...
 modparam("acc_json", "output_syslog", 1)
 modparam("acc_json", "output_syslog", 1)
-modparam("acc_json", "log_level", 2)
-modparam("acc_json", "log_facility", "LOG_DAEMON")
+modparam("acc_json", "acc_log_level", 2)
+modparam("acc_json", "acc_log_facility", "LOG_DAEMON")
 ...
 ...
 </programlisting>
 </programlisting>
 		</example>
 		</example>
 	</section>
 	</section>
 
 
-	<section id="acc_json.p.log_facility">
-		<title><varname>log_facility</varname> (integer)</title>
+	<section id="acc_json.p.acc_log_facility">
+		<title><varname>acc_log_facility</varname> (integer)</title>
 		<para>
 		<para>
 		Log facility to which accounting messages are issued to syslog.
 		Log facility to which accounting messages are issued to syslog.
                 This allows to easily separate the accounting specific logging
                 This allows to easily separate the accounting specific logging
@@ -267,10 +268,10 @@ modparam("acc_json", "log_facility", "LOG_DAEMON")
 		Default value is LOG_DAEMON.
 		Default value is LOG_DAEMON.
 		</para>
 		</para>
 		<example>
 		<example>
-		<title>log_facility example</title>
+		<title>acc_log_facility example</title>
 		<programlisting format="linespecific">
 		<programlisting format="linespecific">
 ...
 ...
-modparam("acc_json", "log_facility", "LOG_LOCAL0")
+modparam("acc_json", "acc_log_facility", "LOG_LOCAL0")
 
 
 # modify you syslog/rsyslog config
 # modify you syslog/rsyslog config
 # /etc/rsyslog.d/default.conf
 # /etc/rsyslog.d/default.conf
@@ -283,8 +284,160 @@ modparam("acc_json", "log_facility", "LOG_LOCAL0")
 		</example>
 		</example>
 	</section>
 	</section>
 
 
-	<section id="acc_json.p.log_level">
-		<title><varname>log_level</varname> (integer)</title>
+	<section id="acc_json.p.acc_log_level">
+		<title><varname>acc_log_level</varname> (integer)</title>
+		<para>
+		Log level at which accounting messages are issued to syslog.
+		</para>
+		<para>
+		Default value is 1 (L_NOTICE).
+		</para>
+		<example>
+			<title>acc_log_level example</title>
+		<programlisting format="linespecific">
+...
+modparam("acc_json", "acc_log_level", 2) # Set acc_log_level to 2 (L_INFO)
+...
+</programlisting>
+		</example>
+	</section>
+	
+	<section id="acc.p.cdr_enable">
+		<title><varname>cdr_enable</varname> (str)</title>
+		<para>
+		Enable Call Data Record generation.
+		</para>
+		<para>
+		Default value is 0 (disabled).
+		</para>
+		<example>
+		<title>cdr_enable example</title>
+		<programlisting format="linespecific">
+...
+modparam("acc_json", "cdr_enable", 1)
+...
+</programlisting>
+		</example>
+	</section>	
+	
+	<section id="acc.p.cdr_extra">
+		<title><varname>cdr_extra</varname> (str)</title>
+		<para>
+		Set of pseudo-variables defining custom CDR fields. 
+		</para>
+		<para>
+		Default value is NULL.
+		</para>
+		<example>
+		<title>cdr_extra example</title>
+		<programlisting format="linespecific">
+...
+modparam("acc_json", "cdr_extra", "ci=$dlg_var(call_id);ft=$dlg_var(from_tag)")
+...
+</programlisting>
+		</example>
+	</section>
+
+	
+	<section id="acc.p.cdr_expired_dlg_enable">
+		<title><varname>cdr_expired_dlg_enable</varname> (str)</title>
+		<para>
+		Should CDR-based logging be enabled in case of expired dialogs? 
+		</para>
+		<para>
+		0 - off (default). 1 - on. 
+		</para>
+		<example>
+		<title>cdr_expired_dlg_enable example</title>
+		<programlisting format="linespecific">
+...
+modparam("acc_json", "cdr_expired_dlg_enable", 1)
+...
+</programlisting>
+		</example>
+	</section>
+	
+	<section id="acc_json.p.cdr_output_mqueue">
+		<title><varname>cdr_output_mqueue</varname> (integer)</title>
+		<para>
+                Requires the mqueue module.
+                The acc module will queue json cdr events in the specified mqueue.
+                Using a rtimer module exec you can access the queue and process them.
+		</para>
+		<para>
+                You can also fetch the cdr events using mqueue.fetch over JSON-RPC.
+		</para>
+		<para>
+		Default value is not-set mqueue will not be required.
+		</para>
+		<example>
+		<title>cdr_output_mqueue usage example</title>
+		<programlisting format="linespecific">
+...
+# example using json_mqueue/http_client to publish to NSQD
+modparam("mqueue", "mqueue", "name=cdr_events;size=100000")
+modparam("acc_json", "cdr_enable", 1)
+modparam("acc_json", "cdr_output_mqueue", "cdr_events")
+modparam("acc_json", "cdr_extra", "ci=$dlg_var(call_id)")
+modparam("rtimer", "timer", "name=nsqt;interval=1;mode=1;")
+modparam("rtimer", "exec", "timer=nsqt;route=RUN_CDR_PUBLISH")
+modparam("http_client", "keep_connections", 1)
+modparam("http_client", "httpcon", "nsqd=>http://localhost:4151/pub?topic=acc")
+
+route[RUN_CDR_PUBLISH] {
+   $var(count) = 0;
+   while (mq_fetch("cdr_events")) {
+      $var(q_size) = mq_size("cdr_events");
+      $var(count) = $var(count) + 1;
+      xinfo("[RUN_CDR_PUBLISH][$var(q_size)][$var(count)][$mqk(cdr_events)][$mqv(cdr_events)]\n");
+      $var(res) = http_connect_raw("nsqd", "", "application/json", $mqv(cdr_events), "$var(nsq_res)");
+      if ($var(res) &lt; 0) {
+         xerr("[RUN_CDR_PUBLISH][$var(res)] http_connect_raw: timeout or error !\n");
+         mq_add("cdr_events", "cdr_key", "$mqv(cdr_events)");
+      } else if ($var(res) &lt; 200 || $var(res) &gt; 299) {
+         xerr("[RUN_CDR_PUBLISH][$var(res)] http unexpected response code !\n");
+         mq_add("cdr_dead_letter_queue", "cdr_key", "$mqv(cdr_events)");
+         return;
+      }
+   }
+   if ($var(count) &gt; 0 ) {
+      xinfo("[RUN_CDR_PUBLISH]done count[$var(count)]\n");
+   }
+}
+...
+</programlisting>
+		</example>
+	</section>
+	
+<section id="acc_json.p.cdr_log_facility">
+		<title><varname>cdr_log_facility</varname> (integer)</title>
+		<para>
+		Log facility to which accounting messages are issued to syslog.
+                This allows to easily separate the accounting specific logging
+                from the other log messages.
+		</para>
+		<para>
+		Default value is LOG_DAEMON.
+		</para>
+		<example>
+		<title>cdr_log_facility example</title>
+		<programlisting format="linespecific">
+...
+modparam("acc_json", "cdr_log_facility", "LOG_LOCAL0")
+
+# modify you syslog/rsyslog config
+# /etc/rsyslog.d/default.conf
+# remove local0 from default log file
+# *.*;local0,auth,authpriv.none /var/log/syslog
+# add local0 to another log file
+# local0.*                      /var/log/json_cdr.log
+...
+</programlisting>
+		</example>
+	</section>
+
+	<section id="acc_json.p.cdr_log_level">
+		<title><varname>cdr_log_level</varname> (integer)</title>
 		<para>
 		<para>
 		Log level at which accounting messages are issued to syslog.
 		Log level at which accounting messages are issued to syslog.
 		</para>
 		</para>
@@ -292,10 +445,10 @@ modparam("acc_json", "log_facility", "LOG_LOCAL0")
 		Default value is 1 (L_NOTICE).
 		Default value is 1 (L_NOTICE).
 		</para>
 		</para>
 		<example>
 		<example>
-			<title>log_level example</title>
+			<title>cdr_log_level example</title>
 		<programlisting format="linespecific">
 		<programlisting format="linespecific">
 ...
 ...
-modparam("acc_json", "log_level", 2) # Set log_level to 2 (L_INFO)
+modparam("acc_json", "cdr_log_level", 2) # Set cdr_log_level to 2 (L_INFO)
 ...
 ...
 </programlisting>
 </programlisting>
 		</example>
 		</example>