modules/timer/timer.c
3f0b52da
 /*
  * Copyright (C) 2006 iptelorg GmbH
  *
bc2b423d
  * This file is part of Kamailio, a free SIP server.
3f0b52da
  *
bc2b423d
  * Kamailio is free software; you can redistribute it and/or modify
3f0b52da
  * 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
  *
bc2b423d
  * Kamailio is distributed in the hope that it will be useful,
3f0b52da
  * 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
3f0b52da
  */
 
 
 #include <string.h>
 #include <stdlib.h>
 #include <stdio.h>
 #include <ctype.h>
 #include "../../timer.h"
 #include "../../timer_ticks.h"
 #include "../../route.h"
 #include "../../sr_module.h"
 #include "../../mem/mem.h"
 #include "../../mem/shm_mem.h"
 #include "../../str.h"
 #include "../../error.h"
 #include "../../config.h"
 #include "../../trim.h"
 #include "../../select.h"
 #include "../../ut.h"
184af565
 #include "../../select_buf.h"
3f0b52da
 
 #include "../../receive.h"
 #include "../../ip_addr.h"
 
 #include "../../receive.h"
 #include "../../globals.h"
 #include "../../route.h"
 #include "../../parser/msg_parser.h"
 #include "../../action.h"
 #include "../../script_cb.h"
 #include "../../dset.h"
 #include "../../usr_avp.h"
 
 
 MODULE_VERSION
 
0b3d4c49
 #define MODULE_NAME "timer"
3f0b52da
 
 struct timer_action {
970e3515
 	char *timer_name;
3f0b52da
 	int route_no;
 	int interval;
 	int enable_on_start;
 	int disable_itself;
 	unsigned short flags; /* slow / fast */
 	struct timer_ln *link;
 
 	struct timer_action* next;
 };
 
 /* list of all operations */
 static struct timer_action* timer_actions = 0;
 static struct timer_action* pkg_timer_actions = 0;
 static struct receive_info rcv_info;
970e3515
 static struct timer_action* timer_executed = 0;
3f0b52da
 
 #define eat_spaces(_p) \
 	while( *(_p)==' ' || *(_p)=='\t' ){\
 	(_p)++;}
 
970e3515
 #define eat_alphanum(_p) \
 	while ( (*(_p) >= 'a' && *(_p) <= 'z') || (*(_p) >= 'A' && *(_p) <= 'Z') || (*(_p) >= '0' && *(_p) <= '9') || (*(_p) == '_') ) {\
 		(_p)++;\
 	}
 
 static struct timer_action* find_action_by_name(struct timer_action* timer_actions, char *name, int len) {
 	struct timer_action *a;
 	if (len == -1) len = strlen(name);
 	for (a=timer_actions; a; a = a->next) {		
 		if (a->timer_name && strlen(a->timer_name)==len && strncmp(name, a->timer_name, len) == 0)
 			return a;
 	}
 	return NULL;
 }
 
 static int sel_root(str* res, select_t* s, struct sip_msg* msg) {  /* dummy */
 	return 0;
 }
 
3f0b52da
 static int sel_timer(str* res, select_t* s, struct sip_msg* msg) {
 	struct timer_action* a;
 	if (!msg) { /* select fixup */
970e3515
 		a = find_action_by_name(timer_actions /* called after mod_init */, s->params[2].v.s.s, s->params[2].v.s.len);
3f0b52da
 		if (!a) {
0b3d4c49
 			ERR(MODULE_NAME": timer_enable_fixup: timer '%.*s' not declared\n", s->params[2].v.s.len, s->params[2].v.s.s);
3f0b52da
 			return E_CFG;
 		}
970e3515
 		s->params[2].v.p = a;
3f0b52da
 	}
 	return 0;
 }
 
 static int sel_enabled(str* res, select_t* s, struct sip_msg* msg) {
 	static char buf[2] = "01";
 	if (!msg) return sel_timer(res, s, msg);
 	res->len = 1;
970e3515
 	res->s = &buf[(((struct timer_action*) s->params[2].v.p)->link->flags & F_TIMER_ACTIVE) != 0];
 	return 0;
 }
 
 static int sel_executed(str* res, select_t* s, struct sip_msg* msg) {
 	if (!timer_executed) return 1;
 	res->s = timer_executed->timer_name;
 	res->len = strlen(res->s);
3f0b52da
 	return 0;
 }
 
 select_row_t sel_declaration[] = {
0b3d4c49
 	{ NULL, SEL_PARAM_STR, STR_STATIC_INIT(MODULE_NAME), sel_root, SEL_PARAM_EXPECTED},
 	{ sel_root, SEL_PARAM_STR, STR_STATIC_INIT("timer"), sel_timer, SEL_PARAM_EXPECTED|CONSUME_NEXT_STR|FIXUP_CALL},
 	{ sel_timer, SEL_PARAM_STR, STR_STATIC_INIT("enabled"), sel_enabled, 0},
 	{ sel_root, SEL_PARAM_STR, STR_STATIC_INIT("executed"), sel_executed, 0},
970e3515
 	
0b3d4c49
 	{ NULL, SEL_PARAM_STR, STR_NULL, NULL, 0}
3f0b52da
 };
 
e18b3055
 static unsigned int timer_msg_no = 0;
3f0b52da
 
 static ticks_t timer_handler(ticks_t ticks, struct timer_ln* tl, void* data) {
e3b1b2c4
 	/*?min length of first line of message is 16 char!?*/
3f0b52da
 	#define MSG "GET /timer HTTP/0.9\n\n"
 	struct sip_msg* msg;
 	struct timer_action *a;
4955c477
 	struct run_act_ctx ra_ctx;
3f0b52da
 
 	a = data;
 	if (!a->disable_itself) {
 
0b3d4c49
 		DEBUG(MODULE_NAME": handler: called at %d ticks, timer: '%s', pid:%d\n", ticks, a->timer_name, getpid());
3f0b52da
 
 		if (a->route_no >= main_rt.idx) {
0b3d4c49
 			BUG(MODULE_NAME": invalid routing table number #%d of %d\n", a->route_no, main_rt.idx);
3f0b52da
 			goto err2;
 		}
 		if (!main_rt.rlist[a->route_no]) {
0b3d4c49
 			WARN(MODULE_NAME": route not declared (hash:%d)\n", a->route_no);
3f0b52da
 			goto err2;
 		}
 		msg=pkg_malloc(sizeof(struct sip_msg));
 		if (msg==0) {
0b3d4c49
 			ERR(MODULE_NAME": handler: no mem for sip_msg\n");
3f0b52da
 			goto err2;
 		}
e18b3055
 		timer_msg_no++;
3f0b52da
 		memset(msg, 0, sizeof(struct sip_msg)); /* init everything to 0 */
 
 		msg->buf=MSG;
 		msg->len=sizeof(MSG)-1;
 
 		msg->rcv= rcv_info;
e18b3055
 		msg->id=timer_msg_no;
3f0b52da
 		msg->set_global_address=default_global_address;
 		msg->set_global_port=default_global_port;
 
 		if (parse_msg(msg->buf, msg->len, msg)!=0){
0b3d4c49
 			ERR(MODULE_NAME": handler: parse_msg failed\n");
3f0b52da
 			goto err;
 		}
 		/* ... clear branches from previous message */
 		clear_branches();
184af565
 		reset_static_buffer();
8216129f
 		if (exec_pre_script_cb(msg, REQUEST_CB_TYPE)==0 )
3f0b52da
 			goto end; /* drop the request */
 		/* exec the routing script */
970e3515
 		timer_executed = a;
4955c477
 		init_run_actions_ctx(&ra_ctx);
 		run_actions(&ra_ctx, main_rt.rlist[a->route_no], msg);
970e3515
 		timer_executed = 0;
3f0b52da
 		/* execute post request-script callbacks */
8216129f
 		exec_post_script_cb(msg, REQUEST_CB_TYPE);
3f0b52da
 	end:
 		reset_avps();
0b3d4c49
 		DEBUG(MODULE_NAME": handler: cleaning up\n");
3f0b52da
 	err:
 		free_sip_msg(msg);
 		pkg_free(msg);
 	err2:	;
 	}
         /* begin critical section */
 	if (a->disable_itself) {
 
23bd471b
 		timer_allow_del();
3f0b52da
 		timer_del(a->link);
 		timer_reinit(a->link);
 		a->disable_itself = 0;
 	        /* end critical section */
 		return 0;   /* do no call more */
 	}
 	else
         	return (ticks_t)(-1); /* periodical */
 }
 
 static int timer_enable_fixup(void** param, int param_no) {
 	struct timer_action* a;
970e3515
  	int /*res, */n;
3f0b52da
 	switch (param_no) {
 		case 1:
970e3515
 			a = find_action_by_name(timer_actions /* called after mod_init*/, (char*) *param, -1);
3f0b52da
 			if (!a) {
0b3d4c49
 				ERR(MODULE_NAME": timer_enable_fixup: timer '%s' not declared\n", (char*) *param);
3f0b52da
 				return E_CFG;
 			}
 			*param = a;
 			break;
 		case 2:
970e3515
 		/*	res = fixup_int_12(param, param_no);
 			if (res < 0) return res; */
 			n=atoi((char *)*param);
74cbfbfe
 			*param = (void*)(long)(/*(int) *param*/n != 0);
3f0b52da
 			break;
 		default: ;
 	}
 	return 0;
 }
 
 static int timer_enable_func(struct sip_msg* m, char* timer_act, char* enable) {
 	struct timer_action* a;
 	int en;
 	a = (void*) timer_act;
74cbfbfe
 	en = (int)(long) enable;
3f0b52da
 	/* timer is not deleted immediately but is removed from handler by itself because timer_del may be slow blocking procedure
 	 * Disable and enable in sequence may be tricky
 	 */
         /* begin critical section */
 	if ((a->link->flags & F_TIMER_ACTIVE) == 0) {
 		if (en) {
 			timer_reinit(a->link);
 			timer_add(a->link, MS_TO_TICKS(a->interval));
 			a->disable_itself = 0;
 		}
 	}
 	else {
 		if (en && a->disable_itself) {
 			a->disable_itself = 0;	/* it's not 100% reliable! */
 		}
 		else if (!en) {
 			a->disable_itself++;
 		}
 	}
         /* end critical section */
 	return 1;
 }
 
 static int get_next_part(char** s, str* part, char delim) {
 	char *c, *c2;
 	c = c2 = *s;
 	eat_spaces(c);
 	while (*c2!=delim && *c2!=0) c2++;
 
 	if (*c2) {
 		*s = c2+1;
 	}
 	else {
 		*s = c2;
 	}
 	eat_spaces(*s);
 	c2--;
 	/* rtrim */
 	while ( c2 >= c && ((*c2 == ' ')||(*c2 == '\t')) ) c2--;
 	part->s = c;
 	part->len = c2-c+1;
 	return part->len;
 }
 
970e3515
 /* timer_id=route_no,interval_ms[,"slow"|"fast"[,"enable"]] */
3f0b52da
 static int declare_timer(modparam_t type, char* param) {
 	int n;
 	unsigned int route_no, interval, enabled, flags;
970e3515
 	struct timer_action *pa;
 	char *p, *save_p, c, *timer_name;
3f0b52da
 	str s;
 
970e3515
 	timer_name = 0;
3f0b52da
 	save_p = p = param;
970e3515
 	eat_alphanum(p);
 	if (*p != '=' || p == save_p) goto err;
 	*p = '\0';
 	timer_name = save_p;
 	p++;
 	if (find_action_by_name(pkg_timer_actions, timer_name, -1) != NULL) {
0b3d4c49
 		ERR(MODULE_NAME": declare_timer: timer '%s' already exists\n", timer_name);
970e3515
 		return E_CFG;
 	}
 
 	save_p = p;
3f0b52da
 	if (!get_next_part(&p, &s, ',')) goto err;
 
 	c = s.s[s.len];
 	s.s[s.len] = '\0';
 	n = route_get(&main_rt, s.s);
 	s.s[s.len] = c;
 	if (n == -1) goto err;
 	route_no = n;
 
 	save_p = p;
 	if (!get_next_part(&p, &s, ',')) goto err;
 	if (str2int(&s, &interval) < 0) goto err;
 
 	save_p = p;
 	flags = 0;
 	if (get_next_part(&p, &s, ',')) {
 		if (s.len == 4 && strncasecmp(s.s, "FAST", 4)==0)
 			flags = F_TIMER_FAST;
 		else if (s.len == 4 && strncasecmp(s.s, "SLOW", 4)==0)
 			;
 		else goto err;
 	}
 
 	save_p = p;
 	enabled = 0;
 	if (get_next_part(&p, &s, ',')) {
 		if (s.len == 6 && strncasecmp(s.s, "ENABLE", 6)==0)
 			enabled = 1;
 		else goto err;
 	}
 
970e3515
 	
 	pa = pkg_malloc(sizeof(*pa));   /* cannot use shmmem here! */
 	if (!pa) {
0b3d4c49
 		ERR(MODULE_NAME": cannot allocate timer data\n");
3f0b52da
 		return E_OUT_OF_MEM;
 	}
970e3515
 	memset(pa, 0, sizeof(*pa));
 	pa->timer_name = timer_name;
 	pa->route_no = route_no;
 	pa->interval = interval;
 	pa->enable_on_start = enabled;
 	pa->flags = flags;
 	pa->next = pkg_timer_actions;
 	pkg_timer_actions = pa;
3f0b52da
 
 	return 0;
 err:
0b3d4c49
 	ERR(MODULE_NAME": declare_timer: timer_name: '%s', error near '%s'\n", timer_name, save_p);
3f0b52da
 	return E_CFG;
 }
 
 static int mod_init() {
 	struct timer_action *a, **pa;
 
0b3d4c49
 	DEBUG(MODULE_NAME": init: initializing, pid=%d\n", getpid());
3f0b52da
 
 	/* copy from pkg to shm memory */
 	for (pa=&timer_actions; pkg_timer_actions; pa=&(*pa)->next) {
 		a = pkg_timer_actions;
 		*pa = shm_malloc(sizeof(**pa));
 		if (!*pa) {
0b3d4c49
 			ERR(MODULE_NAME": cannot allocate timer data\n");
3f0b52da
 			return E_OUT_OF_MEM;
 		}
 		memcpy(*pa, a, sizeof(**pa));
 		(*pa)->next = 0;
 		pkg_timer_actions = a->next;
 		pkg_free(a);
 	}
 
 	for (a=timer_actions; a; a=a->next) {
 		a->link = timer_alloc();
 		if (!a->link) {
0b3d4c49
 			ERR(MODULE_NAME": init: cannot allocate timer\n");
3f0b52da
 			return E_OUT_OF_MEM;
 		}
 		timer_init(a->link, timer_handler, a, a->flags);
 		if (!a->link) {
0b3d4c49
 			ERR(MODULE_NAME": init: cannot initialize timer\n");
3f0b52da
 			return E_CFG;
 		}
 	}
 
 	memset(&rcv_info, 0, sizeof(rcv_info));
 	register_select_table(sel_declaration);
 	return 0;
 }
 
 static int child_init(int rank) {
 	struct timer_action* a;
970e3515
 	/* may I start timer in mod_init ?? */
3f0b52da
 	if (rank!=PROC_TIMER) return 0;
 	for (a=timer_actions; a; a=a->next) {
 		if (a->enable_on_start) {
 			timer_add(a->link, MS_TO_TICKS(a->interval));
 		}
 	}
 	return 0;
 }
 
 static void destroy_mod(void) {
 	struct timer_action* a;
0b3d4c49
 	DEBUG(MODULE_NAME": destroy: destroying, pid=%d\n", getpid());
3f0b52da
 	while (timer_actions) {
 		a = timer_actions;
 		if (a->link) {
 			timer_del(a->link);
 			timer_free(a->link);
 		}
 		timer_actions = a->next;
 		shm_free(a);
 	}
 }
 
 /*
  * Exported functions
  */
 static cmd_export_t cmds[] = {
0b3d4c49
 	{MODULE_NAME"_enable", timer_enable_func, 2, timer_enable_fixup, REQUEST_ROUTE | ONREPLY_ROUTE | FAILURE_ROUTE | BRANCH_ROUTE | ONSEND_ROUTE},
3f0b52da
 	{0, 0, 0, 0, 0}
 };
 
 /*
  * Exported parameters
  */
 static param_export_t params[] = {
 	{"declare_timer", PARAM_STRING|PARAM_USE_FUNC, (void*) declare_timer},
 	{0, 0, 0}
 };
 
 
 struct module_exports exports = {
0b3d4c49
 	MODULE_NAME,
3f0b52da
 	cmds,        /* Exported commands */
 	0,	     /* RPC */
 	params,      /* Exported parameters */
 	mod_init,    /* module initialization function */
 	0,           /* response function*/
 	destroy_mod, /* destroy function */
 	0,           /* oncancel function */
 	child_init   /* per-child init function */
 };