Asterisk模块编写及例子


前言

Asterisk模块无论内容复杂与否,它的结构都是十分简单的。本文将说明如何新建一个res_helloworld.so模块,以及在res_helloworld.so中添加cdr显示和cli功能。


一、新建res_helloworld.so模块

创建文件名为res_helloworld.c,存放在Asterisk的源代码树/res目录下。

首先每个Asterisk模块都包含主要的Asterisk头文件,asterisk.h

#include "asterisk.h"

接下来,包含ASTERISK_FILE_VERSION宏,该宏用于注册该文件的版本,通过CLI命令“core show file version like filename”命令查看文件SVN版本。

ASTERISK_FILE_VERSION(__FILE__, "$Revision: 100001 $")

包含Asterisk模块头文件,包含该头文件是定义实现Asterisk模块所必须的。

#include "asterisk/module.h"

让我们继续进行同时包含使用Asteisk日志模块接口,用于显示Asterisk日志信息,显示日志信息也是本模块所要做的事情。

#include "asterisk/logger.h"

现在包含每个Asterisk模块必须的使用的两个函数,load_module()和unload_module().当Asterisk加载和卸载模块时会调用他们。

static int load_module(void)
{
   ast_log(LOG_NOTICE, "Hello World!/n");
   return AST_MODULE_LOAD_SUCCESS;
}
static int unload_module(void)
{
  ast_log(LOG_NOTICE, "Goodbye World!/n");
  return 0;
}

最后,每个模块必须包含AST_MODULE_INFO宏实例。该宏包含模块必要代码是用于该模块被加载时向Asterisk core注册自己。

AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hello World");

最终的结果构成res_helloworld.c文件

#include "asterisk.h"             //每个Asterisk模块都包含主要的Asterisk头文件
#include "asterisk/module.h"      //实现asterisk模块所必需的
#include "asterisk/logger.h" 
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 100001 $")//该宏用于注册该文件的版本
static int load_module(void)         //Asterisk加载和卸载模块时会调用他们
{
	ast_log(LOG_NOTICE, "Hello World!\n");
	return AST_MODULE_LOAD_SUCCESS;
} 
static int unload_module(void)
{
	ast_log(LOG_NOTICE, "Goodbye World!\n");
	return 0;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hello World");

重新编译Asterisk,编译系统将自动发现该模块,该模块像其他模块一样,也会被编译,最后安装。
编译成功后,将res_helloworld.so 拷贝到 /usr/lib/asterisk/modules目录下。
运行Asterisk,这时可以确认你的模块是否被正确的加载:

*CLI> module show like helloworld
Module 				Description 	Use Count
res_helloworld.so 	Hello World 	0
1 modules loaded

通过CLI命令可以自己卸载、加载你的模块,可以观测到日志信息。

*CLI> module unload res_helloworld.so
	[Jun 19 10:50:57] NOTICE[26612]: res_helloworld.c:35 unload_module: Goodbye World!
*CLI> module load res_helloworld.so
 	[Jun 19 10:51:05] NOTICE[26612]: res_helloworld.c:42 load_module: Hello World!
	Loaded res_helloworld.so => (Hello World)

二、res_helloworld.so模块中引入cdr

第一部分已经介绍了res_helloworld.so 模块。该模块只实现了如下功能:通过Asterisk 编译后,再加载到Asteisk 中去,当被加载或卸载时打印简单的日志信息。现在让该模块做些更有意思的事情。

asterisk模块加载的两个关键函数,load_module和 unload_module,它们的具体功能如下:
load_module是:加载模块,为asterisk添加新的功能供asterisk使用。
unload_module :卸载当前模块,asterisk将无法使用相关的功能,若使用将会报错。

现在是更新模块提供功能给Asterisk 的时候了。我们将从CDR 处理开始,CDR 接口 不算是一个非常简单的应用。首先是添加合适的头文件。

#include "asterisk/cdr.h"

然后,我们增加一个新的函数,该函数在每次CDR post 时将会调用,参数是CDR 本身。

static int cdr_helloworld(struct ast_cdr *cdr)
{
	ast_log(LOG_NOTICE,"We got a CDR for channel '%s'. Source: '%s', Dest: '%s', Duration: %ld/n",cdr->channel, cdr->src, cdr->dst, cdr->duration);
	return 0;
}

接下来,将刚刚实现的应用通过load_module() 和unload_module() 函数添加到该模块中去。

在load_module() 函数中,将增加一个函数调用用于向Asterisk Core 注册该CDR 处理。参数是该应用的名称,简短的描述,以及当CDR post 时Asterisk Core 调用函数。

ast_cdr_register("HelloWorld", "Hello World CDR Handler", cdr_helloworld);

在unload_module() 中,需要对刚刚添加的应用注销。

ast_cdr_unregister("HelloWorld");

更新后的res_helloworld.c文件如下:

#include "asterisk.h"             //每个Asterisk模块都包含主要的Asterisk头文件
#include "asterisk/module.h"      //实现asterisk模块所必需的
#include "asterisk/logger.h" 
#include "asterisk/cdr.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 100001 $")//该宏用于注册该文件的版本
static int cdr_helloworld(struct ast_cdr *cdr)
{
	ast_log(LOG_NOTICE,"We got a CDR for channel '%s'. Source: '%s', Dest: '%s', Duration: %ld/n",cdr->channel, cdr->src, cdr->dst, cdr->duration);
	return 0;
}
static int load_module(void)         //Asterisk加载和卸载模块时会调用他们
{
	//ast_log(LOG_NOTICE, "Hello World!\n");
	ast_cdr_register("HelloWorld", "Hello World CDR Handler", cdr_helloworld);
	return AST_MODULE_LOAD_SUCCESS;
} 
static int unload_module(void)
{
	//ast_log(LOG_NOTICE, "Goodbye World!\n");
	ast_cdr_unregister("HelloWorld");
	return 0;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hello World");

编译安装后,运行Asterisk ,可以去确认你的新CDR 应用是否被注册到了Asterisk Core 。

*CLI> cdr show status
CDR registered backend: HelloWorld

当你挂电话,将会看到CDR 处理被执行。

[Jun 20 18:08:29] NOTICE[4922]: res_helloworld.c:36 cdr_helloworld: We got a CDR for channel ‘SIP/5001-007e9da8′. Source: ‘5001′, Dest: ‘586′, Duration: 1

其中ast_cdr_register函数和ast_cdr_unregister函数是在cdr.c中定义的,通过asterisk/cdr.h调用。

三、res_helloworld.so模块引入CLI

在该部分你将看到如何实现Asterisk CLI命令。对Asterisk来说,CLI是尤为重要的,无论是在进行配置、显示状态以及调试都会用到。该部分将对第二部分的基础上进行添加代码。

首先我们需要包含定义CLI命令接口的头文件。

#include "asterisk/cli.h"

我们要实现的命令是echo,用于回显,不过只回显第一个参数。用于实现CLI回显的部分代码如下,稍后解释。

static char *handle_cli_echo(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
    switch (cmd) {
    case CLI_INIT:
        e->command = "echo";
        e->usage =
            "Usage: echo <stuff>/n"
            "       Print back the first argument./n"
            "Examples:/n"
            "       echo foo/n"
            "       echo /"multiple words/"/n"
            "";
        return NULL;
    case CLI_GENERATE:
        return NULL;
    }
 
    if (a->argc == e->args) {
        ast_cli(a->fd, "You did not provide an argument to echo/n/n");
        return CLI_SHOWUSAGE;
    }
 
    ast_cli(a->fd, "%s/n", a->argv[1]);
 
    return CLI_SUCCESS;
}

首行定义的CLI处理接口符合CLI命令行处理的函数原型。ast_cli_entry包含CLI命令处理的静态信息,当CLI命令非正常执行其他调用的时候,会设置cmd参数。ast_cli_args包含参数信息。

在函数的开头switch是用来处理cmd参数,以指示调用什么功能。有两种情况需要处理:
CLI_INIT:每一个CLI命令都需要处理程序都要执行一次CLI_INIT,用来要求说明命令是什么,用法是什么等。
CLI_GENERATE:用户自动补全,相当于一个默认出口?类似default?

接下来的代码表示至少给函数提供了一个参数。值得注意的是,这里的ast_cli_entry参数用于检索多少命令本身定义的参数和ast_cli_args用于检索多少参数,实际上是在CLI命令行下执行此命令时指定的。如果它们相等,就能提供简单的"echo".

   if (a->argc == e->args) {
        ast_cli(a->fd, "You did not provide an argument to echo/n/n");
        return CLI_SHOWUSAGE;
    }

最后,我们打印一个参数到CLI,返回CLI命令成功执行的结果。

    ast_cli(a->fd, "%s/n", a->argv[1]);
    
    return CLI_SUCCESS;

下一步增加包含在这个模块里的CLI命令表。我们将使用AST_CLI_DEFINE()为表增加单个入口。AST_CLI_DEFINE包含了CLI处理命令函数的指针,以及这个命令是用来做什么的总结。

static struct ast_cli_entry cli_helloworld[] = {
	AST_CLI_DEFINE(handle_cli_echo,"Echo the CLI"),
};

最后,就像我们在前面文章中提到的那样,我们要修改load_module() 和unload_module()来把CLI命令表注册到Asterisk内核中。
卸载模块unload_module,加入:

ast_cli_unregister_multiple(cli_helloworld,ARRAY_LEN(cli_helloworld));

加载模块load_module,加入:

ast_cli_register_multiple(cli_helloworld,ARRAY_LEN(cli_helloworld));

更新后的res_helloworld.c文件如下:

#include "asterisk.h"             //每个Asterisk模块都包含主要的Asterisk头文件
#include "asterisk/module.h"      //实现asterisk模块所必需的
#include "asterisk/logger.h" 
#include "asterisk/cdr.h"
#include "asterisk/cli.h"
ASTERISK_FILE_VERSION(__FILE__, "$Revision: 100001 $")//该宏用于注册该文件的版本
static int cdr_helloworld(struct ast_cdr *cdr)
{
	ast_log(LOG_NOTICE,"We got a CDR for channel '%s'. Source: '%s', Dest: '%s', Duration: %ld/n",cdr->channel, cdr->src, cdr->dst, cdr->duration);
	return 0;
}
static char *handle_cli_echo(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
    switch (cmd) {
    case CLI_INIT:
        e->command = "echo";
        e->usage =
            "Usage: echo <stuff>/n"
            "       Print back the first argument./n"
            "Examples:/n"
            "       echo foo/n"
            "       echo /"multiple words/"/n"
            "";
        return NULL;
    case CLI_GENERATE:
        return NULL;
    }
 
    if (a->argc == e->args) {
        ast_cli(a->fd, "You did not provide an argument to echo/n/n");
        return CLI_SHOWUSAGE;
    }
 
    ast_cli(a->fd, "%s/n", a->argv[1]);
 
    return CLI_SUCCESS;
}
static struct ast_cli_entry cli_helloworld[] = {
	AST_CLI_DEFINE(handle_cli_echo,"Echo the CLI"),
};
static int load_module(void)         //Asterisk加载和卸载模块时会调用他们
{
	//ast_log(LOG_NOTICE, "Hello World!\n");
	ast_cdr_register("HelloWorld", "Hello World CDR Handler", cdr_helloworld);
	ast_cli_register_multiple(cli_helloworld,ARRAY_LEN(cli_helloworld));
	return AST_MODULE_LOAD_SUCCESS;
} 
static int unload_module(void)
{
	//ast_log(LOG_NOTICE, "Goodbye World!\n");
	ast_cdr_unregister("HelloWorld");
	ast_cli_unregister_multiple(cli_helloworld,ARRAY_LEN(cli_helloworld));
	return 0;
}
AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Hello World");

就这样,重新编译安装这个模块,然后你可以试试这个模块了:

*CLI> help echo
Usage:echo
	Print back the first argument.
Examples:
	echo foo
	echo "multiple words"
*CLI> echo
You did not to provide an argument to echo

Usage:echo
	Print back the first argument.
Examples:
	echo foo
	echo "multiple words"
*CLI> echo foo
foo
*CLI> echo helloworld
helloworld
*CLI> echo "helloworld"
helloworld

参考链接

https://wenku.baidu.com/view/3fa41f2d3169a4517623a304.html

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值