summaryrefslogtreecommitdiff
path: root/apps/app_meetme.c
diff options
context:
space:
mode:
authorRussell Bryant <russell@russellbryant.com>2013-07-10 01:56:15 +0000
committerRussell Bryant <russell@russellbryant.com>2013-07-10 01:56:15 +0000
commit0bfe2d4cc4dda5a4bcde039614336c104a88f8d3 (patch)
tree2a0f0f41cd9ea6ac57d2f601aad02d59bdbdaf8a /apps/app_meetme.c
parent3355bb2fa5dd352a61a11aee14c84ea144f25756 (diff)
astobj2-ify the SLA code
The SLA code within app_meetme was written before asotbj2 had been merged into Asterisk. Worse, support for reloads did not exist at first and was added later as a bolt-on feature. I knew at the time that reloading was not safe at all while SLA was in use, so the reload would be queued up to execute when the system was idle. Unfortunately, this approach was still prone to errors beyond the fact that this was the only place in Asterisk where configuration was not reloaded instantly when requested. This patch converts various SLA objects to be reference counted objects using astobj2. This allows reloads to be processed while the system is in use. The code ensures that the objects will not disappear while one of the other threads is using them. However, they will be immediately removed from the global trunk and station containers so no new calls will use them if removed from configuration. Review: https://reviewboard.asterisk.org/r/2581/ ........ Merged revisions 393928 from http://svn.asterisk.org/svn/asterisk/branches/1.8 ........ Merged revisions 393929 from http://svn.asterisk.org/svn/asterisk/branches/11 git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@393930 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'apps/app_meetme.c')
-rw-r--r--apps/app_meetme.c796
1 files changed, 523 insertions, 273 deletions
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index b79ab63eb..70719608b 100644
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -961,17 +961,26 @@ struct sla_station {
/*! This option uses the values in the sla_hold_access enum and sets the
* access control type for hold on this station. */
unsigned int hold_access:1;
- /*! Use count for inside sla_station_exec */
- unsigned int ref_count;
+ /*! Mark used during reload processing */
+ unsigned int mark:1;
};
+/*!
+ * \brief A reference to a station
+ *
+ * This struct looks near useless at first glance. However, its existence
+ * in the list of stations in sla_trunk means that this station references
+ * that trunk. We use the mark to keep track of whether it needs to be
+ * removed from the sla_trunk's list of stations during a reload.
+ */
struct sla_station_ref {
AST_LIST_ENTRY(sla_station_ref) entry;
struct sla_station *station;
+ /*! Mark used during reload processing */
+ unsigned int mark:1;
};
struct sla_trunk {
- AST_RWLIST_ENTRY(sla_trunk) entry;
AST_DECLARE_STRING_FIELDS(
AST_STRING_FIELD(name);
AST_STRING_FIELD(device);
@@ -995,10 +1004,16 @@ struct sla_trunk {
/*! Whether this trunk is currently on hold, meaning that once a station
* connects to it, the trunk channel needs to have UNHOLD indicated to it. */
unsigned int on_hold:1;
- /*! Use count for inside sla_trunk_exec */
- unsigned int ref_count;
+ /*! Mark used during reload processing */
+ unsigned int mark:1;
};
+/*!
+ * \brief A station's reference to a trunk
+ *
+ * An sla_station keeps a list of trunk_refs. This holds metadata about the
+ * stations usage of the trunk.
+ */
struct sla_trunk_ref {
AST_LIST_ENTRY(sla_trunk_ref) entry;
struct sla_trunk *trunk;
@@ -1012,10 +1027,12 @@ struct sla_trunk_ref {
* station. This takes higher priority than a ring delay set at
* the station level. */
unsigned int ring_delay;
+ /*! Mark used during reload processing */
+ unsigned int mark:1;
};
-static AST_RWLIST_HEAD_STATIC(sla_stations, sla_station);
-static AST_RWLIST_HEAD_STATIC(sla_trunks, sla_trunk);
+static struct ao2_container *sla_stations;
+static struct ao2_container *sla_trunks;
static const char sla_registrar[] = "SLA";
@@ -1027,10 +1044,6 @@ enum sla_event_type {
SLA_EVENT_DIAL_STATE,
/*! The state of a ringing trunk has changed */
SLA_EVENT_RINGING_TRUNK,
- /*! A reload of configuration has been requested */
- SLA_EVENT_RELOAD,
- /*! Poke the SLA thread so it can check if it can perform a reload */
- SLA_EVENT_CHECK_RELOAD,
};
struct sla_event {
@@ -1086,8 +1099,6 @@ static struct {
/*! Attempt to handle CallerID, even though it is known not to work
* properly in some situations. */
unsigned int attempt_callerid:1;
- /*! A reload has been requested */
- unsigned int reload:1;
} sla = {
.thread = AST_PTHREADT_NULL,
};
@@ -2117,7 +2128,8 @@ static const char *sla_hold_str(unsigned int hold_access)
static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
- const struct sla_trunk *trunk;
+ struct ao2_iterator i;
+ struct sla_trunk *trunk;
switch (cmd) {
case CLI_INIT:
@@ -2135,12 +2147,17 @@ static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
"=== Configured SLA Trunks ===================================\n"
"=============================================================\n"
"===\n");
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
+ i = ao2_iterator_init(sla_trunks, 0);
+ for (; (trunk = ao2_iterator_next(&i)); ao2_ref(trunk, -1)) {
struct sla_station_ref *station_ref;
char ring_timeout[16] = "(none)";
- if (trunk->ring_timeout)
+
+ ao2_lock(trunk);
+
+ if (trunk->ring_timeout) {
snprintf(ring_timeout, sizeof(ring_timeout), "%u Seconds", trunk->ring_timeout);
+ }
+
ast_cli(a->fd, "=== ---------------------------------------------------------\n"
"=== Trunk Name: %s\n"
"=== ==> Device: %s\n"
@@ -2154,13 +2171,16 @@ static char *sla_show_trunks(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
ring_timeout,
trunk->barge_disabled ? "No" : "Yes",
sla_hold_str(trunk->hold_access));
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry)
+
+ AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
ast_cli(a->fd, "=== ==> Station name: %s\n", station_ref->station->name);
- AST_RWLIST_UNLOCK(&sla_stations);
+ }
+
ast_cli(a->fd, "=== ---------------------------------------------------------\n===\n");
+
+ ao2_unlock(trunk);
}
- AST_RWLIST_UNLOCK(&sla_trunks);
+ ao2_iterator_destroy(&i);
ast_cli(a->fd, "=============================================================\n\n");
return CLI_SUCCESS;
@@ -2182,7 +2202,8 @@ static const char *trunkstate2str(enum sla_trunk_state state)
static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
- const struct sla_station *station;
+ struct ao2_iterator i;
+ struct sla_station *station;
switch (cmd) {
case CLI_INIT:
@@ -2200,11 +2221,14 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
"=== Configured SLA Stations =================================\n"
"=============================================================\n"
"===\n");
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+ i = ao2_iterator_init(sla_stations, 0);
+ for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
struct sla_trunk_ref *trunk_ref;
char ring_timeout[16] = "(none)";
char ring_delay[16] = "(none)";
+
+ ao2_lock(station);
+
if (station->ring_timeout) {
snprintf(ring_timeout, sizeof(ring_timeout),
"%u", station->ring_timeout);
@@ -2225,7 +2249,6 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
S_OR(station->autocontext, "(none)"),
ring_timeout, ring_delay,
sla_hold_str(station->hold_access));
- AST_RWLIST_RDLOCK(&sla_trunks);
AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
if (trunk_ref->ring_timeout) {
snprintf(ring_timeout, sizeof(ring_timeout),
@@ -2245,11 +2268,12 @@ static char *sla_show_stations(struct ast_cli_entry *e, int cmd, struct ast_cli_
trunkstate2str(trunk_ref->state),
ring_timeout, ring_delay);
}
- AST_RWLIST_UNLOCK(&sla_trunks);
ast_cli(a->fd, "=== ---------------------------------------------------------\n"
"===\n");
+
+ ao2_unlock(station);
}
- AST_RWLIST_UNLOCK(&sla_stations);
+ ao2_iterator_destroy(&i);
ast_cli(a->fd, "============================================================\n"
"\n");
@@ -2393,11 +2417,16 @@ static void sla_queue_event_full(enum sla_event_type type,
struct sla_event *event;
if (sla.thread == AST_PTHREADT_NULL) {
+ ao2_ref(station, -1);
+ ao2_ref(trunk_ref, -1);
return;
}
- if (!(event = ast_calloc(1, sizeof(*event))))
+ if (!(event = ast_calloc(1, sizeof(*event)))) {
+ ao2_ref(station, -1);
+ ao2_ref(trunk_ref, -1);
return;
+ }
event->type = type;
event->trunk_ref = trunk_ref;
@@ -2431,6 +2460,7 @@ static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *c
struct sla_station *station;
struct sla_trunk_ref *trunk_ref = NULL;
char *trunk_name;
+ struct ao2_iterator i;
trunk_name = ast_strdupa(conf->confno);
strsep(&trunk_name, "_");
@@ -2439,16 +2469,23 @@ static void sla_queue_event_conf(enum sla_event_type type, struct ast_channel *c
return;
}
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
+ i = ao2_iterator_init(sla_stations, 0);
+ while ((station = ao2_iterator_next(&i))) {
+ ao2_lock(station);
AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name))
+ if (trunk_ref->chan == chan && !strcmp(trunk_ref->trunk->name, trunk_name)) {
+ ao2_ref(trunk_ref, 1);
break;
+ }
}
- if (trunk_ref)
+ ao2_unlock(station);
+ if (trunk_ref) {
+ /* station reference given to sla_queue_event_full() */
break;
+ }
+ ao2_ref(station, -1);
}
- AST_RWLIST_UNLOCK(&sla_stations);
+ ao2_iterator_destroy(&i);
if (!trunk_ref) {
ast_debug(1, "Trunk not found for event!\n");
@@ -5776,34 +5813,30 @@ static void load_config_meetme(void)
ast_config_destroy(cfg);
}
-/*! \brief Find an SLA trunk by name
- * \note This must be called with the sla_trunks container locked
+/*!
+ * \internal
+ * \brief Find an SLA trunk by name
*/
static struct sla_trunk *sla_find_trunk(const char *name)
{
- struct sla_trunk *trunk = NULL;
-
- AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
- if (!strcasecmp(trunk->name, name))
- break;
- }
+ struct sla_trunk tmp_trunk = {
+ .name = name,
+ };
- return trunk;
+ return ao2_find(sla_trunks, &tmp_trunk, OBJ_POINTER);
}
-/*! \brief Find an SLA station by name
- * \note This must be called with the sla_stations container locked
+/*!
+ * \internal
+ * \brief Find an SLA station by name
*/
static struct sla_station *sla_find_station(const char *name)
{
- struct sla_station *station = NULL;
-
- AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
- if (!strcasecmp(station->name, name))
- break;
- }
+ struct sla_station tmp_station = {
+ .name = name,
+ };
- return station;
+ return ao2_find(sla_stations, &tmp_station, OBJ_POINTER);
}
static int sla_check_station_hold_access(const struct sla_trunk *trunk,
@@ -5827,9 +5860,11 @@ static int sla_check_station_hold_access(const struct sla_trunk *trunk,
return 0;
}
-/*! \brief Find a trunk reference on a station by name
+/*!
+ * \brief Find a trunk reference on a station by name
* \param station the station
* \param name the trunk's name
+ * \pre sla_station is locked
* \return a pointer to the station's trunk reference. If the trunk
* is not found, it is not idle and barge is disabled, or if
* it is on hold and private hold is set, then NULL will be returned.
@@ -5856,16 +5891,32 @@ static struct sla_trunk_ref *sla_find_trunk_ref_byname(const struct sla_station
break;
}
+ if (trunk_ref) {
+ ao2_ref(trunk_ref, 1);
+ }
+
return trunk_ref;
}
+static void sla_station_ref_destructor(void *obj)
+{
+ struct sla_station_ref *station_ref = obj;
+
+ if (station_ref->station) {
+ ao2_ref(station_ref->station, -1);
+ station_ref->station = NULL;
+ }
+}
+
static struct sla_station_ref *sla_create_station_ref(struct sla_station *station)
{
struct sla_station_ref *station_ref;
- if (!(station_ref = ast_calloc(1, sizeof(*station_ref))))
+ if (!(station_ref = ao2_alloc(sizeof(*station_ref), sla_station_ref_destructor))) {
return NULL;
+ }
+ ao2_ref(station, 1);
station_ref->station = station;
return station_ref;
@@ -5878,12 +5929,48 @@ static struct sla_ringing_station *sla_create_ringing_station(struct sla_station
if (!(ringing_station = ast_calloc(1, sizeof(*ringing_station))))
return NULL;
+ ao2_ref(station, 1);
ringing_station->station = station;
ringing_station->ring_begin = ast_tvnow();
return ringing_station;
}
+static void sla_ringing_station_destroy(struct sla_ringing_station *ringing_station)
+{
+ if (ringing_station->station) {
+ ao2_ref(ringing_station->station, -1);
+ ringing_station->station = NULL;
+ }
+
+ ast_free(ringing_station);
+}
+
+static struct sla_failed_station *sla_create_failed_station(struct sla_station *station)
+{
+ struct sla_failed_station *failed_station;
+
+ if (!(failed_station = ast_calloc(1, sizeof(*failed_station)))) {
+ return NULL;
+ }
+
+ ao2_ref(station, 1);
+ failed_station->station = station;
+ failed_station->last_try = ast_tvnow();
+
+ return failed_station;
+}
+
+static void sla_failed_station_destroy(struct sla_failed_station *failed_station)
+{
+ if (failed_station->station) {
+ ao2_ref(failed_station->station, -1);
+ failed_station->station = NULL;
+ }
+
+ ast_free(failed_station);
+}
+
static enum ast_device_state sla_state_to_devstate(enum sla_trunk_state state)
{
switch (state) {
@@ -5906,18 +5993,25 @@ static void sla_change_trunk_state(const struct sla_trunk *trunk, enum sla_trunk
{
struct sla_station *station;
struct sla_trunk_ref *trunk_ref;
+ struct ao2_iterator i;
- AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+ i = ao2_iterator_init(sla_stations, 0);
+ while ((station = ao2_iterator_next(&i))) {
+ ao2_lock(station);
AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
if (trunk_ref->trunk != trunk || (inactive_only ? trunk_ref->chan : 0)
- || trunk_ref == exclude)
+ || trunk_ref == exclude) {
continue;
+ }
trunk_ref->state = state;
ast_devstate_changed(sla_state_to_devstate(state), AST_DEVSTATE_CACHABLE,
"SLA:%s_%s", station->name, trunk->name);
break;
}
+ ao2_unlock(station);
+ ao2_ref(station, -1);
}
+ ao2_iterator_destroy(&i);
}
struct run_station_args {
@@ -5935,8 +6029,8 @@ static void answer_trunk_chan(struct ast_channel *chan)
static void *run_station(void *data)
{
- struct sla_station *station;
- struct sla_trunk_ref *trunk_ref;
+ RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
+ RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
struct ast_str *conf_name = ast_str_create(16);
struct ast_flags64 conf_flags = { 0 };
struct ast_conference *conf;
@@ -5979,6 +6073,8 @@ static void *run_station(void *data)
return NULL;
}
+static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk);
+
static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
{
char buf[80];
@@ -5988,10 +6084,11 @@ static void sla_stop_ringing_trunk(struct sla_ringing_trunk *ringing_trunk)
admin_exec(NULL, buf);
sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
- while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry)))
- ast_free(station_ref);
+ while ((station_ref = AST_LIST_REMOVE_HEAD(&ringing_trunk->timed_out_stations, entry))) {
+ ao2_ref(station_ref, -1);
+ }
- ast_free(ringing_trunk);
+ sla_ringing_trunk_destroy(ringing_trunk);
}
static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station,
@@ -6026,7 +6123,7 @@ static void sla_stop_ringing_station(struct sla_ringing_station *ringing_station
}
done:
- ast_free(ringing_station);
+ sla_ringing_station_destroy(ringing_station);
}
static void sla_dial_state_callback(struct ast_dial *dial)
@@ -6078,8 +6175,10 @@ static struct sla_ringing_trunk *sla_choose_ringing_trunk(struct sla_station *st
if (rm)
AST_LIST_REMOVE_CURRENT(entry);
- if (trunk_ref)
+ if (trunk_ref) {
+ ao2_ref(s_trunk_ref, 1);
*trunk_ref = s_trunk_ref;
+ }
break;
}
@@ -6097,7 +6196,7 @@ static void sla_handle_dial_state_event(void)
struct sla_ringing_station *ringing_station;
AST_LIST_TRAVERSE_SAFE_BEGIN(&sla.ringing_stations, ringing_station, entry) {
- struct sla_trunk_ref *s_trunk_ref = NULL;
+ RAII_VAR(struct sla_trunk_ref *, s_trunk_ref, NULL, ao2_cleanup);
struct sla_ringing_trunk *ringing_trunk = NULL;
struct run_station_args args;
enum ast_dial_result dial_res;
@@ -6130,7 +6229,7 @@ static void sla_handle_dial_state_event(void)
ast_dial_join(ringing_station->station->dial);
ast_dial_destroy(ringing_station->station->dial);
ringing_station->station->dial = NULL;
- ast_free(ringing_station);
+ sla_ringing_station_destroy(ringing_station);
break;
}
/* Track the channel that answered this trunk */
@@ -6141,12 +6240,14 @@ static void sla_handle_dial_state_event(void)
/* Now, start a thread that will connect this station to the trunk. The rest of
* the code here sets up the thread and ensures that it is able to save the arguments
* before they are no longer valid since they are allocated on the stack. */
+ ao2_ref(s_trunk_ref, 1);
args.trunk_ref = s_trunk_ref;
+ ao2_ref(ringing_station->station, 1);
args.station = ringing_station->station;
args.cond = &cond;
args.cond_lock = &cond_lock;
- ast_free(ringing_trunk);
- ast_free(ringing_station);
+ sla_ringing_trunk_destroy(ringing_trunk);
+ sla_ringing_station_destroy(ringing_station);
ast_mutex_init(&cond_lock);
ast_cond_init(&cond, NULL);
ast_mutex_lock(&cond_lock);
@@ -6200,7 +6301,7 @@ static int sla_check_failed_station(const struct sla_station *station)
continue;
if (ast_tvdiff_ms(ast_tvnow(), failed_station->last_try) > 1000) {
AST_LIST_REMOVE_CURRENT(entry);
- ast_free(failed_station);
+ sla_failed_station_destroy(failed_station);
break;
}
res = 1;
@@ -6253,11 +6354,9 @@ static int sla_ring_station(struct sla_ringing_trunk *ringing_trunk, struct sla_
if (res != AST_DIAL_RESULT_TRYING) {
struct sla_failed_station *failed_station;
ast_dial_destroy(dial);
- if (!(failed_station = ast_calloc(1, sizeof(*failed_station))))
- return -1;
- failed_station->station = station;
- failed_station->last_try = ast_tvnow();
- AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
+ if ((failed_station = sla_create_failed_station(station))) {
+ AST_LIST_INSERT_HEAD(&sla.failed_stations, failed_station, entry);
+ }
return -1;
}
if (!(ringing_station = sla_create_ringing_station(station))) {
@@ -6297,6 +6396,8 @@ static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *statio
break;
}
+ ao2_ref(trunk_ref, 1);
+
return trunk_ref;
}
@@ -6308,7 +6409,7 @@ static struct sla_trunk_ref *sla_find_trunk_ref(const struct sla_station *statio
static int sla_check_station_delay(struct sla_station *station,
struct sla_ringing_trunk *ringing_trunk)
{
- struct sla_trunk_ref *trunk_ref;
+ RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
unsigned int delay = UINT_MAX;
int time_left, time_elapsed;
@@ -6401,7 +6502,7 @@ static void sla_hangup_stations(void)
ast_dial_join(ringing_station->station->dial);
ast_dial_destroy(ringing_station->station->dial);
ringing_station->station->dial = NULL;
- ast_free(ringing_station);
+ sla_ringing_station_destroy(ringing_station);
}
}
AST_LIST_TRAVERSE_SAFE_END
@@ -6558,8 +6659,10 @@ static int sla_calc_station_delays(unsigned int *timeout)
{
struct sla_station *station;
int res = 0;
+ struct ao2_iterator i;
- AST_LIST_TRAVERSE(&sla_stations, station, entry) {
+ i = ao2_iterator_init(sla_stations, 0);
+ for (; (station = ao2_iterator_next(&i)); ao2_ref(station, -1)) {
struct sla_ringing_trunk *ringing_trunk;
int time_left;
@@ -6589,6 +6692,7 @@ static int sla_calc_station_delays(unsigned int *timeout)
if (time_left < *timeout)
*timeout = time_left;
}
+ ao2_iterator_destroy(&i);
return res;
}
@@ -6630,47 +6734,19 @@ static int sla_process_timers(struct timespec *ts)
return 1;
}
-static int sla_load_config(int reload);
-
-/*!
- * \internal
- * \brief Check if we can do a reload of SLA, and do it if we can
- * \pre sla.lock is locked.
- */
-static void sla_check_reload(void)
+static void sla_event_destroy(struct sla_event *event)
{
- struct sla_station *station;
- struct sla_trunk *trunk;
-
- if (!AST_LIST_EMPTY(&sla.event_q) || !AST_LIST_EMPTY(&sla.ringing_trunks)
- || !AST_LIST_EMPTY(&sla.ringing_stations) || !AST_LIST_EMPTY(&sla.failed_stations)) {
- return;
+ if (event->trunk_ref) {
+ ao2_ref(event->trunk_ref, -1);
+ event->trunk_ref = NULL;
}
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_RWLIST_TRAVERSE(&sla_stations, station, entry) {
- if (station->ref_count)
- break;
- }
- AST_RWLIST_UNLOCK(&sla_stations);
- if (station) {
- return;
- }
-
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
- if (trunk->ref_count || trunk->chan || trunk->active_stations || trunk->hold_stations) {
- break;
- }
- }
- AST_RWLIST_UNLOCK(&sla_trunks);
- if (trunk) {
- return;
+ if (event->station) {
+ ao2_ref(event->station, -1);
+ event->station = NULL;
}
- /* yay */
- sla_load_config(1);
- sla.reload = 0;
+ ast_free(event);
}
static void *sla_thread(void *data)
@@ -6709,27 +6785,21 @@ static void *sla_thread(void *data)
case SLA_EVENT_RINGING_TRUNK:
sla_handle_ringing_trunk_event();
break;
- case SLA_EVENT_RELOAD:
- sla.reload = 1;
- case SLA_EVENT_CHECK_RELOAD:
- break;
}
- ast_free(event);
+ sla_event_destroy(event);
ast_mutex_lock(&sla.lock);
}
-
- if (sla.reload) {
- sla_check_reload();
- }
}
ast_mutex_unlock(&sla.lock);
- while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry)))
- ast_free(ringing_station);
+ while ((ringing_station = AST_LIST_REMOVE_HEAD(&sla.ringing_stations, entry))) {
+ sla_ringing_station_destroy(ringing_station);
+ }
- while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry)))
- ast_free(failed_station);
+ while ((failed_station = AST_LIST_REMOVE_HEAD(&sla.failed_stations, entry))) {
+ sla_failed_station_destroy(failed_station);
+ }
return NULL;
}
@@ -6750,7 +6820,8 @@ static void *dial_trunk(void *data)
char conf_name[MAX_CONFNUM];
struct ast_conference *conf;
struct ast_flags64 conf_flags = { 0 };
- struct sla_trunk_ref *trunk_ref = args->trunk_ref;
+ RAII_VAR(struct sla_trunk_ref *, trunk_ref, args->trunk_ref, ao2_cleanup);
+ RAII_VAR(struct sla_station *, station, args->station, ao2_cleanup);
int caller_is_saved;
struct ast_party_caller caller;
int last_state = 0;
@@ -6822,8 +6893,8 @@ static void *dial_trunk(void *data)
break;
/* check that SLA station that originated trunk call is still alive */
- if (args->station && ast_device_state(args->station->device) == AST_DEVICE_NOT_INUSE) {
- ast_debug(3, "Originating station device %s no longer active\n", args->station->device);
+ if (station && ast_device_state(station->device) == AST_DEVICE_NOT_INUSE) {
+ ast_debug(3, "Originating station device %s no longer active\n", station->device);
trunk_ref->trunk->chan = NULL;
break;
}
@@ -6876,15 +6947,19 @@ static void *dial_trunk(void *data)
return NULL;
}
-/*! \brief For a given station, choose the highest priority idle trunk
+/*!
+ * \brief For a given station, choose the highest priority idle trunk
+ * \pre sla_station is locked
*/
static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *station)
{
struct sla_trunk_ref *trunk_ref = NULL;
AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (trunk_ref->state == SLA_TRUNK_STATE_IDLE)
+ if (trunk_ref->state == SLA_TRUNK_STATE_IDLE) {
+ ao2_ref(trunk_ref, 1);
break;
+ }
}
return trunk_ref;
@@ -6893,8 +6968,8 @@ static struct sla_trunk_ref *sla_choose_idle_trunk(const struct sla_station *sta
static int sla_station_exec(struct ast_channel *chan, const char *data)
{
char *station_name, *trunk_name;
- struct sla_station *station;
- struct sla_trunk_ref *trunk_ref = NULL;
+ RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
+ RAII_VAR(struct sla_trunk_ref *, trunk_ref, NULL, ao2_cleanup);
char conf_name[MAX_CONFNUM];
struct ast_flags64 conf_flags = { 0 };
struct ast_conference *conf;
@@ -6914,25 +6989,21 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
return 0;
}
- AST_RWLIST_WRLOCK(&sla_stations);
station = sla_find_station(station_name);
- if (station)
- ast_atomic_fetchadd_int((int *) &station->ref_count, 1);
- AST_RWLIST_UNLOCK(&sla_stations);
if (!station) {
ast_log(LOG_WARNING, "Station '%s' not found!\n", station_name);
pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "FAILURE");
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
return 0;
}
- AST_RWLIST_RDLOCK(&sla_trunks);
+ ao2_lock(station);
if (!ast_strlen_zero(trunk_name)) {
trunk_ref = sla_find_trunk_ref_byname(station, trunk_name);
- } else
+ } else {
trunk_ref = sla_choose_idle_trunk(station);
- AST_RWLIST_UNLOCK(&sla_trunks);
+ }
+ ao2_unlock(station);
if (!trunk_ref) {
if (ast_strlen_zero(trunk_name))
@@ -6942,8 +7013,6 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
"'%s' due to access controls.\n", trunk_name);
}
pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
- ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
return 0;
}
@@ -6972,7 +7041,7 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
answer_trunk_chan(ringing_trunk->trunk->chan);
sla_change_trunk_state(ringing_trunk->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
- free(ringing_trunk);
+ sla_ringing_trunk_destroy(ringing_trunk);
/* Queue up reprocessing ringing trunks, and then ringing stations again */
sla_queue_event(SLA_EVENT_RINGING_TRUNK);
@@ -6992,6 +7061,8 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
.cond_lock = &cond_lock,
.cond = &cond,
};
+ ao2_ref(trunk_ref, 1);
+ ao2_ref(station, 1);
sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_UP, ALL_TRUNK_REFS, NULL);
/* Create a thread to dial the trunk and dump it into the conference.
* However, we want to wait until the trunk has been dialed and the
@@ -7011,8 +7082,6 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "CONGESTION");
sla_change_trunk_state(trunk_ref->trunk, SLA_TRUNK_STATE_IDLE, ALL_TRUNK_REFS, NULL);
trunk_ref->chan = NULL;
- ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
return 0;
}
}
@@ -7045,19 +7114,28 @@ static int sla_station_exec(struct ast_channel *chan, const char *data)
pbx_builtin_setvar_helper(chan, "SLASTATION_STATUS", "SUCCESS");
- ast_atomic_fetchadd_int((int *) &station->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
-
return 0;
}
+static void sla_trunk_ref_destructor(void *obj)
+{
+ struct sla_trunk_ref *trunk_ref = obj;
+
+ if (trunk_ref->trunk) {
+ ao2_ref(trunk_ref->trunk, -1);
+ trunk_ref->trunk = NULL;
+ }
+}
+
static struct sla_trunk_ref *create_trunk_ref(struct sla_trunk *trunk)
{
struct sla_trunk_ref *trunk_ref;
- if (!(trunk_ref = ast_calloc(1, sizeof(*trunk_ref))))
+ if (!(trunk_ref = ao2_alloc(sizeof(*trunk_ref), sla_trunk_ref_destructor))) {
return NULL;
+ }
+ ao2_ref(trunk, 1);
trunk_ref->trunk = trunk;
return trunk_ref;
@@ -7067,9 +7145,11 @@ static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
{
struct sla_ringing_trunk *ringing_trunk;
- if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk))))
+ if (!(ringing_trunk = ast_calloc(1, sizeof(*ringing_trunk)))) {
return NULL;
-
+ }
+
+ ao2_ref(trunk, 1);
ringing_trunk->trunk = trunk;
ringing_trunk->ring_begin = ast_tvnow();
@@ -7084,6 +7164,16 @@ static struct sla_ringing_trunk *queue_ringing_trunk(struct sla_trunk *trunk)
return ringing_trunk;
}
+static void sla_ringing_trunk_destroy(struct sla_ringing_trunk *ringing_trunk)
+{
+ if (ringing_trunk->trunk) {
+ ao2_ref(ringing_trunk->trunk, -1);
+ ringing_trunk->trunk = NULL;
+ }
+
+ ast_free(ringing_trunk);
+}
+
enum {
SLA_TRUNK_OPT_MOH = (1 << 0),
};
@@ -7102,7 +7192,7 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
char conf_name[MAX_CONFNUM];
struct ast_conference *conf;
struct ast_flags64 conf_flags = { 0 };
- struct sla_trunk *trunk;
+ RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
struct sla_ringing_trunk *ringing_trunk;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(trunk_name);
@@ -7126,16 +7216,11 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
}
}
- AST_RWLIST_WRLOCK(&sla_trunks);
trunk = sla_find_trunk(args.trunk_name);
- if (trunk)
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, 1);
- AST_RWLIST_UNLOCK(&sla_trunks);
if (!trunk) {
ast_log(LOG_ERROR, "SLA Trunk '%s' not found!\n", args.trunk_name);
pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
return 0;
}
@@ -7143,8 +7228,6 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
ast_log(LOG_ERROR, "Call came in on %s, but the trunk is already in use!\n",
args.trunk_name);
pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
return 0;
}
@@ -7152,8 +7235,6 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
if (!(ringing_trunk = queue_ringing_trunk(trunk))) {
pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
return 0;
}
@@ -7161,8 +7242,6 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
conf = build_conf(conf_name, "", "", 1, 1, 1, chan, NULL);
if (!conf) {
pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "FAILURE");
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
return 0;
}
ast_set_flag64(&conf_flags,
@@ -7196,46 +7275,37 @@ static int sla_trunk_exec(struct ast_channel *chan, const char *data)
AST_LIST_TRAVERSE_SAFE_END;
ast_mutex_unlock(&sla.lock);
if (ringing_trunk) {
- ast_free(ringing_trunk);
+ sla_ringing_trunk_destroy(ringing_trunk);
pbx_builtin_setvar_helper(chan, "SLATRUNK_STATUS", "UNANSWERED");
/* Queue reprocessing of ringing trunks to make stations stop ringing
* that shouldn't be ringing after this trunk stopped. */
sla_queue_event(SLA_EVENT_RINGING_TRUNK);
}
- ast_atomic_fetchadd_int((int *) &trunk->ref_count, -1);
- sla_queue_event(SLA_EVENT_CHECK_RELOAD);
-
return 0;
}
static enum ast_device_state sla_state(const char *data)
{
char *buf, *station_name, *trunk_name;
- struct sla_station *station;
+ RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
struct sla_trunk_ref *trunk_ref;
enum ast_device_state res = AST_DEVICE_INVALID;
trunk_name = buf = ast_strdupa(data);
station_name = strsep(&trunk_name, "_");
- AST_RWLIST_RDLOCK(&sla_stations);
- AST_LIST_TRAVERSE(&sla_stations, station, entry) {
- if (strcasecmp(station_name, station->name))
- continue;
- AST_RWLIST_RDLOCK(&sla_trunks);
+ station = sla_find_station(station_name);
+ if (station) {
+ ao2_lock(station);
AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
- if (!strcasecmp(trunk_name, trunk_ref->trunk->name))
+ if (!strcasecmp(trunk_name, trunk_ref->trunk->name)) {
+ res = sla_state_to_devstate(trunk_ref->state);
break;
+ }
}
- if (!trunk_ref) {
- AST_RWLIST_UNLOCK(&sla_trunks);
- break;
- }
- res = sla_state_to_devstate(trunk_ref->state);
- AST_RWLIST_UNLOCK(&sla_trunks);
+ ao2_unlock(station);
}
- AST_RWLIST_UNLOCK(&sla_stations);
if (res == AST_DEVICE_INVALID) {
ast_log(LOG_ERROR, "Could not determine state for trunk %s on station %s!\n",
@@ -7245,26 +7315,39 @@ static enum ast_device_state sla_state(const char *data)
return res;
}
-static void destroy_trunk(struct sla_trunk *trunk)
+static int sla_trunk_release_refs(void *obj, void *arg, int flags)
{
+ struct sla_trunk *trunk = obj;
struct sla_station_ref *station_ref;
- if (!ast_strlen_zero(trunk->autocontext))
- ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
-
- while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry)))
- ast_free(station_ref);
+ while ((station_ref = AST_LIST_REMOVE_HEAD(&trunk->stations, entry))) {
+ ao2_ref(station_ref, -1);
+ }
- ast_string_field_free_memory(trunk);
- ast_free(trunk);
+ return 0;
}
-static void destroy_station(struct sla_station *station)
+static int sla_station_release_refs(void *obj, void *arg, int flags)
{
+ struct sla_station *station = obj;
struct sla_trunk_ref *trunk_ref;
+ while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry))) {
+ ao2_ref(trunk_ref, -1);
+ }
+
+ return 0;
+}
+
+static void sla_station_destructor(void *obj)
+{
+ struct sla_station *station = obj;
+
+ ast_debug(1, "sla_station destructor for '%s'\n", station->name);
+
if (!ast_strlen_zero(station->autocontext)) {
- AST_RWLIST_RDLOCK(&sla_trunks);
+ struct sla_trunk_ref *trunk_ref;
+
AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
char exten[AST_MAX_EXTENSION];
char hint[AST_MAX_APP];
@@ -7275,31 +7358,43 @@ static void destroy_station(struct sla_station *station)
ast_context_remove_extension(station->autocontext, hint,
PRIORITY_HINT, sla_registrar);
}
- AST_RWLIST_UNLOCK(&sla_trunks);
}
- while ((trunk_ref = AST_LIST_REMOVE_HEAD(&station->trunks, entry)))
- ast_free(trunk_ref);
+ sla_station_release_refs(station, NULL, 0);
ast_string_field_free_memory(station);
- ast_free(station);
}
-static void sla_destroy(void)
+static int sla_trunk_hash(const void *obj, const int flags)
{
- struct sla_trunk *trunk;
- struct sla_station *station;
+ const struct sla_trunk *trunk = obj;
+
+ return ast_str_case_hash(trunk->name);
+}
- AST_RWLIST_WRLOCK(&sla_trunks);
- while ((trunk = AST_RWLIST_REMOVE_HEAD(&sla_trunks, entry)))
- destroy_trunk(trunk);
- AST_RWLIST_UNLOCK(&sla_trunks);
+static int sla_trunk_cmp(void *obj, void *arg, int flags)
+{
+ struct sla_trunk *trunk = obj, *trunk2 = arg;
- AST_RWLIST_WRLOCK(&sla_stations);
- while ((station = AST_RWLIST_REMOVE_HEAD(&sla_stations, entry)))
- destroy_station(station);
- AST_RWLIST_UNLOCK(&sla_stations);
+ return !strcasecmp(trunk->name, trunk2->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static int sla_station_hash(const void *obj, const int flags)
+{
+ const struct sla_station *station = obj;
+ return ast_str_case_hash(station->name);
+}
+
+static int sla_station_cmp(void *obj, void *arg, int flags)
+{
+ struct sla_station *station = obj, *station2 = arg;
+
+ return !strcasecmp(station->name, station2->name) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+static void sla_destroy(void)
+{
if (sla.thread != AST_PTHREADT_NULL) {
ast_mutex_lock(&sla.lock);
sla.stop = 1;
@@ -7313,6 +7408,15 @@ static void sla_destroy(void)
ast_mutex_destroy(&sla.lock);
ast_cond_destroy(&sla.cond);
+
+ ao2_callback(sla_trunks, 0, sla_trunk_release_refs, NULL);
+ ao2_callback(sla_stations, 0, sla_station_release_refs, NULL);
+
+ ao2_ref(sla_trunks, -1);
+ sla_trunks = NULL;
+
+ ao2_ref(sla_stations, -1);
+ sla_stations = NULL;
}
static int sla_check_device(const char *device)
@@ -7328,11 +7432,27 @@ static int sla_check_device(const char *device)
return 0;
}
+static void sla_trunk_destructor(void *obj)
+{
+ struct sla_trunk *trunk = obj;
+
+ ast_debug(1, "sla_trunk destructor for '%s'\n", trunk->name);
+
+ if (!ast_strlen_zero(trunk->autocontext)) {
+ ast_context_remove_extension(trunk->autocontext, "s", 1, sla_registrar);
+ }
+
+ sla_trunk_release_refs(trunk, NULL, 0);
+
+ ast_string_field_free_memory(trunk);
+}
+
static int sla_build_trunk(struct ast_config *cfg, const char *cat)
{
- struct sla_trunk *trunk;
+ RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
struct ast_variable *var;
const char *dev;
+ int existing_trunk = 0;
if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
ast_log(LOG_ERROR, "SLA Trunk '%s' defined with no device!\n", cat);
@@ -7340,16 +7460,25 @@ static int sla_build_trunk(struct ast_config *cfg, const char *cat)
}
if (sla_check_device(dev)) {
- ast_log(LOG_ERROR, "SLA Trunk '%s' define with invalid device '%s'!\n",
+ ast_log(LOG_ERROR, "SLA Trunk '%s' defined with invalid device '%s'!\n",
cat, dev);
return -1;
}
- if (!(trunk = ast_calloc_with_stringfields(1, struct sla_trunk, 32))) {
+ if ((trunk = sla_find_trunk(cat))) {
+ trunk->mark = 0;
+ existing_trunk = 1;
+ } else if ((trunk = ao2_alloc(sizeof(*trunk), sla_trunk_destructor))) {
+ if (ast_string_field_init(trunk, 32)) {
+ return -1;
+ }
+ ast_string_field_set(trunk, name, cat);
+ } else {
return -1;
}
- ast_string_field_set(trunk, name, cat);
+ ao2_lock(trunk);
+
ast_string_field_set(trunk, device, dev);
for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
@@ -7378,54 +7507,65 @@ static int sla_build_trunk(struct ast_config *cfg, const char *cat)
}
}
+ ao2_unlock(trunk);
+
if (!ast_strlen_zero(trunk->autocontext)) {
struct ast_context *context;
context = ast_context_find_or_create(NULL, NULL, trunk->autocontext, sla_registrar);
if (!context) {
ast_log(LOG_ERROR, "Failed to automatically find or create "
"context '%s' for SLA!\n", trunk->autocontext);
- destroy_trunk(trunk);
return -1;
}
if (ast_add_extension2(context, 0 /* don't replace */, "s", 1,
NULL, NULL, slatrunk_app, ast_strdup(trunk->name), ast_free_ptr, sla_registrar)) {
ast_log(LOG_ERROR, "Failed to automatically create extension "
"for trunk '%s'!\n", trunk->name);
- destroy_trunk(trunk);
return -1;
}
}
- AST_RWLIST_WRLOCK(&sla_trunks);
- AST_RWLIST_INSERT_TAIL(&sla_trunks, trunk, entry);
- AST_RWLIST_UNLOCK(&sla_trunks);
+ if (!existing_trunk) {
+ ao2_link(sla_trunks, trunk);
+ }
return 0;
}
+/*!
+ * \internal
+ * \pre station is not locked
+ */
static void sla_add_trunk_to_station(struct sla_station *station, struct ast_variable *var)
{
- struct sla_trunk *trunk;
- struct sla_trunk_ref *trunk_ref;
+ RAII_VAR(struct sla_trunk *, trunk, NULL, ao2_cleanup);
+ struct sla_trunk_ref *trunk_ref = NULL;
struct sla_station_ref *station_ref;
char *trunk_name, *options, *cur;
+ int existing_trunk_ref = 0;
+ int existing_station_ref = 0;
options = ast_strdupa(var->value);
trunk_name = strsep(&options, ",");
-
- AST_RWLIST_RDLOCK(&sla_trunks);
- AST_RWLIST_TRAVERSE(&sla_trunks, trunk, entry) {
- if (!strcasecmp(trunk->name, trunk_name))
- break;
- }
- AST_RWLIST_UNLOCK(&sla_trunks);
+ trunk = sla_find_trunk(trunk_name);
if (!trunk) {
ast_log(LOG_ERROR, "Trunk '%s' not found!\n", var->value);
return;
}
- if (!(trunk_ref = create_trunk_ref(trunk)))
+
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ if (trunk_ref->trunk == trunk) {
+ trunk_ref->mark = 0;
+ existing_trunk_ref = 1;
+ break;
+ }
+ }
+
+ if (!trunk_ref && !(trunk_ref = create_trunk_ref(trunk))) {
return;
+ }
+
trunk_ref->state = SLA_TRUNK_STATE_IDLE;
while ((cur = strsep(&options, ","))) {
@@ -7449,41 +7589,73 @@ static void sla_add_trunk_to_station(struct sla_station *station, struct ast_var
}
}
- if (!(station_ref = sla_create_station_ref(station))) {
- ast_free(trunk_ref);
+ AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
+ if (station_ref->station == station) {
+ station_ref->mark = 0;
+ existing_station_ref = 1;
+ break;
+ }
+ }
+
+ if (!station_ref && !(station_ref = sla_create_station_ref(station))) {
+ if (!existing_trunk_ref) {
+ ao2_ref(trunk_ref, -1);
+ } else {
+ trunk_ref->mark = 1;
+ }
return;
}
- ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
- AST_RWLIST_WRLOCK(&sla_trunks);
- AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
- AST_RWLIST_UNLOCK(&sla_trunks);
- AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
+
+ if (!existing_station_ref) {
+ ao2_lock(trunk);
+ AST_LIST_INSERT_TAIL(&trunk->stations, station_ref, entry);
+ ast_atomic_fetchadd_int((int *) &trunk->num_stations, 1);
+ ao2_unlock(trunk);
+ }
+
+ if (!existing_trunk_ref) {
+ ao2_lock(station);
+ AST_LIST_INSERT_TAIL(&station->trunks, trunk_ref, entry);
+ ao2_unlock(station);
+ }
}
static int sla_build_station(struct ast_config *cfg, const char *cat)
{
- struct sla_station *station;
+ RAII_VAR(struct sla_station *, station, NULL, ao2_cleanup);
struct ast_variable *var;
const char *dev;
+ int existing_station = 0;
if (!(dev = ast_variable_retrieve(cfg, cat, "device"))) {
ast_log(LOG_ERROR, "SLA Station '%s' defined with no device!\n", cat);
return -1;
}
- if (!(station = ast_calloc_with_stringfields(1, struct sla_station, 32))) {
+ if ((station = sla_find_station(cat))) {
+ station->mark = 0;
+ existing_station = 1;
+ } else if ((station = ao2_alloc(sizeof(*station), sla_station_destructor))) {
+ if (ast_string_field_init(station, 32)) {
+ return -1;
+ }
+ ast_string_field_set(station, name, cat);
+ } else {
return -1;
}
- ast_string_field_set(station, name, cat);
+ ao2_lock(station);
+
ast_string_field_set(station, device, dev);
for (var = ast_variable_browse(cfg, cat); var; var = var->next) {
- if (!strcasecmp(var->name, "trunk"))
+ if (!strcasecmp(var->name, "trunk")) {
+ ao2_unlock(station);
sla_add_trunk_to_station(station, var);
- else if (!strcasecmp(var->name, "autocontext"))
+ ao2_lock(station);
+ } else if (!strcasecmp(var->name, "autocontext")) {
ast_string_field_set(station, autocontext, var->value);
- else if (!strcasecmp(var->name, "ringtimeout")) {
+ } else if (!strcasecmp(var->name, "ringtimeout")) {
if (sscanf(var->value, "%30u", &station->ring_timeout) != 1) {
ast_log(LOG_WARNING, "Invalid ringtimeout '%s' specified for station '%s'\n",
var->value, station->name);
@@ -7511,6 +7683,8 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
}
}
+ ao2_unlock(station);
+
if (!ast_strlen_zero(station->autocontext)) {
struct ast_context *context;
struct sla_trunk_ref *trunk_ref;
@@ -7518,7 +7692,6 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
if (!context) {
ast_log(LOG_ERROR, "Failed to automatically find or create "
"context '%s' for SLA!\n", station->autocontext);
- destroy_station(station);
return -1;
}
/* The extension for when the handset goes off-hook.
@@ -7527,10 +7700,8 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
NULL, NULL, slastation_app, ast_strdup(station->name), ast_free_ptr, sla_registrar)) {
ast_log(LOG_ERROR, "Failed to automatically create extension "
"for trunk '%s'!\n", station->name);
- destroy_station(station);
return -1;
}
- AST_RWLIST_RDLOCK(&sla_trunks);
AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
char exten[AST_MAX_EXTENSION];
char hint[AST_MAX_APP];
@@ -7542,7 +7713,6 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
NULL, NULL, slastation_app, ast_strdup(exten), ast_free_ptr, sla_registrar)) {
ast_log(LOG_ERROR, "Failed to automatically create extension "
"for trunk '%s'!\n", station->name);
- destroy_station(station);
return -1;
}
/* Hint for this line button
@@ -7551,20 +7721,115 @@ static int sla_build_station(struct ast_config *cfg, const char *cat)
NULL, NULL, hint, NULL, NULL, sla_registrar)) {
ast_log(LOG_ERROR, "Failed to automatically create hint "
"for trunk '%s'!\n", station->name);
- destroy_station(station);
return -1;
}
}
- AST_RWLIST_UNLOCK(&sla_trunks);
}
- AST_RWLIST_WRLOCK(&sla_stations);
- AST_RWLIST_INSERT_TAIL(&sla_stations, station, entry);
- AST_RWLIST_UNLOCK(&sla_stations);
+ if (!existing_station) {
+ ao2_link(sla_stations, station);
+ }
+
+ return 0;
+}
+
+static int sla_trunk_mark(void *obj, void *arg, int flags)
+{
+ struct sla_trunk *trunk = obj;
+ struct sla_station_ref *station_ref;
+
+ ao2_lock(trunk);
+
+ trunk->mark = 1;
+
+ AST_LIST_TRAVERSE(&trunk->stations, station_ref, entry) {
+ station_ref->mark = 1;
+ }
+
+ ao2_unlock(trunk);
+
+ return 0;
+}
+
+static int sla_station_mark(void *obj, void *arg, int flags)
+{
+ struct sla_station *station = obj;
+ struct sla_trunk_ref *trunk_ref;
+
+ ao2_lock(station);
+
+ station->mark = 1;
+
+ AST_LIST_TRAVERSE(&station->trunks, trunk_ref, entry) {
+ trunk_ref->mark = 1;
+ }
+
+ ao2_unlock(station);
return 0;
}
+static int sla_trunk_is_marked(void *obj, void *arg, int flags)
+{
+ struct sla_trunk *trunk = obj;
+
+ ao2_lock(trunk);
+
+ if (trunk->mark) {
+ /* Only remove all of the station references if the trunk itself is going away */
+ sla_trunk_release_refs(trunk, NULL, 0);
+ } else {
+ struct sla_station_ref *station_ref;
+
+ /* Otherwise only remove references to stations no longer in the config */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&trunk->stations, station_ref, entry) {
+ if (!station_ref->mark) {
+ continue;
+ }
+ AST_LIST_REMOVE_CURRENT(entry);
+ ao2_ref(station_ref, -1);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ }
+
+ ao2_unlock(trunk);
+
+ return trunk->mark ? CMP_MATCH : 0;
+}
+
+static int sla_station_is_marked(void *obj, void *arg, int flags)
+{
+ struct sla_station *station = obj;
+
+ ao2_lock(station);
+
+ if (station->mark) {
+ /* Only remove all of the trunk references if the station itself is going away */
+ sla_station_release_refs(station, NULL, 0);
+ } else {
+ struct sla_trunk_ref *trunk_ref;
+
+ /* Otherwise only remove references to trunks no longer in the config */
+ AST_LIST_TRAVERSE_SAFE_BEGIN(&station->trunks, trunk_ref, entry) {
+ if (!trunk_ref->mark) {
+ continue;
+ }
+ AST_LIST_REMOVE_CURRENT(entry);
+ ao2_ref(trunk_ref, -1);
+ }
+ AST_LIST_TRAVERSE_SAFE_END
+ }
+
+ ao2_unlock(station);
+
+ return station->mark ? CMP_MATCH : 0;
+}
+
+static int sla_in_use(void)
+{
+ return ao2_container_count(sla_trunks) || ao2_container_count(sla_stations);
+}
+
static int sla_load_config(int reload)
{
struct ast_config *cfg;
@@ -7576,6 +7841,8 @@ static int sla_load_config(int reload)
if (!reload) {
ast_mutex_init(&sla.lock);
ast_cond_init(&sla.cond, NULL);
+ sla_trunks = ao2_container_alloc(1, sla_trunk_hash, sla_trunk_cmp);
+ sla_stations = ao2_container_alloc(1, sla_station_hash, sla_station_cmp);
}
if (!(cfg = ast_config_load(SLA_CONFIG_FILE, config_flags))) {
@@ -7588,21 +7855,8 @@ static int sla_load_config(int reload)
}
if (reload) {
- struct sla_station *station;
- struct sla_trunk *trunk;
-
- /* We need to actually delete the previous versions of trunks and stations now */
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_stations, station, entry) {
- AST_RWLIST_REMOVE_CURRENT(entry);
- ast_free(station);
- }
- AST_RWLIST_TRAVERSE_SAFE_END;
-
- AST_RWLIST_TRAVERSE_SAFE_BEGIN(&sla_trunks, trunk, entry) {
- AST_RWLIST_REMOVE_CURRENT(entry);
- ast_free(trunk);
- }
- AST_RWLIST_TRAVERSE_SAFE_END;
+ ao2_callback(sla_trunks, 0, sla_trunk_mark, NULL);
+ ao2_callback(sla_stations, 0, sla_station_mark, NULL);
}
if ((val = ast_variable_retrieve(cfg, "general", "attemptcallerid")))
@@ -7629,9 +7883,13 @@ static int sla_load_config(int reload)
ast_config_destroy(cfg);
- /* Even if we don't have any stations, we may after a reload and we need to
- * be able to process the SLA_EVENT_RELOAD event in that case */
- if (sla.thread == AST_PTHREADT_NULL && (!AST_LIST_EMPTY(&sla_stations) || !AST_LIST_EMPTY(&sla_trunks))) {
+ if (reload) {
+ ao2_callback(sla_trunks, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_trunk_is_marked, NULL);
+ ao2_callback(sla_stations, OBJ_NODATA | OBJ_UNLINK | OBJ_MULTIPLE, sla_station_is_marked, NULL);
+ }
+
+ /* Start SLA event processing thread once SLA has been configured. */
+ if (sla.thread == AST_PTHREADT_NULL && sla_in_use()) {
ast_pthread_create(&sla.thread, NULL, sla_thread, NULL);
}
@@ -7716,15 +7974,7 @@ static struct ast_custom_function meetme_info_acf = {
static int load_config(int reload)
{
load_config_meetme();
-
- if (reload && sla.thread != AST_PTHREADT_NULL) {
- sla_queue_event(SLA_EVENT_RELOAD);
- ast_log(LOG_NOTICE, "A reload of the SLA configuration has been requested "
- "and will be completed when the system is idle.\n");
- return 0;
- }
-
- return sla_load_config(0);
+ return sla_load_config(reload);
}
#define MEETME_DATA_EXPORT(MEMBER) \