modules/xlog/xlog.c
31ccf6a2
 /**
  * Copyright (C) 2001-2003 FhG Fokus
  *
27642a08
  * This file is part of Kamailio, a free SIP server.
31ccf6a2
  *
27642a08
  * Kamailio is free software; you can redistribute it and/or modify
31ccf6a2
  * it under the terms of the GNU General Public License as published by
  * the Free Software Foundation; either version 2 of the License, or
  * (at your option) any later version
  *
27642a08
  * Kamailio is distributed in the hope that it will be useful,
31ccf6a2
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License 
  * along with this program; if not, write to the Free Software 
9e1ff448
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
31ccf6a2
  */
e7593d59
 /*!
  * \file
  * \brief Module interface
  * \ingroup xlog
  * Module: \ref xlog
  */
 
 /**
  * @defgroup xlog xlog :: Kamailio xlog module
  * @brief Kamailio xlog module
  * Extended logging from the configuration script using pv:s.
  * Can log to multiple channels as well as standard out.
  *
  */
31ccf6a2
 
 #include <stdio.h>
 #include <string.h>
 #include <stdlib.h>
 #include <sys/types.h>
 #include <sys/ipc.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <time.h>
adafd759
 #include <ctype.h>
31ccf6a2
 
 #include "../../sr_module.h"
 #include "../../dprint.h"
 #include "../../error.h"
ec19b133
 #include "../../cfg/cfg.h"
31ccf6a2
 #include "../../mem/mem.h"
a5a58239
 #include "../../parser/parse_param.h"
31ccf6a2
 
 #include "xl_lib.h"
 
b0a7f212
 #include "../../pvar.h"
 
224c6c69
 #define NOFACILITY -1
31ccf6a2
 
 MODULE_VERSION
 
09110884
 char *_xlog_buf = NULL;
 char *_xlog_prefix = "<script>: ";
31ccf6a2
 
 /** parameters */
7ae36a90
 static int buf_size=4096;
 static int force_color=0;
e01ed4b2
 static int long_format=0;
ff10fef4
 static int xlog_facility = DEFAULT_FACILITY;
 static char *xlog_facility_name = NULL;
31ccf6a2
 
ec19b133
 /** cfg dynamic parameters */
 struct cfg_group_xlog {
 	int methods_filter;
 };
 static struct cfg_group_xlog xlog_default_cfg = {
 	-1	/* methods filter */
 };
 static void *xlog_cfg = &xlog_default_cfg;
 static cfg_def_t xlog_cfg_def[] = {
 	{"methods_filter",		CFG_VAR_INT | CFG_ATOMIC, 	0, 0, 0, 0,
 		"Methods filter value for xlogm(...)."},
 	{0, 0, 0, 0, 0, 0}
 };
 
31ccf6a2
 /** module functions */
 static int mod_init(void);
 
b4fe1e84
 static int xlog_1(struct sip_msg*, char*, char*);
 static int xlog_2(struct sip_msg*, char*, char*);
14f5884a
 static int xlog_3(struct sip_msg*, char*, char*, char*);
31ccf6a2
 static int xdbg(struct sip_msg*, char*, char*);
 
7ae36a90
 static int xlogl_1(struct sip_msg*, char*, char*);
 static int xlogl_2(struct sip_msg*, char*, char*);
14f5884a
 static int xlogl_3(struct sip_msg*, char*, char*, char*);
7ae36a90
 static int xdbgl(struct sip_msg*, char*, char*);
 
ec19b133
 static int xlogm_2(struct sip_msg*, char*, char*);
 
7ae36a90
 static int xlog_fixup(void** param, int param_no);
14f5884a
 static int xlog3_fixup(void** param, int param_no);
7ae36a90
 static int xdbg_fixup(void** param, int param_no);
 static int xlogl_fixup(void** param, int param_no);
14f5884a
 static int xlogl3_fixup(void** param, int param_no);
7ae36a90
 static int xdbgl_fixup(void** param, int param_no);
31ccf6a2
 
81fad30a
 static void destroy(void);
31ccf6a2
 
a5a58239
 static int xlog_log_colors_param(modparam_t type, void *val);
 
adafd759
 int pv_parse_color_name(pv_spec_p sp, str *in);
 static int pv_get_color(struct sip_msg *msg, pv_param_t *param, 
 		pv_value_t *res);
 
d3c12b38
 typedef struct _xl_level
 {
 	int type;
 	union {
 		long level;
 		pv_spec_t sp;
 	} v;
 } xl_level_t, *xl_level_p;
 
7ae36a90
 typedef struct _xl_msg
 {
 	pv_elem_t *m;
 	struct action *a;
 } xl_msg_t;
 
adafd759
 static pv_export_t mod_items[] = {
b162288d
 	{ {"C", sizeof("C")-1}, PVT_OTHER, pv_get_color, 0,
adafd759
 		pv_parse_color_name, 0, 0, 0 },
 	{ {0, 0}, 0, 0, 0, 0, 0, 0, 0 }
 };
 
 
31ccf6a2
 static cmd_export_t cmds[]={
7ae36a90
 	{"xlog",   (cmd_function)xlog_1,   1, xdbg_fixup,  0, ANY_ROUTE},
 	{"xlog",   (cmd_function)xlog_2,   2, xlog_fixup,  0, ANY_ROUTE},
14f5884a
 	{"xlog",   (cmd_function)xlog_3,   3, xlog3_fixup, 0, ANY_ROUTE},
7ae36a90
 	{"xdbg",   (cmd_function)xdbg,     1, xdbg_fixup,  0, ANY_ROUTE},
 	{"xlogl",  (cmd_function)xlogl_1,  1, xdbgl_fixup, 0, ANY_ROUTE},
 	{"xlogl",  (cmd_function)xlogl_2,  2, xlogl_fixup, 0, ANY_ROUTE},
14f5884a
 	{"xlogl",  (cmd_function)xlogl_3,  3, xlogl3_fixup,0, ANY_ROUTE},
7ae36a90
 	{"xdbgl",  (cmd_function)xdbgl,    1, xdbgl_fixup, 0, ANY_ROUTE},
ec19b133
 	{"xlogm",  (cmd_function)xlogm_2,  2, xlog_fixup,  0, ANY_ROUTE},
80998a7f
 	{0,0,0,0,0,0}
31ccf6a2
 };
 
 
 static param_export_t params[]={
064e390a
 	{"buf_size",     INT_PARAM, &buf_size},
 	{"force_color",  INT_PARAM, &force_color},
e01ed4b2
 	{"long_format",  INT_PARAM, &long_format},
c135633f
 	{"prefix",       PARAM_STRING, &_xlog_prefix},
 	{"log_facility", PARAM_STRING, &xlog_facility_name},
 	{"log_colors",   PARAM_STRING|USE_FUNC_PARAM, (void*)xlog_log_colors_param},
ec19b133
 	{"methods_filter",  PARAM_INT, &xlog_default_cfg.methods_filter},
31ccf6a2
 	{0,0,0}
 };
 
 
 /** module exports */
 struct module_exports exports= {
 	"xlog",
51716422
 	DEFAULT_DLFLAGS, /* dlopen flags */
31ccf6a2
 	cmds,
 	params,
739aec8b
 	0,          /* exported statistics */
de26de44
 	0  ,        /* exported MI functions */
adafd759
 	mod_items,  /* exported pseudo-variables */
3eee3a4e
 	0,          /* extra processes */
31ccf6a2
 	mod_init,   /* module initialization function */
3ed44241
 	0,
31ccf6a2
 	(destroy_function) destroy,
9fdc0d93
 	0           /* per-child init function */
31ccf6a2
 };
 
 /**
  * init module function
  */
 static int mod_init(void)
 {
ff10fef4
 	int lf;
ec19b133
 	if(cfg_declare("xlog", xlog_cfg_def, &xlog_default_cfg,
 				cfg_sizeof(xlog), &xlog_cfg)){
 		LM_ERR("Fail to declare the xlog cfg framework structure\n");
 		return -1;
 	}
ff10fef4
 	if (xlog_facility_name!=NULL) {
 		lf = str2facility(xlog_facility_name);
 		if (lf != -1) {
 			xlog_facility = lf;
 		} else {
 			LM_ERR("invalid syslog facility %s\n", xlog_facility_name);
 			return -1;
 		}
 	}
 
09110884
 	_xlog_buf = (char*)pkg_malloc((buf_size+1)*sizeof(char));
 	if(_xlog_buf==NULL)
31ccf6a2
 	{
55e41ddd
 		LM_ERR("no pkg memory left\n");
31ccf6a2
 		return -1;
 	}
 	return 0;
 }
 
7ae36a90
 static inline int xlog_helper(struct sip_msg* msg, xl_msg_t *xm,
224c6c69
 		int level, int line, int facility)
7ae36a90
 {
 	str txt;
 
 	txt.len = buf_size;
 
 	if(xl_print_log(msg, xm->m, _xlog_buf, &txt.len)<0)
 		return -1;
 	txt.s = _xlog_buf;
224c6c69
 	/* if facility is not explicitely defined use the xlog default facility */
 	if (facility==NOFACILITY) {
 		facility = xlog_facility;
 	} 
 
7ae36a90
 	if(line>0)
e01ed4b2
 		if(long_format==1)
224c6c69
 			LOG_(facility, level, _xlog_prefix,
e01ed4b2
 				"%s:%d:%.*s",
 				(xm->a)?(((xm->a->cfile)?xm->a->cfile:"")):"",
 				(xm->a)?xm->a->cline:0, txt.len, txt.s);
 		else
224c6c69
 			LOG_(facility, level, _xlog_prefix,
e01ed4b2
 				"%d:%.*s", (xm->a)?xm->a->cline:0, txt.len, txt.s);
7ae36a90
 	else
224c6c69
 		LOG_(facility, level, _xlog_prefix,
7ae36a90
 			"%.*s", txt.len, txt.s);
 	return 1;
 }
 
31ccf6a2
 /**
7ae36a90
  * print log message to L_ERR level
31ccf6a2
  */
224c6c69
 static int xlog_1_helper(struct sip_msg* msg, char* frm, char* str2, int mode, int facility)
b4fe1e84
 {
aa5d9a66
 	if(!is_printable(L_ERR))
 		return 1;
 
224c6c69
 	return xlog_helper(msg, (xl_msg_t*)frm, L_ERR, mode, facility);
 }
 static int xlog_1(struct sip_msg* msg, char* frm, char* str2)
 {
 	return xlog_1_helper(msg, frm, str2, 0, NOFACILITY);
b4fe1e84
 }
 
 /**
8831d195
  * print log message to L_ERR level along with cfg line
  */
 static int xlogl_1(struct sip_msg* msg, char* frm, char* str2)
 {
224c6c69
 	return xlog_1_helper(msg, frm, str2, 1, NOFACILITY);
8831d195
 }
 
224c6c69
 static int xlog_2_helper(struct sip_msg* msg, char* lev, char* frm, int mode, int facility)
31ccf6a2
 {
d3c12b38
 	long level;
 	xl_level_p xlp;
 	pv_value_t value;
31ccf6a2
 
d3c12b38
 	xlp = (xl_level_p)lev;
 	if(xlp->type==1)
 	{
 		if(pv_get_spec_value(msg, &xlp->v.sp, &value)!=0 
 			|| value.flags&PV_VAL_NULL || !(value.flags&PV_VAL_INT))
 		{
 			LM_ERR("invalid log level value [%d]\n", value.flags);
 			return -1;
 		}
 		level = (long)value.ri;
 	} else {
 		level = xlp->v.level;
 	}
 
 	if(!is_printable((int)level))
aa5d9a66
 		return 1;
 
224c6c69
 	return xlog_helper(msg, (xl_msg_t*)frm, (int)level, mode, facility);
 }
 
 /**
  * print log message to level given in parameter
  */
 static int xlog_2(struct sip_msg* msg, char* lev, char* frm)
 {
 	return xlog_2_helper(msg, lev, frm, 0, NOFACILITY);
31ccf6a2
 }
 
 /**
7ae36a90
  * print log message to level given in parameter along with cfg line
  */
 static int xlogl_2(struct sip_msg* msg, char* lev, char* frm)
 {
224c6c69
 	return xlog_2_helper(msg, lev, frm, 1, NOFACILITY);
 }
7ae36a90
 
ec19b133
 /**
  * print log message to level given in parameter applying methods filter
  */
 static int xlogm_2(struct sip_msg* msg, char* lev, char* frm)
 {
 	int mfilter;
 
 	mfilter = cfg_get(xlog, xlog_cfg, methods_filter);
 
 	if(mfilter==-1)
 		return 1;
 
 	if(msg->first_line.type==SIP_REQUEST) {
 		if (msg->first_line.u.request.method_value & mfilter) {
 			return 1;
 		}
 	} else {
 		if (parse_headers(msg, HDR_CSEQ_F, 0) != 0 || msg->cseq==NULL) {
 			LM_ERR("cannot parse cseq header\n");
 			return -1;
 		}
 		if (get_cseq(msg)->method_id & mfilter) {
 			return 1;
 		}
 	}
 
 	return xlog_2_helper(msg, lev, frm, 0, NOFACILITY);
 }
 
14f5884a
 static int xlog_3_helper(struct sip_msg* msg, char* fac, char* lev, char* frm, int mode)
 {
 	long level;
 	int facility;
 	xl_level_p xlp;
 	pv_value_t value;
 
 	xlp = (xl_level_p)lev;
 	if(xlp->type==1)
 	{
 		if(pv_get_spec_value(msg, &xlp->v.sp, &value)!=0 
 			|| value.flags&PV_VAL_NULL || !(value.flags&PV_VAL_INT))
 		{
 			LM_ERR("invalid log level value [%d]\n", value.flags);
 			return -1;
 		}
 		level = (long)value.ri;
 	} else {
 		level = xlp->v.level;
 	}
 	facility = *(int*)fac;
 
 	if(!is_printable((int)level))
 		return 1;
 
 	return xlog_helper(msg, (xl_msg_t*)frm, (int)level, mode, facility);
 }
 
 /**
  * print log message to level given in parameter
  * add dedicated logfacility
  */
 static int xlog_3(struct sip_msg* msg, char* fac, char* lev, char* frm)
 {
 	return xlog_3_helper(msg, fac, lev, frm, 0);
 }
 
 /**
  * print log message to level given in parameter along with cfg line
  * add dedicated logfacility
  */
 static int xlogl_3(struct sip_msg* msg, char* fac, char* lev, char* frm)
 {
 	return xlog_3_helper(msg, fac, lev, frm, 1);
 }
 
224c6c69
 static int xdbg_helper(struct sip_msg* msg, char* frm, char* str2, int mode, int facility)
 {
 	if(!is_printable(L_DBG))
7ae36a90
 		return 1;
224c6c69
 	return xlog_helper(msg, (xl_msg_t*)frm, L_DBG, mode, facility);
31ccf6a2
 }
 
 /**
8831d195
  * print log message to L_DBG level
  */
 static int xdbg(struct sip_msg* msg, char* frm, char* str2)
 {
224c6c69
 	return xdbg_helper(msg, frm, str2, 0, NOFACILITY);
8831d195
 }
 
 /**
7ae36a90
  * print log message to L_DBG level along with cfg line
  */
 static int xdbgl(struct sip_msg* msg, char* frm, char* str2)
 {
224c6c69
 	return xdbg_helper(msg, frm, str2, 1, NOFACILITY);
7ae36a90
 }
 
 /**
  * module destroy function
31ccf6a2
  */
81fad30a
 static void destroy(void)
31ccf6a2
 {
09110884
 	if(_xlog_buf)
 		pkg_free(_xlog_buf);
31ccf6a2
 }
 
7ae36a90
 static int xdbg_fixup_helper(void** param, int param_no, int mode)
 {
 	xl_msg_t *xm;
 	str s;
 
 	xm = (xl_msg_t*)pkg_malloc(sizeof(xl_msg_t));
 	if(xm==NULL)
 	{
 		LM_ERR("no more pkg\n");
 		return -1;
 	}
 	memset(xm, 0, sizeof(xl_msg_t));
 	if(mode==1)
581ab12a
 		xm->a = get_action_from_param(param, param_no);
7ae36a90
 	s.s = (char*)(*param); s.len = strlen(s.s);
 
 	if(pv_parse_format(&s, &xm->m)<0)
 	{
 		LM_ERR("wrong format[%s]\n", (char*)(*param));
 		return E_UNSPEC;
 	}
 	*param = (void*)xm;
 	return 0;
 }
 
 static int xlog_fixup_helper(void** param, int param_no, int mode)
31ccf6a2
 {
d3c12b38
 	xl_level_p xlp;
 	str s;
31ccf6a2
 	
 	if(param_no==1)
 	{
d3c12b38
 		s.s = (char*)(*param);
 		if(s.s==NULL || strlen(s.s)<2)
31ccf6a2
 		{
55e41ddd
 			LM_ERR("wrong log level\n");
31ccf6a2
 			return E_UNSPEC;
 		}
d3c12b38
 
 		xlp = (xl_level_p)pkg_malloc(sizeof(xl_level_t));
 		if(xlp == NULL)
 		{
 			LM_ERR("no more memory\n");
 			return E_UNSPEC;
 		}
 		memset(xlp, 0, sizeof(xl_level_t));
 		if(s.s[0]==PV_MARKER)
31ccf6a2
 		{
d3c12b38
 			xlp->type = 1;
 			s.len = strlen(s.s);
 			if(pv_parse_spec(&s, &xlp->v.sp)==NULL)
 			{
 				LM_ERR("invalid level param\n");
31ccf6a2
 				return E_UNSPEC;
d3c12b38
 			}
 		} else {
 			xlp->type = 0;
 			switch(((char*)(*param))[2])
 			{
 				case 'A': xlp->v.level = L_ALERT; break;
1b569b14
 				case 'B': xlp->v.level = L_BUG; break;
 				case 'C': xlp->v.level = L_CRIT2; break;
d3c12b38
 				case 'E': xlp->v.level = L_ERR; break;
 				case 'W': xlp->v.level = L_WARN; break;
 				case 'N': xlp->v.level = L_NOTICE; break;
 				case 'I': xlp->v.level = L_INFO; break;
 				case 'D': xlp->v.level = L_DBG; break;
 				default:
 					LM_ERR("unknown log level\n");
 					return E_UNSPEC;
 			}
31ccf6a2
 		}
 		pkg_free(*param);
d3c12b38
 		*param = (void*)xlp;
31ccf6a2
 		return 0;
 	}
 
 	if(param_no==2)
7ae36a90
 		return xdbg_fixup_helper(param, 2, mode);
985f4318
 
 	return 0;
31ccf6a2
 }
 
14f5884a
 /*
  * fixup log facility
  */
 static int xlog3_fixup_helper(void** param, int param_no)
 {
 	int *facility;
 	str s;
 
 	s.s = (char*)(*param);
 	if(s.s==NULL)
 	{
 		LM_ERR("wrong log facility\n");
 		return E_UNSPEC;
 	}
 	facility = (int*)pkg_malloc(sizeof(int));
 	if(facility == NULL)
 	{
 		LM_ERR("no more memory\n");
 		return E_UNSPEC;
 	}
 	*facility = str2facility(s.s);
 	if (*facility == -1) {
 		LM_ERR("invalid syslog facility %s\n", s.s);
9f8f66c4
 		pkg_free(facility);
14f5884a
 		return E_UNSPEC;
 	}
 
 	pkg_free(*param);
 	*param = (void*)facility;
 	return 0;
 }
 
7ae36a90
 static int xlog_fixup(void** param, int param_no)
31ccf6a2
 {
7ae36a90
 	if(param==NULL || *param==NULL)
 	{
 		LM_ERR("invalid parameter number %d\n", param_no);
 		return E_UNSPEC;
 	}
 	return xlog_fixup_helper(param, param_no, 0);
 }
31ccf6a2
 
14f5884a
 static int xlog3_fixup(void** param, int param_no)
 {
 	if(param==NULL || *param==NULL)
 	{
 		LM_ERR("invalid parameter number %d\n", param_no);
 		return E_UNSPEC;
 	}
 	/* fixup loglevel */
 	if (param_no == 2) {
 		return xlog_fixup_helper(param, 1, 0);
 	}
 	/* fixup log message */
 	if (param_no == 3) {
 		return xdbg_fixup_helper(param, 3, 0);
 	}
 	/* fixup facility */
 	return xlog3_fixup_helper(param, param_no);
 }
 
7ae36a90
 static int xdbg_fixup(void** param, int param_no)
 {
 	if(param_no!=1 || param==NULL || *param==NULL)
31ccf6a2
 	{
7ae36a90
 		LM_ERR("invalid parameter number %d\n", param_no);
 		return E_UNSPEC;
31ccf6a2
 	}
7ae36a90
 	return xdbg_fixup_helper(param, param_no, 0);
 }
31ccf6a2
 
14f5884a
 static int xlogl3_fixup(void** param, int param_no)
 {
 	if(param==NULL || *param==NULL)
 	{
 		LM_ERR("invalid parameter number %d\n", param_no);
 		return E_UNSPEC;
 	}
 	/* fixup loglevel */
 	if (param_no == 2) {
 		return xlog_fixup_helper(param, 1, 1);
 	}
 	/* fixup log message */
 	if (param_no == 3) {
 		return xdbg_fixup_helper(param, 3, 1);
 	}
 	/* fixup facility */
 	return xlog3_fixup_helper(param, param_no);
 }
 
7ae36a90
 static int xlogl_fixup(void** param, int param_no)
 {
 	if(param==NULL || *param==NULL)
 	{
 		LM_ERR("invalid parameter number %d\n", param_no);
 		return E_UNSPEC;
 	}
 	return xlog_fixup_helper(param, param_no, 1);
31ccf6a2
 }
adafd759
 
7ae36a90
 static int xdbgl_fixup(void** param, int param_no)
 {
 	if(param_no!=1 || param==NULL || *param==NULL)
 	{
 		LM_ERR("invalid parameter number %d\n", param_no);
 		return E_UNSPEC;
 	}
 	return xdbg_fixup_helper(param, param_no, 1);
 }
adafd759
 
 int pv_parse_color_name(pv_spec_p sp, str *in)
 {
 
 	if(in==NULL || in->s==NULL || sp==NULL)
 		return -1;
 
 	if(in->len != 2)
 	{
 		LM_ERR("color name must have two chars\n");
 		return -1;
 	}
 	
 	/* foreground */
 	switch(in->s[0])
 	{
 		case 'x':
 		case 's': case 'r': case 'g':
 		case 'y': case 'b': case 'p':
 		case 'c': case 'w': case 'S':
 		case 'R': case 'G': case 'Y':
 		case 'B': case 'P': case 'C':
 		case 'W':
 		break;
 		default: 
 			goto error;
 	}
                                
 	/* background */
 	switch(in->s[1])
 	{
 		case 'x':
 		case 's': case 'r': case 'g':
 		case 'y': case 'b': case 'p':
 		case 'c': case 'w':
 		break;   
 		default: 
 			goto error;
 	}
 	
 	sp->pvp.pvn.type = PV_NAME_INTSTR;
 	sp->pvp.pvn.u.isname.type = AVP_NAME_STR;
 	sp->pvp.pvn.u.isname.name.s = *in;
 
 	sp->getf = pv_get_color;
 
 	/* force the color PV type */
 	sp->type = PVT_COLOR;
 	return 0;
 error:
 	LM_ERR("invalid color name\n");
 	return -1;
 }
 
 static int pv_get_color(struct sip_msg *msg, pv_param_t *param,
 		pv_value_t *res)
 {
c79dffa9
 	str s = {"", 0};
adafd759
 
b2f45a4b
 	if(log_stderr==0 && force_color==0)
 	{
c79dffa9
 		LM_DBG("ignoring colors\n");
81524950
 		return pv_get_strval(msg, param, res, &s);
b2f45a4b
 	}
 
c79dffa9
 	dprint_term_color(param->pvn.u.isname.name.s.s[0],
 			param->pvn.u.isname.name.s.s[1], &s);
81524950
 	return pv_get_strval(msg, param, res, &s);
adafd759
 }
 
a5a58239
 /**
  *
  */
 static int xlog_log_colors_param(modparam_t type, void *val)
 {
 	param_t* params_list = NULL;
 	param_hooks_t phooks;
 	param_t *pit=NULL;
 	str s;
 	int level;
 
 	if(val==NULL)
 		goto error;
 
 	s.s = (char*)val;
 	s.len = strlen(s.s);
 
 	if(s.len<=0)
 		goto error;
 
 	if(s.s[s.len-1]==';')
 		s.len--;
 	if (parse_params(&s, CLASS_ANY, &phooks, &params_list)<0)
 		goto error;
 
 	for (pit = params_list; pit; pit=pit->next)
 	{
 		if (pit->name.len==7
 				&& strncasecmp(pit->name.s, "l_alert", 7)==0) {
 			level = L_ALERT;
 		} else if (pit->name.len==5
 				&& strncasecmp(pit->name.s, "l_bug", 5)==0) {
 			level = L_BUG;
 		} else if (pit->name.len==7
 				&& strncasecmp(pit->name.s, "l_crit2", 7)==0) {
 			level = L_CRIT2;
 		} else if (pit->name.len==6
 				&& strncasecmp(pit->name.s, "l_crit", 6)==0) {
 			level = L_CRIT;
 		} else if (pit->name.len==5
 				&& strncasecmp(pit->name.s, "l_err", 5)==0) {
 			level = L_ERR;
 		} else if (pit->name.len==6
 				&& strncasecmp(pit->name.s, "l_warn", 6)==0) {
 			level = L_WARN;
 		} else if (pit->name.len==8
 				&& strncasecmp(pit->name.s, "l_notice", 8)==0) {
 			level = L_NOTICE;
 		} else if (pit->name.len==6
 				&& strncasecmp(pit->name.s, "l_info", 6)==0) {
 			level = L_INFO;
 		} else if (pit->name.len==5
 				&& strncasecmp(pit->name.s, "l_dbg", 5)==0) {
 			level = L_DBG;
 		} else {
 			LM_ERR("invalid level name %.*s\n",
 					pit->name.len, pit->name.s);
 			goto error;
 		}
 			
 		if(pit->body.len!=2) {
 			LM_ERR("invalid color spec for level %.*s (%.*s)\n",
 					pit->name.len, pit->name.s,
 					pit->body.len, pit->body.s);
 			goto error;
 		}
 		dprint_color_update(level, pit->body.s[0], pit->body.s[1]);
 	}
 
 	if(params_list!=NULL)
 		free_params(params_list);
 	return 0;
 
 error:
 	if(params_list!=NULL)
 		free_params(params_list);
 	return -1;
 
 }