Asterisk - The Open Source Telephony Project GIT-master-8924258
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Macros Modules Pages
func_dialgroup.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2007, Tilghman Lesher
5 *
6 * Tilghman Lesher <func_dialgroup__200709@the-tilghman.com>
7 *
8 * See http://www.asterisk.org for more information about
9 * the Asterisk project. Please do not directly contact
10 * any of the maintainers of this project for assistance;
11 * the project provides a web site, mailing lists and IRC
12 * channels for your use.
13 *
14 * This program is free software, distributed under the terms of
15 * the GNU General Public License Version 2. See the LICENSE file
16 * at the top of the source tree.
17 */
18
19/*! \file
20 *
21 * \brief Dial group dialplan function
22 *
23 * \author Tilghman Lesher <func_dialgroup__200709@the-tilghman.com>
24 *
25 * \ingroup functions
26 */
27
28/*** MODULEINFO
29 <support_level>core</support_level>
30 ***/
31
32#include "asterisk.h"
33
34#include <sys/stat.h>
35
36#include "asterisk/module.h"
37#include "asterisk/channel.h"
38#include "asterisk/pbx.h"
39#include "asterisk/utils.h"
40#include "asterisk/app.h"
41#include "asterisk/astobj2.h"
42#include "asterisk/astdb.h"
43
44/*** DOCUMENTATION
45 <function name="DIALGROUP" language="en_US">
46 <since>
47 <version>1.6.0</version>
48 </since>
49 <synopsis>
50 Manages a group of users for dialing.
51 </synopsis>
52 <syntax>
53 <parameter name="group" required="true" />
54 <parameter name="op">
55 <para>The operation name, possible values are:</para>
56 <para><literal>add</literal> - add a channel name or interface (write-only)</para>
57 <para><literal>del</literal> - remove a channel name or interface (write-only)</para>
58 </parameter>
59 </syntax>
60 <description>
61 <para>Presents an interface meant to be used in concert with the Dial
62 application, by presenting a list of channels which should be dialled when
63 referenced.</para>
64 <para>When DIALGROUP is read from, the argument is interpreted as the particular
65 <replaceable>group</replaceable> for which a dial should be attempted. When DIALGROUP is written to
66 with no arguments, the entire list is replaced with the argument specified.</para>
67 <para>Functionality is similar to a queue, except that when no interfaces are
68 available, execution may continue in the dialplan. This is useful when
69 you want certain people to be the first to answer any calls, with immediate
70 fallback to a queue when the front line people are busy or unavailable, but
71 you still want front line people to log in and out of that group, just like
72 a queue.</para>
73 <example title="Add 2 endpoints to a dial group">
74 exten => 1,1,Set(DIALGROUP(mygroup,add)=SIP/10)
75 same => n,Set(DIALGROUP(mygroup,add)=SIP/20)
76 same => n,Dial(${DIALGROUP(mygroup)})
77 </example>
78 </description>
79 </function>
80 ***/
81
83
86};
87
88struct group {
91};
92
93static void group_destroy(void *vgroup)
94{
95 struct group *group = vgroup;
96 ao2_ref(group->entries, -1);
97}
98
99static int group_hash_fn(const void *obj, const int flags)
100{
101 const struct group *g = obj;
102 return ast_str_hash(g->name);
103}
104
105static int group_cmp_fn(void *obj1, void *name2, int flags)
106{
107 struct group *g1 = obj1, *g2 = name2;
108 char *name = name2;
109 if (flags & OBJ_POINTER)
110 return strcmp(g1->name, g2->name) ? 0 : CMP_MATCH | CMP_STOP;
111 else
112 return strcmp(g1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
113}
114
115static int entry_hash_fn(const void *obj, const int flags)
116{
117 const struct group_entry *e = obj;
118 return ast_str_hash(e->name);
119}
120
121static int entry_cmp_fn(void *obj1, void *name2, int flags)
122{
123 struct group_entry *e1 = obj1, *e2 = name2;
124 char *name = name2;
125 if (flags & OBJ_POINTER)
126 return strcmp(e1->name, e2->name) ? 0 : CMP_MATCH | CMP_STOP;
127 else
128 return strcmp(e1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
129}
130
131static int dialgroup_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
132{
133 struct ao2_iterator i;
134 struct group *grhead = ao2_find(group_container, data, 0);
135 struct group_entry *entry;
136 size_t bufused = 0;
137 int trunc_warning = 0;
138 int res = 0;
139
140 if (!grhead) {
141 if (!ast_strlen_zero(cmd)) {
142 ast_log(LOG_WARNING, "No such dialgroup '%s'\n", data);
143 }
144 return -1;
145 }
146
147 buf[0] = '\0';
148
149 i = ao2_iterator_init(grhead->entries, 0);
150 while ((entry = ao2_iterator_next(&i))) {
151 int tmp = strlen(entry->name);
152 /* Ensure that we copy only complete names, not partials */
153 if (len - bufused > tmp + 2) {
154 if (bufused != 0)
155 buf[bufused++] = '&';
156 ast_copy_string(buf + bufused, entry->name, len - bufused);
157 bufused += tmp;
158 } else if (trunc_warning++ == 0) {
159 if (!ast_strlen_zero(cmd)) {
160 ast_log(LOG_WARNING, "Dialgroup '%s' is too large. Truncating list.\n", data);
161 } else {
162 res = 1;
163 ao2_ref(entry, -1);
164 break;
165 }
166 }
167 ao2_ref(entry, -1);
168 }
170 ao2_ref(grhead, -1);
171
172 return res;
173}
174
175static int dialgroup_refreshdb(struct ast_channel *chan, const char *cdialgroup)
176{
177 int len = 500, res = 0;
178 char *buf = NULL;
179 char *new_buf;
180 char *dialgroup = ast_strdupa(cdialgroup);
181
182 do {
183 len *= 2;
184 new_buf = ast_realloc(buf, len);
185 if (!new_buf) {
186 ast_free(buf);
187 return -1;
188 }
189 buf = new_buf;
190
191 if ((res = dialgroup_read(chan, "", dialgroup, buf, len)) < 0) {
192 ast_free(buf);
193 return -1;
194 }
195 } while (res == 1);
196
197 if (ast_strlen_zero(buf)) {
198 ast_db_del("dialgroup", cdialgroup);
199 } else {
200 ast_db_put("dialgroup", cdialgroup, buf);
201 }
202 ast_free(buf);
203 return 0;
204}
205
206static int dialgroup_write(struct ast_channel *chan, const char *cmd, char *data, const char *cvalue)
207{
208 struct group *grhead;
209 struct group_entry *entry;
210 int j, needrefresh = 1;
213 AST_APP_ARG(op);
214 );
216 AST_APP_ARG(faces)[100];
217 );
218 char *value = ast_strdupa(cvalue);
219
221 AST_NONSTANDARD_APP_ARGS(inter, value, '&');
222
223 if (!(grhead = ao2_find(group_container, args.group, 0))) {
224 /* Create group */
225 grhead = ao2_alloc(sizeof(*grhead), group_destroy);
226 if (!grhead)
227 return -1;
230 if (!grhead->entries) {
231 ao2_ref(grhead, -1);
232 return -1;
233 }
234 ast_copy_string(grhead->name, args.group, sizeof(grhead->name));
235 ao2_link(group_container, grhead);
236 }
237
238 if (ast_strlen_zero(args.op)) {
239 /* Wholesale replacement of the group */
240 args.op = "add";
241
242 /* Remove all existing */
243 ao2_ref(grhead->entries, -1);
246 if (!grhead->entries) {
248 ao2_ref(grhead, -1);
249 return -1;
250 }
251 }
252
253 if (strcasecmp(args.op, "add") == 0) {
254 for (j = 0; j < inter.argc; j++) {
255 /* Eliminate duplicates */
256 if ((entry = ao2_find(grhead->entries, inter.faces[j], 0))) {
257 ao2_ref(entry, -1);
258 continue;
259 }
260 if ((entry = ao2_alloc(sizeof(*entry), NULL))) {
261 ast_copy_string(entry->name, inter.faces[j], sizeof(entry->name));
262 ao2_link(grhead->entries, entry);
263 ao2_ref(entry, -1);
264 } else {
265 ast_log(LOG_WARNING, "Unable to add '%s' to dialgroup '%s'\n", inter.faces[j], grhead->name);
266 }
267 }
268 } else if (strncasecmp(args.op, "del", 3) == 0) {
269 for (j = 0; j < inter.argc; j++) {
270 if ((entry = ao2_find(grhead->entries, inter.faces[j], OBJ_UNLINK))) {
271 ao2_ref(entry, -1);
272 } else {
273 ast_log(LOG_WARNING, "Interface '%s' not found in dialgroup '%s'\n", inter.faces[j], grhead->name);
274 }
275 }
276 } else {
277 ast_log(LOG_ERROR, "Unrecognized operation: %s\n", args.op);
278 needrefresh = 0;
279 }
280 ao2_ref(grhead, -1);
281
282 if (needrefresh) {
283 dialgroup_refreshdb(chan, args.group);
284 }
285
286 return 0;
287}
288
290 .name = "DIALGROUP",
291 .read = dialgroup_read,
292 .write = dialgroup_write,
293};
294
295static int unload_module(void)
296{
299 return res;
300}
301
302static int load_module(void)
303{
304 struct ast_db_entry *dbtree, *tmp;
305 char groupname[AST_MAX_EXTENSION], *ptr;
306
309 if (group_container) {
310 /* Refresh groups from astdb */
311 if ((dbtree = ast_db_gettree("dialgroup", NULL))) {
312 for (tmp = dbtree; tmp; tmp = tmp->next) {
313 ast_copy_string(groupname, tmp->key, sizeof(groupname));
314 if ((ptr = strrchr(groupname, '/'))) {
315 ptr++;
316 dialgroup_write(NULL, "", ptr, tmp->data);
317 }
318 }
319 ast_db_freetree(dbtree);
320 }
322 } else {
324 }
325}
326
327AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialgroup dialplan function");
Persistent data storage (akin to *doze registry)
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: db.c:335
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: db.c:472
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition: db.c:635
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: db.c:695
Asterisk main include file. File version handling, generic pbx functions.
#define ast_free(a)
Definition: astmm.h:180
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
#define ast_log
Definition: astobj2.c:42
#define ao2_iterator_next(iter)
Definition: astobj2.h:1911
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532
@ CMP_MATCH
Definition: astobj2.h:1027
@ CMP_STOP
Definition: astobj2.h:1028
#define OBJ_POINTER
Definition: astobj2.h:1150
@ AO2_ALLOC_OPT_LOCK_MUTEX
Definition: astobj2.h:363
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
#define ao2_find(container, arg, flags)
Definition: astobj2.h:1736
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
@ OBJ_UNLINK
Definition: astobj2.h:1039
#define ao2_alloc(data_size, destructor_fn)
Definition: astobj2.h:409
#define ao2_container_alloc_hash(ao2_options, container_options, n_buckets, hash_fn, sort_fn, cmp_fn)
Allocate and initialize a hash container with the desired number of buckets.
Definition: astobj2.h:1303
General Asterisk PBX channel definitions.
#define AST_CHANNEL_NAME
Definition: channel.h:173
#define AST_MAX_EXTENSION
Definition: channel.h:134
char buf[BUFSIZE]
Definition: eagi_proxy.c:66
static const char name[]
Definition: format_mp3.c:68
static int entry_cmp_fn(void *obj1, void *name2, int flags)
static int dialgroup_refreshdb(struct ast_channel *chan, const char *cdialgroup)
static int entry_hash_fn(const void *obj, const int flags)
static int dialgroup_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static void group_destroy(void *vgroup)
static struct ao2_container * group_container
static int dialgroup_write(struct ast_channel *chan, const char *cmd, char *data, const char *cvalue)
static struct ast_custom_function dialgroup_function
static int group_cmp_fn(void *obj1, void *name2, int flags)
static int group_hash_fn(const void *obj, const int flags)
static int load_module(void)
static int unload_module(void)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
#define LOG_ERROR
#define LOG_WARNING
Asterisk module definitions.
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:581
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
@ AST_MODULE_LOAD_DECLINE
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
Core PBX routines and definitions.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1559
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
#define NULL
Definition: resample.c:96
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:65
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Generic container type.
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
Main Channel structure associated with a channel.
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
const char * name
Definition: pbx.h:119
Definition: astdb.h:31
struct ast_db_entry * next
Definition: astdb.h:32
char * key
Definition: astdb.h:33
char data[0]
Definition: astdb.h:34
char name[AST_CHANNEL_NAME]
struct ao2_container * entries
char name[AST_MAX_EXTENSION]
int value
Definition: syslog.c:37
const char * args
Utility functions.