[[TOC]]

事件路由简介

在某些模块中,我们看到有一些模块自定义的事件路由。

例如dispatcher模块,或者rtpengine模块。

event_route[dispatcher:dst-down] {
    xlog("L_ERR", "Destination down: $rm $ru ($du)\n");
}

event_route[rtpengine:dtmf-event] {
    xlog("L_INFO", "callid: $avp(dtmf_event_callid)\n");
		xlog("L_INFO", "source_tag: $avp(dtmf_event_source_tag)\n");
		xlog("L_INFO", "timestamp: $avp(dtmf_event_timestamp)\n");
		xlog("L_INFO", "dtmf: $avp(dtmf_event)\n");
}

disapcher模块

在dispatch.c文件中,我们看到如下代码

if(!ds_skip_dst(old_state) && ds_skip_dst(idx->dlist[i].flags)) {
		ds_run_route(msg, address, "dispatcher:dst-down", rctx);
} else {
		if(ds_skip_dst(old_state) && !ds_skip_dst(idx->dlist[i].flags))
			ds_run_route(msg, address, "dispatcher:dst-up", rctx);
}

ds_run_route还是定义在dispatch.c文件中,

static void ds_run_route(sip_msg_t *msg, str *uri, char *route, ds_rctx_t *rctx)

接着又一个重要调用。 这里似乎在查找路由。

route这个参数其实就是dispatcher:dst-down, 或者 dispatcher:dst-up

那么event_rt又是什么鬼呢?

rt = route_lookup(&event_rt, route);

event_rt是一个route_list的结构体

// route.c
struct route_list event_rt;

route_list的结构如下,重点是这个str_hash_table这个字段,它似乎是一个hash

struct route_list
{
	struct action **rlist;
	int idx;					 /* first empty entry */
	int entries;				 /* total number of entries */
	struct str_hash_table names; /* name to route index mappings */
};

str_hash_table的结构如下:

struct str_hash_table
{
	struct str_hash_head *table;
	int size;
};

struct str_hash_head
{
	struct str_hash_entry *next;
	struct str_hash_entry *prev;
};

struct str_hash_entry
{
	struct str_hash_entry *next;
	struct str_hash_entry *prev;
	str key;
	unsigned int flags;
	union
	{
		void *p;
		char *s;
		int n;
		char data[sizeof(void *)];
	} u;
};
int route_lookup(struct route_list *rt, char *name)
{
	int len;
	struct str_hash_entry *e;

	len = strlen(name);
	/* check if exists and non empty*/
	e = str_hash_get(&rt->names, name, len);
	if(e) {
		return e->u.n;
	} else {
		return -1;
	}
}

上面介绍了一大堆,我自己都有点晕了。

你给我介绍了事件路由是怎么一层一层调用的,

那我请问你,**这个事件路由是怎么安插代码里的?**事件路由的逻辑在脚本里,而并不是可以映射成模块的某个函数的?

要回答上面上面的问题,就必须要理解yyparse

yyparse

说实在的我也不太懂这个上古神器,我只是简单了解它的功能,就是把输入一些定制脚本语言,然后输出c语言的函数调用。有点类似编译器的概念。

event_route的关键字是

ROUTE_EVENT event_route
| {rt=EVENT_ROUTE;}   event_route_stm

下面就开始进入瞎猜模式,😂

// cfg.y
// 以下注释纯属瞎猜,如果正确,就是凑巧;如果错误,纯属正常;
event_route_stm:
	event_route_main LBRACK EVENT_RT_NAME RBRACK LBRACE actions RBRACE {
		if (!shm_initialized() && init_shm()<0) {
			yyerror("Can't initialize shared memory");
			YYABORT;
		}
    // $3, 在这里代指EVENT_RT_NAME, 就是事件路由的名称
    // 这里其实就是根据名称查询路由的名称
		i_tmp=route_get(&event_rt, $3);
    // -1就是内部错误
		if (i_tmp==-1){
			yyerror("internal error");
			YYABORT;
		}
    // 正值就是重复了
		if (event_rt.rlist[i_tmp]){
			yyerror("duplicate route");
			YYABORT;
		}
    // 最后就是正常,然后把$6, 也就是action放到路由里面
		push($6, &event_rt.rlist[i_tmp]);
	}

route_get就是根据事件路由的名称,找到一个放置事件路由的座位号


/*
 * if the "name" route already exists, return its index, else
 * create a new empty route
 * return route index in rt->rlist or -1 on error
 */
int route_get(struct route_list *rt, char *name)
{
	int len;
	struct str_hash_entry *e;
	int i;

	len = strlen(name);
	/* check if exists and non empty*/
	e = str_hash_get(&rt->names, name, len);
	if(e) {
		i = e->u.n;
	} else {
		i = route_new_list(rt);
		if(i == -1)
			goto error;
		if(route_add(rt, name, i) < 0) {
			goto error;
		}
	}
	return i;
error:
	return -1;
}

然后我们拿着座位号,把事件路由安置上

/* adds an action list to head; a must be null terminated (last a->next=0))*/
void push(struct action *a, struct action **head)
{
	struct action *t;
	if(*head == 0) {
		*head = a;
		return;
	}
	for(t = *head; t->next; t = t->next)
		;
	t->next = a;
}

至此,我们就回来我们自己提出的问题:事件路由的内容是如何被安插进路由模块的。