diff --git a/include/asterisk/indications.h b/include/asterisk/indications.h index a6c1d7c9d6e44d289e95c38186397acbae1a01cd..72561819cb33e12b423b07cf212b5bdee37debb4 100644 --- a/include/asterisk/indications.h +++ b/include/asterisk/indications.h @@ -46,7 +46,7 @@ struct tone_zone_sound { }; struct tone_zone { - struct tone_zone* next; /* next in list */ + AST_RWLIST_ENTRY(tone_zone) list; char country[5]; /* Country code */ char alias[5]; /* is this an alias? */ char description[40]; /* Description */ diff --git a/main/indications.c b/main/indications.c index 359a5900f8b9e89792f2c21b48d83b0afd8266ef..f51b4abd137a4d44b2fc4bd9c56a44dbd314818c 100644 --- a/main/indications.c +++ b/main/indications.c @@ -37,12 +37,13 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include <string.h> #include <math.h> +#include "asterisk/lock.h" +#include "asterisk/linkedlists.h" #include "asterisk/indications.h" #include "asterisk/frame.h" #include "asterisk/options.h" #include "asterisk/channel.h" #include "asterisk/logger.h" -#include "asterisk/lock.h" #include "asterisk/utils.h" static int midi_tohz[128] = { @@ -103,20 +104,25 @@ struct playtones_state { static void playtones_release(struct ast_channel *chan, void *params) { struct playtones_state *ps = params; - if (chan) { + + if (chan) ast_set_write_format(chan, ps->origwfmt); - } - if (ps->items) free(ps->items); + if (ps->items) + free(ps->items); + free(ps); } static void * playtones_alloc(struct ast_channel *chan, void *params) { struct playtones_def *pd = params; - struct playtones_state *ps; + struct playtones_state *ps = NULL; + if (!(ps = ast_calloc(1, sizeof(*ps)))) return NULL; + ps->origwfmt = chan->writeformat; + if (ast_set_write_format(chan, AST_FORMAT_SLINEAR)) { ast_log(LOG_WARNING, "Unable to set '%s' to signed linear format (write)\n", chan->name); playtones_release(NULL, ps); @@ -128,11 +134,13 @@ static void * playtones_alloc(struct ast_channel *chan, void *params) ps->items = pd->items; ps->oldnpos = -1; } + /* Let interrupts interrupt :) */ if (pd->interruptible) ast_set_flag(chan, AST_FLAG_WRITE_INT); else ast_clear_flag(chan, AST_FLAG_WRITE_INT); + return ps; } @@ -329,103 +337,113 @@ void ast_playtones_stop(struct ast_channel *chan) /*--------------------------------------------*/ -static struct tone_zone *tone_zones; +static AST_RWLIST_HEAD_STATIC(tone_zones, tone_zone); static struct tone_zone *current_tonezone; -/* Protect the tone_zones list (highly unlikely that two things would change - * it at the same time, but still! */ -AST_MUTEX_DEFINE_STATIC(tzlock); - struct tone_zone *ast_walk_indications(const struct tone_zone *cur) { - struct tone_zone *tz; + struct tone_zone *tz = NULL; + + AST_RWLIST_RDLOCK(&tone_zones); + /* If cur is not NULL, then we have to iterate through - otherwise just return the first entry */ + if (cur) { + AST_RWLIST_TRAVERSE(&tone_zones, tz, list) { + if (tz == cur) + break; + } + tz = AST_RWLIST_NEXT(tz, list); + } else { + tz = AST_RWLIST_FIRST(&tone_zones); + } + AST_RWLIST_UNLOCK(&tone_zones); - if (cur == NULL) - return tone_zones; - ast_mutex_lock(&tzlock); - for (tz = tone_zones; tz; tz = tz->next) - if (tz == cur) - break; - if (tz) - tz = tz->next; - ast_mutex_unlock(&tzlock); return tz; } /* Set global indication country */ int ast_set_indication_country(const char *country) { - if (country) { - struct tone_zone *z = ast_get_indication_zone(country); - if (z) { - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Setting default indication country to '%s'\n",country); - current_tonezone = z; - return 0; - } - } - return 1; /* not found */ + struct tone_zone *zone = NULL; + + /* If no country is specified or we are unable to find the zone, then return not found */ + if (!country || !(zone = ast_get_indication_zone(country))) + return 1; + + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Setting default indication country to '%s'\n", country); + + /* Protect the current tonezone using the tone_zones lock as well */ + AST_RWLIST_WRLOCK(&tone_zones); + current_tonezone = zone; + AST_RWLIST_UNLOCK(&tone_zones); + + /* Zone was found */ + return 0; } /* locate tone_zone, given the country. if country == NULL, use the default country */ struct tone_zone *ast_get_indication_zone(const char *country) { - struct tone_zone *tz; + struct tone_zone *tz = NULL; int alias_loop = 0; - /* we need some tonezone, pick the first */ - if (country == NULL && current_tonezone) - return current_tonezone; /* default country? */ - if (country == NULL && tone_zones) - return tone_zones; /* any country? */ - if (country == NULL) - return 0; /* not a single country insight */ - - ast_mutex_lock(&tzlock); - do { - for (tz=tone_zones; tz; tz=tz->next) { - if (strcasecmp(country,tz->country)==0) { - /* tone_zone found */ - if (tz->alias && tz->alias[0]) { - country = tz->alias; + AST_RWLIST_RDLOCK(&tone_zones); + + if (!country) { + if (current_tonezone) + tz = current_tonezone; + else + tz = AST_LIST_FIRST(&tone_zones); + } else { + do { + AST_RWLIST_TRAVERSE(&tone_zones, tz, list) { + if (!strcasecmp(tz->country, country)) break; - } - ast_mutex_unlock(&tzlock); - return tz; } - } - } while (++alias_loop<20 && tz); - ast_mutex_unlock(&tzlock); - if (alias_loop==20) - ast_log(LOG_NOTICE,"Alias loop for '%s' forcefull broken\n",country); - /* nothing found, sorry */ - return 0; + /* If this is an alias then we have to search yet again otherwise we have found the zonezone */ + if (tz->alias && tz->alias[0]) + country = tz->alias; + else + break; + } while ((++alias_loop < 20) && tz); + } + + AST_RWLIST_UNLOCK(&tone_zones); + + /* If we reached the maximum loops to find the proper country via alias, print out a notice */ + if (alias_loop == 20) + ast_log(LOG_NOTICE, "Alias loop for '%s' is bonkers\n", country); + + return tz; } /* locate a tone_zone_sound, given the tone_zone. if tone_zone == NULL, use the default tone_zone */ struct tone_zone_sound *ast_get_indication_tone(const struct tone_zone *zone, const char *indication) { - struct tone_zone_sound *ts; - - /* we need some tonezone, pick the first */ - if (zone == NULL && current_tonezone) - zone = current_tonezone; /* default country? */ - if (zone == NULL && tone_zones) - zone = tone_zones; /* any country? */ - if (zone == NULL) - return 0; /* not a single country insight */ - - ast_mutex_lock(&tzlock); - for (ts=zone->tones; ts; ts=ts->next) { - if (strcasecmp(indication,ts->name)==0) { - /* found indication! */ - ast_mutex_unlock(&tzlock); - return ts; + struct tone_zone_sound *ts = NULL; + + AST_RWLIST_RDLOCK(&tone_zones); + + /* If no zone is already specified we need to try to pick one */ + if (!zone) { + if (current_tonezone) { + zone = current_tonezone; + } else if (!(zone = AST_LIST_FIRST(&tone_zones))) { + /* No zone has been found ;( */ + AST_RWLIST_UNLOCK(&tone_zones); + return NULL; } } - /* nothing found, sorry */ - ast_mutex_unlock(&tzlock); - return 0; + + /* Look through list of tones in the zone searching for the right one */ + for (ts = zone->tones; ts; ts = ts->next) { + if (!strcasecmp(ts->name, indication)) + break; + } + + AST_RWLIST_UNLOCK(&tone_zones); + + return ts; } /* helper function to delete a tone_zone in its entirety */ @@ -438,8 +456,10 @@ static inline void free_zone(struct tone_zone* zone) free(zone->tones); zone->tones = tmp; } + if (zone->ringcadence) free(zone->ringcadence); + free(zone); } @@ -448,36 +468,33 @@ static inline void free_zone(struct tone_zone* zone) /* add a new country, if country exists, it will be replaced. */ int ast_register_indication_country(struct tone_zone *zone) { - struct tone_zone *tz,*pz; - - ast_mutex_lock(&tzlock); - for (pz=NULL,tz=tone_zones; tz; pz=tz,tz=tz->next) { - if (strcasecmp(zone->country,tz->country)==0) { - /* tone_zone already there, replace */ - zone->next = tz->next; - if (pz) - pz->next = zone; - else - tone_zones = zone; - /* if we are replacing the default zone, re-point it */ - if (tz == current_tonezone) - current_tonezone = zone; - /* now free the previous zone */ - free_zone(tz); - ast_mutex_unlock(&tzlock); - return 0; - } + struct tone_zone *tz = NULL; + + AST_RWLIST_WRLOCK(&tone_zones); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) { + /* If this is not the same zone, then just continue to the next entry */ + if (strcasecmp(zone->country, tz->country)) + continue; + /* If this zone we are going to remove is the current default then make the new zone the default */ + if (tz == current_tonezone) + current_tonezone = zone; + /* Remove from the linked list */ + AST_RWLIST_REMOVE_CURRENT(&tone_zones, list); + /* Finally free the zone itself */ + free_zone(tz); + break; } - /* country not there, add */ - zone->next = NULL; - if (pz) - pz->next = zone; - else - tone_zones = zone; - ast_mutex_unlock(&tzlock); + AST_RWLIST_TRAVERSE_SAFE_END + + /* Add zone to the list */ + AST_RWLIST_INSERT_TAIL(&tone_zones, zone, list); + + /* It's all over. */ + AST_RWLIST_UNLOCK(&tone_zones); if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Registered indication country '%s'\n",zone->country); + ast_verbose(VERBOSE_PREFIX_3 "Registered indication country '%s'\n", zone->country); + return 0; } @@ -485,41 +502,28 @@ int ast_register_indication_country(struct tone_zone *zone) * Also, all countries which are an alias for the specified country are removed. */ int ast_unregister_indication_country(const char *country) { - struct tone_zone *tz, *pz = NULL, *tmp; + struct tone_zone *tz = NULL; int res = -1; - ast_mutex_lock(&tzlock); - tz = tone_zones; - while (tz) { - if (country==NULL || - (strcasecmp(country, tz->country)==0 || - strcasecmp(country, tz->alias)==0)) { - /* tone_zone found, remove */ - tmp = tz->next; - if (pz) - pz->next = tmp; - else - tone_zones = tmp; - /* if we are unregistering the default country, w'll notice */ - if (tz == current_tonezone) { - ast_log(LOG_NOTICE,"Removed default indication country '%s'\n",tz->country); - current_tonezone = NULL; - } - if (option_verbose > 2) - ast_verbose(VERBOSE_PREFIX_3 "Unregistered indication country '%s'\n",tz->country); - free_zone(tz); - if (tone_zones == tz) - tone_zones = tmp; - tz = tmp; - res = 0; - } - else { - /* next zone please */ - pz = tz; - tz = tz->next; + AST_RWLIST_WRLOCK(&tone_zones); + AST_RWLIST_TRAVERSE_SAFE_BEGIN(&tone_zones, tz, list) { + if (country && (strcasecmp(country, tz->country) && strcasecmp(country, tz->alias))) + continue; + /* If this tonezone is the current default then unset it */ + if (tz == current_tonezone) { + ast_log(LOG_NOTICE,"Removed default indication country '%s'\n", tz->country); + current_tonezone = NULL; } + /* Remove from the list */ + AST_RWLIST_REMOVE_CURRENT(&tone_zones, list); + if (option_verbose > 2) + ast_verbose(VERBOSE_PREFIX_3 "Unregistered indication country '%s'\n", tz->country); + free_zone(tz); + res = 0; } - ast_mutex_unlock(&tzlock); + AST_RWLIST_TRAVERSE_SAFE_END + AST_RWLIST_UNLOCK(&tone_zones); + return res; } @@ -527,13 +531,13 @@ int ast_unregister_indication_country(const char *country) * exists, it will be replaced. */ int ast_register_indication(struct tone_zone *zone, const char *indication, const char *tonelist) { - struct tone_zone_sound *ts,*ps; + struct tone_zone_sound *ts, *ps; /* is it an alias? stop */ if (zone->alias[0]) return -1; - ast_mutex_lock(&tzlock); + AST_RWLIST_WRLOCK(&tone_zones); for (ps=NULL,ts=zone->tones; ts; ps=ts,ts=ts->next) { if (strcasecmp(indication,ts->name)==0) { /* indication already there, replace */ @@ -545,20 +549,20 @@ int ast_register_indication(struct tone_zone *zone, const char *indication, cons if (!ts) { /* not there, we have to add */ if (!(ts = ast_malloc(sizeof(*ts)))) { - ast_mutex_unlock(&tzlock); + AST_RWLIST_UNLOCK(&tone_zones); return -2; } ts->next = NULL; } if (!(ts->name = ast_strdup(indication)) || !(ts->data = ast_strdup(tonelist))) { - ast_mutex_unlock(&tzlock); + AST_RWLIST_UNLOCK(&tone_zones); return -2; } if (ps) ps->next = ts; else zone->tones = ts; - ast_mutex_unlock(&tzlock); + AST_RWLIST_UNLOCK(&tone_zones); return 0; } @@ -572,7 +576,7 @@ int ast_unregister_indication(struct tone_zone *zone, const char *indication) if (zone->alias[0]) return -1; - ast_mutex_lock(&tzlock); + AST_RWLIST_WRLOCK(&tone_zones); ts = zone->tones; while (ts) { if (strcasecmp(indication,ts->name)==0) { @@ -595,6 +599,6 @@ int ast_unregister_indication(struct tone_zone *zone, const char *indication) } } /* indication not found, goodbye */ - ast_mutex_unlock(&tzlock); + AST_RWLIST_UNLOCK(&tone_zones); return res; }