diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/cdr.c | 48 | ||||
-rw-r--r-- | main/channel.c | 301 | ||||
-rw-r--r-- | main/data.c | 494 | ||||
-rw-r--r-- | main/indications.c | 41 | ||||
-rw-r--r-- | main/pbx.c | 52 |
5 files changed, 850 insertions, 86 deletions
diff --git a/main/cdr.c b/main/cdr.c index db277af29..f14af2cb9 100644 --- a/main/cdr.c +++ b/main/cdr.c @@ -49,6 +49,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/config.h" #include "asterisk/cli.h" #include "asterisk/stringfields.h" +#include "asterisk/data.h" /*! Default AMA flag for billing records (CDR's) */ int ast_default_amaflags = AST_CDR_DOCUMENTATION; @@ -1635,3 +1636,50 @@ int ast_cdr_engine_reload(void) return do_reload(1); } +int ast_cdr_data_add_structure(struct ast_data *tree, struct ast_cdr *cdr, int recur) +{ + struct ast_cdr *tmpcdr; + struct ast_data *level; + struct ast_var_t *variables; + const char *var, *val; + int x = 1, i; + char workspace[256]; + char *tmp; + + if (!cdr) { + return -1; + } + + for (tmpcdr = cdr; tmpcdr; tmpcdr = (recur ? tmpcdr->next : NULL)) { + level = ast_data_add_node(tree, "level"); + if (!level) { + continue; + } + + ast_data_add_int(level, "level_number", x); + + AST_LIST_TRAVERSE(&tmpcdr->varshead, variables, entries) { + if (variables && (var = ast_var_name(variables)) && + (val = ast_var_value(variables)) && !ast_strlen_zero(var) + && !ast_strlen_zero(val)) { + ast_data_add_str(level, var, val); + } else { + break; + } + } + + for (i = 0; cdr_readonly_vars[i]; i++) { + workspace[0] = 0; /* null out the workspace, because the cdr_get_tv() won't write anything if time is NULL, so you get old vals */ + ast_cdr_getvar(tmpcdr, cdr_readonly_vars[i], &tmp, workspace, sizeof(workspace), 0, 0); + if (!tmp) { + continue; + } + ast_data_add_str(level, cdr_readonly_vars[i], tmp); + } + + x++; + } + + return 0; +} + diff --git a/main/channel.c b/main/channel.c index af2c036a9..f6d9ada94 100644 --- a/main/channel.c +++ b/main/channel.c @@ -72,6 +72,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <sys/epoll.h> #endif +#ifdef HAVE_PRI +#include "sig_pri.h" +#endif + struct ast_epoll_data { struct ast_channel *chan; int which; @@ -135,6 +139,17 @@ static AST_RWLIST_HEAD_STATIC(backends, chanlist); #define NUM_CHANNEL_BUCKETS 1567 #endif +#define DATA_EXPORT_CALLERID(MEMBER) \ + MEMBER(ast_callerid, cid_dnid, AST_DATA_STRING) \ + MEMBER(ast_callerid, cid_num, AST_DATA_STRING) \ + MEMBER(ast_callerid, cid_name, AST_DATA_STRING) \ + MEMBER(ast_callerid, cid_ani, AST_DATA_STRING) \ + MEMBER(ast_callerid, cid_pres, AST_DATA_INTEGER) \ + MEMBER(ast_callerid, cid_ani2, AST_DATA_INTEGER) \ + MEMBER(ast_callerid, cid_tag, AST_DATA_STRING) + +AST_DATA_STRUCTURE(ast_callerid, DATA_EXPORT_CALLERID); + #define DATA_EXPORT_CHANNEL(MEMBER) \ MEMBER(ast_channel, blockproc, AST_DATA_STRING) \ MEMBER(ast_channel, appl, AST_DATA_STRING) \ @@ -151,28 +166,14 @@ static AST_RWLIST_HEAD_STATIC(backends, chanlist); MEMBER(ast_channel, parkinglot, AST_DATA_STRING) \ MEMBER(ast_channel, hangupsource, AST_DATA_STRING) \ MEMBER(ast_channel, dialcontext, AST_DATA_STRING) \ - MEMBER(ast_channel, _softhangup, AST_DATA_INTEGER) \ - MEMBER(ast_channel, streamid, AST_DATA_INTEGER) \ - MEMBER(ast_channel, vstreamid, AST_DATA_INTEGER) \ - MEMBER(ast_channel, oldwriteformat, AST_DATA_INTEGER) \ - MEMBER(ast_channel, _state, AST_DATA_INTEGER) \ MEMBER(ast_channel, rings, AST_DATA_INTEGER) \ MEMBER(ast_channel, priority, AST_DATA_INTEGER) \ MEMBER(ast_channel, macropriority, AST_DATA_INTEGER) \ - MEMBER(ast_channel, amaflags, AST_DATA_INTEGER) \ MEMBER(ast_channel, adsicpe, AST_DATA_INTEGER) \ MEMBER(ast_channel, fin, AST_DATA_UNSIGNED_INTEGER) \ MEMBER(ast_channel, fout, AST_DATA_UNSIGNED_INTEGER) \ - MEMBER(ast_channel, hangupcause, AST_DATA_INTEGER) \ - MEMBER(ast_channel, flags, AST_DATA_UNSIGNED_INTEGER) \ - MEMBER(ast_channel, nativeformats, AST_DATA_INTEGER) \ - MEMBER(ast_channel, readformat, AST_DATA_INTEGER) \ - MEMBER(ast_channel, writeformat, AST_DATA_INTEGER) \ - MEMBER(ast_channel, rawreadformat, AST_DATA_INTEGER) \ - MEMBER(ast_channel, rawwriteformat, AST_DATA_INTEGER) \ MEMBER(ast_channel, emulate_dtmf_duration, AST_DATA_UNSIGNED_INTEGER) \ MEMBER(ast_channel, visible_indication, AST_DATA_INTEGER) \ - MEMBER(ast_channel, transfercapability, AST_DATA_INTEGER) \ MEMBER(ast_channel, context, AST_DATA_STRING) \ MEMBER(ast_channel, exten, AST_DATA_STRING) \ MEMBER(ast_channel, macrocontext, AST_DATA_STRING) \ @@ -259,9 +260,170 @@ struct ast_variable *ast_channeltype_list(void) return var; } -int ast_channel_data_add_structure(struct ast_data *tree, struct ast_channel *chan) +static void channel_data_add_flags(struct ast_data *tree, + struct ast_channel *chan) +{ + ast_data_add_bool(tree, "DEFER_DTMF", ast_test_flag(chan, AST_FLAG_DEFER_DTMF)); + ast_data_add_bool(tree, "WRITE_INT", ast_test_flag(chan, AST_FLAG_WRITE_INT)); + ast_data_add_bool(tree, "BLOCKING", ast_test_flag(chan, AST_FLAG_BLOCKING)); + ast_data_add_bool(tree, "ZOMBIE", ast_test_flag(chan, AST_FLAG_ZOMBIE)); + ast_data_add_bool(tree, "EXCEPTION", ast_test_flag(chan, AST_FLAG_EXCEPTION)); + ast_data_add_bool(tree, "MOH", ast_test_flag(chan, AST_FLAG_MOH)); + ast_data_add_bool(tree, "SPYING", ast_test_flag(chan, AST_FLAG_SPYING)); + ast_data_add_bool(tree, "NBRIDGE", ast_test_flag(chan, AST_FLAG_NBRIDGE)); + ast_data_add_bool(tree, "IN_AUTOLOOP", ast_test_flag(chan, AST_FLAG_IN_AUTOLOOP)); + ast_data_add_bool(tree, "OUTGOING", ast_test_flag(chan, AST_FLAG_OUTGOING)); + ast_data_add_bool(tree, "IN_DTMF", ast_test_flag(chan, AST_FLAG_IN_DTMF)); + ast_data_add_bool(tree, "EMULATE_DTMF", ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)); + ast_data_add_bool(tree, "END_DTMF_ONLY", ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)); + ast_data_add_bool(tree, "ANSWERED_ELSEWHERE", ast_test_flag(chan, AST_FLAG_ANSWERED_ELSEWHERE)); + ast_data_add_bool(tree, "MASQ_NOSTREAM", ast_test_flag(chan, AST_FLAG_MASQ_NOSTREAM)); + ast_data_add_bool(tree, "BRIDGE_HANGUP_RUN", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_RUN)); + ast_data_add_bool(tree, "BRIDGE_HANGUP_DONT", ast_test_flag(chan, AST_FLAG_BRIDGE_HANGUP_DONT)); + ast_data_add_bool(tree, "DISABLE_WORKAROUNDS", ast_test_flag(chan, AST_FLAG_DISABLE_WORKAROUNDS)); +} + +static const char *callerid_ton2str(int ton) +{ +#ifdef HAVE_PRI + switch (ton) { + case PRI_TON_INTERNATIONAL: + return "International Number"; + case PRI_TON_NATIONAL: + return "National Number"; + case PRI_TON_NET_SPECIFIC: + return "Network Specific Number"; + case PRI_TON_SUBSCRIBER: + return "Subscriber Number"; + case PRI_TON_ABBREVIATED: + return "Abbreviated number"; + case PRI_TON_RESERVED: + return "Reserved Number"; + case PRI_TON_UNKNOWN: + default: + return "Unknown Number Type"; + } +#endif + return ""; +} + +int ast_channel_data_add_structure(struct ast_data *tree, + struct ast_channel *chan, int add_bridged) { - return ast_data_add_structure(ast_channel, tree, chan); + struct ast_channel *bc; + struct ast_data *data_bridged, *data_cdr, *data_flags, *data_zones; + struct ast_data *data_callerid, *enum_node, *data_softhangup; + + if (!tree) { + return -1; + } + + ast_data_add_structure(ast_channel, tree, chan); + + if (add_bridged) { + bc = ast_bridged_channel(chan); + if (bc) { + data_bridged = ast_data_add_node(tree, "bridged"); + if (!data_bridged) { + return -1; + } + ast_channel_data_add_structure(data_bridged, bc, 0); + } + } + + ast_data_add_codecs(tree, "oldwriteformat", chan->oldwriteformat); + ast_data_add_codecs(tree, "nativeformats", chan->nativeformats); + ast_data_add_codecs(tree, "readformat", chan->readformat); + ast_data_add_codecs(tree, "writeformat", chan->writeformat); + ast_data_add_codecs(tree, "rawreadformat", chan->rawreadformat); + ast_data_add_codecs(tree, "rawwriteformat", chan->rawwriteformat); + + /* state */ + enum_node = ast_data_add_node(tree, "state"); + if (!enum_node) { + return -1; + } + ast_data_add_str(enum_node, "text", ast_state2str(chan->_state)); + ast_data_add_int(enum_node, "value", chan->_state); + + /* hangupcause */ + enum_node = ast_data_add_node(tree, "hangupcause"); + if (!enum_node) { + return -1; + } + ast_data_add_str(enum_node, "text", ast_cause2str(chan->hangupcause)); + ast_data_add_int(enum_node, "value", chan->hangupcause); + + /* amaflags */ + enum_node = ast_data_add_node(tree, "amaflags"); + if (!enum_node) { + return -1; + } + ast_data_add_str(enum_node, "text", ast_cdr_flags2str(chan->amaflags)); + ast_data_add_int(enum_node, "value", chan->amaflags); + + /* transfercapability */ + enum_node = ast_data_add_node(tree, "transfercapability"); + if (!enum_node) { + return -1; + } + ast_data_add_str(enum_node, "text", ast_transfercapability2str(chan->transfercapability)); + ast_data_add_int(enum_node, "value", chan->transfercapability); + + /* _softphangup */ + data_softhangup = ast_data_add_node(tree, "softhangup"); + if (!data_softhangup) { + return -1; + } + ast_data_add_bool(data_softhangup, "dev", chan->_softhangup & AST_SOFTHANGUP_DEV); + ast_data_add_bool(data_softhangup, "asyncgoto", chan->_softhangup & AST_SOFTHANGUP_ASYNCGOTO); + ast_data_add_bool(data_softhangup, "shutdown", chan->_softhangup & AST_SOFTHANGUP_SHUTDOWN); + ast_data_add_bool(data_softhangup, "timeout", chan->_softhangup & AST_SOFTHANGUP_TIMEOUT); + ast_data_add_bool(data_softhangup, "appunload", chan->_softhangup & AST_SOFTHANGUP_APPUNLOAD); + ast_data_add_bool(data_softhangup, "explicit", chan->_softhangup & AST_SOFTHANGUP_EXPLICIT); + ast_data_add_bool(data_softhangup, "unbridge", chan->_softhangup & AST_SOFTHANGUP_UNBRIDGE); + + /* channel flags */ + data_flags = ast_data_add_node(tree, "flags"); + if (!data_flags) { + return -1; + } + channel_data_add_flags(data_flags, chan); + + ast_data_add_uint(tree, "timetohangup", chan->whentohangup.tv_sec); + + /* callerid */ + data_callerid = ast_data_add_node(tree, "callerid"); + if (!data_callerid) { + return -1; + } + ast_data_add_structure(ast_callerid, data_callerid, &(chan->cid)); + /* insert the callerid ton */ + enum_node = ast_data_add_node(data_callerid, "cid_ton"); + if (!enum_node) { + return -1; + } + ast_data_add_int(enum_node, "value", chan->cid.cid_ton); + ast_data_add_str(enum_node, "text", callerid_ton2str(chan->cid.cid_ton)); + + /* tone zone */ + if (chan->zone) { + data_zones = ast_data_add_node(tree, "zone"); + if (!data_zones) { + return -1; + } + ast_tone_zone_data_add_structure(data_zones, chan->zone); + } + + /* insert cdr */ + data_cdr = ast_data_add_node(tree, "cdr"); + if (!data_cdr) { + return -1; + } + + ast_cdr_data_add_structure(data_cdr, chan->cdr, 1); + + return 0; } int ast_channel_data_cmp_structure(const struct ast_data_search *tree, @@ -6790,52 +6952,29 @@ int ast_plc_reload(void) static int data_channels_provider_handler(const struct ast_data_search *search, struct ast_data *root) { - struct ast_channel *c, *bc; + struct ast_channel *c; struct ast_channel_iterator *iter = NULL; - struct ast_data *data_channel, *data_bridged; - int channel_match, bridged_match; - - channel_match = ast_data_search_has_condition(search, - "channel"); - bridged_match = ast_data_search_has_condition(search, - "channel/bridged"); + struct ast_data *data_channel; for (iter = ast_channel_iterator_all_new(); iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) { ast_channel_lock(c); - if (channel_match && - ast_channel_data_cmp_structure(search, c, "channel")) { - ast_channel_unlock(c); - continue; - } - - bc = ast_bridged_channel(c); - - if (bridged_match && bc && - ast_channel_data_cmp_structure(search, bc, "channel/bridged")) { - ast_channel_unlock(c); - continue; - } - data_channel = ast_data_add_node(root, "channel"); if (!data_channel) { ast_channel_unlock(c); continue; } - ast_channel_data_add_structure(data_channel, c); - - if (bc) { - data_bridged = ast_data_add_node(data_channel, "bridged"); - if (!data_bridged) { - ast_channel_unlock(c); - continue; - } - ast_channel_data_add_structure(data_bridged, bc); + if (ast_channel_data_add_structure(data_channel, c, 1) < 0) { + ast_log(LOG_ERROR, "Unable to add channel structure for channel: %s\n", c->name); } ast_channel_unlock(c); + + if (!ast_data_search_match(search, data_channel)) { + ast_data_remove_node(root, data_channel); + } } if (iter) { ast_channel_iterator_destroy(iter); @@ -6846,6 +6985,64 @@ static int data_channels_provider_handler(const struct ast_data_search *search, /*! * \internal + * \brief Implements the channeltypes provider. + */ +static int data_channeltypes_provider_handler(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct chanlist *cl; + struct ast_data *data_type; + + AST_RWLIST_RDLOCK(&backends); + AST_RWLIST_TRAVERSE(&backends, cl, list) { + data_type = ast_data_add_node(data_root, "type"); + if (!data_type) { + continue; + } + ast_data_add_str(data_type, "name", cl->tech->type); + ast_data_add_str(data_type, "description", cl->tech->description); + ast_data_add_bool(data_type, "devicestate", cl->tech->devicestate ? 1 : 0); + ast_data_add_bool(data_type, "indications", cl->tech->indicate ? 1 : 0); + ast_data_add_bool(data_type, "transfer", cl->tech->transfer ? 1 : 0); + ast_data_add_bool(data_type, "send_digit_begin", cl->tech->send_digit_begin ? 1 : 0); + ast_data_add_bool(data_type, "send_digit_end", cl->tech->send_digit_end ? 1 : 0); + ast_data_add_bool(data_type, "call", cl->tech->call ? 1 : 0); + ast_data_add_bool(data_type, "hangup", cl->tech->hangup ? 1 : 0); + ast_data_add_bool(data_type, "answer", cl->tech->answer ? 1 : 0); + ast_data_add_bool(data_type, "read", cl->tech->read ? 1 : 0); + ast_data_add_bool(data_type, "write", cl->tech->write ? 1 : 0); + ast_data_add_bool(data_type, "send_text", cl->tech->send_text ? 1 : 0); + ast_data_add_bool(data_type, "send_image", cl->tech->send_image ? 1 : 0); + ast_data_add_bool(data_type, "send_html", cl->tech->send_html ? 1 : 0); + ast_data_add_bool(data_type, "exception", cl->tech->exception ? 1 : 0); + ast_data_add_bool(data_type, "bridge", cl->tech->bridge ? 1 : 0); + ast_data_add_bool(data_type, "early_bridge", cl->tech->early_bridge ? 1 : 0); + ast_data_add_bool(data_type, "fixup", cl->tech->fixup ? 1 : 0); + ast_data_add_bool(data_type, "setoption", cl->tech->setoption ? 1 : 0); + ast_data_add_bool(data_type, "queryoption", cl->tech->queryoption ? 1 : 0); + ast_data_add_bool(data_type, "write_video", cl->tech->write_video ? 1 : 0); + ast_data_add_bool(data_type, "write_text", cl->tech->write_text ? 1 : 0); + ast_data_add_bool(data_type, "bridged_channel", cl->tech->bridged_channel ? 1 : 0); + ast_data_add_bool(data_type, "func_channel_read", cl->tech->func_channel_read ? 1 : 0); + ast_data_add_bool(data_type, "func_channel_write", cl->tech->func_channel_write ? 1 : 0); + ast_data_add_bool(data_type, "get_base_channel", cl->tech->get_base_channel ? 1 : 0); + ast_data_add_bool(data_type, "set_base_channel", cl->tech->set_base_channel ? 1 : 0); + ast_data_add_bool(data_type, "get_pvt_uniqueid", cl->tech->get_pvt_uniqueid ? 1 : 0); + ast_data_add_bool(data_type, "cc_callback", cl->tech->cc_callback ? 1 : 0); + + ast_data_add_codecs(data_type, "capabilities", cl->tech->capabilities); + + if (!ast_data_search_match(search, data_type)) { + ast_data_remove_node(data_root, data_type); + } + } + AST_RWLIST_UNLOCK(&backends); + + return 0; +} + +/*! + * \internal * \brief /asterisk/core/channels provider. */ static const struct ast_data_handler channels_provider = { @@ -6853,8 +7050,18 @@ static const struct ast_data_handler channels_provider = { .get = data_channels_provider_handler }; +/*! + * \internal + * \brief /asterisk/core/channeltypes provider. + */ +static const struct ast_data_handler channeltypes_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = data_channeltypes_provider_handler +}; + static const struct ast_data_entry channel_providers[] = { AST_DATA_ENTRY("/asterisk/core/channels", &channels_provider), + AST_DATA_ENTRY("/asterisk/core/channeltypes", &channeltypes_provider), }; void ast_channels_init(void) diff --git a/main/data.c b/main/data.c index c402c4fa6..2a4559c04 100644 --- a/main/data.c +++ b/main/data.c @@ -40,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/term.h" #include "asterisk/manager.h" #include "asterisk/test.h" +#include "asterisk/frame.h" /*** DOCUMENTATION <manager name="DataGet" language="en_US"> @@ -85,8 +86,9 @@ struct ast_data { int32_t sint; uint32_t uint; double dbl; - unsigned int boolean:1; + unsigned int boolean; char *str; + char character; struct in_addr ipaddr; void *ptr; } payload; @@ -981,7 +983,19 @@ static struct ast_data_search *data_search_get_node(const struct ast_data_search return current; } -int ast_data_search_cmp_string(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current string value. + * .search = "somename=somestring" + * name = "somename" + * value is the current value of something and will be evaluated against "somestring". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The value to compare. + * \returns The strcmp return value. + */ +static int data_search_cmp_string(const struct ast_data_search *root, const char *name, char *value) { struct ast_data_search *child; @@ -1001,7 +1015,19 @@ int ast_data_search_cmp_string(const struct ast_data_search *root, const char *n return data_search_comparison_result(ret, cmp_type); } -int ast_data_search_cmp_ptr(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current pointer address value. + * .search = "something=0x32323232" + * name = "something" + * value is the current value of something and will be evaluated against "0x32323232". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] ptr The pointer address to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_ptr(const struct ast_data_search *root, const char *name, void *ptr) { struct ast_data_search *child; @@ -1024,7 +1050,19 @@ int ast_data_search_cmp_ptr(const struct ast_data_search *root, const char *name return data_search_comparison_result((node_ptr - ptr), cmp_type); } -int ast_data_search_cmp_ipaddr(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current ipv4 address value. + * .search = "something=192.168.2.2" + * name = "something" + * value is the current value of something and will be evaluated against "192.168.2.2". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] addr The ipv4 address value to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_ipaddr(const struct ast_data_search *root, const char *name, struct in_addr addr) { struct ast_data_search *child; @@ -1044,7 +1082,19 @@ int ast_data_search_cmp_ipaddr(const struct ast_data_search *root, const char *n return data_search_comparison_result((node_addr.s_addr - addr.s_addr), cmp_type); } -int ast_data_search_cmp_bool(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current boolean value. + * .search = "something=true" + * name = "something" + * value is the current value of something and will be evaluated against "true". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The boolean value to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_bool(const struct ast_data_search *root, const char *name, unsigned int value) { struct ast_data_search *child; @@ -1064,7 +1114,19 @@ int ast_data_search_cmp_bool(const struct ast_data_search *root, const char *nam return data_search_comparison_result(value - node_value, cmp_type); } -int ast_data_search_cmp_dbl(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current double value. + * .search = "something=222" + * name = "something" + * value is the current value of something and will be evaluated against "222". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The double value to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_dbl(const struct ast_data_search *root, const char *name, double value) { struct ast_data_search *child; @@ -1084,7 +1146,19 @@ int ast_data_search_cmp_dbl(const struct ast_data_search *root, const char *name return data_search_comparison_result(value - node_value, cmp_type); } -int ast_data_search_cmp_uint(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current unsigned integer value. + * .search = "something=10" + * name = "something" + * value is the current value of something and will be evaluated against "10". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The unsigned value to compare. + * \returns The strcmp return value. + */ +static int data_search_cmp_uint(const struct ast_data_search *root, const char *name, unsigned int value) { struct ast_data_search *child; @@ -1104,7 +1178,19 @@ int ast_data_search_cmp_uint(const struct ast_data_search *root, const char *nam return data_search_comparison_result(value - node_value, cmp_type); } -int ast_data_search_cmp_int(const struct ast_data_search *root, const char *name, +/*! + * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current signed integer value. + * .search = "something=10" + * name = "something" + * value is the current value of something and will be evaluated against "10". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The value to compare. + * \returns The strcmp return value. + */ +static int data_search_cmp_int(const struct ast_data_search *root, const char *name, int value) { struct ast_data_search *child; @@ -1126,6 +1212,38 @@ int ast_data_search_cmp_int(const struct ast_data_search *root, const char *name /*! * \internal + * \brief Based on a search tree, evaluate the specified 'name' inside the tree with the + * current character value. + * .search = "something=c" + * name = "something" + * value is the current value of something and will be evaluated against "c". + * \param[in] root The root node pointer of the search tree. + * \param[in] name The name of the specific. + * \param[in] value The boolean value to compare. + * \returns The (value - current_value) result. + */ +static int data_search_cmp_char(const struct ast_data_search *root, const char *name, + char value) +{ + struct ast_data_search *child; + char node_value; + enum data_search_comparison cmp_type; + + child = data_search_get_node(root, name); + if (!child) { + return 0; + } + + node_value = *(child->value); + cmp_type = child->cmp_type; + + ao2_ref(child, -1); + + return data_search_comparison_result(value - node_value, cmp_type); +} + +/*! + * \internal * \brief Get the member pointer, from a mapping structure, based on its name. * \XXX We will need to improve performance here!!. * \retval <0 if the member was not found. @@ -1146,21 +1264,6 @@ static inline int data_search_mapping_find(const struct ast_data_mapping_structu return -1; } -int ast_data_search_has_condition(const struct ast_data_search *search, - const char *compare_condition) -{ - struct ast_data_search *child; - - child = data_search_get_node(search, compare_condition); - if (!child) { - return 0; - } - - ao2_ref(child, -1); - - return 1; -} - int __ast_data_search_cmp_structure(const struct ast_data_search *search, const struct ast_data_mapping_structure *mapping, size_t mapping_len, void *structure, const char *structure_name) @@ -1191,38 +1294,63 @@ int __ast_data_search_cmp_structure(const struct ast_data_search *search, notmatch = 0; switch (mapping[member].type) { + case AST_DATA_PASSWORD: + notmatch = data_search_cmp_string(struct_children, + node->name, + mapping[member].get.AST_DATA_PASSWORD(structure)); + break; + case AST_DATA_TIMESTAMP: + notmatch = data_search_cmp_uint(struct_children, + node->name, + mapping[member].get.AST_DATA_TIMESTAMP(structure)); + break; + case AST_DATA_SECONDS: + notmatch = data_search_cmp_uint(struct_children, + node->name, + mapping[member].get.AST_DATA_SECONDS(structure)); + break; + case AST_DATA_MILLISECONDS: + notmatch = data_search_cmp_uint(struct_children, + node->name, + mapping[member].get.AST_DATA_MILLISECONDS(structure)); + break; case AST_DATA_STRING: - notmatch = ast_data_search_cmp_string(struct_children, + notmatch = data_search_cmp_string(struct_children, node->name, mapping[member].get.AST_DATA_STRING(structure)); break; + case AST_DATA_CHARACTER: + notmatch = data_search_cmp_char(struct_children, + node->name, + mapping[member].get.AST_DATA_CHARACTER(structure)); + break; case AST_DATA_INTEGER: - notmatch = ast_data_search_cmp_int(struct_children, + notmatch = data_search_cmp_int(struct_children, node->name, mapping[member].get.AST_DATA_INTEGER(structure)); break; case AST_DATA_BOOLEAN: - notmatch = ast_data_search_cmp_bool(struct_children, + notmatch = data_search_cmp_bool(struct_children, node->name, mapping[member].get.AST_DATA_BOOLEAN(structure)); break; case AST_DATA_UNSIGNED_INTEGER: - notmatch = ast_data_search_cmp_uint(struct_children, + notmatch = data_search_cmp_uint(struct_children, node->name, mapping[member].get.AST_DATA_UNSIGNED_INTEGER(structure)); break; case AST_DATA_DOUBLE: - notmatch = ast_data_search_cmp_dbl(struct_children, + notmatch = data_search_cmp_dbl(struct_children, node->name, mapping[member].get.AST_DATA_DOUBLE(structure)); break; case AST_DATA_IPADDR: - notmatch = ast_data_search_cmp_ipaddr(struct_children, + notmatch = data_search_cmp_ipaddr(struct_children, node->name, mapping[member].get.AST_DATA_IPADDR(structure)); break; case AST_DATA_POINTER: - notmatch = ast_data_search_cmp_ptr(struct_children, + notmatch = data_search_cmp_ptr(struct_children, node->name, mapping[member].get.AST_DATA_POINTER(structure)); break; @@ -1248,11 +1376,18 @@ static void data_result_destructor(void *obj) struct ast_data *root = obj; switch (root->type) { - case AST_DATA_POINTER: + case AST_DATA_PASSWORD: case AST_DATA_STRING: - ast_free(root->payload.ptr); + ast_free(root->payload.str); + ao2_ref(root->children, -1); + break; + case AST_DATA_POINTER: + case AST_DATA_CHARACTER: case AST_DATA_CONTAINER: case AST_DATA_INTEGER: + case AST_DATA_TIMESTAMP: + case AST_DATA_SECONDS: + case AST_DATA_MILLISECONDS: case AST_DATA_UNSIGNED_INTEGER: case AST_DATA_DOUBLE: case AST_DATA_BOOLEAN: @@ -1315,6 +1450,105 @@ static struct ast_data *data_result_find_child(struct ast_data *root, const char return found; } +int ast_data_search_match(const struct ast_data_search *search, struct ast_data *data) +{ + struct ao2_iterator i, ii; + struct ast_data_search *s, *s_child; + struct ast_data *d_child; + int notmatch = 1; + + if (!search) { + return 1; + } + + s_child = data_search_find(search->children, data->name); + if (!s_child) { + /* nothing to compare */ + ao2_ref(s_child, -1); + return 1; + } + + i = ao2_iterator_init(s_child->children, 0); + while ((s = ao2_iterator_next(&i))) { + if (!ao2_container_count(s->children)) { + /* compare this search node with every data node */ + d_child = data_result_find_child(data, s->name); + if (!d_child) { + ao2_ref(s, -1); + notmatch = 1; + continue; + } + + switch (d_child->type) { + case AST_DATA_PASSWORD: + case AST_DATA_STRING: + notmatch = data_search_cmp_string(s_child, d_child->name, + d_child->payload.str); + break; + case AST_DATA_CHARACTER: + notmatch = data_search_cmp_char(s_child, d_child->name, + d_child->payload.character); + break; + case AST_DATA_INTEGER: + notmatch = data_search_cmp_int(s_child, d_child->name, + d_child->payload.sint); + break; + case AST_DATA_BOOLEAN: + notmatch = data_search_cmp_bool(s_child, d_child->name, + d_child->payload.boolean); + break; + case AST_DATA_UNSIGNED_INTEGER: + notmatch = data_search_cmp_uint(s_child, d_child->name, + d_child->payload.uint); + break; + case AST_DATA_TIMESTAMP: + case AST_DATA_SECONDS: + case AST_DATA_MILLISECONDS: + case AST_DATA_DOUBLE: + notmatch = data_search_cmp_uint(s_child, d_child->name, + d_child->payload.dbl); + break; + case AST_DATA_IPADDR: + notmatch = data_search_cmp_ipaddr(s_child, d_child->name, + d_child->payload.ipaddr); + break; + case AST_DATA_POINTER: + notmatch = data_search_cmp_ptr(s_child, d_child->name, + d_child->payload.ptr); + break; + case AST_DATA_CONTAINER: + break; + } + ao2_ref(d_child, -1); + } else { + ii = ao2_iterator_init(data->children, 0); + while ((d_child = ao2_iterator_next(&ii))) { + if (strcmp(d_child->name, s->name)) { + ao2_ref(d_child, -1); + continue; + } + if (!(notmatch = !ast_data_search_match(s_child, d_child))) { + /* do not continue if we have a match. */ + ao2_ref(d_child, -1); + break; + } + ao2_ref(d_child, -1); + } + ao2_iterator_destroy(&ii); + } + ao2_ref(s, -1); + if (notmatch) { + /* do not continue if we don't have a match. */ + break; + } + } + ao2_iterator_destroy(&i); + + ao2_ref(s_child, -1); + + return !notmatch; +} + /*! * \internal * \brief Get an internal node, from the result set. @@ -1872,9 +2106,32 @@ static void data_get_xml_add_child(struct ast_data *parent_data, case AST_DATA_CONTAINER: data_get_xml_add_child(node, child_xml); break; + case AST_DATA_PASSWORD: + ast_xml_set_text(child_xml, node->payload.str); + break; + case AST_DATA_TIMESTAMP: + snprintf(node_content, sizeof(node_content), "%d", + node->payload.uint); + ast_xml_set_text(child_xml, node_content); + break; + case AST_DATA_SECONDS: + snprintf(node_content, sizeof(node_content), "%d", + node->payload.uint); + ast_xml_set_text(child_xml, node_content); + break; + case AST_DATA_MILLISECONDS: + snprintf(node_content, sizeof(node_content), "%d", + node->payload.uint); + ast_xml_set_text(child_xml, node_content); + break; case AST_DATA_STRING: ast_xml_set_text(child_xml, node->payload.str); break; + case AST_DATA_CHARACTER: + snprintf(node_content, sizeof(node_content), "%c", + node->payload.character); + ast_xml_set_text(child_xml, node_content); + break; case AST_DATA_INTEGER: snprintf(node_content, sizeof(node_content), "%d", node->payload.sint); @@ -2006,15 +2263,24 @@ static struct ast_data *__ast_data_add(struct ast_data *root, const char *name, node->payload.boolean = *(unsigned int *) ptr; break; case AST_DATA_INTEGER: - node->payload.sint = *(unsigned int *) ptr; + node->payload.sint = *(int *) ptr; break; + case AST_DATA_TIMESTAMP: + case AST_DATA_SECONDS: + case AST_DATA_MILLISECONDS: case AST_DATA_UNSIGNED_INTEGER: - node->payload.sint = *(unsigned int *) ptr; + node->payload.uint = *(unsigned int *) ptr; break; case AST_DATA_DOUBLE: node->payload.dbl = *(double *) ptr; break; + case AST_DATA_PASSWORD: case AST_DATA_STRING: + node->payload.str = (char *) ptr; + break; + case AST_DATA_CHARACTER: + node->payload.character = *(char *) ptr; + break; case AST_DATA_POINTER: node->payload.ptr = ptr; break; @@ -2052,6 +2318,11 @@ struct ast_data *ast_data_add_int(struct ast_data *root, const char *name, int v return __ast_data_add(root, name, AST_DATA_INTEGER, &value); } +struct ast_data *ast_data_add_char(struct ast_data *root, const char *name, char value) +{ + return __ast_data_add(root, name, AST_DATA_CHARACTER, &value); +} + struct ast_data *ast_data_add_uint(struct ast_data *root, const char *name, unsigned int value) { @@ -2082,6 +2353,45 @@ struct ast_data *ast_data_add_ptr(struct ast_data *root, const char *childname, return __ast_data_add(root, childname, AST_DATA_POINTER, ptr); } +struct ast_data *ast_data_add_timestamp(struct ast_data *root, const char *childname, + unsigned int timestamp) +{ + return __ast_data_add(root, childname, AST_DATA_TIMESTAMP, ×tamp); +} + +struct ast_data *ast_data_add_seconds(struct ast_data *root, const char *childname, + unsigned int seconds) +{ + return __ast_data_add(root, childname, AST_DATA_SECONDS, &seconds); +} + +struct ast_data *ast_data_add_milliseconds(struct ast_data *root, const char *childname, + unsigned int milliseconds) +{ + return __ast_data_add(root, childname, AST_DATA_MILLISECONDS, &milliseconds); +} + +struct ast_data *ast_data_add_password(struct ast_data *root, const char *childname, + const char *value) +{ + char *name; + size_t namelen = 1 + (ast_strlen_zero(value) ? 0 : strlen(value)); + struct ast_data *res; + + if (!(name = ast_malloc(namelen))) { + return NULL; + } + + strcpy(name, (ast_strlen_zero(value) ? "" : value)); + + res = __ast_data_add(root, childname, AST_DATA_PASSWORD, name); + if (!res) { + ast_free(name); + } + + return res; +} + struct ast_data *ast_data_add_str(struct ast_data *root, const char *childname, const char *value) { @@ -2127,10 +2437,30 @@ int __ast_data_add_structure(struct ast_data *root, ast_data_add_bool(root, mapping[i].name, mapping[i].get.AST_DATA_BOOLEAN(structure)); break; + case AST_DATA_PASSWORD: + ast_data_add_password(root, mapping[i].name, + mapping[i].get.AST_DATA_PASSWORD(structure)); + break; + case AST_DATA_TIMESTAMP: + ast_data_add_timestamp(root, mapping[i].name, + mapping[i].get.AST_DATA_TIMESTAMP(structure)); + break; + case AST_DATA_SECONDS: + ast_data_add_seconds(root, mapping[i].name, + mapping[i].get.AST_DATA_SECONDS(structure)); + break; + case AST_DATA_MILLISECONDS: + ast_data_add_milliseconds(root, mapping[i].name, + mapping[i].get.AST_DATA_MILLISECONDS(structure)); + break; case AST_DATA_STRING: ast_data_add_str(root, mapping[i].name, mapping[i].get.AST_DATA_STRING(structure)); break; + case AST_DATA_CHARACTER: + ast_data_add_char(root, mapping[i].name, + mapping[i].get.AST_DATA_CHARACTER(structure)); + break; case AST_DATA_CONTAINER: break; case AST_DATA_IPADDR: @@ -2277,6 +2607,21 @@ int ast_data_retrieve(struct ast_data *tree, const char *path, case AST_DATA_STRING: content->value.AST_DATA_STRING = node->payload.str; break; + case AST_DATA_PASSWORD: + content->value.AST_DATA_PASSWORD = node->payload.str; + break; + case AST_DATA_TIMESTAMP: + content->value.AST_DATA_TIMESTAMP = node->payload.uint; + break; + case AST_DATA_SECONDS: + content->value.AST_DATA_SECONDS = node->payload.uint; + break; + case AST_DATA_MILLISECONDS: + content->value.AST_DATA_MILLISECONDS = node->payload.uint; + break; + case AST_DATA_CHARACTER: + content->value.AST_DATA_CHARACTER = node->payload.character; + break; case AST_DATA_INTEGER: content->value.AST_DATA_INTEGER = node->payload.sint; break; @@ -2310,7 +2655,12 @@ static const struct { enum ast_data_type type; int color; } data_result_color[] = { - { AST_DATA_STRING, COLOR_CYAN }, + { AST_DATA_STRING, COLOR_BLUE }, + { AST_DATA_PASSWORD, COLOR_BRBLUE }, + { AST_DATA_TIMESTAMP, COLOR_CYAN }, + { AST_DATA_SECONDS, COLOR_MAGENTA }, + { AST_DATA_MILLISECONDS, COLOR_BRMAGENTA }, + { AST_DATA_CHARACTER, COLOR_GRAY }, { AST_DATA_INTEGER, COLOR_RED }, { AST_DATA_UNSIGNED_INTEGER, COLOR_RED }, { AST_DATA_DOUBLE, COLOR_RED }, @@ -2373,16 +2723,43 @@ static void data_result_print_cli_node(int fd, const struct ast_data *node, uint ast_str_append(&output, 0, "%s%s: %p\n", ast_str_buffer(tabs), node->name, node->payload.ptr); break; + case AST_DATA_PASSWORD: + ast_str_append(&output, 0, "%s%s: \"%s\"\n", + ast_str_buffer(tabs), + node->name, + node->payload.str); + break; case AST_DATA_STRING: ast_str_append(&output, 0, "%s%s: \"%s\"\n", ast_str_buffer(tabs), node->name, node->payload.str); break; + case AST_DATA_CHARACTER: + ast_str_append(&output, 0, "%s%s: \'%c\'\n", + ast_str_buffer(tabs), + node->name, + node->payload.character); + break; case AST_DATA_CONTAINER: ast_str_append(&output, 0, "%s%s\n", ast_str_buffer(tabs), node->name); break; + case AST_DATA_TIMESTAMP: + ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs), + node->name, + node->payload.uint); + break; + case AST_DATA_SECONDS: + ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs), + node->name, + node->payload.uint); + break; + case AST_DATA_MILLISECONDS: + ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs), + node->name, + node->payload.uint); + break; case AST_DATA_INTEGER: ast_str_append(&output, 0, "%s%s: %d\n", ast_str_buffer(tabs), node->name, @@ -2470,6 +2847,8 @@ static void data_result_print_cli(int fd, const struct ast_data *root) ast_free(output); __data_result_print_cli(fd, root, 0); + + ast_cli(fd, "\n"); } /*! @@ -2644,12 +3023,21 @@ static void data_result_manager_output(struct mansession *s, const char *name, case AST_DATA_INTEGER: astman_append(s, ": %d\r\n", node->payload.sint); break; + case AST_DATA_TIMESTAMP: + case AST_DATA_SECONDS: + case AST_DATA_MILLISECONDS: case AST_DATA_UNSIGNED_INTEGER: astman_append(s, ": %u\r\n", node->payload.uint); break; + case AST_DATA_PASSWORD: + astman_append(s, ": %s\r\n", node->payload.str); + break; case AST_DATA_STRING: astman_append(s, ": %s\r\n", node->payload.str); break; + case AST_DATA_CHARACTER: + astman_append(s, ": %c\r\n", node->payload.character); + break; case AST_DATA_IPADDR: astman_append(s, ": %s\r\n", ast_inet_ntoa(node->payload.ipaddr)); break; @@ -2712,6 +3100,34 @@ static int manager_data_get(struct mansession *s, const struct message *m) return RESULT_SUCCESS; } +int ast_data_add_codecs(struct ast_data *root, const char *node_name, format_t capability) +{ + struct ast_data *codecs, *codec; + size_t fmlist_size; + const struct ast_format_list *fmlist; + int x; + + codecs = ast_data_add_node(root, node_name); + if (!codecs) { + return -1; + } + fmlist = ast_get_format_list(&fmlist_size); + for (x = 0; x < fmlist_size; x++) { + if (fmlist[x].bits & capability) { + codec = ast_data_add_node(codecs, "codec"); + if (!codec) { + return -1; + } + ast_data_add_str(codec, "name", fmlist[x].name); + ast_data_add_int(codec, "samplespersecond", fmlist[x].samplespersecond); + ast_data_add_str(codec, "description", fmlist[x].desc); + ast_data_add_int(codec, "frame_length", fmlist[x].fr_len); + } + } + + return 0; +} + #ifdef TEST_FRAMEWORK /*! @@ -2753,10 +3169,6 @@ static int test_data_full_provider(const struct ast_data_search *search, .a_uint = 20 }; - if (ast_data_search_cmp_structure(search, test_structure, &local_test_structure, "test_structure")) { - return 0; - } - test_structure = ast_data_add_node(root, "test_structure"); if (!test_structure) { ast_debug(1, "Internal data api error\n"); @@ -2766,6 +3178,10 @@ static int test_data_full_provider(const struct ast_data_search *search, /* add the complete structure. */ ast_data_add_structure(test_structure, test_structure, &local_test_structure); + if (!ast_data_search_match(search, test_structure)) { + ast_data_remove_node(root, test_structure); + } + return 0; } diff --git a/main/indications.c b/main/indications.c index 0687648bc..b2d65bcc7 100644 --- a/main/indications.c +++ b/main/indications.c @@ -38,9 +38,23 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/cli.h" #include "asterisk/module.h" #include "asterisk/astobj2.h" +#include "asterisk/data.h" #include "asterisk/_private.h" /* _init(), _reload() */ +#define DATA_EXPORT_TONE_ZONE(MEMBER) \ + MEMBER(ast_tone_zone, country, AST_DATA_STRING) \ + MEMBER(ast_tone_zone, description, AST_DATA_STRING) \ + MEMBER(ast_tone_zone, nrringcadence, AST_DATA_UNSIGNED_INTEGER) + +AST_DATA_STRUCTURE(ast_tone_zone, DATA_EXPORT_TONE_ZONE); + +#define DATA_EXPORT_TONE_ZONE_SOUND(MEMBER) \ + MEMBER(ast_tone_zone_sound, name, AST_DATA_STRING) \ + MEMBER(ast_tone_zone_sound, data, AST_DATA_STRING) + +AST_DATA_STRUCTURE(ast_tone_zone_sound, DATA_EXPORT_TONE_ZONE_SOUND); + /* Globals */ static const char config[] = "indications.conf"; @@ -1102,6 +1116,33 @@ static int ast_tone_zone_cmp(void *obj, void *arg, int flags) CMP_MATCH | CMP_STOP : 0; } +int ast_tone_zone_data_add_structure(struct ast_data *tree, struct ast_tone_zone *zone) +{ + struct ast_data *data_zone_sound; + struct ast_tone_zone_sound *s; + + ast_data_add_structure(ast_tone_zone, tree, zone); + + if (AST_LIST_EMPTY(&zone->tones)) { + return 0; + } + + data_zone_sound = ast_data_add_node(tree, "tones"); + if (!data_zone_sound) { + return -1; + } + + ast_tone_zone_lock(zone); + + AST_LIST_TRAVERSE(&zone->tones, s, entry) { + ast_data_add_structure(ast_tone_zone_sound, data_zone_sound, s); + } + + ast_tone_zone_unlock(zone); + + return 0; +} + /*! \brief Load indications module */ int ast_indications_init(void) { diff --git a/main/pbx.c b/main/pbx.c index 24203ede7..dbc4db497 100644 --- a/main/pbx.c +++ b/main/pbx.c @@ -9717,6 +9717,57 @@ static void device_state_cb(const struct ast_event *event, void *unused) } } +/*! + * \internal + * \brief Implements the hints data provider. + */ +static int hints_data_provider_get(const struct ast_data_search *search, + struct ast_data *data_root) +{ + struct ast_data *data_hint; + struct ast_hint *hint; + int watchers; + struct ast_state_cb *watcher; + + AST_RWLIST_RDLOCK(&hints); + if (AST_RWLIST_EMPTY(&hints)) { + AST_RWLIST_UNLOCK(&hints); + return 0; + } + + AST_RWLIST_TRAVERSE(&hints, hint, list) { + watchers = 0; + AST_LIST_TRAVERSE(&hint->callbacks, watcher, entry) { + watchers++; + } + data_hint = ast_data_add_node(data_root, "hint"); + if (!data_hint) { + continue; + } + ast_data_add_str(data_hint, "extension", ast_get_extension_name(hint->exten)); + ast_data_add_str(data_hint, "context", ast_get_context_name(ast_get_extension_context(hint->exten))); + ast_data_add_str(data_hint, "application", ast_get_extension_app(hint->exten)); + ast_data_add_str(data_hint, "state", ast_extension_state2str(hint->laststate)); + ast_data_add_int(data_hint, "watchers", watchers); + + if (!ast_data_search_match(search, data_hint)) { + ast_data_remove_node(data_root, data_hint); + } + } + AST_RWLIST_UNLOCK(&hints); + + return 0; +} + +static const struct ast_data_handler hints_data_provider = { + .version = AST_DATA_HANDLER_VERSION, + .get = hints_data_provider_get +}; + +static const struct ast_data_entry pbx_data_providers[] = { + AST_DATA_ENTRY("asterisk/core/hints", &hints_data_provider), +}; + int load_pbx(void) { int x; @@ -9729,6 +9780,7 @@ int load_pbx(void) ast_verb(1, "Registering builtin applications:\n"); ast_cli_register_multiple(pbx_cli, ARRAY_LEN(pbx_cli)); + ast_data_register_multiple_core(pbx_data_providers, ARRAY_LEN(pbx_data_providers)); __ast_custom_function_register(&exception_function, NULL); __ast_custom_function_register(&testtime_function, NULL); |