From 99ad18c4c51fa92bb7399997d5df4bae622cd750 Mon Sep 17 00:00:00 2001 From: Michiel van Baak Date: Fri, 27 Feb 2009 20:34:00 +0000 Subject: Add reload support to chan_skinny. Special thanks goes to DEA who had to redo this patch twice because we first put unload/load support in and later redid the way we configure devices and lines. (closes issue #10297) Reported by: DEA Patches: skinny-reload-trunkv2.diff uploaded by wedhorn (license 30) skinny-reload-trunk-v4.txt uploaded by DEA (license 3) With mods by me based on feedback from wedhorn and Russell and seanbright Tested by: DEA, mvanbaak, pj Review: http://reviewboard.digium.com/r/130/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@179122 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- channels/chan_skinny.c | 257 ++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 209 insertions(+), 48 deletions(-) (limited to 'channels') diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c index b765915bd..aba614d60 100644 --- a/channels/chan_skinny.c +++ b/channels/chan_skinny.c @@ -970,6 +970,7 @@ int skinny_header_size = 12; *****************************/ static int skinnydebug = 0; +static int skinnyreload = 0; /* a hostname, portnumber, socket and such is usefull for VoIP protocols */ static struct sockaddr_in bindaddr; @@ -1181,7 +1182,8 @@ struct skinny_subchannel { int immediate; \ int hookstate; \ int nat; \ - int canreinvite; + int canreinvite; \ + int prune; struct skinny_line { SKINNY_LINE_OPTIONS @@ -1212,6 +1214,7 @@ struct skinny_line_options{ .capability = 0, .getforward = 0, .needdestroy = 0, + .prune = 0, .hookstate = SKINNY_ONHOOK, }; struct skinny_line_options *default_line = &default_line_struct; @@ -1256,7 +1259,8 @@ struct skinny_addon { int transfer; \ int callwaiting; \ int mwiblink; \ - int dnd; + int dnd; \ + int prune; struct skinny_device { SKINNY_DEVICE_OPTIONS @@ -1284,6 +1288,7 @@ struct skinny_device_options{ .dnd = 0, .confcapability = AST_FORMAT_ULAW | AST_FORMAT_ALAW, .capability = 0, + .prune = 0, }; struct skinny_device_options *default_device = &default_device_struct; @@ -1324,6 +1329,7 @@ static int skinny_senddigit_begin(struct ast_channel *ast, char digit); static int skinny_senddigit_end(struct ast_channel *ast, char digit, unsigned int duration); static int handle_time_date_req_message(struct skinny_req *req, struct skinnysession *s); static void mwi_event_cb(const struct ast_event *event, void *userdata); +static int skinny_reload(void); static const struct ast_channel_tech skinny_tech = { .type = "Skinny", @@ -2718,6 +2724,27 @@ static char *handle_skinny_set_debug(struct ast_cli_entry *e, int cmd, struct as } } +static char *handle_skinny_reload(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) +{ + switch (cmd) { + case CLI_INIT: + e->command = "skinny reload"; + e->usage = + "Usage: skinny reload\n" + " Reloads the chan_skinny configuration\n"; + return NULL; + case CLI_GENERATE: + return NULL; + } + + if (a->argc != e->args) + return CLI_SHOWUSAGE; + + skinny_reload(); + return CLI_SUCCESS; + +} + static char *complete_skinny_devices(const char *word, int state) { struct skinny_device *d; @@ -3521,6 +3548,7 @@ static struct ast_cli_entry cli_skinny[] = { AST_CLI_DEFINE(handle_skinny_show_settings, "List global Skinny settings"), AST_CLI_DEFINE(handle_skinny_set_debug, "Enable/Disable Skinny debugging"), AST_CLI_DEFINE(handle_skinny_reset, "Reset Skinny device(s)"), + AST_CLI_DEFINE(handle_skinny_reload, "Reload Skinny config"), }; static void start_rtp(struct skinny_subchannel *sub) @@ -6786,7 +6814,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da if (type & (TYPE_DEVICE)) { struct skinny_line *l; AST_LIST_TRAVERSE(&lines, l, all) { - if (!strcasecmp(v->value, l->name)) { + if (!strcasecmp(v->value, l->name) && !l->prune) { /* FIXME: temp solution about line conflicts */ struct skinny_device *d; @@ -6794,7 +6822,7 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da int lineinuse = 0; AST_LIST_TRAVERSE(&devices, d, list) { AST_LIST_TRAVERSE(&d->lines, l2, list) { - if (l2 == l) { + if (l2 == l && strcasecmp(d->id, CDEV->id)) { ast_log(LOG_WARNING, "Line %s already used by %s. Not connecting to %s.\n", l->name, d->name, CDEV->name); lineinuse++; } @@ -6868,32 +6896,35 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da static struct skinny_line *config_line(const char *lname, struct ast_variable *v) { - struct skinny_line *l; + struct skinny_line *l, *temp; + int update = 0; ast_log(LOG_NOTICE, "Configuring skinny line %s.\n", lname); - + + /* We find the old line and remove it just before the new + line is created */ AST_LIST_LOCK(&lines); - AST_LIST_TRAVERSE(&lines, l, all) { - if (!strcasecmp(lname, l->name)) { - ast_log(LOG_NOTICE, "Line %s already exists. Reconfiguring.\n", lname); + AST_LIST_TRAVERSE(&lines, temp, all) { + if (!strcasecmp(lname, temp->name) && temp->prune) { + update = 1; break; } } - if (!l) { - ast_log(LOG_NOTICE, "Creating line %s.\n", lname); - if (!(l=ast_calloc(1, sizeof(*l)))) { - ast_verb(1, "Unable to allocate memory for line %s.\n", lname); - AST_LIST_UNLOCK(&lines); - return NULL; - } - memcpy(l, default_line, sizeof(*default_line)); - ast_mutex_init(&l->lock); - ast_copy_string(l->name, lname, sizeof(l->name)); - AST_LIST_INSERT_TAIL(&lines, l, all); + + if (!(l=ast_calloc(1, sizeof(*l)))) { + ast_verb(1, "Unable to allocate memory for line %s.\n", lname); + AST_LIST_UNLOCK(&lines); + return NULL; } + + memcpy(l, default_line, sizeof(*default_line)); + ast_mutex_init(&l->lock); + ast_copy_string(l->name, lname, sizeof(l->name)); + AST_LIST_INSERT_TAIL(&lines, l, all); + ast_mutex_lock(&l->lock); AST_LIST_UNLOCK(&lines); - + config_parse_variables(TYPE_LINE, l, v); if (!ast_strlen_zero(l->mailbox)) { @@ -6911,32 +6942,43 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da } ast_mutex_unlock(&l->lock); + + /* We do not want to unlink or free the line yet, it needs + to be available to detect a device reconfig when we load the + devices. Old lines will be pruned after the reload completes */ + + ast_verb(3, "%s config for line '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), l->name); + return l; } static struct skinny_device *config_device(const char *dname, struct ast_variable *v) { - struct skinny_device *d; + struct skinny_device *d, *temp; + struct skinny_line *l, *ltemp; + struct skinny_subchannel *sub; + int update = 0; ast_log(LOG_NOTICE, "Configuring skinny device %s.\n", dname); - + AST_LIST_LOCK(&devices); - AST_LIST_TRAVERSE(&devices, d, list) { - if (!strcasecmp(dname, d->name)) { + AST_LIST_TRAVERSE(&devices, temp, list) { + if (!strcasecmp(dname, temp->name) && temp->prune) { + update = 1; break; } } - if (!d) { - if (!(d = ast_calloc(1, sizeof(*d)))) { - ast_verb(1, "Unable to allocate memory for device %s.\n", dname); - AST_LIST_UNLOCK(&devices); - return NULL; - } - memcpy(d, default_device, sizeof(*default_device)); - ast_mutex_init(&d->lock); - ast_copy_string(d->name, dname, sizeof(d->name)); - AST_LIST_INSERT_HEAD(&devices, d, list); + + if (!(d = ast_calloc(1, sizeof(*d)))) { + ast_verb(1, "Unable to allocate memory for device %s.\n", dname); + AST_LIST_UNLOCK(&devices); + return NULL; } + memcpy(d, default_device, sizeof(*default_device)); + ast_mutex_init(&d->lock); + ast_copy_string(d->name, dname, sizeof(d->name)); + AST_LIST_INSERT_TAIL(&devices, d, list); + ast_mutex_lock(&d->lock); AST_LIST_UNLOCK(&devices); @@ -6951,8 +6993,52 @@ static struct ast_channel *skinny_request(const char *type, int format, void *da d->addr.sin_port = htons(DEFAULT_SKINNY_PORT); } + if (skinnyreload){ + AST_LIST_LOCK(&devices); + AST_LIST_TRAVERSE(&devices, temp, list) { + if (strcasecmp(d->id, temp->id) || !temp->prune || !temp->session) { + continue; + } + ast_mutex_lock(&d->lock); + d->session = temp->session; + d->session->device = d; + + AST_LIST_LOCK(&d->lines); + AST_LIST_TRAVERSE(&d->lines, l, list){ + l->device = d; + + AST_LIST_LOCK(&temp->lines); + AST_LIST_TRAVERSE(&temp->lines, ltemp, list) { + if (strcasecmp(l->name, ltemp->name)) { + continue; + } + ast_mutex_lock(<emp->lock); + l->instance = ltemp->instance; + l->hookstate = ltemp->hookstate; + if (!AST_LIST_EMPTY(<emp->sub)) { + ast_mutex_lock(&l->lock); + l->sub = ltemp->sub; + AST_LIST_TRAVERSE(&l->sub, sub, list) { + sub->parent = l; + } + ast_mutex_unlock(&l->lock); + } + ast_mutex_unlock(<emp->lock); + } + AST_LIST_UNLOCK(&temp->lines); + } + AST_LIST_UNLOCK(&d->lines); + ast_mutex_unlock(&d->lock); + } + AST_LIST_UNLOCK(&devices); + } + ast_mutex_unlock(&d->lock); - return d; + + ast_verb(3, "%s config for device '%s'\n", update ? "Updated" : (skinnyreload ? "Reloaded" : "Created"), d->name); + + return d; + } static int config_load(void) @@ -7102,19 +7188,87 @@ static void delete_devices(void) AST_LIST_UNLOCK(&devices); } -#if 0 -/* - * XXX This never worked properly anyways. - * Let's get rid of it, until we can fix it. - */ -static int reload(void) +int skinny_reload(void) { - delete_devices(); - config_load(); - restart_monitor(); - return 0; + struct skinny_device *d; + struct skinny_line *l; + struct skinny_speeddial *sd; + struct skinny_addon *a; + struct skinny_req *req; + + if (skinnyreload) { + ast_verb(3, "Chan_skinny is already reloading.\n"); + return 0; + } + + skinnyreload = 1; + + /* Mark all devices and lines as candidates to be pruned */ + AST_LIST_LOCK(&devices); + AST_LIST_TRAVERSE(&devices, d, list) { + d->prune = 1; + } + AST_LIST_UNLOCK(&devices); + + AST_LIST_LOCK(&lines); + AST_LIST_TRAVERSE(&lines, l, all) { + l->prune = 1; + } + AST_LIST_UNLOCK(&lines); + + config_load(); + + /* Remove any devices that no longer exist in the config */ + AST_LIST_LOCK(&devices); + AST_LIST_TRAVERSE_SAFE_BEGIN(&devices, d, list) { + if (!d->prune) { + continue; + } + ast_verb(3, "Removing device '%s'\n", d->name); + /* Delete all lines for this device. + We do not want to free the line here, that + will happen below. */ + while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) { + } + /* Delete all speeddials for this device */ + while ((sd = AST_LIST_REMOVE_HEAD(&d->speeddials, list))) { + free(sd); + } + /* Delete all addons for this device */ + while ((a = AST_LIST_REMOVE_HEAD(&d->addons, list))) { + free(a); + } + AST_LIST_REMOVE_CURRENT(list); + free(d); + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&devices); + + AST_LIST_LOCK(&lines); + AST_LIST_TRAVERSE_SAFE_BEGIN(&lines, l, all) { + if (l->prune) { + AST_LIST_REMOVE_CURRENT(all); + free(l); + } + } + AST_LIST_TRAVERSE_SAFE_END; + AST_LIST_UNLOCK(&lines); + + AST_LIST_TRAVERSE(&devices, d, list) { + /* Do a soft reset to re-register the devices after + cleaning up the removed devices and lines */ + if (d->session) { + ast_verb(3, "Restarting device '%s'\n", d->name); + if ((req = req_alloc(sizeof(struct reset_message), RESET_MESSAGE))) { + req->data.reset.resetType = 2; + transmit_response(d, req); + } + } + } + + skinnyreload = 0; + return 0; } -#endif static int load_module(void) { @@ -7158,7 +7312,7 @@ static int load_module(void) /* And start the monitor for the first time */ restart_monitor(); - return res; + return AST_MODULE_LOAD_SUCCESS; } static int unload_module(void) @@ -7237,7 +7391,14 @@ static int unload_module(void) return 0; } +static int reload(void) +{ + skinny_reload(); + return 0; +} + AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Skinny Client Control Protocol (Skinny)", .load = load_module, .unload = unload_module, + .reload = reload, ); -- cgit v1.2.3