From c6d755de11e98c1f6f33b1b35db9725f64f72053 Mon Sep 17 00:00:00 2001 From: Sebastien Duthil Date: Fri, 11 Nov 2016 11:45:37 -0500 Subject: res_ari: Add support for channel variables in ARI events. This works the same as for AMI manager variables. Set "channelvars=foo,bar" in your ari.conf general section, and then the channel variables "foo" and "bar" (along with their values), will appear in every Stasis websocket channel event. ASTERISK-26492 #close patches: ari_vars.diff submitted by Mark Michelson Change-Id: I5609ba239259577c0948645df776d7f3bc864229 --- CHANGES | 6 +++ configs/samples/ari.conf.sample | 5 +++ include/asterisk/channel.h | 30 +++++++++++++++ include/asterisk/json.h | 12 ++++++ include/asterisk/stasis_channels.h | 1 + main/channel.c | 78 ++++++++++++++++++++++++++++---------- main/json.c | 13 +++++++ main/stasis_channels.c | 5 +++ res/ari/ari_model_validators.c | 9 +++++ res/ari/ari_model_validators.h | 1 + res/ari/config.c | 20 ++++++++++ res/res_ari.c | 3 ++ rest-api/api-docs/channels.json | 5 +++ 13 files changed, 169 insertions(+), 19 deletions(-) diff --git a/CHANGES b/CHANGES index a57ed88e3..b9d19a6f2 100644 --- a/CHANGES +++ b/CHANGES @@ -95,6 +95,12 @@ Queue * A new dialplan variable, ABANDONED, is set when the call is not answered by an agent. +res_ari +------------------ + * The configuration file ari.conf now supports a channelvars option, which + specifies a list of channel variables to include in each channel-oriented + ARI event. + ------------------------------------------------------------------------------ --- Functionality changes from Asterisk 14.0.0 to Asterisk 14.1.0 ---------- ------------------------------------------------------------------------------ diff --git a/configs/samples/ari.conf.sample b/configs/samples/ari.conf.sample index 59f9a44e5..1294814e2 100644 --- a/configs/samples/ari.conf.sample +++ b/configs/samples/ari.conf.sample @@ -13,6 +13,11 @@ enabled = yes ; When set to no, ARI support is disabled. ; receiving clients are slow to process the received information. Value is in ; milliseconds; default is 100 ms. ;websocket_write_timeout = 100 +; +; Display certain channel variables every time a channel-oriented +; event is emitted: +; +;channelvars = var1,var2,var3 ;[username] ;type = user ; Specifies user configuration diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index ff92cc878..cfd8384cb 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -4339,6 +4339,36 @@ void ast_channel_set_manager_vars(size_t varc, char **vars); */ struct varshead *ast_channel_get_manager_vars(struct ast_channel *chan); +/*! + * \since 14.2.0 + * \brief Return whether or not any ARI variables have been set + * + * \retval 0 if no ARI variables are expected + * \retval 1 if ARI variables are expected + */ +int ast_channel_has_ari_vars(void); + +/*! + * \since 14.2.0 + * \brief Sets the variables to be stored in the \a ari_vars field of all + * snapshots. + * \param varc Number of variable names. + * \param vars Array of variable names. + */ +void ast_channel_set_ari_vars(size_t varc, char **vars); + +/*! + * \since 14.2.0 + * \brief Gets the variables for a given channel, as specified by ast_channel_set_ari_vars(). + * + * The returned variable list is an AO2 object, so ao2_cleanup() to free it. + * + * \param chan Channel to get variables for. + * \return List of channel variables. + * \return \c NULL on error + */ +struct varshead *ast_channel_get_ari_vars(struct ast_channel *chan); + /*! * \since 12 * \brief Gets the variables for a given channel, as set using pbx_builtin_setvar_helper(). diff --git a/include/asterisk/json.h b/include/asterisk/json.h index cfd9a2997..bd6ba86b9 100644 --- a/include/asterisk/json.h +++ b/include/asterisk/json.h @@ -1076,6 +1076,18 @@ enum ast_json_to_ast_vars_code { */ enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_variables, struct ast_variable **variables); +struct varshead; + +/*! + * \brief Construct a JSON object from a \c ast_var_t list + * \since 14.2.0 + * + * \param channelvars The list of \c ast_var_t to represent as JSON + * + * \return JSON object with variable names as keys and variable values as values + */ +struct ast_json *ast_json_channel_vars(struct varshead *channelvars); + /*!@}*/ #endif /* _ASTERISK_JSON_H */ diff --git a/include/asterisk/stasis_channels.h b/include/asterisk/stasis_channels.h index 6c6cd51f1..deb79b0d0 100644 --- a/include/asterisk/stasis_channels.h +++ b/include/asterisk/stasis_channels.h @@ -73,6 +73,7 @@ struct ast_channel_snapshot { struct ast_flags softhangup_flags; /*!< softhangup channel flags */ struct varshead *manager_vars; /*!< Variables to be appended to manager events */ int tech_properties; /*!< Properties of the channel's technology */ + struct varshead *ari_vars; /*!< Variables to be appended to ARI events */ }; /*! diff --git a/main/channel.c b/main/channel.c index cdb6569c3..bdf918fab 100644 --- a/main/channel.c +++ b/main/channel.c @@ -7756,35 +7756,48 @@ struct manager_channel_variable { char name[]; }; -static AST_RWLIST_HEAD_STATIC(channelvars, manager_channel_variable); +AST_RWLIST_HEAD(external_vars, manager_channel_variable); -static void free_channelvars(void) +static struct external_vars ami_vars; +static struct external_vars ari_vars; + +static void free_external_channelvars(struct external_vars *channelvars) { struct manager_channel_variable *var; - AST_RWLIST_WRLOCK(&channelvars); - while ((var = AST_RWLIST_REMOVE_HEAD(&channelvars, entry))) { + AST_RWLIST_WRLOCK(channelvars); + while ((var = AST_RWLIST_REMOVE_HEAD(channelvars, entry))) { ast_free(var); } - AST_RWLIST_UNLOCK(&channelvars); + AST_RWLIST_UNLOCK(channelvars); } -int ast_channel_has_manager_vars(void) +static int channel_has_external_vars(struct external_vars *channelvars) { int vars_present; - AST_RWLIST_RDLOCK(&channelvars); - vars_present = !AST_LIST_EMPTY(&channelvars); - AST_RWLIST_UNLOCK(&channelvars); + AST_RWLIST_RDLOCK(channelvars); + vars_present = !AST_LIST_EMPTY(channelvars); + AST_RWLIST_UNLOCK(channelvars); return vars_present; } -void ast_channel_set_manager_vars(size_t varc, char **vars) +int ast_channel_has_manager_vars(void) +{ + return channel_has_external_vars(&ami_vars); +} + +int ast_channel_has_ari_vars(void) +{ + return channel_has_external_vars(&ari_vars); +} + +static void channel_set_external_vars(struct external_vars *channelvars, size_t varc, char **vars) { size_t i; - free_channelvars(); - AST_RWLIST_WRLOCK(&channelvars); + free_external_channelvars(channelvars); + AST_RWLIST_WRLOCK(channelvars); for (i = 0; i < varc; ++i) { const char *var = vars[i]; struct manager_channel_variable *mcv; @@ -7795,9 +7808,20 @@ void ast_channel_set_manager_vars(size_t varc, char **vars) if (strchr(var, '(')) { mcv->isfunc = 1; } - AST_RWLIST_INSERT_TAIL(&channelvars, mcv, entry); + AST_RWLIST_INSERT_TAIL(channelvars, mcv, entry); } - AST_RWLIST_UNLOCK(&channelvars); + AST_RWLIST_UNLOCK(channelvars); + +} + +void ast_channel_set_manager_vars(size_t varc, char **vars) +{ + channel_set_external_vars(&ami_vars, varc, vars); +} + +void ast_channel_set_ari_vars(size_t varc, char **vars) +{ + channel_set_external_vars(&ari_vars, varc, vars); } /*! @@ -7839,14 +7863,15 @@ struct varshead *ast_channel_get_vars(struct ast_channel *chan) return ret; } -struct varshead *ast_channel_get_manager_vars(struct ast_channel *chan) +static struct varshead *channel_get_external_vars(struct external_vars *channelvars, + struct ast_channel *chan) { RAII_VAR(struct varshead *, ret, NULL, ao2_cleanup); RAII_VAR(struct ast_str *, tmp, NULL, ast_free); struct manager_channel_variable *mcv; - SCOPED_LOCK(lock, &channelvars, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); + SCOPED_LOCK(lock, channelvars, AST_RWLIST_RDLOCK, AST_RWLIST_UNLOCK); - if (AST_LIST_EMPTY(&channelvars)) { + if (AST_LIST_EMPTY(channelvars)) { return NULL; } @@ -7857,7 +7882,7 @@ struct varshead *ast_channel_get_manager_vars(struct ast_channel *chan) return NULL; } - AST_LIST_TRAVERSE(&channelvars, mcv, entry) { + AST_LIST_TRAVERSE(channelvars, mcv, entry) { const char *val = NULL; struct ast_var_t *var; @@ -7882,11 +7907,23 @@ struct varshead *ast_channel_get_manager_vars(struct ast_channel *chan) ao2_ref(ret, +1); return ret; + +} + +struct varshead *ast_channel_get_manager_vars(struct ast_channel *chan) +{ + return channel_get_external_vars(&ami_vars, chan); +} + +struct varshead *ast_channel_get_ari_vars(struct ast_channel *chan) +{ + return channel_get_external_vars(&ari_vars, chan); } static void channels_shutdown(void) { - free_channelvars(); + free_external_channelvars(&ami_vars); + free_external_channelvars(&ari_vars); ast_data_unregister(NULL); ast_cli_unregister_multiple(cli_channel, ARRAY_LEN(cli_channel)); @@ -7919,6 +7956,9 @@ int ast_channels_init(void) ast_register_cleanup(channels_shutdown); + AST_RWLIST_HEAD_INIT(&ami_vars); + AST_RWLIST_HEAD_INIT(&ari_vars); + return 0; } diff --git a/main/json.c b/main/json.c index 7b5cfbe7e..a28dbb2e2 100644 --- a/main/json.c +++ b/main/json.c @@ -1048,3 +1048,16 @@ enum ast_json_to_ast_vars_code ast_json_to_ast_variables(struct ast_json *json_v return AST_JSON_TO_AST_VARS_CODE_SUCCESS; } + +struct ast_json *ast_json_channel_vars(struct varshead *channelvars) +{ + struct ast_json *ret; + struct ast_var_t *var; + + ret = ast_json_object_create(); + AST_LIST_TRAVERSE(channelvars, var, entries) { + ast_json_object_set(ret, var->name, ast_json_string_create(var->value)); + } + + return ret; +} diff --git a/main/stasis_channels.c b/main/stasis_channels.c index 91f209290..4897af89e 100644 --- a/main/stasis_channels.c +++ b/main/stasis_channels.c @@ -270,6 +270,7 @@ struct ast_channel_snapshot *ast_channel_snapshot_create(struct ast_channel *cha ast_set_flag(&snapshot->softhangup_flags, ast_channel_softhangup_internal_flag(chan)); snapshot->manager_vars = ast_channel_get_manager_vars(chan); + snapshot->ari_vars = ast_channel_get_ari_vars(chan); snapshot->tech_properties = ast_channel_tech(chan)->properties; return snapshot; @@ -918,6 +919,10 @@ struct ast_json *ast_channel_snapshot_to_json( "creationtime", ast_json_timeval(snapshot->creationtime, NULL), "language", snapshot->language); + if (snapshot->ari_vars && !AST_LIST_EMPTY(snapshot->ari_vars)) { + ast_json_object_set(json_chan, "channelvars", ast_json_channel_vars(snapshot->ari_vars)); + } + return ast_json_ref(json_chan); } diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c index 03cd3a238..9fd844c4d 100644 --- a/res/ari/ari_model_validators.c +++ b/res/ari/ari_model_validators.c @@ -1051,6 +1051,15 @@ int ast_ari_validate_channel(struct ast_json *json) res = 0; } } else + if (strcmp("channelvars", ast_json_object_iter_key(iter)) == 0) { + int prop_is_valid; + prop_is_valid = ast_ari_validate_object( + ast_json_object_iter_value(iter)); + if (!prop_is_valid) { + ast_log(LOG_ERROR, "ARI Channel field channelvars failed validation\n"); + res = 0; + } + } else if (strcmp("connected", ast_json_object_iter_key(iter)) == 0) { int prop_is_valid; has_connected = 1; diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h index 0b08ce85e..fcd9fc11c 100644 --- a/res/ari/ari_model_validators.h +++ b/res/ari/ari_model_validators.h @@ -1432,6 +1432,7 @@ ari_validator ast_ari_validate_application_fn(void); * Channel * - accountcode: string (required) * - caller: CallerID (required) + * - channelvars: object * - connected: CallerID (required) * - creationtime: Date (required) * - dialplan: DialplanCEP (required) diff --git a/res/ari/config.c b/res/ari/config.c index deaa78073..a080bb713 100644 --- a/res/ari/config.c +++ b/res/ari/config.c @@ -26,6 +26,8 @@ #include "asterisk/config_options.h" #include "asterisk/http_websocket.h" +#include "asterisk/app.h" +#include "asterisk/channel.h" #include "internal.h" /*! \brief Locking container for safe configuration access. */ @@ -316,6 +318,22 @@ static int process_config(int reload) return 0; } +#define MAX_VARS 128 + +static int channelvars_handler(const struct aco_option *opt, struct ast_variable *var, void *obj) +{ + char *parse = NULL; + AST_DECLARE_APP_ARGS(args, + AST_APP_ARG(vars)[MAX_VARS]; + ); + + parse = ast_strdupa(var->value); + AST_STANDARD_APP_ARGS(args, parse); + + ast_channel_set_ari_vars(args.argc, args.vars); + return 0; +} + int ast_ari_config_init(void) { if (aco_info_init(&cfg_info)) { @@ -339,6 +357,8 @@ int ast_ari_config_init(void) aco_option_register(&cfg_info, "websocket_write_timeout", ACO_EXACT, general_options, AST_DEFAULT_WEBSOCKET_WRITE_TIMEOUT_STR, OPT_INT_T, PARSE_IN_RANGE, FLDSET(struct ast_ari_conf_general, write_timeout), 1, INT_MAX); + aco_option_register_custom(&cfg_info, "channelvars", ACO_EXACT, general_options, + "", channelvars_handler, 0); /* ARI type=user category options */ aco_option_register(&cfg_info, "type", ACO_EXACT, user, NULL, diff --git a/res/res_ari.c b/res/res_ari.c index e19881515..e362a5811 100644 --- a/res/res_ari.c +++ b/res/res_ari.c @@ -112,6 +112,9 @@ Comma separated list of allowed origins, for Cross-Origin Resource Sharing. May be set to * to allow all origins. + + Comma separated list of channel variables to display in channel json. + diff --git a/rest-api/api-docs/channels.json b/rest-api/api-docs/channels.json index 4ca8d6582..241b22ae8 100644 --- a/rest-api/api-docs/channels.json +++ b/rest-api/api-docs/channels.json @@ -1761,6 +1761,11 @@ "required": true, "type": "string", "description": "The default spoken language" + }, + "channelvars": { + "required": false, + "type": "object", + "description": "Channel variables" } } } -- cgit v1.2.3