summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
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));