Asterisk - The Open Source Telephony Project  21.4.1
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  <synopsis>
47  Manages a group of users for dialing.
48  </synopsis>
49  <syntax>
50  <parameter name="group" required="true" />
51  <parameter name="op">
52  <para>The operation name, possible values are:</para>
53  <para><literal>add</literal> - add a channel name or interface (write-only)</para>
54  <para><literal>del</literal> - remove a channel name or interface (write-only)</para>
55  </parameter>
56  </syntax>
57  <description>
58  <para>Presents an interface meant to be used in concert with the Dial
59  application, by presenting a list of channels which should be dialled when
60  referenced.</para>
61  <para>When DIALGROUP is read from, the argument is interpreted as the particular
62  <replaceable>group</replaceable> for which a dial should be attempted. When DIALGROUP is written to
63  with no arguments, the entire list is replaced with the argument specified.</para>
64  <para>Functionality is similar to a queue, except that when no interfaces are
65  available, execution may continue in the dialplan. This is useful when
66  you want certain people to be the first to answer any calls, with immediate
67  fallback to a queue when the front line people are busy or unavailable, but
68  you still want front line people to log in and out of that group, just like
69  a queue.</para>
70  <example title="Add 2 endpoints to a dial group">
71  exten => 1,1,Set(DIALGROUP(mygroup,add)=SIP/10)
72  same => n,Set(DIALGROUP(mygroup,add)=SIP/20)
73  same => n,Dial(${DIALGROUP(mygroup)})
74  </example>
75  </description>
76  </function>
77  ***/
78 
79 static struct ao2_container *group_container = NULL;
80 
81 struct group_entry {
82  char name[AST_CHANNEL_NAME];
83 };
84 
85 struct group {
86  char name[AST_MAX_EXTENSION];
87  struct ao2_container *entries;
88 };
89 
90 static void group_destroy(void *vgroup)
91 {
92  struct group *group = vgroup;
93  ao2_ref(group->entries, -1);
94 }
95 
96 static int group_hash_fn(const void *obj, const int flags)
97 {
98  const struct group *g = obj;
99  return ast_str_hash(g->name);
100 }
101 
102 static int group_cmp_fn(void *obj1, void *name2, int flags)
103 {
104  struct group *g1 = obj1, *g2 = name2;
105  char *name = name2;
106  if (flags & OBJ_POINTER)
107  return strcmp(g1->name, g2->name) ? 0 : CMP_MATCH | CMP_STOP;
108  else
109  return strcmp(g1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
110 }
111 
112 static int entry_hash_fn(const void *obj, const int flags)
113 {
114  const struct group_entry *e = obj;
115  return ast_str_hash(e->name);
116 }
117 
118 static int entry_cmp_fn(void *obj1, void *name2, int flags)
119 {
120  struct group_entry *e1 = obj1, *e2 = name2;
121  char *name = name2;
122  if (flags & OBJ_POINTER)
123  return strcmp(e1->name, e2->name) ? 0 : CMP_MATCH | CMP_STOP;
124  else
125  return strcmp(e1->name, name) ? 0 : CMP_MATCH | CMP_STOP;
126 }
127 
128 static int dialgroup_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
129 {
130  struct ao2_iterator i;
131  struct group *grhead = ao2_find(group_container, data, 0);
132  struct group_entry *entry;
133  size_t bufused = 0;
134  int trunc_warning = 0;
135  int res = 0;
136 
137  if (!grhead) {
138  if (!ast_strlen_zero(cmd)) {
139  ast_log(LOG_WARNING, "No such dialgroup '%s'\n", data);
140  }
141  return -1;
142  }
143 
144  buf[0] = '\0';
145 
146  i = ao2_iterator_init(grhead->entries, 0);
147  while ((entry = ao2_iterator_next(&i))) {
148  int tmp = strlen(entry->name);
149  /* Ensure that we copy only complete names, not partials */
150  if (len - bufused > tmp + 2) {
151  if (bufused != 0)
152  buf[bufused++] = '&';
153  ast_copy_string(buf + bufused, entry->name, len - bufused);
154  bufused += tmp;
155  } else if (trunc_warning++ == 0) {
156  if (!ast_strlen_zero(cmd)) {
157  ast_log(LOG_WARNING, "Dialgroup '%s' is too large. Truncating list.\n", data);
158  } else {
159  res = 1;
160  ao2_ref(entry, -1);
161  break;
162  }
163  }
164  ao2_ref(entry, -1);
165  }
167  ao2_ref(grhead, -1);
168 
169  return res;
170 }
171 
172 static int dialgroup_refreshdb(struct ast_channel *chan, const char *cdialgroup)
173 {
174  int len = 500, res = 0;
175  char *buf = NULL;
176  char *new_buf;
177  char *dialgroup = ast_strdupa(cdialgroup);
178 
179  do {
180  len *= 2;
181  new_buf = ast_realloc(buf, len);
182  if (!new_buf) {
183  ast_free(buf);
184  return -1;
185  }
186  buf = new_buf;
187 
188  if ((res = dialgroup_read(chan, "", dialgroup, buf, len)) < 0) {
189  ast_free(buf);
190  return -1;
191  }
192  } while (res == 1);
193 
194  if (ast_strlen_zero(buf)) {
195  ast_db_del("dialgroup", cdialgroup);
196  } else {
197  ast_db_put("dialgroup", cdialgroup, buf);
198  }
199  ast_free(buf);
200  return 0;
201 }
202 
203 static int dialgroup_write(struct ast_channel *chan, const char *cmd, char *data, const char *cvalue)
204 {
205  struct group *grhead;
206  struct group_entry *entry;
207  int j, needrefresh = 1;
210  AST_APP_ARG(op);
211  );
212  AST_DECLARE_APP_ARGS(inter,
213  AST_APP_ARG(faces)[100];
214  );
215  char *value = ast_strdupa(cvalue);
216 
217  AST_STANDARD_APP_ARGS(args, data);
218  AST_NONSTANDARD_APP_ARGS(inter, value, '&');
219 
220  if (!(grhead = ao2_find(group_container, args.group, 0))) {
221  /* Create group */
222  grhead = ao2_alloc(sizeof(*grhead), group_destroy);
223  if (!grhead)
224  return -1;
225  grhead->entries = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 37,
226  entry_hash_fn, NULL, entry_cmp_fn);
227  if (!grhead->entries) {
228  ao2_ref(grhead, -1);
229  return -1;
230  }
231  ast_copy_string(grhead->name, args.group, sizeof(grhead->name));
232  ao2_link(group_container, grhead);
233  }
234 
235  if (ast_strlen_zero(args.op)) {
236  /* Wholesale replacement of the group */
237  args.op = "add";
238 
239  /* Remove all existing */
240  ao2_ref(grhead->entries, -1);
241  grhead->entries = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 37,
242  entry_hash_fn, NULL, entry_cmp_fn);
243  if (!grhead->entries) {
244  ao2_unlink(group_container, grhead);
245  ao2_ref(grhead, -1);
246  return -1;
247  }
248  }
249 
250  if (strcasecmp(args.op, "add") == 0) {
251  for (j = 0; j < inter.argc; j++) {
252  /* Eliminate duplicates */
253  if ((entry = ao2_find(grhead->entries, inter.faces[j], 0))) {
254  ao2_ref(entry, -1);
255  continue;
256  }
257  if ((entry = ao2_alloc(sizeof(*entry), NULL))) {
258  ast_copy_string(entry->name, inter.faces[j], sizeof(entry->name));
259  ao2_link(grhead->entries, entry);
260  ao2_ref(entry, -1);
261  } else {
262  ast_log(LOG_WARNING, "Unable to add '%s' to dialgroup '%s'\n", inter.faces[j], grhead->name);
263  }
264  }
265  } else if (strncasecmp(args.op, "del", 3) == 0) {
266  for (j = 0; j < inter.argc; j++) {
267  if ((entry = ao2_find(grhead->entries, inter.faces[j], OBJ_UNLINK))) {
268  ao2_ref(entry, -1);
269  } else {
270  ast_log(LOG_WARNING, "Interface '%s' not found in dialgroup '%s'\n", inter.faces[j], grhead->name);
271  }
272  }
273  } else {
274  ast_log(LOG_ERROR, "Unrecognized operation: %s\n", args.op);
275  needrefresh = 0;
276  }
277  ao2_ref(grhead, -1);
278 
279  if (needrefresh) {
280  dialgroup_refreshdb(chan, args.group);
281  }
282 
283  return 0;
284 }
285 
286 static struct ast_custom_function dialgroup_function = {
287  .name = "DIALGROUP",
288  .read = dialgroup_read,
289  .write = dialgroup_write,
290 };
291 
292 static int unload_module(void)
293 {
294  int res = ast_custom_function_unregister(&dialgroup_function);
295  ao2_ref(group_container, -1);
296  return res;
297 }
298 
299 static int load_module(void)
300 {
301  struct ast_db_entry *dbtree, *tmp;
302  char groupname[AST_MAX_EXTENSION], *ptr;
303 
304  group_container = ao2_container_alloc_hash(AO2_ALLOC_OPT_LOCK_MUTEX, 0, 37,
305  group_hash_fn, NULL, group_cmp_fn);
306  if (group_container) {
307  /* Refresh groups from astdb */
308  if ((dbtree = ast_db_gettree("dialgroup", NULL))) {
309  for (tmp = dbtree; tmp; tmp = tmp->next) {
310  ast_copy_string(groupname, tmp->key, sizeof(groupname));
311  if ((ptr = strrchr(groupname, '/'))) {
312  ptr++;
313  dialgroup_write(NULL, "", ptr, tmp->data);
314  }
315  }
316  ast_db_freetree(dbtree);
317  }
318  return ast_custom_function_register(&dialgroup_function);
319  } else {
321  }
322 }
323 
324 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Dialgroup dialplan function");
const char * name
Definition: pbx.h:119
Main Channel structure associated with a channel.
Asterisk main include file. File version handling, generic pbx functions.
#define ast_realloc(p, len)
A wrapper for realloc()
Definition: astmm.h:226
#define OBJ_POINTER
Definition: astobj2.h:1150
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
void ast_db_freetree(struct ast_db_entry *entry)
Free structure created by ast_db_gettree()
Definition: main/db.c:677
void ao2_iterator_destroy(struct ao2_iterator *iter)
Destroy a container iterator.
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
Utility functions.
General Asterisk PBX channel definitions.
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: astmm.h:298
Data structure associated with a custom dialplan function.
Definition: pbx.h:118
#define ao2_ref(o, delta)
Reference/unreference an object and return the old refcount.
Definition: astobj2.h:459
#define AST_MAX_EXTENSION
Definition: channel.h:134
Core PBX routines and definitions.
#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
#define ao2_unlink(container, obj)
Remove an object from a container.
Definition: astobj2.h:1578
struct ast_db_entry * ast_db_gettree(const char *family, const char *keytree)
Get a list of values within the astdb tree.
Definition: main/db.c:610
#define AST_NONSTANDARD_APP_ARGS(args, parse, sep)
Performs the 'nonstandard' argument separation process for an application.
Definition: astdb.h:31
#define AST_CHANNEL_NAME
Definition: channel.h:171
Module has failed to load, may be in an inconsistent state.
Definition: module.h:78
When we need to walk through a container, we use an ao2_iterator to keep track of the current positio...
Definition: astobj2.h:1821
int ast_db_del(const char *family, const char *key)
Delete entry in astdb.
Definition: main/db.c:476
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition: strings.h:425
Definition: search.h:40
int ast_db_put(const char *family, const char *key, const char *value)
Store value addressed by family/key.
Definition: main/db.c:342
Generic container type.
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:46
Asterisk module definitions.
Persistent data storage (akin to *doze registry)
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct ao2_iterator ao2_iterator_init(struct ao2_container *c, int flags) attribute_warn_unused_result
Create an iterator for a container.
#define ast_custom_function_register(acf)
Register a custom function.
Definition: pbx.h:1558
#define AST_APP_ARG(name)
Define an application argument.
static force_inline int attribute_pure ast_str_hash(const char *str)
Compute a hash value on a string.
Definition: strings.h:1259
#define ao2_link(container, obj)
Add an object to a container.
Definition: astobj2.h:1532