From: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
Date: Thu, 8 Aug 2013 22:09:07 +0000
Subject: [PATCH] chan_dahdi: create channels at run-time
Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=396474

This code adds chan_dahdi the command 'dahdi create channels <range>'
(where <range> is a single <n>-<m> or 'new') and updates 'dahdi destroy
channel' with a similar 'dahdi destroy channels'. It allows DAHDI
channels and spans to be added after the initial channel load
(without destroying all other channels as in 'dahdi restart').

It also includes some fixes to the D-Channel / span destruction code
(r394552).

This change is intended to provide a hook for a script running from
udev once a span has been assigned ("registered") / unassigned
("unregistered") for its channels. The udev hook configures the span's
channels with dahdi_cfg -S, and can then ask Asterisk to create ethe
channels. See the scripts added to DAHDI-tools in 2.7.0.

Review: https://reviewboard.asterisk.org/r/1598/

A trivial backport from trunk / 12.

---
 channels/chan_dahdi.c |  322 +++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 261 insertions(+), 61 deletions(-)

--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -404,6 +404,8 @@ static const char config[] = "chan_dahdi
 static int num_cadence = 4;
 static int user_has_defined_cadences = 0;
 
+static int has_pseudo;
+
 static struct dahdi_ring_cadence cadences[NUM_CADENCE_MAX] = {
 	{ { 125, 125, 2000, 4000 } },			/*!< Quick chirp followed by normal ring */
 	{ { 250, 250, 500, 1000, 250, 250, 500, 4000 } }, /*!< British style ring */
@@ -1391,6 +1393,18 @@ struct dahdi_chan_conf {
 	 * \note Set from the "smdiport" string read in from chan_dahdi.conf
 	 */
 	char smdi_port[SMDI_MAX_FILENAME_LEN];
+
+	/*!
+	 * \brief Don't create channels below this number
+	 * \note by default is 0 (no limit)
+	 */
+	int wanted_channels_start;
+
+	/*!
+	 * \brief Don't create channels above this number (infinity by default)
+	 * \note by default is 0 (special value that means "no limit").
+	 */
+	int wanted_channels_end;
 };
 
 /*! returns a new dahdi_chan_conf with default values (by-value) */
@@ -3217,7 +3231,7 @@ static int sig_pri_tone_to_dahditone(enu
 #endif	/* defined(HAVE_PRI) */
 
 #if defined(HAVE_PRI)
-static int pri_destroy_dchan(struct sig_pri_span *pri);
+static void pri_destroy_span(struct sig_pri_span *pri);
 
 static void my_handle_dchan_exception(struct sig_pri_span *pri, int index)
 {
@@ -3247,7 +3261,7 @@ static void my_handle_dchan_exception(st
 		pri_event_noalarm(pri, index, 0);
 		break;
 	case DAHDI_EVENT_REMOVED:
-		pri_destroy_dchan(pri);
+		pri_destroy_span(pri);
 		break;
 	default:
 		break;
@@ -11670,29 +11684,121 @@ static int mwi_send_process_event(struct
 	return handled;
 }
 
-/* destroy a DAHDI channel, identified by its number */
-static int dahdi_destroy_channel_bynum(int channel)
+/* destroy a range DAHDI channels, identified by their number */
+static void dahdi_destroy_channel_range(int start, int end)
 {
 	struct dahdi_pvt *cur;
+	struct dahdi_pvt *next;
+	int destroyed_first = 0;
+	int destroyed_last = 0;
 
 	ast_mutex_lock(&iflock);
-	for (cur = iflist; cur; cur = cur->next) {
-		if (cur->channel == channel) {
+	ast_debug(1, "range: %d-%d\n", start, end);
+	for (cur = iflist; cur; cur = next) {
+		next = cur->next;
+		if (cur->channel >= start && cur->channel <= end) {
 			int x = DAHDI_FLASH;
 
+			if (cur->channel > destroyed_last) {
+				destroyed_last = cur->channel;
+			}
+			if (destroyed_first < 1 || cur->channel < destroyed_first) {
+				destroyed_first = cur->channel;
+			}
+			ast_debug(3, "Destroying %d\n", cur->channel);
 			/* important to create an event for dahdi_wait_event to register so that all analog_ss_threads terminate */
 			ioctl(cur->subs[SUB_REAL].dfd, DAHDI_HOOK, &x);
 
 			destroy_channel(cur, 1);
-			ast_mutex_unlock(&iflock);
 			ast_module_unref(ast_module_info->self);
-			return RESULT_SUCCESS;
 		}
 	}
 	ast_mutex_unlock(&iflock);
-	return RESULT_FAILURE;
+	if (destroyed_first > start || destroyed_last < end) {
+		ast_debug(1, "Asked to destroy %d-%d, destroyed %d-%d,\n",
+			start, end, destroyed_first, destroyed_last);
+	}
+}
+
+static int setup_dahdi(int reload);
+static int setup_dahdi_int(int reload, struct dahdi_chan_conf *default_conf, struct dahdi_chan_conf *base_conf, struct dahdi_chan_conf *conf);
+
+/*!
+ * \internal
+ * \brief create a range of new DAHDI channels
+ *
+ * \param start first channel in the range
+ * \param end last channel in the range
+ *
+ * \retval RESULT_SUCCESS on success.
+ * \retval RESULT_FAILURE on error.
+ */
+static int dahdi_create_channel_range(int start, int end)
+{
+	struct dahdi_pvt *cur;
+	struct dahdi_chan_conf default_conf = dahdi_chan_conf_default();
+	struct dahdi_chan_conf base_conf = dahdi_chan_conf_default();
+	struct dahdi_chan_conf conf = dahdi_chan_conf_default();
+	int i, x;
+	int ret = RESULT_FAILURE; /* be pessimistic */
+
+	ast_debug(1, "channel range caps: %d - %d\n", start, end);
+	ast_mutex_lock(&iflock);
+	for (cur = iflist; cur; cur = cur->next) {
+		if (cur->channel >= start && cur->channel <= end) {
+			ast_log(LOG_ERROR,
+				"channel range %d-%d is occupied\n",
+				start, end);
+			goto out;
+		}
+	}
+	for (x = 0; x < NUM_SPANS; x++) {
+#ifdef HAVE_PRI
+		struct dahdi_pri *pri = pris + x;
+
+		if (!pris[x].pri.pvts[0]) {
+			break;
+		}
+		for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) {
+			int channo = pri->dchannels[i];
+
+			if (!channo) {
+				break;
+			}
+			if (!pri->pri.fds[i]) {
+				break;
+			}
+			if (channo >= start && channo <= end) {
+				ast_log(LOG_ERROR,
+						"channel range %d-%d is occupied by span %d\n",
+						start, end, x + 1);
+				goto out;
+			}
+		}
+#endif
+	}
+	if (!default_conf.chan.cc_params || !base_conf.chan.cc_params ||
+		!conf.chan.cc_params) {
+		goto out;
+	}
+	default_conf.wanted_channels_start = start;
+	base_conf.wanted_channels_start = start;
+	conf.wanted_channels_start = start;
+	default_conf.wanted_channels_end = end;
+	base_conf.wanted_channels_end = end;
+	conf.wanted_channels_end = end;
+	if (setup_dahdi_int(0, &default_conf, &base_conf, &conf) == 0) {
+		ret = RESULT_SUCCESS;
+	}
+out:
+	ast_cc_config_params_destroy(default_conf.chan.cc_params);
+	ast_cc_config_params_destroy(base_conf.chan.cc_params);
+	ast_cc_config_params_destroy(conf.chan.cc_params);
+	ast_mutex_unlock(&iflock);
+	return ret;
 }
 
+
 static struct dahdi_pvt *handle_init_event(struct dahdi_pvt *i, int event)
 {
 	int res;
@@ -12058,11 +12164,7 @@ static void *do_monitor(void *data)
 		doomed = NULL;
 		for (i = iflist;; i = i->next) {
 			if (doomed) {
-				int res;
-				res = dahdi_destroy_channel_bynum(doomed->channel);
-				if (res != RESULT_SUCCESS) {
-					ast_log(LOG_WARNING, "Couldn't find channel to destroy, hopefully another destroy operation just happened.\n");
-				}
+				dahdi_destroy_channel_range(doomed->channel, doomed->channel);
 				doomed = NULL;
 			}
 			if (!i) {
@@ -14497,10 +14599,21 @@ static int prepare_pri(struct dahdi_pri
 	for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) {
 		if (!pri->dchannels[i])
 			break;
+		if (pri->pri.fds[i] >= 0) {
+			/* A partial range addition. Not a complete setup. */
+			break;
+		}
 		pri->pri.fds[i] = open("/dev/dahdi/channel", O_RDWR);
+		if ((pri->pri.fds[i] < 0)) {
+			ast_log(LOG_ERROR, "Unable to open D-channel (fd=%d) (%s)\n",
+				pri->pri.fds[i], strerror(errno));
+			return -1;
+		}
 		x = pri->dchannels[i];
-		if ((pri->pri.fds[i] < 0) || (ioctl(pri->pri.fds[i],DAHDI_SPECIFY,&x) == -1)) {
-			ast_log(LOG_ERROR, "Unable to open D-channel %d (%s)\n", x, strerror(errno));
+		res = ioctl(pri->pri.fds[i], DAHDI_SPECIFY, &x);
+		if (res) {
+			dahdi_close_pri_fd(pri, i);
+			ast_log(LOG_ERROR, "Unable to SPECIFY channel %d (%s)\n", x, strerror(errno));
 			return -1;
 		}
 		memset(&p, 0, sizeof(p));
@@ -14901,22 +15014,44 @@ static char *handle_pri_show_spans(struc
  *
  * \param pri the pri span
  *
- * \return TRUE if the span was valid and we attempted destroying.
- *
  * Shuts down a span and destroys its D-Channel. Further destruction
  * of the B-channels using dahdi_destroy_channel() would probably be required
  * for the B-Channels.
  */
-static int pri_destroy_dchan(struct sig_pri_span *pri)
+static void pri_destroy_span(struct sig_pri_span *pri)
 {
 	int i;
+	int res;
+	int cancel_code;
 	struct dahdi_pri* dahdi_pri;
+	pthread_t master = pri->master;
 
-	if (!pri->master || (pri->master == AST_PTHREADT_NULL)) {
-		return 0;
+	if (!master || (master == AST_PTHREADT_NULL)) {
+		return;
+	}
+	ast_debug(2, "About to destroy DAHDI channels of span %d.\n", pri->span);
+	for (i = 0; i < pri->numchans; i++) {
+		int channel;
+		struct sig_pri_chan *pvt = pri->pvts[i];
+
+		if (!pvt) {
+			continue;
+		}
+		channel = pvt->channel;
+		ast_debug(2, "About to destroy B-channel %d.\n", channel);
+		dahdi_destroy_channel_range(channel, channel);
+	}
+
+	cancel_code = pthread_cancel(master);
+	ast_debug(4,
+		"Waiting to join thread of span %d "
+		"with pid=%p cancel_code=%d\n",
+		pri->span, (void *)master, cancel_code);
+	res = pthread_join(master, NULL);
+	if (res != 0) {
+		ast_log(LOG_NOTICE, "pthread_join failed: %d\n", res);
 	}
-	pthread_cancel(pri->master);
-	pthread_join(pri->master, NULL);
+	pri->master = AST_PTHREADT_NULL;
 
 	/* The 'struct dahdi_pri' that contains our 'struct sig_pri_span' */
 	dahdi_pri = container_of(pri, struct dahdi_pri, pri);
@@ -14924,17 +15059,16 @@ static int pri_destroy_dchan(struct sig_
 		ast_debug(4, "closing pri_fd %d\n", i);
 		dahdi_close_pri_fd(dahdi_pri, i);
 	}
-	pri->pri = NULL;
+	sig_pri_init_pri(pri);
 	ast_debug(1, "PRI span %d destroyed\n", pri->span);
-	return 1;
 }
 
 static char *handle_pri_destroy_span(struct ast_cli_entry *e, int cmd,
 		struct ast_cli_args *a)
 {
 	int span;
-	int i;
 	int res;
+	struct sig_pri_span *pri;
 
 	switch (cmd) {
 	case CLI_INIT:
@@ -14958,25 +15092,13 @@ static char *handle_pri_destroy_span(str
 			a->argv[3], 1, NUM_SPANS);
 		return CLI_SUCCESS;
 	}
-	if (!pris[span - 1].pri.pri) {
+	pri = &pris[span - 1].pri;
+	if (!pri->pri) {
 		ast_cli(a->fd, "No PRI running on span %d\n", span);
 		return CLI_SUCCESS;
 	}
 
-	for (i = 0; i < pris[span - 1].pri.numchans; i++) {
-		int channel;
-		struct sig_pri_chan *pvt = pris[span - 1].pri.pvts[i];
-
-		if (!pvt) {
-			continue;
-		}
-		channel = pvt->channel;
-		ast_debug(2, "About to destroy B-channel %d.\n", channel);
-		dahdi_destroy_channel_bynum(channel);
-	}
-	ast_debug(2, "About to destroy D-channel of span %d.\n", span);
-	pri_destroy_dchan(&pris[span - 1].pri);
-
+	pri_destroy_span(pri);
 	return CLI_SUCCESS;
 }
 
@@ -15428,26 +15550,97 @@ static struct ast_cli_entry dahdi_mfcr2_
 
 #endif /* HAVE_OPENR2 */
 
-static char *dahdi_destroy_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+static char *dahdi_destroy_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
-	int channel;
-	int ret;
+	int start;
+	int end;
 	switch (cmd) {
 	case CLI_INIT:
-		e->command = "dahdi destroy channel";
+		e->command = "dahdi destroy channels";
 		e->usage =
-			"Usage: dahdi destroy channel <chan num>\n"
+			"Usage: dahdi destroy channels <from_channel> [<to_channel>]\n"
 			"	DON'T USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING.  Immediately removes a given channel, whether it is in use or not\n";
 		return NULL;
 	case CLI_GENERATE:
 		return NULL;
 	}
-	if (a->argc != 4)
+	if ((a->argc < 4) || a->argc > 5) {
 		return CLI_SHOWUSAGE;
+	}
+	start = atoi(a->argv[3]);
+	if (start < 1) {
+		ast_cli(a->fd, "Invalid starting channel number %s.\n",
+				a->argv[4]);
+		return CLI_FAILURE;
+	}
+	if (a->argc == 5) {
+		end = atoi(a->argv[4]);
+		if (end < 1) {
+			ast_cli(a->fd, "Invalid ending channel number %s.\n",
+					a->argv[4]);
+			return CLI_FAILURE;
+		}
+	} else {
+		end = start;
+	}
 
-	channel = atoi(a->argv[3]);
-	ret = dahdi_destroy_channel_bynum(channel);
-	return ( RESULT_SUCCESS == ret ) ? CLI_SUCCESS : CLI_FAILURE;
+	if (end < start) {
+		ast_cli(a->fd,
+			"range end (%d) is smaller than range start (%d)\n",
+			end, start);
+		return CLI_FAILURE;
+	}
+	dahdi_destroy_channel_range(start, end);
+	return CLI_SUCCESS;
+}
+
+static char *dahdi_create_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+	int start;
+	int end;
+	int ret;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "dahdi create channels";
+		e->usage = "Usage: dahdi create channels <from> [<to>] - a range of channels\n"
+			   "       dahdi create channels new           - add channels not yet created\n"
+			   "For ISDN  and SS7 the range should include complete spans.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return NULL;
+	}
+	if ((a->argc < 4) || a->argc > 5) {
+		return CLI_SHOWUSAGE;
+	}
+	if (a->argc == 4 && !strcmp(a->argv[3], "new")) {
+		ret = dahdi_create_channel_range(0, 0);
+		return (RESULT_SUCCESS == ret) ? CLI_SUCCESS : CLI_FAILURE;
+	}
+	start = atoi(a->argv[3]);
+	if (start <= 0) {
+		ast_cli(a->fd, "Invalid starting channel number '%s'.\n",
+				a->argv[3]);
+		return CLI_FAILURE;
+	}
+	if (a->argc == 5) {
+		end = atoi(a->argv[4]);
+		if (end <= 0) {
+			ast_cli(a->fd, "Invalid ending channel number '%s'.\n",
+					a->argv[4]);
+			return CLI_FAILURE;
+		}
+	} else {
+		end = start;
+	}
+	if (end < start) {
+		ast_cli(a->fd,
+			"range end (%d) is smaller than range start (%d)\n",
+			end, start);
+		return CLI_FAILURE;
+	}
+	ret = dahdi_create_channel_range(start, end);
+	return (RESULT_SUCCESS == ret) ? CLI_SUCCESS : CLI_FAILURE;
 }
 
 static void dahdi_softhangup_all(void)
@@ -15478,7 +15671,6 @@ retry:
 	ast_mutex_unlock(&iflock);
 }
 
-static int setup_dahdi(int reload);
 static int dahdi_restart(void)
 {
 #if defined(HAVE_PRI) || defined(HAVE_SS7)
@@ -16254,7 +16446,8 @@ static struct ast_cli_entry dahdi_cli[]
 	AST_CLI_DEFINE(handle_dahdi_show_cadences, "List cadences"),
 	AST_CLI_DEFINE(dahdi_show_channels, "Show active DAHDI channels"),
 	AST_CLI_DEFINE(dahdi_show_channel, "Show information on a channel"),
-	AST_CLI_DEFINE(dahdi_destroy_channel, "Destroy a channel"),
+	AST_CLI_DEFINE(dahdi_destroy_channels, "Destroy channels"),
+	AST_CLI_DEFINE(dahdi_create_channels, "Create channels"),
 	AST_CLI_DEFINE(dahdi_restart_cmd, "Fully restart DAHDI channels"),
 	AST_CLI_DEFINE(dahdi_show_status, "Show all DAHDI cards status"),
 	AST_CLI_DEFINE(dahdi_show_version, "Show the DAHDI version in use"),
@@ -17278,7 +17471,7 @@ static char *parse_spanchan(char *chanst
 	return p;
 }
 
-static int build_channels(struct dahdi_chan_conf *conf, const char *value, int reload, int lineno, int *found_pseudo)
+static int build_channels(struct dahdi_chan_conf *conf, const char *value, int reload, int lineno)
 {
 	char *c, *chan;
 	char *subdir;
@@ -17301,8 +17494,6 @@ static int build_channels(struct dahdi_c
 			finish = start;
 		} else if (!strcasecmp(chan, "pseudo")) {
 			finish = start = CHAN_PSEUDO;
-			if (found_pseudo)
-				*found_pseudo = 1;
 		} else {
 			ast_log(LOG_ERROR, "Syntax error parsing '%s' at '%s'\n", value, chan);
 			return -1;
@@ -17332,6 +17523,12 @@ static int build_channels(struct dahdi_c
 					}
 				}
 			}
+			if (conf->wanted_channels_start &&
+				(real_channel < conf->wanted_channels_start ||
+				 real_channel > conf->wanted_channels_end)
+			   ) {
+				continue;
+			}
 			tmp = mkintf(real_channel, conf, reload);
 
 			if (tmp) {
@@ -17341,6 +17538,9 @@ static int build_channels(struct dahdi_c
 						(reload == 1) ? "reconfigure" : "register", value);
 				return -1;
 			}
+			if (real_channel == CHAN_PSEUDO) {
+				has_pseudo = 1;
+			}
 		}
 	}
 
@@ -17527,7 +17727,6 @@ static int process_dahdi(struct dahdi_ch
 {
 	struct dahdi_pvt *tmp;
 	int y;
-	int found_pseudo = 0;
 	struct ast_variable *dahdichan = NULL;
 
 	for (; v; v = v->next) {
@@ -17540,7 +17739,7 @@ static int process_dahdi(struct dahdi_ch
 				ast_log(LOG_WARNING, "Channel '%s' ignored.\n", v->value);
 				continue;
 			}
-			if (build_channels(confp, v->value, reload, v->lineno, &found_pseudo)) {
+			if (build_channels(confp, v->value, reload, v->lineno)) {
 				if (confp->ignore_failed_channels) {
 					ast_log(LOG_WARNING, "Channel '%s' failure ignored: ignore_failed_channels.\n", v->value);
 					continue;
@@ -18673,8 +18872,7 @@ static int process_dahdi(struct dahdi_ch
 
 	if (dahdichan) {
 		/* Process the deferred dahdichan value. */
-		if (build_channels(confp, dahdichan->value, reload, dahdichan->lineno,
-			&found_pseudo)) {
+		if (build_channels(confp, dahdichan->value, reload, dahdichan->lineno)) {
 			if (confp->ignore_failed_channels) {
 				ast_log(LOG_WARNING,
 					"Dahdichan '%s' failure ignored: ignore_failed_channels.\n",
@@ -18697,7 +18895,7 @@ static int process_dahdi(struct dahdi_ch
 
 	/*< \todo why check for the pseudo in the per-channel section.
 	 * Any actual use for manual setup of the pseudo channel? */
-	if (!found_pseudo && reload != 1 && !(options & PROC_DAHDI_OPT_NOCHAN)) {
+	if (!has_pseudo && reload != 1 && !(options & PROC_DAHDI_OPT_NOCHAN)) {
 		/* use the default configuration for a channel, so
 		   that any settings from real configured channels
 		   don't "leak" into the pseudo channel config
@@ -18711,6 +18909,7 @@ static int process_dahdi(struct dahdi_ch
 		}
 		if (tmp) {
 			ast_verb(3, "Automatically generated pseudo channel\n");
+			has_pseudo = 1;
 		} else {
 			ast_log(LOG_WARNING, "Unable to register pseudo channel!\n");
 		}
@@ -18990,7 +19189,8 @@ static int setup_dahdi_int(int reload, s
 	if (reload != 1) {
 		int x;
 		for (x = 0; x < NUM_SPANS; x++) {
-			if (pris[x].pri.pvts[0]) {
+			if (pris[x].pri.pvts[0] &&
+					pris[x].pri.master == AST_PTHREADT_NULL) {
 				prepare_pri(pris + x);
 				if (sig_pri_start_pri(&pris[x].pri)) {
 					ast_log(LOG_ERROR, "Unable to start D-channel on span %d\n", x + 1);
