diff options
-rw-r--r-- | apps/app_meetme.c | 796 | ||||
-rw-r--r-- | configs/sla.conf.sample | 11 |
2 files changed, 534 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) \ diff --git a/configs/sla.conf.sample b/configs/sla.conf.sample index 502700979..b5e1ef614 100644 --- a/configs/sla.conf.sample +++ b/configs/sla.conf.sample @@ -15,6 +15,17 @@ ; ------------------------------------- +; ******************************** +; **** Configuration Ordering **** +; ******************************** + +; Note that SLA configuration processing assumes that *all* trunk declarations are +; listed in the configuration file before any stations. + +; ******************************** +; ******************************** + + ; ---- Trunk Declarations ------------- ; ;[line1] ; Provide a name for this trunk. |