diff options
Diffstat (limited to 'main')
-rw-r--r-- | main/channel.c | 168 | ||||
-rw-r--r-- | main/channel_internal_api.c | 29 | ||||
-rw-r--r-- | main/features.c | 46 |
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)); |