summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorMatthew Jordan <mjordan@digium.com>2012-08-07 12:46:36 +0000
committerMatthew Jordan <mjordan@digium.com>2012-08-07 12:46:36 +0000
commit5c4578f4ad9af0d13638ec72a2bc141227ec8b3c (patch)
tree77427fe11562062a59774f755a23ffc82c5cbff5 /main
parent096baa0897f556b2de6bfb8fa0acc09a504e39a4 (diff)
Add named callgroups/pickupgroups
This patch adds named calledgroups/pickupgroups to Asterisk. Named groups are implemented in parallel to the existing numbered callgroup/pickupgroup implementation. However, unlike the existing implementation, which is limited to a maximum of 64 defined groups, the number of defined groups allowed for named callgroups/pickupgroups is effectively unlimited. Named groups are configured with the keywords "namedcallgroup" and "namedpickupgroup". This corresponds to the numbered group definitions of "callgroup" and "pickupgroup". Note that as the implementation of named groups coexists with the existing numbered implementation, a defined named group of "4" does not equate to numbered group 4. Support for the named groups has been added to the SIP, DAHDI, and mISDN channel drivers. Review: https://reviewboard.asterisk.org/r/2043 Uploaded by: Guenther Kelleter(license #6372) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@370831 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main')
-rw-r--r--main/channel.c168
-rw-r--r--main/channel_internal_api.c29
-rw-r--r--main/features.c46
3 files changed, 237 insertions, 6 deletions
diff --git a/main/channel.c b/main/channel.c
index fdb4f005d..487090284 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -953,6 +953,7 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
struct ast_format_cap *nativeformats;
struct ast_sched_context *schedctx;
struct ast_timer *timer;
+ struct timeval now;
/* If shutting down, don't allocate any new channels */
if (ast_shutting_down()) {
@@ -1042,6 +1043,9 @@ __ast_channel_alloc_ap(int needqueue, int state, const char *cid_num, const char
ast_channel_fin_set(tmp, global_fin);
ast_channel_fout_set(tmp, global_fout);
+ now = ast_tvnow();
+ ast_channel_creationtime_set(tmp, &now);
+
if (ast_strlen_zero(ast_config_AST_SYSTEM_NAME)) {
ast_channel_uniqueid_build(tmp, "%li.%d", (long) time(NULL),
ast_atomic_fetchadd_int(&uniqueint, 1));
@@ -2339,6 +2343,10 @@ static void ast_channel_destructor(void *obj)
if (callid) {
ast_callid_unref(callid);
}
+
+ ast_channel_named_callgroups_set(chan, NULL);
+ ast_channel_named_pickupgroups_set(chan, NULL);
+
ast_atomic_fetchadd_int(&chancount, -1);
}
@@ -8074,6 +8082,113 @@ ast_group_t ast_get_group(const char *s)
return group;
}
+/*! \brief Comparison function used for named group container */
+static int namedgroup_cmp_cb(void *obj, void *arg, int flags)
+{
+ const struct namedgroup_entry *an = obj;
+ const struct namedgroup_entry *bn = arg;
+ return strcmp(an->name, bn->name) ? 0 : CMP_MATCH | CMP_STOP;
+}
+
+/*! \brief Hashing function used for named group container */
+static int namedgroup_hash_cb(const void *obj, const int flags)
+{
+ const struct namedgroup_entry *entry = obj;
+ return entry->hash;
+}
+
+static void namedgroup_dtor(void *obj)
+{
+ ast_free(((struct namedgroup_entry*)obj)->name);
+}
+
+/*! \internal \brief Actually a refcounted ao2_object. An indirect ao2_container to hide the implementation of namedgroups. */
+struct ast_namedgroups {
+ struct ao2_container *container;
+};
+
+static void ast_namedgroups_dtor(void *obj)
+{
+ ao2_cleanup(((struct ast_namedgroups *)obj)->container);
+}
+
+struct ast_namedgroups *ast_get_namedgroups(const char *s)
+{
+ struct ast_namedgroups *namedgroups;
+ char *piece;
+ char *c;
+
+ if (ast_strlen_zero(s)) {
+ return NULL;
+ }
+ c = ast_strdupa(s);
+ if (!c) {
+ return NULL;
+ }
+
+ namedgroups = ao2_alloc_options(sizeof(*namedgroups), ast_namedgroups_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!namedgroups) {
+ return NULL;
+ }
+ namedgroups->container = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK, 19, namedgroup_hash_cb, namedgroup_cmp_cb);
+ if (!namedgroups->container) {
+ ao2_ref(namedgroups, -1);
+ return NULL;
+ }
+
+ /*! \brief Remove leading and trailing whitespace */
+ c = ast_strip(c);
+
+ while ((piece = strsep(&c, ","))) {
+ struct namedgroup_entry *entry;
+
+ /* remove leading/trailing whitespace */
+ piece = ast_strip(piece);
+ if (strlen(piece) == 0) {
+ ast_log(LOG_ERROR, "Syntax error parsing named group configuration '%s' at '%s'. Ignoring.\n", s, piece);
+ continue;
+ }
+
+ entry = ao2_alloc_options(sizeof(*entry), namedgroup_dtor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+ if (!entry) {
+ ao2_ref(namedgroups, -1);
+ return NULL;
+ }
+ entry->name = ast_strdup(piece);
+ if (!entry->name) {
+ ao2_ref(entry, -1);
+ ao2_ref(namedgroups, -1);
+ return NULL;
+ }
+ entry->hash = ast_str_hash(entry->name);
+ /* every group name may exist only once, delete duplicates */
+ ao2_find(namedgroups->container, entry, OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
+ ao2_link(namedgroups->container, entry);
+ ao2_ref(entry, -1);
+ }
+
+ if (ao2_container_count(namedgroups->container) == 0) {
+ ao2_ref(namedgroups, -1);
+ namedgroups = NULL;
+ }
+
+ return namedgroups;
+}
+
+struct ast_namedgroups *ast_unref_namedgroups(struct ast_namedgroups *groups)
+{
+ ao2_cleanup(groups);
+ return NULL;
+}
+
+struct ast_namedgroups *ast_ref_namedgroups(struct ast_namedgroups *groups)
+{
+ if (groups) {
+ ao2_ref(groups, 1);
+ }
+ return groups;
+}
+
static int (*ast_moh_start_ptr)(struct ast_channel *, const char *, const char *) = NULL;
static void (*ast_moh_stop_ptr)(struct ast_channel *) = NULL;
static void (*ast_moh_cleanup_ptr)(struct ast_channel *) = NULL;
@@ -8304,6 +8419,59 @@ char *ast_print_group(char *buf, int buflen, ast_group_t group)
return buf;
}
+/*! \brief Print named call group and named pickup group ---*/
+char *ast_print_namedgroups(struct ast_str **buf, struct ast_namedgroups *group)
+{
+ struct namedgroup_entry *ng;
+ int first = 1;
+ struct ao2_iterator it;
+
+ if (group == NULL) {
+ return ast_str_buffer(*buf);
+ }
+
+ it = ao2_iterator_init(group->container, 0);
+ while ((ng = ao2_iterator_next(&it))) {
+ if (!first) {
+ ast_str_append(buf, 0, ", ");
+ } else {
+ first = 0;
+ }
+ ast_str_append(buf, 0, "%s", ng->name);
+ ao2_ref(ng, -1);
+ }
+ ao2_iterator_destroy(&it);
+
+ return ast_str_buffer(*buf);
+}
+
+static int namedgroup_match(void *obj, void *arg, int flags)
+{
+ void *match;
+
+ match = ao2_find(arg, obj, OBJ_POINTER);
+ ao2_cleanup(match);
+
+ return match ? CMP_MATCH | CMP_STOP : 0;
+}
+
+int ast_namedgroups_intersect(struct ast_namedgroups *a, struct ast_namedgroups *b)
+{
+ /*
+ * Do a and b intersect? Since b is hash table average time complexity is O(|a|)
+ */
+ void *match;
+
+ if (!a || !b) {
+ return 0;
+ }
+
+ match = ao2_callback(a->container, 0, namedgroup_match, b->container);
+ ao2_cleanup(match);
+
+ return match != NULL;
+}
+
void ast_set_variables(struct ast_channel *chan, struct ast_variable *vars)
{
struct ast_variable *cur;
diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c
index b659fb9b6..368940fa1 100644
--- a/main/channel_internal_api.c
+++ b/main/channel_internal_api.c
@@ -136,6 +136,9 @@ struct ast_channel {
struct varshead varshead; /*!< A linked list for channel variables. See \ref AstChanVar */
ast_group_t callgroup; /*!< Call group for call pickups */
ast_group_t pickupgroup; /*!< Pickup group - which calls groups can be picked up? */
+ struct ast_namedgroups *named_callgroups; /*!< Named call group for call pickups */
+ struct ast_namedgroups *named_pickupgroups; /*!< Named pickup group - which call groups can be picked up? */
+ struct timeval creationtime; /*!< The time of channel creation */
struct ast_readq_list readq;
struct ast_jb jb; /*!< The jitterbuffer state */
struct timeval dtmf_tv; /*!< The time that an in process digit began, or the last digit ended */
@@ -983,6 +986,14 @@ void ast_channel_varshead_set(struct ast_channel *chan, struct varshead *value)
{
chan->varshead = *value;
}
+struct timeval ast_channel_creationtime(struct ast_channel *chan)
+{
+ return chan->creationtime;
+}
+void ast_channel_creationtime_set(struct ast_channel *chan, struct timeval *value)
+{
+ chan->creationtime = *value;
+}
/* Evil softhangup accessors */
int ast_channel_softhangup_internal_flag(struct ast_channel *chan)
@@ -1026,6 +1037,24 @@ void ast_channel_pickupgroup_set(struct ast_channel *chan, ast_group_t value)
{
chan->pickupgroup = value;
}
+struct ast_namedgroups *ast_channel_named_callgroups(const struct ast_channel *chan)
+{
+ return chan->named_callgroups;
+}
+void ast_channel_named_callgroups_set(struct ast_channel *chan, struct ast_namedgroups *value)
+{
+ ast_unref_namedgroups(chan->named_callgroups);
+ chan->named_callgroups = ast_ref_namedgroups(value);
+}
+struct ast_namedgroups *ast_channel_named_pickupgroups(const struct ast_channel *chan)
+{
+ return chan->named_pickupgroups;
+}
+void ast_channel_named_pickupgroups_set(struct ast_channel *chan, struct ast_namedgroups *value)
+{
+ ast_unref_namedgroups(chan->named_pickupgroups);
+ chan->named_pickupgroups = ast_ref_namedgroups(value);
+}
/* Alertpipe functions */
int ast_channel_alert_write(struct ast_channel *chan)
diff --git a/main/features.c b/main/features.c
index 5d0f8ec5d..0794a2f94 100644
--- a/main/features.c
+++ b/main/features.c
@@ -7679,10 +7679,19 @@ static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
struct ast_channel *chan = data;/*!< Channel wanting to pickup call */
ast_channel_lock(target);
- if (chan != target && (ast_channel_pickupgroup(chan) & ast_channel_callgroup(target))
- && ast_can_pickup(target)) {
- /* Return with the channel still locked on purpose */
- return CMP_MATCH | CMP_STOP;
+
+ /*
+ * Both, callgroup and namedcallgroup pickup variants are matched independently.
+ * Checking for named group match is done last since it's a rather expensive operation.
+ */
+ if (chan != target && ast_can_pickup(target)
+ && ((ast_channel_pickupgroup(chan) & ast_channel_callgroup(target))
+ || (ast_namedgroups_intersect(ast_channel_named_pickupgroups(chan), ast_channel_named_callgroups(target))))) {
+ /*
+ * Return with the channel unlocked on purpose, else we would lock many channels with the chance for deadlock
+ */
+ ast_channel_unlock(target);
+ return CMP_MATCH;
}
ast_channel_unlock(target);
@@ -7700,11 +7709,36 @@ static int find_channel_by_group(void *obj, void *arg, void *data, int flags)
int ast_pickup_call(struct ast_channel *chan)
{
struct ast_channel *target;/*!< Potential pickup target */
+ struct ao2_iterator *targets_it;/*!< Potential pickup targets, must find the oldest of them */
+ struct ast_channel *candidate;/*!< Potential new older target */
int res = -1;
ast_debug(1, "pickup attempt by %s\n", ast_channel_name(chan));
- /* The found channel is already locked. */
- target = ast_channel_callback(find_channel_by_group, NULL, chan, 0);
+ /*
+ * Transfer all pickup-able channels to another container-iterator.
+ * Iterate it to find the oldest channel.
+ */
+ targets_it = (struct ao2_iterator *)ast_channel_callback(find_channel_by_group, NULL, chan, OBJ_MULTIPLE);
+
+ target = NULL;
+ while ((candidate = ao2_iterator_next(targets_it))) {
+ if (!target || ast_tvcmp(ast_channel_creationtime(candidate), ast_channel_creationtime(target)) < 0) {
+ target = candidate;
+ }
+ ast_channel_unref(candidate);
+ }
+ if (target) {
+ /* The found channel must be locked and ref'd. */
+ ast_channel_lock(ast_channel_ref(target));
+ /* Recheck pickup ability */
+ if (!ast_can_pickup(target)) {
+ ast_channel_unlock(target);
+ target = ast_channel_unref(target);/* Bad luck */
+ }
+ }
+
+ ao2_iterator_destroy(targets_it);
+
if (target) {
ast_log(LOG_NOTICE, "pickup %s attempt by %s\n", ast_channel_name(target), ast_channel_name(chan));