Merge 6da9f031b7
into 953dc3d127
This commit is contained in:
commit
8baffa578c
File diff suppressed because it is too large
Load Diff
|
@ -1196,9 +1196,48 @@ struct ast_group_info;
|
|||
/*! \brief Split a group string into group and category, returning a default category if none is provided. */
|
||||
int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max);
|
||||
|
||||
/*! \brief Remove a channel from a group meta assignment */
|
||||
int ast_app_group_remove_channel(struct ast_channel *chan, char *group, char *category);
|
||||
|
||||
/*! \brief Add a channel to a group meta assignment, create a group meta item if it doesn't exist */
|
||||
int ast_app_group_add_channel(struct ast_channel *chan, char *group, char *category);
|
||||
|
||||
/*! \brief Remove channel assignments for the specified group */
|
||||
int ast_app_group_remove_all_channels(const char *group, const char *category);
|
||||
|
||||
/*! \brief Rename a group@category while retaining all the channel memberships */
|
||||
int ast_app_group_rename(const char *old_group, const char *old_category, const char *new_group, const char *new_category);
|
||||
|
||||
/*! \brief Set the group for a channel, splitting the provided data into group and category, if specified. */
|
||||
int ast_app_group_set_channel(struct ast_channel *chan, const char *data);
|
||||
|
||||
/*!
|
||||
* \brief Set a group variable for a group@category
|
||||
*
|
||||
* \param chan channel (if any) that is setting the group variable (can be NULL)
|
||||
* \param group group to set the variable on (cannot be null)
|
||||
* \param category category to set the variable on (cannot be null)
|
||||
* \param name name of the variable to set (cannot be null)
|
||||
* \param value value of the variable to set (cannot be null)
|
||||
*
|
||||
* \retval 0 On success
|
||||
* \retval -1 On failure (group@category not found)
|
||||
* \retval -2 On failure (input validation error)
|
||||
*/
|
||||
int ast_app_group_set_var(struct ast_channel *chan, const char *group, const char *category, const char *name, const char *value);
|
||||
|
||||
/*!
|
||||
* \brief Get a group variable for a group@category
|
||||
*
|
||||
* \param group group to get the variable from
|
||||
* \param category category to get the variable from
|
||||
* \param name name of the variable to get
|
||||
*
|
||||
* \retval NOT NULL On success return char* of variable contents
|
||||
* \retval NULL On failure (variable in group@category not found)
|
||||
*/
|
||||
const char* ast_app_group_get_var(const char *group, const char *category, const char *name);
|
||||
|
||||
/*! \brief Get the current channel count of the specified group and category. */
|
||||
int ast_app_group_get_count(const char *group, const char *category);
|
||||
|
||||
|
@ -1223,6 +1262,15 @@ struct ast_group_info *ast_app_group_list_head(void);
|
|||
/*! \brief Unlock the group count list */
|
||||
int ast_app_group_list_unlock(void);
|
||||
|
||||
/*! \brief Read Lock the group meta list */
|
||||
int ast_app_group_meta_rdlock(void);
|
||||
|
||||
/*! \brief Get the head of the group meta list */
|
||||
struct ast_group_meta *ast_app_group_meta_head(void);
|
||||
|
||||
/*! \brief Unlock the group meta list */
|
||||
int ast_app_group_meta_unlock(void);
|
||||
|
||||
/*!
|
||||
\brief Define an application argument
|
||||
\param name The name of the argument
|
||||
|
|
|
@ -122,6 +122,8 @@ References:
|
|||
#ifndef _ASTERISK_CHANNEL_H
|
||||
#define _ASTERISK_CHANNEL_H
|
||||
|
||||
#include <regex.h>
|
||||
|
||||
#include "asterisk/alertpipe.h"
|
||||
#include "asterisk/abstract_jb.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
|
@ -173,6 +175,9 @@ extern "C" {
|
|||
#define MAX_MUSICCLASS 80 /*!< Max length of the music class setting */
|
||||
#define AST_MAX_USER_FIELD 256 /*!< Max length of the channel user field */
|
||||
|
||||
#define MAX_GROUP_LEN 80 /*!< Max length of a channel group */
|
||||
#define MAX_CATEGORY_LEN 80 /*!< Max length of a channel group category */
|
||||
|
||||
#include "asterisk/frame.h"
|
||||
#include "asterisk/chanvars.h"
|
||||
#include "asterisk/config.h"
|
||||
|
@ -2919,6 +2924,19 @@ struct ast_group_info {
|
|||
AST_LIST_ENTRY(ast_group_info) group_list;
|
||||
};
|
||||
|
||||
/*! \brief list of groups currently in use, with a pointer to a list of channels within the groups
|
||||
*/
|
||||
struct ast_group_meta {
|
||||
int num_channels; /*!< number of channels in this group */
|
||||
struct varshead varshead; /*!< A linked list for group variables. See \ref AstGroupVar */
|
||||
|
||||
AST_LIST_ENTRY(ast_group_info) channels_list; /*!< List of channels in this group */
|
||||
AST_LIST_ENTRY(ast_group_meta) group_meta_list; /*!< Next group */
|
||||
|
||||
char category[MAX_CATEGORY_LEN];
|
||||
char group[MAX_GROUP_LEN];
|
||||
};
|
||||
|
||||
#define ast_channel_lock(chan) ao2_lock(chan)
|
||||
#define ast_channel_unlock(chan) ao2_unlock(chan)
|
||||
#define ast_channel_trylock(chan) ao2_trylock(chan)
|
||||
|
@ -3133,6 +3151,35 @@ struct ast_channel *ast_channel_get_by_name_prefix(const char *name, size_t name
|
|||
*/
|
||||
struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *context);
|
||||
|
||||
/*!
|
||||
* \brief Find a channel by a regex string
|
||||
*
|
||||
* \arg regex_string the regex pattern to search for
|
||||
*
|
||||
* Return a channel that where the regex pattern matches the channel name
|
||||
*
|
||||
* \retval a channel that matches the regex pattern
|
||||
* \retval NULL if no channel was found or pattern is bad
|
||||
*
|
||||
* \since 18
|
||||
*/
|
||||
struct ast_channel *ast_channel_get_by_regex(const char *regex_string);
|
||||
|
||||
/*!
|
||||
* \brief Find a channel by a regex pattern
|
||||
*
|
||||
* \arg regex the compiled regex pattern to search for
|
||||
*
|
||||
* Return a channel that where the regex pattern matches the channel name
|
||||
*
|
||||
* \retval a channel that matches the regex pattern
|
||||
* \retval NULL if no channel was found
|
||||
*
|
||||
* \since 18
|
||||
*/
|
||||
struct ast_channel *ast_channel_get_by_regex_compiled(regex_t *regex);
|
||||
|
||||
|
||||
/*! @} End channel search functions. */
|
||||
|
||||
/*!
|
||||
|
|
469
main/app.c
469
main/app.c
|
@ -61,6 +61,7 @@
|
|||
#include "asterisk/indications.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
#include "asterisk/threadstorage.h"
|
||||
#include "asterisk/manager.h"
|
||||
#include "asterisk/test.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/astobj2.h"
|
||||
|
@ -69,6 +70,95 @@
|
|||
#include "asterisk/json.h"
|
||||
#include "asterisk/format_cache.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<managerEvent language="en_US" name="GroupCreate">
|
||||
<managerEventInstance class="EVENT_FLAG_REPORTING">
|
||||
<synopsis>Raised when a group has been created. Note: GroupChannelAdd events will show which channels have been assigned this group@category</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Category">
|
||||
<para>The category portion of the group@category that has been created</para>
|
||||
</parameter>
|
||||
<parameter name="Group">
|
||||
<para>The group portion of the group@category that has been created</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="GroupChannelAdd">
|
||||
<managerEventInstance class="EVENT_FLAG_REPORTING">
|
||||
<synopsis>Raised when a channel is now a part of a group@category GROUP() assignment</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Channel">
|
||||
<para>Channel that is now part of this group@category</para>
|
||||
</parameter>
|
||||
<parameter name="Uniqueid">
|
||||
<para>Uniqueid of the channel that is now part of this group@category</para>
|
||||
</parameter>
|
||||
<parameter name="Category">
|
||||
<para>The category portion of the group@category of the GROUP() that the channel is now a part of</para>
|
||||
</parameter>
|
||||
<parameter name="Group">
|
||||
<para>The group portion of the group@category of the GROUP() that the channel is now a part of</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="GroupVarSet">
|
||||
<managerEventInstance class="EVENT_FLAG_REPORTING">
|
||||
<synopsis>Raised in response to when a Group Variable is set using GROUP_VAR() on a group@category GROUP()</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Group">
|
||||
<para>Name of the group that the variable was set on</para>
|
||||
</parameter>
|
||||
<parameter name="Category">
|
||||
<para>Name of the category that the variable was set on</para>
|
||||
</parameter>
|
||||
<parameter name="Channel">
|
||||
<para>Channel (if any) that caused the variable to be set</para>
|
||||
</parameter>
|
||||
<parameter name="Variable">
|
||||
<para>The variable that was set</para>
|
||||
</parameter>
|
||||
<parameter name="Value">
|
||||
<para>The value of the variable</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="GroupChannelRemove">
|
||||
<managerEventInstance class="EVENT_FLAG_REPORTING">
|
||||
<synopsis>Raised when a channel is no longer part of a group@category GROUP() assignment</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Channel">
|
||||
<para>Channel that no longer is part of this group@category</para>
|
||||
</parameter>
|
||||
<parameter name="Uniqueid">
|
||||
<para>Uniqueid of the channel that is no longer is part of this group</para>
|
||||
</parameter>
|
||||
<parameter name="Category">
|
||||
<para>The category portion of the group@category of the GROUP() that the channel was a part of</para>
|
||||
</parameter>
|
||||
<parameter name="Group">
|
||||
<para>The group portion of the group@category of the GROUP() that the channel was a part of</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
<managerEvent language="en_US" name="GroupDestroy">
|
||||
<managerEventInstance class="EVENT_FLAG_REPORTING">
|
||||
<synopsis>Raised when a group has been completely removed. Note: GroupChannelRemove events will show which channels no longer are assigned to this group@category</synopsis>
|
||||
<syntax>
|
||||
<parameter name="Category">
|
||||
<para>The category portion of the group@category that is no longer present</para>
|
||||
</parameter>
|
||||
<parameter name="Group">
|
||||
<para>The group portion of the group@category that is no longer present</para>
|
||||
</parameter>
|
||||
</syntax>
|
||||
</managerEventInstance>
|
||||
</managerEvent>
|
||||
***/
|
||||
|
||||
AST_THREADSTORAGE_PUBLIC(ast_str_thread_global_buf);
|
||||
|
||||
static pthread_t shaun_of_the_dead_thread = AST_PTHREADT_NULL;
|
||||
|
@ -118,10 +208,36 @@ static void *shaun_of_the_dead(void *data)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
struct group_data_store;
|
||||
|
||||
/* \brief a single group assignment entry */
|
||||
struct group_list_entry {
|
||||
struct ast_str *group; /*!< A group assignment is group@category, this is the group part */
|
||||
struct ast_str *category; /*!< This is the category part */
|
||||
|
||||
struct group_data_store *group_store; /*!< The group_data_store we live on */
|
||||
|
||||
AST_LIST_ENTRY(group_list_entry) entries; /*!< Next group */
|
||||
};
|
||||
|
||||
AST_LIST_HEAD_NOLOCK(group_list, group_list_entry); /*!< All GROUP assignments */
|
||||
|
||||
/* Datastore for GROUP() entry storage */
|
||||
struct group_data_store {
|
||||
void *datastore; /*!< Pointer to the datastore that was allocated in */
|
||||
struct ast_channel *chan; /*!< Channel this datastore is on */
|
||||
|
||||
struct group_list group_list_head; /*!< All the GROUPs that this channel is in */
|
||||
|
||||
AST_LIST_ENTRY(group_data_store) entries; /*!< Next GROUP datastore */
|
||||
};
|
||||
|
||||
AST_RWLIST_HEAD_STATIC(group_stores_list, group_data_store);
|
||||
|
||||
#define AST_MAX_FORMATS 10
|
||||
|
||||
static AST_RWLIST_HEAD_STATIC(groups, ast_group_info);
|
||||
static AST_RWLIST_HEAD_STATIC(groups, ast_group_info); /*!< List of channels that are in groups */
|
||||
static AST_RWLIST_HEAD_STATIC(groups_meta, ast_group_meta); /*!< List of groups and their metadata */
|
||||
|
||||
/*!
|
||||
* \brief This function presents a dialtone and reads an extension into 'collect'
|
||||
|
@ -2183,7 +2299,7 @@ int ast_app_group_split_group(const char *data, char *group, int group_max, char
|
|||
int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
int res = 0;
|
||||
char group[80] = "", category[80] = "";
|
||||
char group[MAX_GROUP_LEN] = "", category[MAX_CATEGORY_LEN] = "";
|
||||
struct ast_group_info *gi = NULL;
|
||||
size_t len = 0;
|
||||
|
||||
|
@ -2197,9 +2313,13 @@ int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
|
|||
len += strlen(category) + 1;
|
||||
}
|
||||
|
||||
/* Remove previous group assignment within this category if there is one */
|
||||
AST_RWLIST_WRLOCK(&groups);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {
|
||||
if ((gi->chan == chan) && ((ast_strlen_zero(category) && ast_strlen_zero(gi->category)) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {
|
||||
/* Find our group meta data, remove the entire group metadata if we're the last channel */
|
||||
ast_app_group_remove_channel(chan, gi->group, gi->category);
|
||||
|
||||
AST_RWLIST_REMOVE_CURRENT(group_list);
|
||||
ast_free(gi);
|
||||
break;
|
||||
|
@ -2218,6 +2338,8 @@ int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
|
|||
strcpy(gi->category, category);
|
||||
}
|
||||
AST_RWLIST_INSERT_TAIL(&groups, gi, group_list);
|
||||
|
||||
ast_app_group_add_channel(chan, group, category);
|
||||
} else {
|
||||
res = -1;
|
||||
}
|
||||
|
@ -2227,6 +2349,111 @@ int ast_app_group_set_channel(struct ast_channel *chan, const char *data)
|
|||
return res;
|
||||
}
|
||||
|
||||
int ast_app_group_set_var(struct ast_channel *chan, const char *group, const char *category, const char *name, const char *value)
|
||||
{
|
||||
struct ast_group_meta *gmi = NULL;
|
||||
|
||||
struct varshead *headp;
|
||||
struct ast_var_t *newvariable = NULL;
|
||||
|
||||
if (!group || !name) {
|
||||
ast_log(LOG_WARNING, "<%s> GROUP assignment failed for %s@%s, group/name cannot be NULL, group variable '%s' not set\n", ast_channel_name(chan), group, category, name);
|
||||
return -2;
|
||||
}
|
||||
|
||||
if (!category) {
|
||||
category = "";
|
||||
}
|
||||
|
||||
if (!value) {
|
||||
value = "";
|
||||
}
|
||||
|
||||
/* Find our group meta data */
|
||||
AST_RWLIST_WRLOCK(&groups_meta);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups_meta, gmi, group_meta_list) {
|
||||
if ((strcasecmp(gmi->group, group) != 0) || (strcasecmp(gmi->category, category) != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
headp = &gmi->varshead;
|
||||
|
||||
AST_LIST_TRAVERSE(headp, newvariable, entries) {
|
||||
if (strcasecmp(ast_var_name(newvariable), name) == 0) {
|
||||
/* there is already such a variable, delete it */
|
||||
AST_LIST_REMOVE(headp, newvariable, entries);
|
||||
ast_var_delete(newvariable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
newvariable = ast_var_assign(name, value);
|
||||
|
||||
AST_LIST_INSERT_HEAD(headp, newvariable, entries);
|
||||
manager_event(EVENT_FLAG_DIALPLAN, "GroupVarSet",
|
||||
"Channel: %s\r\n"
|
||||
"Category: %s\r\n"
|
||||
"Group: %s\r\n"
|
||||
"Variable: %s\r\n"
|
||||
"Value: %s\r\n"
|
||||
"Uniqueid: %s\r\n",
|
||||
chan ? ast_channel_name(chan) : "none",
|
||||
category, group, name, value,
|
||||
chan ? ast_channel_uniqueid(chan) : "none");
|
||||
|
||||
break; /* We only have one list item per group@category */
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&groups_meta);
|
||||
|
||||
if (!newvariable) {
|
||||
ast_log(LOG_WARNING, "<%s> GROUP assignment %s@%s doesn't exist, group variable '%s' not set\n", ast_channel_name(chan), group, category, name);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *ast_app_group_get_var(const char *group, const char *category, const char *name)
|
||||
{
|
||||
struct ast_group_meta *gmi = NULL;
|
||||
|
||||
struct varshead *headp;
|
||||
const char *variable;
|
||||
struct ast_var_t *ast_var;
|
||||
|
||||
if (!group || !name) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!category) {
|
||||
category = "";
|
||||
}
|
||||
|
||||
/* Find our group meta data */
|
||||
AST_RWLIST_RDLOCK(&groups_meta);
|
||||
AST_RWLIST_TRAVERSE(&groups_meta, gmi, group_meta_list) {
|
||||
if ((strcasecmp(gmi->group, group) != 0) || (strcasecmp(gmi->category, category) != 0)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
headp = &gmi->varshead;
|
||||
|
||||
AST_LIST_TRAVERSE(headp, ast_var, entries) {
|
||||
variable = ast_var_name(ast_var);
|
||||
|
||||
if (!strcasecmp(variable, name)) {
|
||||
/* found it */
|
||||
AST_RWLIST_UNLOCK(&groups_meta);
|
||||
return ast_var_value(ast_var);
|
||||
}
|
||||
}
|
||||
}
|
||||
AST_RWLIST_UNLOCK(&groups_meta);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int ast_app_group_get_count(const char *group, const char *category)
|
||||
{
|
||||
struct ast_group_info *gi = NULL;
|
||||
|
@ -2265,6 +2492,7 @@ int ast_app_group_match_get_count(const char *groupmatch, const char *category)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* if regex compilation fails, return zero matches */
|
||||
if (!ast_strlen_zero(category) && regcomp(®exbuf_category, category, REG_EXTENDED | REG_NOSUB)) {
|
||||
ast_log(LOG_ERROR, "Regex compile failed on: %s\n", category);
|
||||
regfree(®exbuf_group);
|
||||
|
@ -2290,14 +2518,21 @@ int ast_app_group_match_get_count(const char *groupmatch, const char *category)
|
|||
int ast_app_group_update(struct ast_channel *old, struct ast_channel *new)
|
||||
{
|
||||
struct ast_group_info *gi = NULL;
|
||||
struct ast_group_info *gi_new = NULL;
|
||||
|
||||
AST_RWLIST_WRLOCK(&groups);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {
|
||||
/* keep channel groups on transfer */
|
||||
if (gi->chan == old) {
|
||||
/* only move group if it doesn't already exist on new */
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi_new, group_list) {
|
||||
if (gi_new->chan == old && !strcasecmp(gi_new->group, gi->group) && !strcasecmp(gi_new->category, gi->category)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
|
||||
gi->chan = new;
|
||||
} else if (gi->chan == new) {
|
||||
AST_RWLIST_REMOVE_CURRENT(group_list);
|
||||
ast_free(gi);
|
||||
}
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
|
@ -2306,16 +2541,219 @@ int ast_app_group_update(struct ast_channel *old, struct ast_channel *new)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* Remove a channel from a group meta assignment */
|
||||
/* Right now this just removes all the group metadata for a group@category if this is the last channel in the group@category */
|
||||
/* Ideally we would have direct pointers from the channel group assignments into the metadata struct, so we don't have to search */
|
||||
int ast_app_group_remove_channel(struct ast_channel *chan, char *group, char *category)
|
||||
{
|
||||
struct ast_group_meta *gmi = NULL; /*!< Group metadatas */
|
||||
|
||||
struct varshead *headp;
|
||||
struct ast_var_t *vardata;
|
||||
|
||||
int destroy = 0;
|
||||
|
||||
if (!category) {
|
||||
category = "";
|
||||
}
|
||||
|
||||
AST_RWLIST_WRLOCK(&groups_meta);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups_meta, gmi, group_meta_list) {
|
||||
if (!strcasecmp(gmi->group, group) && !strcasecmp(gmi->category, category) && (--gmi->num_channels <= 0)) {
|
||||
/* Remove all group variables */
|
||||
headp = &gmi->varshead;
|
||||
|
||||
while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries))) {
|
||||
ast_var_delete(vardata);
|
||||
}
|
||||
|
||||
AST_RWLIST_REMOVE_CURRENT(group_meta_list);
|
||||
ast_free(gmi);
|
||||
|
||||
destroy = 1;
|
||||
break; /* We only have one list item per group@category */
|
||||
}
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&groups_meta);
|
||||
|
||||
manager_event(EVENT_FLAG_DIALPLAN, "GroupChannelRemove",
|
||||
"Channel: %s\r\n"
|
||||
"Category: %s\r\n"
|
||||
"Group: %s\r\n"
|
||||
"Uniqueid: %s\r\n",
|
||||
ast_channel_name(chan),
|
||||
category, group,
|
||||
ast_channel_uniqueid(chan));
|
||||
|
||||
if (destroy) {
|
||||
manager_event(EVENT_FLAG_DIALPLAN, "GroupDestroy",
|
||||
"Category: %s\r\n"
|
||||
"Group: %s\r\n",
|
||||
category, group);
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Add a channel to a group meta assignment, create a group meta item if it doesn't exist */
|
||||
int ast_app_group_add_channel(struct ast_channel *chan, char *group, char *category)
|
||||
{
|
||||
struct ast_group_meta *gmi = NULL; /*!< Group metadatas */
|
||||
|
||||
AST_RWLIST_WRLOCK(&groups_meta);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups_meta, gmi, group_meta_list) {
|
||||
if (!strcasecmp(gmi->group, group) && !strcasecmp(gmi->category, category)) {
|
||||
break; /* We only have one list item per group@category */
|
||||
}
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
|
||||
if (!gmi) {
|
||||
if (!(gmi = ast_calloc(1, sizeof(struct ast_group_meta)))) {
|
||||
AST_RWLIST_UNLOCK(&groups_meta);
|
||||
return -1;
|
||||
}
|
||||
|
||||
ast_copy_string(gmi->group, group, MAX_GROUP_LEN);
|
||||
|
||||
if (!ast_strlen_zero(category)) {
|
||||
ast_copy_string(gmi->category, category, MAX_CATEGORY_LEN);
|
||||
}
|
||||
|
||||
AST_RWLIST_INSERT_TAIL(&groups_meta, gmi, group_meta_list);
|
||||
|
||||
manager_event(EVENT_FLAG_DIALPLAN, "GroupCreate",
|
||||
"Category: %s\r\n"
|
||||
"Group: %s\r\n",
|
||||
category, group);
|
||||
}
|
||||
|
||||
gmi->num_channels++;
|
||||
|
||||
AST_RWLIST_UNLOCK(&groups_meta);
|
||||
|
||||
manager_event(EVENT_FLAG_DIALPLAN, "GroupChannelAdd",
|
||||
"Channel: %s\r\n"
|
||||
"Category: %s\r\n"
|
||||
"Group: %s\r\n"
|
||||
"Uniqueid: %s\r\n",
|
||||
ast_channel_name(chan),
|
||||
category, group,
|
||||
ast_channel_uniqueid(chan));
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* Remove all channels from the given group */
|
||||
int ast_app_group_remove_all_channels(const char *group, const char *category)
|
||||
{
|
||||
struct ast_group_info *gi = NULL;
|
||||
|
||||
int channels_found = 0;
|
||||
|
||||
if (!category) {
|
||||
category = "";
|
||||
}
|
||||
|
||||
/* Traverse group@category channel assignments */
|
||||
AST_RWLIST_WRLOCK(&groups);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {
|
||||
if (strcasecmp(gi->group, group) || strcasecmp(gi->category, category)) {
|
||||
/* Need to match group@category exactly */
|
||||
continue;
|
||||
}
|
||||
|
||||
ast_app_group_remove_channel(gi->chan, gi->group, gi->category);
|
||||
AST_RWLIST_REMOVE_CURRENT(group_list);
|
||||
ast_free(gi);
|
||||
|
||||
channels_found++;
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&groups);
|
||||
|
||||
return !(channels_found > 0);
|
||||
}
|
||||
|
||||
int ast_app_group_rename(const char *old_group, const char *old_category, const char *new_group, const char *new_category)
|
||||
{
|
||||
struct ast_group_info *gi = NULL;
|
||||
struct ast_group_meta *gmi = NULL; /*!< Group metadatas */
|
||||
|
||||
int channels_found = 0;
|
||||
|
||||
if (!old_category) {
|
||||
old_category = "";
|
||||
}
|
||||
|
||||
if (!new_category) {
|
||||
new_category = "";
|
||||
}
|
||||
|
||||
/* Traverse group@category channel assignments */
|
||||
AST_RWLIST_WRLOCK(&groups);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {
|
||||
if (strcasecmp(gi->group, old_group) || strcasecmp(gi->category, old_category)) {
|
||||
/* Need to match group@category exactly */
|
||||
continue;
|
||||
}
|
||||
|
||||
ast_copy_string(gi->group, new_group, MAX_GROUP_LEN);
|
||||
ast_copy_string(gi->category, new_category, MAX_CATEGORY_LEN);
|
||||
channels_found++;
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&groups);
|
||||
|
||||
|
||||
/* Group Variables */
|
||||
AST_RWLIST_WRLOCK(&groups_meta);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups_meta, gmi, group_meta_list) {
|
||||
if (strcasecmp(gmi->group, old_group) || strcasecmp(gmi->category, old_category)) {
|
||||
/* Need to match group@category exactly */
|
||||
continue;
|
||||
}
|
||||
|
||||
strncpy(gmi->group, new_group, MAX_GROUP_LEN - 1);
|
||||
strncpy(gmi->category, new_category, MAX_CATEGORY_LEN - 1);
|
||||
channels_found++;
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&groups_meta);
|
||||
|
||||
if (channels_found) {
|
||||
manager_event(EVENT_FLAG_DIALPLAN, "GroupRename",
|
||||
"OldGroup: %s\r\n"
|
||||
"OldCategory: %s\r\n"
|
||||
"NewGroup: %s\r\n"
|
||||
"NewCategory: %s\r\n",
|
||||
old_group,
|
||||
old_category,
|
||||
new_group,
|
||||
new_category);
|
||||
}
|
||||
|
||||
return !(channels_found > 0);
|
||||
}
|
||||
|
||||
int ast_app_group_discard(struct ast_channel *chan)
|
||||
{
|
||||
struct ast_group_info *gi = NULL;
|
||||
|
||||
/* Find and remove all groups associated to this channel */
|
||||
AST_RWLIST_WRLOCK(&groups);
|
||||
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {
|
||||
if (gi->chan == chan) {
|
||||
AST_RWLIST_REMOVE_CURRENT(group_list);
|
||||
ast_free(gi);
|
||||
if (gi->chan != chan) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Find our group meta data, remove the entire group metadata if we're the last channel */
|
||||
ast_app_group_remove_channel(chan, gi->group, gi->category);
|
||||
|
||||
/* Remove this group assignment for this channel */
|
||||
AST_RWLIST_REMOVE_CURRENT(group_list);
|
||||
ast_free(gi);
|
||||
}
|
||||
AST_RWLIST_TRAVERSE_SAFE_END;
|
||||
AST_RWLIST_UNLOCK(&groups);
|
||||
|
@ -2343,6 +2781,21 @@ int ast_app_group_list_unlock(void)
|
|||
return AST_RWLIST_UNLOCK(&groups);
|
||||
}
|
||||
|
||||
int ast_app_group_meta_rdlock(void)
|
||||
{
|
||||
return AST_RWLIST_RDLOCK(&groups_meta);
|
||||
}
|
||||
|
||||
struct ast_group_meta *ast_app_group_meta_head(void)
|
||||
{
|
||||
return AST_RWLIST_FIRST(&groups_meta);
|
||||
}
|
||||
|
||||
int ast_app_group_meta_unlock(void)
|
||||
{
|
||||
return AST_RWLIST_UNLOCK(&groups_meta);
|
||||
}
|
||||
|
||||
unsigned int __ast_app_separate_args(char *buf, char delim, int remove_chars, char **array, int arraylen)
|
||||
{
|
||||
int argc;
|
||||
|
|
63
main/cli.c
63
main/cli.c
|
@ -1916,8 +1916,9 @@ static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc < 3 || a->argc > 4)
|
||||
if (a->argc < 3 || a->argc > 4) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
if (a->argc == 4) {
|
||||
if (regcomp(®exbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
|
||||
|
@ -1948,6 +1949,64 @@ static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cl
|
|||
#undef FORMAT_STRING
|
||||
}
|
||||
|
||||
static char *group_show_variables(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
#define FORMAT_STRING "%-20s %-20s\n"
|
||||
#define FORMAT_STRING_VAR " %s=%s\n"
|
||||
|
||||
struct ast_group_meta *gmi = NULL;
|
||||
struct varshead *headp;
|
||||
struct ast_var_t *variable = NULL;
|
||||
int numgroups = 0;
|
||||
char group[80];
|
||||
char category[80];
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "group show variables";
|
||||
e->usage =
|
||||
"Usage: group show variables [group@[category]]\n"
|
||||
" Lists all currently active groups and their variables.\n"
|
||||
" Optional group, or group@category can be used to only show a specific group\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (a->argc < 3 || a->argc > 4) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
if (a->argc == 4) {
|
||||
if (ast_app_group_split_group(a->argv[3], group, sizeof(group), category, sizeof(category))) {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
ast_cli(a->fd, "Group\n Category\nVariables\n");
|
||||
ast_cli(a->fd, "------------------------------\n");
|
||||
|
||||
/* Print group variables */
|
||||
ast_app_group_meta_rdlock();
|
||||
gmi = ast_app_group_meta_head();
|
||||
while (gmi) {
|
||||
ast_cli(a->fd, FORMAT_STRING, gmi->group, (strcmp(gmi->category, "") ? gmi->category : "(Default)"));
|
||||
numgroups++;
|
||||
headp = &gmi->varshead;
|
||||
|
||||
AST_LIST_TRAVERSE(headp, variable, entries) {
|
||||
ast_cli(a->fd, FORMAT_STRING_VAR, ast_var_name(variable), ast_var_value(variable));
|
||||
}
|
||||
|
||||
gmi = AST_LIST_NEXT(gmi, group_meta_list);
|
||||
}
|
||||
ast_app_group_meta_unlock();
|
||||
|
||||
ast_cli(a->fd, "%d active group%s\n", numgroups, ESS(numgroups));
|
||||
return CLI_SUCCESS;
|
||||
#undef FORMAT_STRING
|
||||
}
|
||||
|
||||
static char *handle_cli_wait_fullybooted(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
switch (cmd) {
|
||||
|
@ -2021,7 +2080,6 @@ static struct ast_cli_entry cli_cli[] = {
|
|||
AST_CLI_DEFINE(handle_debug, "Set level of debug chattiness"),
|
||||
AST_CLI_DEFINE(handle_trace, "Set level of trace chattiness"),
|
||||
AST_CLI_DEFINE(handle_verbose, "Set level of verbose chattiness"),
|
||||
|
||||
AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
|
||||
AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
|
||||
|
||||
|
@ -2052,6 +2110,7 @@ static struct ast_cli_entry cli_channels_cli[] = {
|
|||
AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
|
||||
AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel"),
|
||||
AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
|
||||
AST_CLI_DEFINE(group_show_variables, "Display active channels with group(s), along with group variables"),
|
||||
AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue