<p>Mark Murawski has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/c/asterisk/+/17655">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">func_groupcount.c: Adding Group Variables and additional Group functions<br><br>DumpGroups<br>-------------------<br>* New application. This will dump all channel group membership and associated<br> variables<br><br>Groups and Group Variables<br>------------------<br> * Group variables can be set on a group once the group is created<br> When a group is destroyed, all variables on that group are also destroyed<br><br> A group variable is somewhat like a global variable on a per-group basis.<br><br> GroupSet - Adds functionality to the manager to be able to set a GROUP()<br> on a channel.<br> GroupsShowChannels - Show each channel and it's associated groups<br> (a channel will be repeated for each group@category<br> it's a member of)<br> GroupsShowVariables - Show variables in each group@category, one event per<br> group, all variables are contained in each<br> group@category event<br> GroupVarSet - Set a group variable (the group must already exist)<br> GroupVarGet - Get a group variable<br><br> * New Manager events:<br> ------------------<br> GroupCreate - Event is fired any time a group is made,<br> ie: Set(GROUP=x) or Set(GROUP()=x@y).<br> This event is only sent on when a channel is added to a group<br> that did not exist previously.<br> GroupChannelAdd - Event is fired any time a channel is added to a group<br> GroupChannelRemove - Event is fired any time a channel is removed from a<br> group<br> GroupDestroy - Event is fired when there are no longer any channels assigned<br> to the group<br> GroupVarSet - Event is fired when any group variable is changed<br><br> * New CLI Command<br> ---------------<br> group show variables<br><br> * New Application<br> ---------------<br> DumpGroups() - Show groups and group assigments (similar to DumpChan)<br><br>ASTERISK-15439 #close<br><br>Change-Id: I23e48d1cdfc8adaffdfec2e936e56143603914f2<br>---<br>M apps/app_dial.c<br>M funcs/func_groupcount.c<br>M include/asterisk/app.h<br>M include/asterisk/channel.h<br>M main/app.c<br>M main/cli.c<br>6 files changed, 1,408 insertions(+), 38 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/55/17655/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;"><span>diff --git a/apps/app_dial.c b/apps/app_dial.c</span><br><span>index f073af3..688773b 100644</span><br><span>--- a/apps/app_dial.c</span><br><span>+++ b/apps/app_dial.c</span><br><span>@@ -2740,7 +2740,7 @@</span><br><span> </span><br><span> /* If we have an outbound group, set this peer channel to it */</span><br><span> if (outbound_group)</span><br><span style="color: hsl(0, 100%, 40%);">- ast_app_group_set_channel(tc, outbound_group);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_set_channel(tc, outbound_group, 0);</span><br><span> /* If the calling channel has the ANSWERED_ELSEWHERE flag set, inherit it. This is to support local channels */</span><br><span> if (ast_channel_hangupcause(chan) == AST_CAUSE_ANSWERED_ELSEWHERE)</span><br><span> ast_channel_hangupcause_set(tc, AST_CAUSE_ANSWERED_ELSEWHERE);</span><br><span>diff --git a/funcs/func_groupcount.c b/funcs/func_groupcount.c</span><br><span>index f6dd5c6..6d63c42 100644</span><br><span>--- a/funcs/func_groupcount.c</span><br><span>+++ b/funcs/func_groupcount.c</span><br><span>@@ -26,12 +26,16 @@</span><br><span> ***/</span><br><span> </span><br><span> #include "asterisk.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "regex.h"</span><br><span> </span><br><span> #include "asterisk/module.h"</span><br><span> #include "asterisk/channel.h"</span><br><span> #include "asterisk/pbx.h"</span><br><span> #include "asterisk/utils.h"</span><br><span> #include "asterisk/app.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/manager.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/strings.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/cli.h"</span><br><span> </span><br><span> /*** DOCUMENTATION</span><br><span> <function name="GROUP_COUNT" language="en_US"></span><br><span>@@ -77,6 +81,13 @@</span><br><span> <parameter name="category"></span><br><span> <para>Category name.</para></span><br><span> </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="options"></span><br><span style="color: hsl(120, 100%, 40%);">+ <optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ <option name="i"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Inherit. Group membership will be kept when a channel is transferred.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </option></span><br><span style="color: hsl(120, 100%, 40%);">+ </optionlist></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span> </syntax></span><br><span> <description></span><br><span> <para><replaceable>category</replaceable> can be employed for more fine grained group management. Each channel</span><br><span>@@ -92,15 +103,174 @@</span><br><span> <para>Gets a list of the groups set on a channel.</para></span><br><span> </description></span><br><span> </function></span><br><span style="color: hsl(0, 100%, 40%);">-</span><br><span style="color: hsl(120, 100%, 40%);">+ <manager name="GroupSet" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Add channel group assignments</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="channel"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Channel to operate on.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="group"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Group name to set.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="category"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Category name to set.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>For more information, see the dialplan function GROUP()</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </manager></span><br><span style="color: hsl(120, 100%, 40%);">+ <manager name="GroupRemove" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Remove channel group assignments</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="channel"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Channel to operate on.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="group"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Group name to remove.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="category"></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>Category name to remove.</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </parameter></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>For more information, see the dialplan function GROUP()</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </manager></span><br><span style="color: hsl(120, 100%, 40%);">+ <application name="DumpGroups" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Dump all group information to the console</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ <para>When executed, this will show all group assignments and group variables</span><br><span style="color: hsl(120, 100%, 40%);">+ in the console</para></span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </application></span><br><span style="color: hsl(120, 100%, 40%);">+ <manager name="GroupVarGet" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Get channel group variables.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Group" required="false" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Category" required="false" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Variable" required="true" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ At a minimum, either group or category must be provided.</span><br><span style="color: hsl(120, 100%, 40%);">+ For more information, see the dialplan function GROUP_VAR().</span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </manager></span><br><span style="color: hsl(120, 100%, 40%);">+ <manager name="GroupVarSet" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Set channel group variables.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Group" required="false" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Category" required="false" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Variable" required="true" /></span><br><span style="color: hsl(120, 100%, 40%);">+ <parameter name="Value" required="false" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ At a minimum, either group or category must be provided.</span><br><span style="color: hsl(120, 100%, 40%);">+ For more information, see the dialplan function GROUP_VAR().</span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </manager></span><br><span style="color: hsl(120, 100%, 40%);">+ <manager name="GroupsShow" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Show channel groups.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ This will return a list of channel groups that are in use.</span><br><span style="color: hsl(120, 100%, 40%);">+ For more information, see the dialplan function GROUP().</span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </manager></span><br><span style="color: hsl(120, 100%, 40%);">+ <manager name="GroupsShowChannels" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Show group channel assignments.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ This will return a list the channels that are within each group.</span><br><span style="color: hsl(120, 100%, 40%);">+ For more information, see the dialplan function GROUP().</span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </manager></span><br><span style="color: hsl(120, 100%, 40%);">+ <manager name="GroupsShowVariables" language="en_US"></span><br><span style="color: hsl(120, 100%, 40%);">+ <synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ Show group channel variable assignments.</span><br><span style="color: hsl(120, 100%, 40%);">+ </synopsis></span><br><span style="color: hsl(120, 100%, 40%);">+ <syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" /></span><br><span style="color: hsl(120, 100%, 40%);">+ </syntax></span><br><span style="color: hsl(120, 100%, 40%);">+ <description></span><br><span style="color: hsl(120, 100%, 40%);">+ This will return a list of groups and the variables assigned in each group.</span><br><span style="color: hsl(120, 100%, 40%);">+ For more information, see the dialplan function GROUP_VAR().</span><br><span style="color: hsl(120, 100%, 40%);">+ </description></span><br><span style="color: hsl(120, 100%, 40%);">+ </manager></span><br><span> ***/</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static void search_info_free(void *data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static const char *dumpgroups_app = "DumpGroups";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_datastore_info search_info = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .type = "GROUP_SEARCH",</span><br><span style="color: hsl(120, 100%, 40%);">+ .destroy = search_info_free,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct found_group {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_ENTRY(found_group) entries; /*!< Next group */</span><br><span style="color: hsl(120, 100%, 40%);">+ char category[MAX_CATEGORY_LEN]; /*!< Copy of group_meta, since the group may have gone away while reading results */</span><br><span style="color: hsl(120, 100%, 40%);">+ char group[MAX_GROUP_LEN];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_RWLIST_HEAD(found_groups_list, found_group);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Datastore for search operations */</span><br><span style="color: hsl(120, 100%, 40%);">+struct search_data_store {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct found_groups_list found_groups_head;</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static void search_info_free(void *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct search_data_store *search_store = (struct search_data_store *) data;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct found_groups_list *found_groups_list;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct found_group *fg = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ found_groups_list = &search_store->found_groups_head;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(found_groups_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((fg = AST_LIST_REMOVE_HEAD(found_groups_list, entries))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(fg);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(found_groups_list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(data);</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int group_count_function_read(struct ast_channel *chan, const char *cmd,</span><br><span> char *data, char *buf, size_t len)</span><br><span> {</span><br><span> int ret = -1;</span><br><span> int count = -1;</span><br><span style="color: hsl(0, 100%, 40%);">- char group[80] = "", category[80] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char group[MAX_GROUP_LEN] = "", category[MAX_CATEGORY_LEN] = "";</span><br><span> </span><br><span> if (!chan) {</span><br><span> ast_log(LOG_WARNING, "No channel was provided to %s function.\n", cmd);</span><br><span>@@ -149,8 +319,8 @@</span><br><span> const char *cmd, char *data, char *buf,</span><br><span> size_t len)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- char group[80] = "";</span><br><span style="color: hsl(0, 100%, 40%);">- char category[80] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char group[MAX_GROUP_LEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char category[MAX_CATEGORY_LEN] = "";</span><br><span> </span><br><span> ast_app_group_split_group(data, group, sizeof(group), category,</span><br><span> sizeof(category));</span><br><span>@@ -224,7 +394,7 @@</span><br><span> ast_copy_string(grpcat, value, sizeof(grpcat));</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_app_group_set_channel(chan, grpcat))</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_app_group_set_channel(chan, grpcat, 0))</span><br><span> ast_log(LOG_WARNING,</span><br><span> "Setting a group requires an argument (group name)\n");</span><br><span> </span><br><span>@@ -237,6 +407,341 @@</span><br><span> .write = group_function_write,</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* GROUP_VAR and related */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int group_var_function_read(struct ast_channel *chan, const char *cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ char *data, char *buf, size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char group[MAX_GROUP_LEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char category[MAX_CATEGORY_LEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *variable_value;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(groupcategory);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(varname);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[0] = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.groupcategory) || ast_strlen_zero(args.varname)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Syntax GROUP_VAR(group[@category],<varname>)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_app_group_split_group(args.groupcategory, group, sizeof(group), category, sizeof(category)))</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ variable_value = ast_app_group_get_var(group, category, args.varname);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (variable_value) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(buf, variable_value, len);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int group_var_function_write(struct ast_channel *chan, const char *cmd,</span><br><span style="color: hsl(120, 100%, 40%);">+ char *data, const char *value)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char group[MAX_GROUP_LEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char category[MAX_CATEGORY_LEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(groupcategory);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(varname);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!value) {</span><br><span style="color: hsl(120, 100%, 40%);">+ value = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.groupcategory) || ast_strlen_zero(args.varname)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Syntax GROUP_VAR(group[@category],<varname>)=<value>)\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_split_group(args.groupcategory, group, sizeof(group), category, sizeof(category));</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_set_var(chan, group, category, args.varname, value);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int manager_group_set(struct mansession *s, const struct message *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *channel = astman_get_header(m, "Channel");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *group = astman_get_header(m, "Group");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *category = astman_get_header(m, "Category");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ char *group_category;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(channel)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Channel not specified.");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ chan = ast_channel_get_by_name(channel);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!chan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Channel not found.");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!group) {</span><br><span style="color: hsl(120, 100%, 40%);">+ group = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ category = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ group_category = ast_malloc(strlen(group) + strlen(category) + 2); /* 1 for @, 1 for NULL*/</span><br><span style="color: hsl(120, 100%, 40%);">+ sprintf(group_category, "%s@%s", group, category);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_app_group_set_channel(chan, group_category, 0) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_ack(s, m, "Group Set");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unref(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Group set failed.");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_unref(chan);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int manager_group_remove(struct mansession *s, const struct message *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *group = astman_get_header(m, "Group");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *category = astman_get_header(m, "Category");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_app_group_remove_all_channels(group, category) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_ack(s, m, "Group Removed");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Group remove failed.");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int manager_group_var_get(struct mansession *s, const struct message *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *id = astman_get_header(m, "ActionID");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *group = astman_get_header(m, "Group");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *category = astman_get_header(m, "Category");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *variable = astman_get_header(m, "Variable");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *variable_value;</span><br><span style="color: hsl(120, 100%, 40%);">+ char idText[256] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(group)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "No group specified.");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(variable)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "No variable specified.");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(id))</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ category = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ variable_value = ast_app_group_get_var(group, category, variable);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!variable_value) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Group variable not found");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_ack(s, m, "Result will follow");</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_append(s, "Event: GroupVarGetResponse\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Group: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Category: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Variable: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Value: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+ "\r\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ group, category, variable, variable_value, idText);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int manager_group_var_set(struct mansession *s, const struct message *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *group = astman_get_header(m, "Group");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *category = astman_get_header(m, "Category");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *variable = astman_get_header(m, "Variable");</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *value = astman_get_header(m, "Value");</span><br><span style="color: hsl(120, 100%, 40%);">+ int result = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(group)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "No group specified.");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(variable)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "No variable specified.");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ category = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!value) {</span><br><span style="color: hsl(120, 100%, 40%);">+ value = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ result = ast_app_group_set_var(NULL, group, category, variable, value);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!result) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_error(s, m, "Variable set failed (group doesn't exist)");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_ack(s, m, "Variable Set");</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int manager_groups_show(struct mansession *s, const struct message *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *id = astman_get_header(m, "ActionID");</span><br><span style="color: hsl(120, 100%, 40%);">+// const char *group = astman_get_header(m, "Group"); // todo: filtering</span><br><span style="color: hsl(120, 100%, 40%);">+// const char *category = astman_get_header(m, "Category"); // todo: filtering</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char idText[256] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ int groups = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(id)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_listack(s, m, "Groups will follow", "start");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_rdlock();</span><br><span style="color: hsl(120, 100%, 40%);">+ for (gmi = ast_app_group_meta_head(); gmi; gmi = AST_LIST_NEXT(gmi, group_meta_list)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_append(s,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Event: GroupsShow\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Group: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Category: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+ "\r\n", gmi->group, gmi->category, idText);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ groups++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_unlock();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_append(s,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Event: GroupsShowComplete\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "EventList: Complete\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "ListItems: %d\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+ "\r\n", groups, idText);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int manager_groups_show_channels(struct mansession *s, const struct message *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *id = astman_get_header(m, "ActionID");</span><br><span style="color: hsl(120, 100%, 40%);">+// const char *group = astman_get_header(m, "Group"); // todo: filtering</span><br><span style="color: hsl(120, 100%, 40%);">+// const char *category = astman_get_header(m, "Category"); // todo: filtering</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_info *gi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char idText[256] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ int channels = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(id)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_listack(s, m, "Group channels will follow", "start");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_list_rdlock();</span><br><span style="color: hsl(120, 100%, 40%);">+ for (gi = ast_app_group_list_head(); gi; gi = AST_LIST_NEXT(gi, group_list)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_append(s,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Event: GroupsShowChannels\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Group: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Category: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Channel: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+ "\r\n", gi->group, gi->category, ast_channel_name(gi->chan), idText);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ channels++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_list_unlock();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_append(s,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Event: GroupsShowChannelsComplete\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "EventList: Complete\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "ListItems: %d\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+ "\r\n", channels, idText);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int manager_groups_show_variables(struct mansession *s, const struct message *m)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *id = astman_get_header(m, "ActionID");</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ char action_id[256] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ int groups = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct varshead *headp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_var_t *vardata;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *variables = ast_str_create(100);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(id)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(action_id, sizeof(action_id), "ActionID: %s\r\n", id);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_send_listack(s, m, "Group variables will follow", "start");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_rdlock();</span><br><span style="color: hsl(120, 100%, 40%);">+ for (gmi = ast_app_group_meta_head(); gmi; gmi = AST_LIST_NEXT(gmi, group_meta_list)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ headp = &gmi->varshead;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_reset(variables);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(headp, vardata, entries) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&variables, 0, "Variable(%s): %s\r\n", ast_var_name(vardata), ast_var_value(vardata));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_append(s,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Event: GroupsShowVariables\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Group: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Category: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+ "\r\n", gmi->group, (gmi->category ? gmi->category : ""), action_id, ast_str_buffer((variables)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ groups++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_unlock();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ astman_append(s,</span><br><span style="color: hsl(120, 100%, 40%);">+ "Event: GroupsShowVariablesComplete\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "EventList: Complete\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "ListItems: %d\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "%s"</span><br><span style="color: hsl(120, 100%, 40%);">+ "\r\n", groups, action_id);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(variables);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_custom_function group_var_function = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "GROUP_VAR",</span><br><span style="color: hsl(120, 100%, 40%);">+ .syntax = "GROUP_VAR(groupname[@category],var)",</span><br><span style="color: hsl(120, 100%, 40%);">+ .synopsis = "Gets or sets a channel group variable.",</span><br><span style="color: hsl(120, 100%, 40%);">+ .desc = "Gets or sets a channel group variable.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ .read = group_var_function_read,</span><br><span style="color: hsl(120, 100%, 40%);">+ .write = group_var_function_write,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int group_list_function_read(struct ast_channel *chan, const char *cmd,</span><br><span> char *data, char *buf, size_t len)</span><br><span> {</span><br><span>@@ -273,20 +778,321 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static int group_list_function_start_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_datastore *data_store = ast_channel_datastore_find(chan, &search_info, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct search_data_store *search_store;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct found_groups_list *found_groups_list;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct found_group *fg = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ char groupmatch[MAX_GROUP_LEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char categorymatch[MAX_CATEGORY_LEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ int groups = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char groups_ret[1024] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ regex_t regexbuf_group;</span><br><span style="color: hsl(120, 100%, 40%);">+ regex_t regexbuf_category;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[0] = '0'; buf[1] = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!data_store) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_debug(1, "Channel %s has no group search datastore, so we're allocating one.\n", ast_channel_name(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ data_store = ast_datastore_alloc(&search_info, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!data_store) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Unable to allocate new group search datastore. Group search will fail.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ data_store->data = ast_calloc(1, sizeof(struct search_data_store));</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!data_store->data) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Unable to allocate new group search datastore list head(s). Group search will fail.\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_datastore_free(data_store);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_datastore_add(chan, data_store);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ search_store = (struct search_data_store *) data_store->data;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_groups_list = &search_store->found_groups_head;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_HEAD_INIT(found_groups_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ else {</span><br><span style="color: hsl(120, 100%, 40%);">+ search_store = (struct search_data_store *) data_store->data;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_groups_list = &search_store->found_groups_head;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* clear old search (if any) */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(found_groups_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((fg = AST_LIST_REMOVE_HEAD(found_groups_list, entries))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(fg);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(found_groups_list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_split_group(data, groupmatch, sizeof(groupmatch), categorymatch, sizeof(categorymatch));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* if regex compilation fails, return zero matches */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (regcomp(®exbuf_group, groupmatch, REG_EXTENDED | REG_NOSUB)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Regex compile failed on: %s\n", groupmatch);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (regcomp(®exbuf_category, categorymatch, REG_EXTENDED | REG_NOSUB)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_ERROR, "Regex compile failed on: %s\n", categorymatch);</span><br><span style="color: hsl(120, 100%, 40%);">+ regfree(®exbuf_group);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Traverse all groups, and keep track of what we find */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_rdlock();</span><br><span style="color: hsl(120, 100%, 40%);">+ for (gmi = ast_app_group_meta_head(); gmi; gmi = AST_LIST_NEXT(gmi, group_meta_list)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!regexec(®exbuf_group, gmi->group, 0, NULL, 0) && (ast_strlen_zero(categorymatch) || (!ast_strlen_zero(gmi->category) && !regexec(®exbuf_category, gmi->category, 0, NULL, 0)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(fg = ast_calloc(1, sizeof(struct found_group)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_unlock();</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ strncpy(fg->category, gmi->category, sizeof(fg->category));</span><br><span style="color: hsl(120, 100%, 40%);">+ strncpy(fg->group, gmi->group, sizeof(fg->group));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_INSERT_TAIL(found_groups_list, fg, entries);</span><br><span style="color: hsl(120, 100%, 40%);">+ groups++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_unlock();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ snprintf(groups_ret, sizeof(groups_ret), "%d", groups);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(buf, groups_ret, len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ regfree(®exbuf_group);</span><br><span style="color: hsl(120, 100%, 40%);">+ regfree(®exbuf_category);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int group_list_function_next_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_datastore *data_store = ast_channel_datastore_find(chan, &search_info, NULL);</span><br><span style="color: hsl(120, 100%, 40%);">+ struct search_data_store *search_store;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct found_groups_list *found_groups_list;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct found_group *fg = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *foundgroup_str;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!data_store) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "GROUP_MATCH_LIST_NEXT() called without starting a search using GROUP_MATCH_LIST_START()\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ search_store = (struct search_data_store *) data_store->data;</span><br><span style="color: hsl(120, 100%, 40%);">+ found_groups_list = &search_store->found_groups_head;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(found_groups_list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (AST_LIST_EMPTY(found_groups_list)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "GROUP_MATCH_LIST_NEXT() called after the last item was already asked for\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(found_groups_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* we don't need this item anymore after being read... another search will clear this list anyway */</span><br><span style="color: hsl(120, 100%, 40%);">+ fg = AST_LIST_REMOVE_HEAD(found_groups_list, entries);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(found_groups_list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Does this group still exist? */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_app_group_get_count(fg->group, fg->category) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ *buf = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(fg);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ foundgroup_str = ast_str_create(MAX_GROUP_LEN + MAX_CATEGORY_LEN);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&foundgroup_str, 0, "%s@%s", fg->group, fg->category);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_copy_string(buf, ast_str_buffer(foundgroup_str), len);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(fg);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(foundgroup_str);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int dumpgroups_exec(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+#define FORMAT_STRING_CHANNELS "%-25s %-20s %-20s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FORMAT_STRING_GROUPS "%-20s %-20s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FORMAT_STRING_VAR " %s=%s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ static char *line = "================================================================================";</span><br><span style="color: hsl(120, 100%, 40%);">+ static char *thinline = "-----------------------------------";</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_info *gi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct varshead *headp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_var_t *variable = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int numgroups = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ int numchans = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *out = ast_str_create(4096);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verbose("%s\n", line);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verbose(FORMAT_STRING_CHANNELS, "Channel", "Group", "Category\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_list_rdlock();</span><br><span style="color: hsl(120, 100%, 40%);">+ gi = ast_app_group_list_head();</span><br><span style="color: hsl(120, 100%, 40%);">+ while (gi) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&out, 0, FORMAT_STRING_CHANNELS, ast_channel_name(gi->chan), gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));</span><br><span style="color: hsl(120, 100%, 40%);">+ numchans++;</span><br><span style="color: hsl(120, 100%, 40%);">+ gi = AST_LIST_NEXT(gi, group_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_list_unlock();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&out, 0, "%d active channel%s in groups\n", numchans, ESS(numchans));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&out, 0, "%s\n", thinline);</span><br><span style="color: hsl(120, 100%, 40%);">+ /****************** Group Variables ******************/</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&out, 0, "Group Variables Category\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Print group variables */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_rdlock();</span><br><span style="color: hsl(120, 100%, 40%);">+ gmi = ast_app_group_meta_head();</span><br><span style="color: hsl(120, 100%, 40%);">+ while (gmi) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&out, 0, FORMAT_STRING_GROUPS, gmi->group, (strcmp(gmi->category, "") ? gmi->category : "(Default)"));</span><br><span style="color: hsl(120, 100%, 40%);">+ numgroups++;</span><br><span style="color: hsl(120, 100%, 40%);">+ headp = &gmi->varshead;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(headp, variable, entries) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&out, 0, FORMAT_STRING_VAR, ast_var_name(variable), ast_var_value(variable));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ gmi = AST_LIST_NEXT(gmi, group_meta_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_unlock();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&out, 0, "%s\n", line);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_verbose("%s\n", ast_str_buffer(out));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(out);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FORMAT_STRING_CHANNELS</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FORMAT_STRING_GROUPS</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FORMAT_STRING_VAR</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_custom_function group_match_list_start_function = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "GROUP_MATCH_LIST_START",</span><br><span style="color: hsl(120, 100%, 40%);">+ .syntax = "GROUP_MATCH_LIST_START(group_regex[@category_regex])",</span><br><span style="color: hsl(120, 100%, 40%);">+ .synopsis =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Start a find of groups by regular expression. Each call to GROUP_MATCH_LIST_NEXT() will return a matched group.\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "The return value is the number of groups found.\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Note: the search is executed and stored at the time of calling this function.\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "If GROUP() assignments change during successive calls to _NEXT, you will need to call _START again to see the new groups\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "An empty string is returned to indicate the end of the list.\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Uses standard regular expression matching (see regex(7)).",</span><br><span style="color: hsl(120, 100%, 40%);">+ .desc = "Find groups by regular expression search.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ .read = group_list_function_start_read,</span><br><span style="color: hsl(120, 100%, 40%);">+ .write = NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_custom_function group_match_list_next_function = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "GROUP_MATCH_LIST_NEXT",</span><br><span style="color: hsl(120, 100%, 40%);">+ .syntax = "GROUP_MATCH_LIST_NEXT()",</span><br><span style="color: hsl(120, 100%, 40%);">+ .synopsis =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Get the next matched group from a GROUP_MATCH_LIST_START() search\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "An empty string is returned to indicate the end of the list",</span><br><span style="color: hsl(120, 100%, 40%);">+ .desc = "Find groups by regular expression search.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ .read = group_list_function_next_read,</span><br><span style="color: hsl(120, 100%, 40%);">+ .write = NULL,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static struct ast_custom_function group_list_function = {</span><br><span> .name = "GROUP_LIST",</span><br><span> .read = group_list_function_read,</span><br><span> .write = NULL,</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static int group_channel_list_function_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ char group[MAX_GROUP_LEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char category[MAX_CATEGORY_LEN] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_info *gi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *out = ast_str_create(1024);</span><br><span style="color: hsl(120, 100%, 40%);">+ int out_len = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DECLARE_APP_ARGS(args,</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_APP_ARG(groupcategory);</span><br><span style="color: hsl(120, 100%, 40%);">+ );</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_STANDARD_APP_ARGS(args, data);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ buf[0] = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(args.groupcategory)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_log(LOG_WARNING, "Syntax GROUP_CHANNEL_LIST(group[@category])\n");</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_app_group_split_group(args.groupcategory, group, sizeof(group), category, sizeof(category))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_list_rdlock();</span><br><span style="color: hsl(120, 100%, 40%);">+ gi = ast_app_group_list_head();</span><br><span style="color: hsl(120, 100%, 40%);">+ while (gi) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(gi->group, group) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_str_append(&out, 0, "%s,", ast_channel_name(gi->chan));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ gi = AST_LIST_NEXT(gi, group_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_list_unlock();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ out_len = strlen(ast_str_buffer(out));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (out_len) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (out_len <= len) {</span><br><span style="color: hsl(120, 100%, 40%);">+ len -= 1; /* rid , only if we didn't go over the buffer limit */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ strncpy(buf, ast_str_buffer(out), len);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(out);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+static struct ast_custom_function group_channel_list_function = {</span><br><span style="color: hsl(120, 100%, 40%);">+ .name = "GROUP_CHANNEL_LIST",</span><br><span style="color: hsl(120, 100%, 40%);">+ .syntax = "GROUP_CHANNEL_LIST(group[@category])",</span><br><span style="color: hsl(120, 100%, 40%);">+ .synopsis = "Gets a comma delimited list of channels that are part of the given group@category.",</span><br><span style="color: hsl(120, 100%, 40%);">+ .desc = "Gets the list of channels in a group.\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ .read = group_channel_list_function_read,</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static int unload_module(void)</span><br><span> {</span><br><span> int res = 0;</span><br><span> </span><br><span> res |= ast_custom_function_unregister(&group_count_function);</span><br><span> res |= ast_custom_function_unregister(&group_match_count_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_unregister(&group_match_list_start_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_unregister(&group_match_list_next_function);</span><br><span> res |= ast_custom_function_unregister(&group_list_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_unregister(&group_var_function);</span><br><span> res |= ast_custom_function_unregister(&group_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_unregister(&group_channel_list_function);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_unregister_application(dumpgroups_app);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_unregister("GroupVarGet");</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_unregister("GroupVarSet");</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_unregister("GroupsShow");</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_unregister("GroupsShowChannels");</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_unregister("GroupsShowVariables");</span><br><span> </span><br><span> return res;</span><br><span> }</span><br><span>@@ -297,8 +1103,22 @@</span><br><span> </span><br><span> res |= ast_custom_function_register(&group_count_function);</span><br><span> res |= ast_custom_function_register(&group_match_count_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_register(&group_match_list_start_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_register(&group_match_list_next_function);</span><br><span> res |= ast_custom_function_register(&group_list_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_register(&group_var_function);</span><br><span> res |= ast_custom_function_register(&group_function);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_custom_function_register(&group_channel_list_function);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_register_application_xml(dumpgroups_app, dumpgroups_exec);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_register_xml("GroupSet", EVENT_FLAG_CALL, manager_group_set);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_register_xml("GroupRemove", EVENT_FLAG_CALL, manager_group_remove);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_register_xml("GroupVarGet", EVENT_FLAG_CALL, manager_group_var_get);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_register_xml("GroupVarSet", EVENT_FLAG_CALL, manager_group_var_set);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_register_xml("GroupsShow", EVENT_FLAG_REPORTING, manager_groups_show);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_register_xml("GroupsShowChannels", EVENT_FLAG_REPORTING, manager_groups_show_channels);</span><br><span style="color: hsl(120, 100%, 40%);">+ res |= ast_manager_register_xml("GroupsShowVariables", EVENT_FLAG_REPORTING, manager_groups_show_variables);</span><br><span> </span><br><span> return res;</span><br><span> }</span><br><span>diff --git a/include/asterisk/app.h b/include/asterisk/app.h</span><br><span>index 55b7386..2aa03a0 100644</span><br><span>--- a/include/asterisk/app.h</span><br><span>+++ b/include/asterisk/app.h</span><br><span>@@ -1217,8 +1217,26 @@</span><br><span> /*! \brief Split a group string into group and category, returning a default category if none is provided. */</span><br><span> int ast_app_group_split_group(const char *data, char *group, int group_max, char *category, int category_max);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Remove a channel from a group meta assignment */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_remove_channel(struct ast_channel *chan, char *group, char *category);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Add a channel to a group meta assignment, create a group meta item if it doesn't exist */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_add_channel(struct ast_channel *chan, char *group, char *category);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Remove channel assignments for the specified group */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_remove_all_channels(const char *group, const char *category);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Rename a group@category while retaining all the channel memberships */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_rename(const char *old_group, const char *old_category, const char *new_group, const char *new_category);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! \brief Set the group for a channel, splitting the provided data into group and category, if specified. */</span><br><span style="color: hsl(0, 100%, 40%);">-int ast_app_group_set_channel(struct ast_channel *chan, const char *data);</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_set_channel(struct ast_channel *chan, const char *data, int inherit);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Set a group variable for a group@category */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_set_var(struct ast_channel *chan, const char *group, const char *category, const char *name, const char *value);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Get a group variable from a group@category */</span><br><span style="color: hsl(120, 100%, 40%);">+const char* ast_app_group_get_var(const char *group, const char *category, const char *name);</span><br><span> </span><br><span> /*! \brief Get the current channel count of the specified group and category. */</span><br><span> int ast_app_group_get_count(const char *group, const char *category);</span><br><span>@@ -1244,6 +1262,18 @@</span><br><span> /*! \brief Unlock the group count list */</span><br><span> int ast_app_group_list_unlock(void);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Write Lock the group meta list */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_meta_wrlock(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Read Lock the group meta list */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_meta_rdlock(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Get the head of the group meta list */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_group_meta *ast_app_group_meta_head(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief Unlock the group meta list */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_meta_unlock(void);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*!</span><br><span> \brief Define an application argument</span><br><span> \param name The name of the argument</span><br><span>diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h</span><br><span>index 59d25a8..b7d6b81 100644</span><br><span>--- a/include/asterisk/channel.h</span><br><span>+++ b/include/asterisk/channel.h</span><br><span>@@ -126,6 +126,7 @@</span><br><span> #include "asterisk/abstract_jb.h"</span><br><span> #include "asterisk/astobj2.h"</span><br><span> #include "asterisk/poll-compat.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "regex.h"</span><br><span> </span><br><span> #if defined(__cplusplus) || defined(c_plusplus)</span><br><span> extern "C" {</span><br><span>@@ -173,6 +174,9 @@</span><br><span> #define MAX_MUSICCLASS 80 /*!< Max length of the music class setting */</span><br><span> #define AST_MAX_USER_FIELD 256 /*!< Max length of the channel user field */</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+#define MAX_GROUP_LEN 80 /*!< Max length of a channel group */</span><br><span style="color: hsl(120, 100%, 40%);">+#define MAX_CATEGORY_LEN 80 /*!< Max length of a channel group category */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #include "asterisk/frame.h"</span><br><span> #include "asterisk/chanvars.h"</span><br><span> #include "asterisk/config.h"</span><br><span>@@ -2919,6 +2923,19 @@</span><br><span> AST_LIST_ENTRY(ast_group_info) group_list;</span><br><span> };</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*! \brief list of groups currently in use, with a pointer to a list of channels within the groups</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_group_meta {</span><br><span style="color: hsl(120, 100%, 40%);">+ int num_channels; /*!< number of channels in this group */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct varshead varshead; /*!< A linked list for group variables. See \ref AstGroupVar */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_ENTRY(ast_group_info) channels_list; /*!< List of channels in this group */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_ENTRY(ast_group_meta) group_meta_list; /*!< Next group */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ char category[MAX_CATEGORY_LEN];</span><br><span style="color: hsl(120, 100%, 40%);">+ char group[MAX_GROUP_LEN];</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> #define ast_channel_lock(chan) ao2_lock(chan)</span><br><span> #define ast_channel_unlock(chan) ao2_unlock(chan)</span><br><span> #define ast_channel_trylock(chan) ao2_trylock(chan)</span><br><span>@@ -3133,6 +3150,35 @@</span><br><span> */</span><br><span> struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *context);</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Find a channel by a regex string</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \arg regex_string the regex pattern to search for</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Return a channel that where the regex pattern matches the channel name</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval a channel that matches the regex pattern</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL if no channel was found or pattern is bad</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 11</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_channel *ast_channel_get_by_regex(const char *regex_string);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Find a channel by a regex pattern</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \arg regex the compiled regex pattern to search for</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * Return a channel that where the regex pattern matches the channel name</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval a channel that matches the regex pattern</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL if no channel was found</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \since 11</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_channel *ast_channel_get_by_regex_compiled(regex_t *regex);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> /*! @} End channel search functions. */</span><br><span> </span><br><span> /*!</span><br><span>diff --git a/main/app.c b/main/app.c</span><br><span>index c20e370..fb3b34f 100644</span><br><span>--- a/main/app.c</span><br><span>+++ b/main/app.c</span><br><span>@@ -60,7 +60,9 @@</span><br><span> #include "asterisk/lock.h"</span><br><span> #include "asterisk/indications.h"</span><br><span> #include "asterisk/linkedlists.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/dlinkedlists.h"</span><br><span> #include "asterisk/threadstorage.h"</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/manager.h"</span><br><span> #include "asterisk/test.h"</span><br><span> #include "asterisk/module.h"</span><br><span> #include "asterisk/astobj2.h"</span><br><span>@@ -118,10 +120,37 @@</span><br><span> return NULL;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+struct group_data_store;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* \brief a single group assignment entry */</span><br><span style="color: hsl(120, 100%, 40%);">+struct group_list_entry {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *group; /*!< A group assignment is group@category, this is the group part */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *category; /*!< This is the category part */</span><br><span style="color: hsl(120, 100%, 40%);">+ int transfer_fixup:1; /*!< Whether we will run a fixup on masquerade */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct group_data_store *group_store; /*!< The group_data_store we live on */ </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DLLIST_ENTRY(group_list_entry) entries; /*!< Next group */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_DLLIST_HEAD_NOLOCK(group_list, group_list_entry); /*!< All GROUP assignments */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Datastore for GROUP() entry storage */</span><br><span style="color: hsl(120, 100%, 40%);">+struct group_data_store {</span><br><span style="color: hsl(120, 100%, 40%);">+ void *datastore; /*!< Pointer to the datastore that was allocated in */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan; /*!< Channel this datastore is on */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct group_list group_list_head; /*!< All the GROUPs that this channel is in */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DLLIST_ENTRY(group_data_store) entries; /*!< Next GROUP datastore */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+AST_RWDLLIST_HEAD_STATIC(group_stores_list, group_data_store);</span><br><span> </span><br><span> #define AST_MAX_FORMATS 10</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-static AST_RWLIST_HEAD_STATIC(groups, ast_group_info);</span><br><span style="color: hsl(120, 100%, 40%);">+static AST_RWLIST_HEAD_STATIC(groups, ast_group_info); /*!< List of channels that are in groups */</span><br><span style="color: hsl(120, 100%, 40%);">+static AST_RWLIST_HEAD_STATIC(groups_meta, ast_group_meta); /*!< List of groups and their metadata */</span><br><span> </span><br><span> /*!</span><br><span> * \brief This function presents a dialtone and reads an extension into 'collect'</span><br><span>@@ -2077,10 +2106,10 @@</span><br><span> return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">-int ast_app_group_set_channel(struct ast_channel *chan, const char *data)</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_set_channel(struct ast_channel *chan, const char *data, int inherit)</span><br><span> {</span><br><span> int res = 0;</span><br><span style="color: hsl(0, 100%, 40%);">- char group[80] = "", category[80] = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ char group[MAX_GROUP_LEN] = "", category[MAX_CATEGORY_LEN] = "";</span><br><span> struct ast_group_info *gi = NULL;</span><br><span> size_t len = 0;</span><br><span> </span><br><span>@@ -2094,9 +2123,13 @@</span><br><span> len += strlen(category) + 1;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Remove previous group assignment within this category if there is one */</span><br><span> AST_RWLIST_WRLOCK(&groups);</span><br><span> AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {</span><br><span> if ((gi->chan == chan) && ((ast_strlen_zero(category) && ast_strlen_zero(gi->category)) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Find our group meta data, remove the entire group metadata if we're the last channel */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_remove_channel(chan, gi->group, gi->category);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> AST_RWLIST_REMOVE_CURRENT(group_list);</span><br><span> ast_free(gi);</span><br><span> break;</span><br><span>@@ -2115,6 +2148,8 @@</span><br><span> strcpy(gi->category, category);</span><br><span> }</span><br><span> AST_RWLIST_INSERT_TAIL(&groups, gi, group_list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_add_channel(chan, group, category);</span><br><span> } else {</span><br><span> res = -1;</span><br><span> }</span><br><span>@@ -2124,24 +2159,149 @@</span><br><span> return res;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Set a group variable for a group@category</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param chan channel (if any) that is setting the group variable (can be NULL)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param group group to set the variable on (cannot be null)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param category category to set the variable on (cannot be null)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name name of the variable to set (cannot be null)</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param value value of the variable to set (cannot be null)</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 1 On success</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval 0 On failure (group@category not found)</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_set_var(struct ast_channel *chan, const char *group, const char *category, const char *name, const char *value) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct varshead *headp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_var_t *newvariable = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!group || !name) {</span><br><span style="color: hsl(120, 100%, 40%);">+ 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);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ category = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!value) {</span><br><span style="color: hsl(120, 100%, 40%);">+ value = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Find our group meta data */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups_meta, gmi, group_meta_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((strcasecmp(gmi->group, group) != 0) || (strcasecmp(gmi->category, category) != 0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ headp = &gmi->varshead;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(headp, newvariable, entries) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcasecmp(ast_var_name(newvariable), name) == 0) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* there is already such a variable, delete it */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_REMOVE(headp, newvariable, entries);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_var_delete(newvariable);</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ newvariable = ast_var_assign(name, value);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_INSERT_HEAD(headp, newvariable, entries);</span><br><span style="color: hsl(120, 100%, 40%);">+ manager_event(EVENT_FLAG_DIALPLAN, "GroupVarSet",</span><br><span style="color: hsl(120, 100%, 40%);">+ "Channel: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Category: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Group: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Variable: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Value: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Uniqueid: %s\r\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ chan ? ast_channel_name(chan) : "none",</span><br><span style="color: hsl(120, 100%, 40%);">+ category, group, name, value,</span><br><span style="color: hsl(120, 100%, 40%);">+ chan ? ast_channel_uniqueid(chan) : "none");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ break; /* We only have one list item per group@category */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (newvariable == NULL) {</span><br><span style="color: hsl(120, 100%, 40%);">+ 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);</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/*!</span><br><span style="color: hsl(120, 100%, 40%);">+ * \brief Get a group variable for a group@category</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param group group to get the variable from</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param category category to get the variable from</span><br><span style="color: hsl(120, 100%, 40%);">+ * \param name name of the variable to get</span><br><span style="color: hsl(120, 100%, 40%);">+ *</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NOT NULL On success return char* of variable contents</span><br><span style="color: hsl(120, 100%, 40%);">+ * \retval NULL On failure (variable in group@category not found)</span><br><span style="color: hsl(120, 100%, 40%);">+ */</span><br><span style="color: hsl(120, 100%, 40%);">+const char* ast_app_group_get_var(const char *group, const char *category, const char *name) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct varshead *headp;</span><br><span style="color: hsl(120, 100%, 40%);">+ const char *variable;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_var_t *ast_var;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!group || !name) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ category = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Find our group meta data */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_RDLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE(&groups_meta, gmi, group_meta_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if ((strcasecmp(gmi->group, group) != 0) || (strcasecmp(gmi->category, category) != 0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ headp = &gmi->varshead;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(headp, ast_var, entries) {</span><br><span style="color: hsl(120, 100%, 40%);">+ variable = ast_var_name(ast_var);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(variable, name)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* found it */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+ return ast_var_value(ast_var);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> int ast_app_group_get_count(const char *group, const char *category)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_group_info *gi = NULL;</span><br><span style="color: hsl(0, 100%, 40%);">- int count = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_info *gi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int count = 0;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- if (ast_strlen_zero(group)) {</span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_strlen_zero(group)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- AST_RWLIST_RDLOCK(&groups);</span><br><span style="color: hsl(0, 100%, 40%);">- AST_RWLIST_TRAVERSE(&groups, gi, group_list) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (!strcasecmp(gi->group, group) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {</span><br><span style="color: hsl(0, 100%, 40%);">- count++;</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- AST_RWLIST_UNLOCK(&groups);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_RDLOCK(&groups);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE(&groups, gi, group_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(gi->group, group) && (ast_strlen_zero(category) || (!ast_strlen_zero(gi->category) && !strcasecmp(gi->category, category)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ count++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- return count;</span><br><span style="color: hsl(120, 100%, 40%);">+ return count;</span><br><span> }</span><br><span> </span><br><span> int ast_app_group_match_get_count(const char *groupmatch, const char *category)</span><br><span>@@ -2162,6 +2322,7 @@</span><br><span> return 0;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* if regex compilation fails, return zero matches */</span><br><span> if (!ast_strlen_zero(category) && regcomp(®exbuf_category, category, REG_EXTENDED | REG_NOSUB)) {</span><br><span> ast_log(LOG_ERROR, "Regex compile failed on: %s\n", category);</span><br><span> regfree(®exbuf_group);</span><br><span>@@ -2186,33 +2347,240 @@</span><br><span> </span><br><span> int ast_app_group_update(struct ast_channel *old, struct ast_channel *new)</span><br><span> {</span><br><span style="color: hsl(0, 100%, 40%);">- struct ast_group_info *gi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_info *gi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_info *gi_new = NULL;</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- AST_RWLIST_WRLOCK(&groups);</span><br><span style="color: hsl(0, 100%, 40%);">- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (gi->chan == old) {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(&groups);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* keep channel groups on transfer */</span><br><span style="color: hsl(120, 100%, 40%);">+ if (gi->chan == old) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* only move group if it doesn't already exist on new */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi_new, group_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (gi_new->chan == old && !strcasecmp(gi_new->group, gi->group) && !strcasecmp(gi_new->category, gi->category)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ break;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> gi->chan = new;</span><br><span style="color: hsl(0, 100%, 40%);">- } else if (gi->chan == new) {</span><br><span style="color: hsl(0, 100%, 40%);">- AST_RWLIST_REMOVE_CURRENT(group_list);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_free(gi);</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- }</span><br><span style="color: hsl(0, 100%, 40%);">- AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(0, 100%, 40%);">- AST_RWLIST_UNLOCK(&groups);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups);</span><br><span> </span><br><span style="color: hsl(0, 100%, 40%);">- return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Remove a channel from a group meta assignment */</span><br><span style="color: hsl(120, 100%, 40%);">+/* Right now this just removes all the group metadata for a group@category if this is the last channel in the group@category */</span><br><span style="color: hsl(120, 100%, 40%);">+/* Ideally we would have direct pointers from the channel group assignments into the metadata struct, so we don't have to search */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_remove_channel(struct ast_channel *chan, char *group, char *category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL; /*!< Group metadatas */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct varshead *headp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_var_t *vardata;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int destroy = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ category = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups_meta, gmi, group_meta_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(gmi->group, group) && !strcasecmp(gmi->category, category) && (--gmi->num_channels <= 0)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Remove all group variables */</span><br><span style="color: hsl(120, 100%, 40%);">+ headp = &gmi->varshead;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ while ((vardata = AST_LIST_REMOVE_HEAD(headp, entries))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_var_delete(vardata);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_REMOVE_CURRENT(group_meta_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(gmi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ destroy = 1;</span><br><span style="color: hsl(120, 100%, 40%);">+ break; /* We only have one list item per group@category */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ manager_event(EVENT_FLAG_DIALPLAN, "GroupChannelRemove",</span><br><span style="color: hsl(120, 100%, 40%);">+ "Channel: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Category: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Group: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Uniqueid: %s\r\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_name(chan),</span><br><span style="color: hsl(120, 100%, 40%);">+ category, group,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_uniqueid(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (destroy) {</span><br><span style="color: hsl(120, 100%, 40%);">+ manager_event(EVENT_FLAG_DIALPLAN, "GroupDestroy",</span><br><span style="color: hsl(120, 100%, 40%);">+ "Category: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Group: %s\r\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ category, group);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Add a channel to a group meta assignment, create a group meta item if it doesn't exist */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_add_channel(struct ast_channel *chan, char *group, char *category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL; /*!< Group metadatas */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups_meta, gmi, group_meta_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!strcasecmp(gmi->group, group) && !strcasecmp(gmi->category, category)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ break; /* We only have one list item per group@category */</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!gmi) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!(gmi = ast_calloc(1, sizeof(struct ast_group_meta)))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+ return -1;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(gmi->group, group);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!ast_strlen_zero(category)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ strcpy(gmi->category, category);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_INSERT_TAIL(&groups_meta, gmi, group_meta_list);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ manager_event(EVENT_FLAG_DIALPLAN, "GroupCreate",</span><br><span style="color: hsl(120, 100%, 40%);">+ "Category: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Group: %s\r\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ category, group);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ gmi->num_channels++;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ manager_event(EVENT_FLAG_DIALPLAN, "GroupChannelAdd",</span><br><span style="color: hsl(120, 100%, 40%);">+ "Channel: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Category: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Group: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "Uniqueid: %s\r\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_name(chan),</span><br><span style="color: hsl(120, 100%, 40%);">+ category, group,</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_channel_uniqueid(chan));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 1;</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+/* Remove all channels from the given group */</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_remove_all_channels(const char *group, const char *category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_info *gi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int channels_found = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ category = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Traverse group@category channel assignments */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(&groups);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcasecmp(gi->group, group) || strcasecmp(gi->category, category)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Need to match group@category exactly */</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_remove_channel(gi->chan, gi->group, gi->category);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_REMOVE_CURRENT(group_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(gi);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ channels_found++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return !(channels_found > 0);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_rename(const char *old_group, const char *old_category, const char *new_group, const char *new_category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_info *gi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL; /*!< Group metadatas */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ int channels_found = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!old_category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ old_category = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (!new_category) {</span><br><span style="color: hsl(120, 100%, 40%);">+ new_category = "";</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Traverse group@category channel assignments */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(&groups);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcasecmp(gi->group, old_group) || strcasecmp(gi->category, old_category)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Need to match group@category exactly */</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ strncpy(gi->group, new_group, MAX_GROUP_LEN);</span><br><span style="color: hsl(120, 100%, 40%);">+ strncpy(gi->category, new_category, MAX_CATEGORY_LEN);</span><br><span style="color: hsl(120, 100%, 40%);">+ channels_found++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Group Variables */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_WRLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups_meta, gmi, group_meta_list) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (strcasecmp(gmi->group, old_group) || strcasecmp(gmi->category, old_category)) {</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Need to match group@category exactly */</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ strncpy(gmi->group, new_group, MAX_GROUP_LEN);</span><br><span style="color: hsl(120, 100%, 40%);">+ strncpy(gmi->category, new_category, MAX_CATEGORY_LEN);</span><br><span style="color: hsl(120, 100%, 40%);">+ channels_found++;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_UNLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (channels_found) {</span><br><span style="color: hsl(120, 100%, 40%);">+ manager_event(EVENT_FLAG_DIALPLAN, "GroupRename",</span><br><span style="color: hsl(120, 100%, 40%);">+ "OldGroup: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "OldCategory: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "NewGroup: %s\r\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ "NewCategory: %s\r\n",</span><br><span style="color: hsl(120, 100%, 40%);">+ old_group,</span><br><span style="color: hsl(120, 100%, 40%);">+ old_category,</span><br><span style="color: hsl(120, 100%, 40%);">+ new_group,</span><br><span style="color: hsl(120, 100%, 40%);">+ new_category);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return !(channels_found > 0);</span><br><span> }</span><br><span> </span><br><span> int ast_app_group_discard(struct ast_channel *chan)</span><br><span> {</span><br><span> struct ast_group_info *gi = NULL;</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ /* Find and remove all groups associated to this channel */</span><br><span> AST_RWLIST_WRLOCK(&groups);</span><br><span> AST_RWLIST_TRAVERSE_SAFE_BEGIN(&groups, gi, group_list) {</span><br><span style="color: hsl(0, 100%, 40%);">- if (gi->chan == chan) {</span><br><span style="color: hsl(0, 100%, 40%);">- AST_RWLIST_REMOVE_CURRENT(group_list);</span><br><span style="color: hsl(0, 100%, 40%);">- ast_free(gi);</span><br><span style="color: hsl(120, 100%, 40%);">+ if (gi->chan != chan) {</span><br><span style="color: hsl(120, 100%, 40%);">+ continue;</span><br><span> }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Find our group meta data, remove the entire group metadata if we're the last channel */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_remove_channel(chan, gi->group, gi->category);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Remove this group assignment for this channel */</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_RWLIST_REMOVE_CURRENT(group_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_free(gi);</span><br><span> }</span><br><span> AST_RWLIST_TRAVERSE_SAFE_END;</span><br><span> AST_RWLIST_UNLOCK(&groups);</span><br><span>@@ -2240,6 +2608,26 @@</span><br><span> return AST_RWLIST_UNLOCK(&groups);</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_meta_wrlock(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_RWLIST_WRLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_meta_rdlock(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_RWLIST_RDLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct ast_group_meta *ast_app_group_meta_head(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_RWLIST_FIRST(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_meta_unlock(void)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+ return AST_RWLIST_UNLOCK(&groups_meta);</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> unsigned int __ast_app_separate_args(char *buf, char delim, int remove_chars, char **array, int arraylen)</span><br><span> {</span><br><span> int argc;</span><br><span>diff --git a/main/cli.c b/main/cli.c</span><br><span>index dc13019..f4034d5 100644</span><br><span>--- a/main/cli.c</span><br><span>+++ b/main/cli.c</span><br><span>@@ -1862,6 +1862,34 @@</span><br><span> return ret;</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+int ast_app_group_all_callback(ao2_callback_fn *callback, int flags);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+#include "asterisk/dlinkedlists.h"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+struct group_list_entry {</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *group; /*!< A group assignment is group@category, this is the group part */</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_str *category; /*!< This is the category part */</span><br><span style="color: hsl(120, 100%, 40%);">+ int transfer_fixup:1; /*!< Whether we will run a fixup on masquerade */</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct group_data_store *group_store; /*!< The group_data_store we live on. (Type: struct tie_data_store *) */ </span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_DLLIST_ENTRY(group_list_entry) entries; /*!< Next group */</span><br><span style="color: hsl(120, 100%, 40%);">+};</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int group_show_channels_callback(void *obj, void *arg, int flags);</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+int group_show_channels_callback(void *obj, void *arg, int flags) {</span><br><span style="color: hsl(120, 100%, 40%);">+#define FORMAT_STRING "%-25s %-20s %-20s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_channel *chan = obj;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct group_list_entry *ge = arg;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(flags, FORMAT_STRING, ast_channel_name(chan), ast_str_buffer(ge->group), (!ge->category ? "(default)" : ast_str_buffer(ge->category)));</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ return 0;</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FORMAT_STRING</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span> {</span><br><span> #define FORMAT_STRING "%-25s %-20s %-20s\n"</span><br><span>@@ -1916,6 +1944,62 @@</span><br><span> #undef FORMAT_STRING</span><br><span> }</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+static char *group_show_variables(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span style="color: hsl(120, 100%, 40%);">+{</span><br><span style="color: hsl(120, 100%, 40%);">+#define FORMAT_STRING "%-20s %-20s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+#define FORMAT_STRING_VAR " %s=%s\n"</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_group_meta *gmi = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct varshead *headp;</span><br><span style="color: hsl(120, 100%, 40%);">+ struct ast_var_t *variable = NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ int numgroups = 0;</span><br><span style="color: hsl(120, 100%, 40%);">+ char group[80];</span><br><span style="color: hsl(120, 100%, 40%);">+ char category[80];</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ switch (cmd) {</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_INIT:</span><br><span style="color: hsl(120, 100%, 40%);">+ e->command = "group show variables";</span><br><span style="color: hsl(120, 100%, 40%);">+ e->usage =</span><br><span style="color: hsl(120, 100%, 40%);">+ "Usage: group show variables [group@[category]]\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Lists all currently active groups and their variables.\n"</span><br><span style="color: hsl(120, 100%, 40%);">+ " Optional group, or group@category can be used to only show a specific group\n";</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ case CLI_GENERATE:</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc < 3 || a->argc > 4)</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SHOWUSAGE;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ if (a->argc == 4) {</span><br><span style="color: hsl(120, 100%, 40%);">+ if (ast_app_group_split_group(a->argv[3], group, sizeof(group), category, sizeof(category))) {</span><br><span style="color: hsl(120, 100%, 40%);">+ return NULL;</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "Group Variables Category\n");</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ /* Print group variables */</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_rdlock();</span><br><span style="color: hsl(120, 100%, 40%);">+ gmi = ast_app_group_meta_head();</span><br><span style="color: hsl(120, 100%, 40%);">+ while (gmi) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, FORMAT_STRING, gmi->group, (strcmp(gmi->category, "") ? gmi->category : "(Default)"));</span><br><span style="color: hsl(120, 100%, 40%);">+ numgroups++;</span><br><span style="color: hsl(120, 100%, 40%);">+ headp = &gmi->varshead;</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ AST_LIST_TRAVERSE(headp, variable, entries) {</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, FORMAT_STRING_VAR, ast_var_name(variable), ast_var_value(variable));</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ gmi = AST_LIST_NEXT(gmi, group_meta_list);</span><br><span style="color: hsl(120, 100%, 40%);">+ }</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_app_group_meta_unlock();</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span style="color: hsl(120, 100%, 40%);">+ ast_cli(a->fd, "%d active group%s\n", numgroups, ESS(numgroups));</span><br><span style="color: hsl(120, 100%, 40%);">+ return CLI_SUCCESS;</span><br><span style="color: hsl(120, 100%, 40%);">+#undef FORMAT_STRING</span><br><span style="color: hsl(120, 100%, 40%);">+}</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> static char *handle_cli_wait_fullybooted(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)</span><br><span> {</span><br><span> switch (cmd) {</span><br><span>@@ -2002,6 +2086,8 @@</span><br><span> </span><br><span> AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),</span><br><span> </span><br><span style="color: hsl(120, 100%, 40%);">+ AST_CLI_DEFINE(group_show_variables, "Display active channels with group(s), along with group variables"),</span><br><span style="color: hsl(120, 100%, 40%);">+</span><br><span> AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),</span><br><span> </span><br><span> AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),</span><br><span></span><br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/c/asterisk/+/17655">change 17655</a>. To unsubscribe, or for help writing mail filters, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/c/asterisk/+/17655"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 18 </div>
<div style="display:none"> Gerrit-Change-Id: I23e48d1cdfc8adaffdfec2e936e56143603914f2 </div>
<div style="display:none"> Gerrit-Change-Number: 17655 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Mark Murawski <markm@intellasoft.net> </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>