Index: channels/chan_skinny.c
===================================================================
--- channels/chan_skinny.c	(revision 158753)
+++ channels/chan_skinny.c	(working copy)
@@ -957,6 +957,7 @@
  *****************************/
 
 static int skinnydebug = 0;
+static int skinnyreload = 0;
 
 /* a hostname, portnumber, socket and such is usefull for VoIP protocols */
 static struct sockaddr_in bindaddr;
@@ -1165,7 +1166,8 @@
 	int immediate;					\
 	int hookstate;					\
 	int nat;					\
-	int canreinvite;
+	int canreinvite;				\
+	int prune;
 
 struct skinny_line {
 	SKINNY_LINE_OPTIONS
@@ -1195,6 +1197,7 @@
  	.capability = 0,
 	.getforward = 0,
  	.needdestroy = 0,
+	.prune = 0,
 	.hookstate = SKINNY_ONHOOK,
 };
 struct skinny_line_options *default_line = &default_line_struct;
@@ -1237,7 +1240,8 @@
 	int transfer;						\
 	int callwaiting;					\
 	int mwiblink;						\
-	int dnd;
+	int dnd;						\
+	int prune;
 
 struct skinny_device {
 	SKINNY_DEVICE_OPTIONS
@@ -1265,6 +1269,7 @@
  	.mwiblink = 0,
  	.dnd = 0,
  	.capability = AST_FORMAT_ULAW | AST_FORMAT_ALAW,
+	.prune = 0,
 };
 struct skinny_device_options *default_device = &default_device_struct;
 	
@@ -1304,6 +1309,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 int skinny_reload(void);
 
 static const struct ast_channel_tech skinny_tech = {
 	.type = "Skinny",
@@ -2569,6 +2575,27 @@
 	}
 }
 
+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;
@@ -3046,6 +3073,7 @@
 	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)
@@ -6298,7 +6326,7 @@
  			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;
@@ -6306,7 +6334,7 @@
 						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++;
 								}
@@ -6380,14 +6408,15 @@
  
  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);
- 	
+
  	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);
+ 		if (!strcasecmp(lname, l->name) && !l->prune) {
+			update = 1;
  			break;
  		}
  	}
@@ -6405,7 +6434,7 @@
  	}
  	ast_mutex_lock(&l->lock);
  	AST_LIST_UNLOCK(&lines);
- 
+
  	config_parse_variables(TYPE_LINE, l, v);
  			
  	if (!ast_strlen_zero(l->mailbox)) {
@@ -6422,19 +6451,39 @@
  			AST_EVENT_IE_END);
  	}
  
+	if (skinnyreload){
+		AST_LIST_TRAVERSE(&lines, temp, list) {
+			if (!strcasecmp(l->name, temp->name) && l->prune) {
+				AST_LIST_REMOVE(&lines, temp, list);
+			}
+		}
+	}
+
+	if (update) {
+		ast_verb(3, "Updated config for line '%s'\n", l->name);
+	} else {
+		if (skinnyreload){
+			ast_verb(3, "Reloaded config for line '%s'\n", l->name);
+		}else {
+			ast_verb(3, "Created config for line '%s'\n", l->name);
+		}
+	}
+
  	ast_mutex_unlock(&l->lock);
  	return l;
  }
  
  static struct skinny_device *config_device(const char *dname, struct ast_variable *v)
  {
- 	struct skinny_device *d;
+ 	struct skinny_device *d, *temp;
+	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)) {
+ 		if (!strcasecmp(dname, d->name) && !d->prune) {
+			update = 1;
  			break;
  		}
  	}
@@ -6450,6 +6499,7 @@
  		AST_LIST_INSERT_HEAD(&devices, d, list);
  	}
  	ast_mutex_lock(&d->lock);
+	
  	AST_LIST_UNLOCK(&devices);
  
  	config_parse_variables(TYPE_DEVICE, d, v);
@@ -6463,8 +6513,31 @@
  		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) {
+				d->session = temp->session;
+				AST_LIST_REMOVE(&devices, temp, list);
+			}
+		}
+		AST_LIST_UNLOCK(&devices);
+	}
+
  	ast_mutex_unlock(&d->lock);
- 	return d;
+
+	if (update) {
+		ast_verb(3, "Updated config for device '%s'\n", d->name);
+	} else {
+		if (skinnyreload){
+			ast_verb(3, "Reloaded config for device '%s'\n", d->name);
+		}else {
+			ast_verb(3, "Created config for device '%s'\n", d->name);
+		}
+	}
+	
+	return d;
+
  }
  
  static int config_load(void)
@@ -6610,19 +6683,60 @@
 	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;
+	skinnyreload = 1;
+
+	/* Mark all devices and lines as candidates to be pruned */
+	AST_LIST_TRAVERSE(&devices, d, list) {
+		d->prune = 1;
+	}
+	AST_LIST_TRAVERSE(&lines, l, list) {
+		l->prune = 1;
+	}
+
+        config_load();
+
+	/* Remove any devices that no longer exist in the config */
+	AST_LIST_TRAVERSE(&devices, d, list) {
+		if (d->prune) {
+			ast_verb(3, "Removing device '%s'\n", d->name);
+			/* Delete all lines for this device */
+			while ((l = AST_LIST_REMOVE_HEAD(&d->lines, list))) {
+				free(l);
+			}
+			/* 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(&devices, d, list);
+			free(d);
+		} else {
+			/* Do a soft reset to re-register the device */
+			/* Soft reset is probably fine for single sub sort of calls, but with calls on hold
+			   etc it may hangup background calls. Could be better to handle during the handle_onhook.
+			   Also, the soft reset seems to leave asterisk out of the onhook loop and we don't send
+			   the hangup stuff to the other end. */
+			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);
+				}
+			}
+		}
+	}
+        return 0;
 }
-#endif
 
 static int load_module(void)
 {
@@ -6656,7 +6770,7 @@
 	/* And start the monitor for the first time */
 	restart_monitor();
 
-	return res;
+	return AST_MODULE_LOAD_SUCCESS;
 }
 
 static int unload_module(void)
@@ -6730,7 +6844,14 @@
 	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,
 );
