summaryrefslogtreecommitdiff
path: root/apps/app_meetme.c
diff options
context:
space:
mode:
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) \