From: Tzafrir Cohen <tzafrir.cohen@xorcom.com>
Date: Wed, 17 Jul 2013 16:56:14 +0000
Subject: handle DAHDI_EVENT_REMOVED on a pri D-Channel
Origin: http://svnview.digium.com/svn/asterisk?view=rev&rev=394552

When a DAHDI device is removed at run-time it sends the event
DAHDI_EVENT_REMOVED on each channel. This is intended to signal the
userspace program to close the respective file handle, as the driver of
the device will need all of them closed to properly clean-up.

This event has long since been handled in chan_dahdi (chan_zap at the
time). However the event that is sent on a D-Channel of a "PRI" (ISDN)
span simply gets ignored.

This commit adds handling for closing the file descriptor (and shutting
down the span, while we're at it).

It also adds a CLI command 'pri destroy span <N>' to destroy the span
and its DAHDI channels.

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

Trivial backport from trunk / 12.

---
 channels/chan_dahdi.c |   97 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 97 insertions(+)

--- a/channels/chan_dahdi.c
+++ b/channels/chan_dahdi.c
@@ -3217,6 +3217,8 @@ 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 my_handle_dchan_exception(struct sig_pri_span *pri, int index)
 {
 	int x;
@@ -3244,6 +3246,9 @@ static void my_handle_dchan_exception(st
 	case DAHDI_EVENT_NOALARM:
 		pri_event_noalarm(pri, index, 0);
 		break;
+	case DAHDI_EVENT_REMOVED:
+		pri_destroy_dchan(pri);
+		break;
 	default:
 		break;
 	}
@@ -14887,6 +14892,97 @@ static char *handle_pri_show_spans(struc
 #endif	/* defined(HAVE_PRI) */
 
 #if defined(HAVE_PRI)
+#define container_of(ptr, type, member) \
+	((type *)((char *)(ptr) - offsetof(type, member)))
+/*!
+ * \internal
+ * \brief Destroy a D-Channel of a PRI span
+ * \since 12
+ *
+ * \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)
+{
+	int i;
+	struct dahdi_pri* dahdi_pri;
+
+	if (!pri->master || (pri->master == AST_PTHREADT_NULL)) {
+		return 0;
+	}
+	pthread_cancel(pri->master);
+	pthread_join(pri->master, NULL);
+
+	/* The 'struct dahdi_pri' that contains our 'struct sig_pri_span' */
+	dahdi_pri = container_of(pri, struct dahdi_pri, pri);
+	for (i = 0; i < SIG_PRI_NUM_DCHANS; i++) {
+		ast_debug(4, "closing pri_fd %d\n", i);
+		dahdi_close_pri_fd(dahdi_pri, i);
+	}
+	pri->pri = NULL;
+	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;
+
+	switch (cmd) {
+	case CLI_INIT:
+		e->command = "pri destroy span";
+		e->usage =
+			"Usage: pri destroy span <span>\n"
+			"       Destorys D-channel of span and its B-channels.\n"
+			"	DON'T USE THIS UNLESS YOU KNOW WHAT YOU ARE DOING.\n";
+		return NULL;
+	case CLI_GENERATE:
+		return complete_span_4(a->line, a->word, a->pos, a->n);
+	}
+
+	if (a->argc < 4) {
+		return CLI_SHOWUSAGE;
+	}
+	res = sscanf(a->argv[3], "%30d", &span);
+	if ((res != 1) || span < 1 || span > NUM_SPANS) {
+		ast_cli(a->fd,
+			"Invalid span '%s'.  Should be a number from %d to %d\n",
+			a->argv[3], 1, NUM_SPANS);
+		return CLI_SUCCESS;
+	}
+	if (!pris[span - 1].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);
+
+	return CLI_SUCCESS;
+}
+
+#endif	/* defined(HAVE_PRI) */
+
+#if defined(HAVE_PRI)
 static char *handle_pri_show_span(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
 {
 	int span;
@@ -14992,6 +15088,7 @@ static struct ast_cli_entry dahdi_pri_cl
 	AST_CLI_DEFINE(handle_pri_show_channels, "Displays PRI channel information"),
 	AST_CLI_DEFINE(handle_pri_show_spans, "Displays PRI span information"),
 	AST_CLI_DEFINE(handle_pri_show_span, "Displays PRI span information"),
+	AST_CLI_DEFINE(handle_pri_destroy_span, "Destroy a PRI span"),
 	AST_CLI_DEFINE(handle_pri_show_debug, "Displays current PRI debug settings"),
 	AST_CLI_DEFINE(handle_pri_set_debug_file, "Sends PRI debug output to the specified file"),
 	AST_CLI_DEFINE(handle_pri_version, "Displays libpri version"),
