From 8a13712e45e41c4d413200e38a96898a4f1a79ab Mon Sep 17 00:00:00 2001
From: Mark Spencer <markster@digium.com>
Date: Tue, 26 Oct 2004 22:25:43 +0000
Subject: [PATCH] Pass concept of status back, permit "leaveempty" to work with
 static agents who are not loggedon (bug #2719)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4106 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 apps/app_chanisavail.c     |   9 +++-
 apps/app_dial.c            |  60 +++++++++++++--------
 apps/app_meetme.c          |   2 +-
 apps/app_queue.c           | 107 ++++++++++++++++++++++++++++++++-----
 channel.c                  |  34 ++++++++----
 channels/chan_agent.c      |   9 +++-
 channels/chan_alsa.c       |   4 +-
 channels/chan_h323.c       |   3 +-
 channels/chan_iax2.c       |   5 +-
 channels/chan_local.c      |   2 +-
 channels/chan_mgcp.c       |   5 +-
 channels/chan_modem.c      |   2 +-
 channels/chan_nbs.c        |   2 +-
 channels/chan_oss.c        |   4 +-
 channels/chan_phone.c      |   6 ++-
 channels/chan_sip.c        |   3 +-
 channels/chan_skinny.c     |   2 +-
 channels/chan_vpb.c        |   2 +-
 channels/chan_zap.c        |  22 ++------
 include/asterisk/causes.h  |   2 +
 include/asterisk/channel.h |   6 +--
 21 files changed, 208 insertions(+), 83 deletions(-)

diff --git a/apps/app_chanisavail.c b/apps/app_chanisavail.c
index d61b0f1d3d..91bf838b2e 100755
--- a/apps/app_chanisavail.c
+++ b/apps/app_chanisavail.c
@@ -51,6 +51,7 @@ LOCAL_USER_DECL;
 static int chanavail_exec(struct ast_channel *chan, void *data)
 {
 	int res=-1;
+	int status;
 	struct localuser *u;
 	char info[512], tmp[512], *peers, *tech, *number, *rest, *cur;
 	struct ast_channel *tempchan;
@@ -80,20 +81,24 @@ static int chanavail_exec(struct ast_channel *chan, void *data)
 			}
 			*number = '\0';
 			number++;
-			if ((tempchan = ast_request(tech, chan->nativeformats, number))) {
+			if ((tempchan = ast_request(tech, chan->nativeformats, number, &status))) {
 					pbx_builtin_setvar_helper(chan, "AVAILCHAN", tempchan->name);
 					/* Store the originally used channel too */
 					snprintf(tmp, sizeof(tmp), "%s/%s", tech, number);
 					pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", tmp);
+					snprintf(tmp, sizeof(tmp), "%d", status);
+					pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
 					ast_hangup(tempchan);
 					tempchan = NULL;
 					res = 1;
 					break;
+			} else {
+				snprintf(tmp, sizeof(tmp), "%d", status);
+				pbx_builtin_setvar_helper(chan, "AVAILSTATUS", tmp);
 			}
 			cur = rest;
 		} while (cur);
 	}
-
 	if (res < 1) {
 		pbx_builtin_setvar_helper(chan, "AVAILCHAN", "");
 		pbx_builtin_setvar_helper(chan, "AVAILORIGCHAN", "");
diff --git a/apps/app_dial.c b/apps/app_dial.c
index fddddebe9e..0bf269a683 100755
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -26,6 +26,7 @@
 #include <asterisk/callerid.h>
 #include <asterisk/utils.h>
 #include <asterisk/app.h>
+#include <asterisk/causes.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
@@ -134,14 +135,32 @@ static void hanguptree(struct localuser *outgoing, struct ast_channel *exception
 
 #define AST_MAX_WATCHERS 256
 
-static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect_in, int *allowdisconnect_out, int *sentringing, char *status, size_t statussize)
+#define HANDLE_CAUSE(blah, bleh) do { \
+	switch(cause) { \
+	case AST_CAUSE_BUSY: \
+		if (bleh->cdr) \
+			ast_cdr_busy(bleh->cdr); \
+		numbusy++; \
+	case AST_CAUSE_CONGESTION: \
+	case AST_CAUSE_UNREGISTERED: \
+		if (bleh->cdr) \
+			ast_cdr_busy(bleh->cdr); \
+		numcongestion++; \
+	default: \
+		numnochan++; \
+	} \
+} while(0)
+
+
+static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localuser *outgoing, int *to, int *allowredir_in, int *allowredir_out, int *allowdisconnect_in, int *allowdisconnect_out, int *sentringing, char *status, size_t statussize, int busystart, int nochanstart, int congestionstart)
 {
 	struct localuser *o;
 	int found;
 	int numlines;
-	int numbusy = 0;
-	int numcongestion = 0;
-	int numnochan = 0;
+	int numbusy = busystart;
+	int numcongestion = congestionstart;
+	int numnochan = nochanstart;
+	int cause;
 	int orig = *to;
 	struct ast_frame *f;
 	struct ast_channel *peer = NULL;
@@ -227,11 +246,11 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu
 					if (option_verbose > 2)
 						ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
 					/* Setup parameters */
-					o->chan = ast_request(tech, in->nativeformats, stuff);
+					o->chan = ast_request(tech, in->nativeformats, stuff, &cause);
 					if (!o->chan) {
 						ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
 						o->stillgoing = 0;
-						numnochan++;
+						HANDLE_CAUSE(cause, in);
 					} else {
 						if (o->chan->cid.cid_num)
 							free(o->chan->cid.cid_num);
@@ -317,9 +336,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu
 							ast_hangup(o->chan);
 							o->chan = NULL;
 							o->stillgoing = 0;
-							if (in->cdr)
-								ast_cdr_busy(in->cdr);
-							numbusy++;
+							HANDLE_CAUSE(AST_CAUSE_BUSY, in);
 							break;
 						case AST_CONTROL_CONGESTION:
 							if (option_verbose > 2)
@@ -328,9 +345,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in, struct localu
 							ast_hangup(o->chan);
 							o->chan = NULL;
 							o->stillgoing = 0;
-							if (in->cdr)
-								ast_cdr_busy(in->cdr);
-							numcongestion++;
+							HANDLE_CAUSE(AST_CAUSE_CONGESTION, in);
 							break;
 						case AST_CONTROL_RINGING:
 							if (option_verbose > 2)
@@ -433,6 +448,10 @@ static int dial_exec(struct ast_channel *chan, void *data)
 	int privacy=0;
 	int announce=0;
 	int resetcdr=0;
+	int numbusy = 0;
+	int numcongestion = 0;
+	int numnochan = 0;
+	int cause;
 	char numsubst[AST_MAX_EXTENSION];
 	char restofit[AST_MAX_EXTENSION];
 	char *transfer = NULL;
@@ -738,14 +757,11 @@ static int dial_exec(struct ast_channel *chan, void *data)
 				ast_log(LOG_DEBUG, "Dialing by extension %s\n", numsubst);
 		}
 		/* Request the peer */
-		tmp->chan = ast_request(tech, chan->nativeformats, numsubst);
+		tmp->chan = ast_request(tech, chan->nativeformats, numsubst, &cause);
 		if (!tmp->chan) {
 			/* If we can't, just go on to the next call */
 			ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", tech);
-			if (chan->cdr)
-				ast_cdr_busy(chan->cdr);
-			free(tmp);
-			cur = rest;
+			HANDLE_CAUSE(cause, chan);
 			continue;
 		}
 		if (!ast_strlen_zero(tmp->chan->call_forward)) {
@@ -767,11 +783,10 @@ static int dial_exec(struct ast_channel *chan, void *data)
 				ast_verbose(VERBOSE_PREFIX_3 "Forwarding %s to '%s/%s' (thanks to %s)\n", chan->name, tech, stuff, tmp->chan->name);
 			/* Setup parameters */
 			ast_hangup(tmp->chan);
-			tmp->chan = ast_request(tech, chan->nativeformats, stuff);
+			tmp->chan = ast_request(tech, chan->nativeformats, stuff, &cause);
 			if (!tmp->chan) {
 				ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
-				free(tmp);
-				cur = rest;
+				HANDLE_CAUSE(cause, chan);
 				continue;
 			}
 		}
@@ -847,8 +862,7 @@ static int dial_exec(struct ast_channel *chan, void *data)
 			else if (option_verbose > 2)
 				ast_verbose(VERBOSE_PREFIX_3 "Couldn't call %s\n", numsubst);
 			ast_hangup(tmp->chan);
-			free(tmp);
-			cur = rest;
+			tmp->chan = NULL;
 			continue;
 		} else
 			if (option_verbose > 2)
@@ -888,7 +902,7 @@ static int dial_exec(struct ast_channel *chan, void *data)
 		strncpy(status, "CHANUNAVAIL", sizeof(status) - 1);
 
 	time(&start_time);
-	peer = wait_for_answer(chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect_in, &allowdisconnect_out, &sentringing, status, sizeof(status));
+	peer = wait_for_answer(chan, outgoing, &to, &allowredir_in, &allowredir_out, &allowdisconnect_in, &allowdisconnect_out, &sentringing, status, sizeof(status), numbusy, numnochan, numcongestion);
 
 	if (!peer) {
 		if (to) 
diff --git a/apps/app_meetme.c b/apps/app_meetme.c
index e853dc87ee..064e52c781 100755
--- a/apps/app_meetme.c
+++ b/apps/app_meetme.c
@@ -222,7 +222,7 @@ static struct ast_conference *build_conf(char *confno, char *pin, int make, int
 			strncpy(cnf->confno, confno, sizeof(cnf->confno) - 1);
 			strncpy(cnf->pin, pin, sizeof(cnf->pin) - 1);
 			cnf->markedusers = 0;
-			cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo");
+			cnf->chan = ast_request("zap", AST_FORMAT_ULAW, "pseudo", NULL);
 			if (cnf->chan) {
 				cnf->fd = cnf->chan->fds[0];	/* for use by conf_play() */
 			} else {
diff --git a/apps/app_queue.c b/apps/app_queue.c
index 0febe56221..f0fbbbbfce 100755
--- a/apps/app_queue.c
+++ b/apps/app_queue.c
@@ -45,6 +45,7 @@
 #include <asterisk/config.h>
 #include <asterisk/monitor.h>
 #include <asterisk/utils.h>
+#include <asterisk/causes.h>
 #include <stdlib.h>
 #include <errno.h>
 #include <unistd.h>
@@ -145,6 +146,7 @@ struct localuser {
 	char tech[40];
 	int stillgoing;
 	int metric;
+	int oldstatus;
 	int allowredirect_in;
 	int allowredirect_out;
 	int ringbackonly;
@@ -182,6 +184,7 @@ struct member {
 	int penalty;				/* Are we a last resort? */
 	int calls;					/* Number of calls serviced by this member */
 	int dynamic;				/* Are we dynamically added? */
+	int status;					/* Status of queue member */
 	time_t lastcall;			/* When last successful call was hungup */
 	struct member *next;		/* Next member */
 };
@@ -280,6 +283,26 @@ static inline void insert_entry(struct ast_call_queue *q,
 	new->opos = *pos;
 }
 
+static int has_no_members(struct ast_call_queue *q)
+{
+	struct member *member;
+	int empty = 1;
+	member = q->members;
+	while(empty && member) {
+		switch(member->status) {
+		case AST_CAUSE_NOSUCHDRIVER:
+		case AST_CAUSE_UNREGISTERED:
+			/* Not logged on, etc */
+			break;
+		default:
+			/* Not empty */
+			empty = 0;
+		}
+		member = member->next;
+	}
+	return empty;
+}
+
 static int join_queue(char *queuename, struct queue_ent *qe)
 {
 	struct ast_call_queue *q;
@@ -293,7 +316,7 @@ static int join_queue(char *queuename, struct queue_ent *qe)
 		if (!strcasecmp(q->name, queuename)) {
 			/* This is our one */
 			ast_mutex_lock(&q->lock);
-			if ((q->members || q->joinempty) && (!q->maxlen || (q->count < q->maxlen))) {
+			if ((!has_no_members(q) || q->joinempty || !q->head) && (!q->maxlen || (q->count < q->maxlen))) {
 				/* There's space for us, put us at the right position inside
 				 * the queue. 
 				 * Take into account the priority of the calling user */
@@ -551,9 +574,29 @@ static void hanguptree(struct localuser *outgoing, struct ast_channel *exception
 	}
 }
 
+static int update_status(struct ast_call_queue *q, struct member *member, int status)
+{
+	struct member *cur;
+	/* Since a reload could have taken place, we have to traverse the list to
+		be sure it's still valid */
+	ast_mutex_lock(&q->lock);
+	cur = q->members;
+	while(cur) {
+		if (member == cur) {
+			cur->status = status;
+			break;
+		}
+		cur = cur->next;
+	}
+	q->callscompleted++;
+	ast_mutex_unlock(&q->lock);
+	return 0;
+}
+
 static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
 {
 	int res;
+	int status;
 	if (qe->parent->wrapuptime && (time(NULL) - tmp->lastcall < qe->parent->wrapuptime)) {
 		ast_log(LOG_DEBUG, "Wrapuptime not yet expired for %s/%s\n", tmp->tech, tmp->numsubst);
 		if (qe->chan->cdr)
@@ -562,7 +605,7 @@ static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
 		return 0;
 	}
 	/* Request the peer */
-	tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst);
+	tmp->chan = ast_request(tmp->tech, qe->chan->nativeformats, tmp->numsubst, &status);
 	if (!tmp->chan) {			/* If we can't, just go on to the next call */
 #if 0
 		ast_log(LOG_NOTICE, "Unable to create channel of type '%s'\n", cur->tech);
@@ -570,8 +613,11 @@ static int ring_entry(struct queue_ent *qe, struct localuser *tmp)
 		if (qe->chan->cdr)
 			ast_cdr_busy(qe->chan->cdr);
 		tmp->stillgoing = 0;
+		update_status(qe->parent, tmp->member, status);
 		return 0;
-	}
+	} else if (status != tmp->oldstatus) 
+		update_status(qe->parent, tmp->member, status);
+	
 	tmp->chan->appl = "AppQueue";
 	tmp->chan->data = "(Outgoing Line)";
 	tmp->chan->whentohangup = 0;
@@ -728,6 +774,7 @@ static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser
 	struct localuser *o;
 	int found;
 	int numlines;
+	int status;
 	int sentringing = 0;
 	int numbusies = 0;
 	int numnochan = 0;
@@ -795,7 +842,9 @@ static struct localuser *wait_for_answer(struct queue_ent *qe, struct localuser
 					if (option_verbose > 2)
 						ast_verbose(VERBOSE_PREFIX_3 "Now forwarding %s to '%s/%s' (thanks to %s)\n", in->name, tech, stuff, o->chan->name);
 					/* Setup parameters */
-					o->chan = ast_request(tech, in->nativeformats, stuff);
+					o->chan = ast_request(tech, in->nativeformats, stuff, &status);
+					if (status != o->oldstatus) 
+						update_status(qe->parent, o->member, status);						
 					if (!o->chan) {
 						ast_log(LOG_NOTICE, "Unable to create local channel for call forward to '%s/%s'\n", tech, stuff);
 						o->stillgoing = 0;
@@ -997,7 +1046,7 @@ static int wait_our_turn(struct queue_ent *qe, int ringing)
 		}
 
 		/* leave the queue if no agents, if enabled */
-		if (!(qe->parent->members) && qe->parent->leavewhenempty) {
+		if (has_no_members(qe->parent) && qe->parent->leavewhenempty) {
 			leave_queue(qe);
 			break;
 		}
@@ -1163,6 +1212,7 @@ static int try_calling(struct queue_ent *qe, char *options, char *announceoverri
 		tmp->member = cur;		/* Never directly dereference!  Could change on reload */
 		strncpy(tmp->tech, cur->tech, sizeof(tmp->tech)-1);
 		strncpy(tmp->numsubst, cur->loc, sizeof(tmp->numsubst)-1);
+		tmp->oldstatus = cur->status;
 		tmp->lastcall = cur->lastcall;
 		/* If we're dialing by extension, look at the extension to know what to dial */
 		if ((newnum = strstr(tmp->numsubst, "BYEXTENSION"))) {
@@ -1728,6 +1778,7 @@ check_turns:
 			}
 		}
 		if (!res) {
+			int makeannouncement = 0;
 			for (;;) {
 				/* This is the wait loop for the head caller*/
 				/* To exit, they may get their call answered; */
@@ -1740,15 +1791,12 @@ check_turns:
 					break;
 				}
 
-				/* leave the queue if no agents, if enabled */
-				if (!((qe.parent)->members) && (qe.parent)->leavewhenempty) {
-					leave_queue(&qe);
-					break;
+				if (makeannouncement) {
+					/* Make a position announcement, if enabled */
+					if (qe.parent->announcefrequency && !ringing)
+						say_position(&qe);
 				}
-
-				/* Make a position announcement, if enabled */
-				if (qe.parent->announcefrequency && !ringing)
-					say_position(&qe);
+				makeannouncement = 1;
 
 				/* Try calling all queue members for 'timeout' seconds */
 				res = try_calling(&qe, options, announceoverride, url, &go_on);
@@ -1761,6 +1809,12 @@ check_turns:
 					break;
 				}
 
+				/* leave the queue if no agents, if enabled */
+				if (has_no_members(qe.parent) && (qe.parent->leavewhenempty)) {
+					res = 0;
+					break;
+				}
+
 				/* Leave if we have exceeded our queuetimeout */
 				if (qe.queuetimeout && ( (time(NULL) - qe.start) >= qe.queuetimeout) ) {
 					res = 0;
@@ -2037,6 +2091,30 @@ static void reload_queues(void)
 	ast_mutex_unlock(&qlock);
 }
 
+static char *status2str(int status, char *buf, int buflen)
+{
+	switch(status) {
+	case AST_CAUSE_BUSY:
+		strncpy(buf, "busy", buflen - 1);
+		break;
+	case AST_CAUSE_CONGESTION:
+		strncpy(buf, "congestion", buflen - 1);
+		break;
+	case AST_CAUSE_FAILURE:
+		strncpy(buf, "failure", buflen - 1);
+		break;
+	case AST_CAUSE_UNREGISTERED:
+		strncpy(buf, "unregistered", buflen - 1);
+		break;
+	case AST_CAUSE_NOSUCHDRIVER:
+		strncpy(buf, "nosuchdriver", buflen - 1);
+		break;
+	default:
+		snprintf(buf, buflen, "unknown status %d", status);
+	}
+	return buf;
+}
+
 static int __queues_show(int fd, int argc, char **argv, int queue_show)
 {
 	struct ast_call_queue *q;
@@ -2046,6 +2124,7 @@ static int __queues_show(int fd, int argc, char **argv, int queue_show)
 	time_t now;
 	char max[80] = "";
 	char calls[80] = "";
+	char tmpbuf[80] = "";
 	float sl = 0;
 
 	time(&now);
@@ -2092,6 +2171,8 @@ static int __queues_show(int fd, int argc, char **argv, int queue_show)
 					max[0] = '\0';
 				if (mem->dynamic)
 					strncat(max, " (dynamic)", sizeof(max) - strlen(max) - 1);
+				if (mem->status)
+					snprintf(max + strlen(max), sizeof(max) - strlen(max), " (%s)", status2str(mem->status, tmpbuf, sizeof(tmpbuf)));
 				if (mem->calls) {
 					snprintf(calls, sizeof(calls), " has taken %d calls (last was %ld secs ago)",
 							mem->calls, (long)(time(NULL) - mem->lastcall));
diff --git a/channel.c b/channel.c
index b9607a03ee..9ef5c90152 100755
--- a/channel.c
+++ b/channel.c
@@ -65,7 +65,7 @@ struct chanlist {
 	char type[80];
 	char description[80];
 	int capabilities;
-	struct ast_channel * (*requester)(const char *type, int format, void *data);
+	struct ast_channel * (*requester)(const char *type, int format, void *data, int *cause);
 	int (*devicestate)(void *data);
 	struct chanlist *next;
 } *backends = NULL;
@@ -154,13 +154,13 @@ void ast_channel_setwhentohangup(struct ast_channel *chan, time_t offset)
 }
 
 int ast_channel_register(const char *type, const char *description, int capabilities,
-		struct ast_channel *(*requester)(const char *type, int format, void *data))
+		struct ast_channel *(*requester)(const char *type, int format, void *data, int *cause))
 {
 	return ast_channel_register_ex(type, description, capabilities, requester, NULL);
 }
 
 int ast_channel_register_ex(const char *type, const char *description, int capabilities,
-		struct ast_channel *(*requester)(const char *type, int format, void *data),
+		struct ast_channel *(*requester)(const char *type, int format, void *data, int *cause),
 		int (*devicestate)(void *data))
 {
 	struct chanlist *chan, *last=NULL;
@@ -1751,11 +1751,12 @@ int ast_set_read_format(struct ast_channel *chan, int fmts)
 struct ast_channel *__ast_request_and_dial(const char *type, int format, void *data, int timeout, int *outstate, const char *cid_num, const char *cid_name, struct outgoing_helper *oh)
 {
 	int state = 0;
+	int cause = 0;
 	struct ast_channel *chan;
 	struct ast_frame *f;
 	int res = 0;
 	char *variable;
-	chan = ast_request(type, format, data);
+	chan = ast_request(type, format, data, &cause);
 	if (chan) {
 		if (oh) {
 			char *tmp, *var;
@@ -1815,9 +1816,18 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d
 				ast_frfree(f);
 			}
 		} else
-			ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
-	} else
+			ast_log(LOG_NOTICE, "Unable to call channel %s/%s\n", type, (char *)data);
+	} else {
 		ast_log(LOG_NOTICE, "Unable to request channel %s/%s\n", type, (char *)data);
+		switch(cause) {
+		case AST_CAUSE_BUSY:
+			state = AST_CONTROL_BUSY;
+			break;
+		case AST_CAUSE_CONGESTION:
+			state = AST_CONTROL_CONGESTION;
+			break;
+		}
+	}
 	if (chan) {
 		/* Final fixups */
 		if (oh) {
@@ -1861,13 +1871,17 @@ struct ast_channel *ast_request_and_dial(const char *type, int format, void *dat
 	return __ast_request_and_dial(type, format, data, timeout, outstate, cidnum, cidname, NULL);
 }
 
-struct ast_channel *ast_request(const char *type, int format, void *data)
+struct ast_channel *ast_request(const char *type, int format, void *data, int *cause)
 {
 	struct chanlist *chan;
 	struct ast_channel *c = NULL;
 	int capabilities;
 	int fmt;
 	int res;
+	int foo;
+	if (!cause)
+		cause = &foo;
+	*cause = AST_CAUSE_NOTDEFINED;
 	if (ast_mutex_lock(&chlock)) {
 		ast_log(LOG_WARNING, "Unable to lock channel list\n");
 		return NULL;
@@ -1885,7 +1899,7 @@ struct ast_channel *ast_request(const char *type, int format, void *data)
 			}
 			ast_mutex_unlock(&chlock);
 			if (chan->requester)
-				c = chan->requester(type, capabilities, data);
+				c = chan->requester(type, capabilities, data, cause);
 			if (c) {
 				if (c->_state == AST_STATE_DOWN) {
 					manager_event(EVENT_FLAG_CALL, "Newchannel",
@@ -1901,8 +1915,10 @@ struct ast_channel *ast_request(const char *type, int format, void *data)
 		}
 		chan = chan->next;
 	}
-	if (!chan)
+	if (!chan) {
 		ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
+		*cause = AST_CAUSE_NOSUCHDRIVER;
+	}
 	ast_mutex_unlock(&chlock);
 	return c;
 }
diff --git a/channels/chan_agent.c b/channels/chan_agent.c
index d16677c7de..8e596a44f3 100755
--- a/channels/chan_agent.c
+++ b/channels/chan_agent.c
@@ -34,6 +34,7 @@
 #include <asterisk/manager.h>
 #include <asterisk/features.h>
 #include <asterisk/utils.h>
+#include <asterisk/causes.h>
 #include <sys/socket.h>
 #include <errno.h>
 #include <unistd.h>
@@ -1051,7 +1052,7 @@ static int check_beep(struct agent_pvt *newlyavailable, int needlock)
 	return res;
 }
 
-static struct ast_channel *agent_request(const char *type, int format, void *data)
+static struct ast_channel *agent_request(const char *type, int format, void *data, int *cause)
 {
 	struct agent_pvt *p;
 	struct ast_channel *chan = NULL;
@@ -1113,7 +1114,7 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat
 						chan = agent_new(p, AST_STATE_DOWN);
 					} else if (!p->owner && !ast_strlen_zero(p->loginchan)) {
 						/* Adjustable agent */
-						p->chan = ast_request("Local", format, p->loginchan);
+						p->chan = ast_request("Local", format, p->loginchan, cause);
 						if (p->chan)
 							chan = agent_new(p, AST_STATE_DOWN);
 					}
@@ -1142,6 +1143,10 @@ static struct ast_channel *agent_request(const char *type, int format, void *dat
 		} else
 			ast_log(LOG_DEBUG, "Not creating place holder for '%s' since nobody logged in\n", s);
 	}
+	if (hasagent)
+		*cause = AST_CAUSE_BUSY;
+	else
+		*cause = AST_CAUSE_UNREGISTERED;
 	ast_mutex_unlock(&agentlock);
 	return chan;
 }
diff --git a/channels/chan_alsa.c b/channels/chan_alsa.c
index 09074ec871..e00eecd446 100755
--- a/channels/chan_alsa.c
+++ b/channels/chan_alsa.c
@@ -19,6 +19,7 @@
 #include <asterisk/config.h>
 #include <asterisk/cli.h>
 #include <asterisk/utils.h>
+#include <asterisk/causes.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -753,7 +754,7 @@ static struct ast_channel *alsa_new(struct chan_alsa_pvt *p, int state)
 	return tmp;
 }
 
-static struct ast_channel *alsa_request(const char *type, int format, void *data)
+static struct ast_channel *alsa_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat = format;
 	struct ast_channel *tmp=NULL;
@@ -765,6 +766,7 @@ static struct ast_channel *alsa_request(const char *type, int format, void *data
 	ast_mutex_lock(&alsalock);
 	if (alsa.owner) {
 		ast_log(LOG_NOTICE, "Already have a call on the ALSA channel\n");
+		*cause = AST_CAUSE_BUSY;
 	} else {
 		tmp= alsa_new(&alsa, AST_STATE_DOWN);
 		if (!tmp) {
diff --git a/channels/chan_h323.c b/channels/chan_h323.c
index a405567cb6..8965fc1259 100755
--- a/channels/chan_h323.c
+++ b/channels/chan_h323.c
@@ -66,6 +66,7 @@ extern "C" {
 #include <asterisk/callerid.h>
 #include <asterisk/cli.h>
 #include <asterisk/dsp.h>
+#include <asterisk/causes.h>
 #ifdef __cplusplus
 }
 #endif
@@ -948,7 +949,7 @@ static int create_addr(struct oh323_pvt *pvt, char *opeer)
 		return 0;
 	}
 }
-static struct ast_channel *oh323_request(const char *type, int format, void *data)
+static struct ast_channel *oh323_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat;
 	struct oh323_pvt *pvt;
diff --git a/channels/chan_iax2.c b/channels/chan_iax2.c
index ed87ad9947..733490e096 100755
--- a/channels/chan_iax2.c
+++ b/channels/chan_iax2.c
@@ -35,6 +35,7 @@
 #include <asterisk/musiconhold.h>
 #include <asterisk/features.h>
 #include <asterisk/utils.h>
+#include <asterisk/causes.h>
 #include <sys/mman.h>
 #include <arpa/inet.h>
 #include <dirent.h>
@@ -6208,7 +6209,7 @@ static void free_context(struct iax2_context *con)
 	}
 }
 
-static struct ast_channel *iax2_request(const char *type, int format, void *data)
+static struct ast_channel *iax2_request(const char *type, int format, void *data, int *cause)
 {
 	int callno;
 	int res;
@@ -6248,6 +6249,7 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data
 
 	/* Populate our address from the given */
 	if (create_addr(&sin, &capability, &sendani, &maxtime, hostname, NULL, &trunk, &notransfer, &usejitterbuf, NULL, 0, NULL, 0, &found, NULL)) {
+		*cause = AST_CAUSE_UNREGISTERED;
 		return NULL;
 	}
 	if (portno) {
@@ -6256,6 +6258,7 @@ static struct ast_channel *iax2_request(const char *type, int format, void *data
 	callno = find_callno(0, 0, &sin, NEW_FORCE, 1);
 	if (callno < 1) {
 		ast_log(LOG_WARNING, "Unable to create call\n");
+		*cause = AST_CAUSE_CONGESTION;
 		return NULL;
 	}
 	ast_mutex_lock(&iaxsl[callno]);
diff --git a/channels/chan_local.c b/channels/chan_local.c
index 8b2c5be54a..661ab403e1 100755
--- a/channels/chan_local.c
+++ b/channels/chan_local.c
@@ -462,7 +462,7 @@ static struct ast_channel *local_new(struct local_pvt *p, int state)
 }
 
 
-static struct ast_channel *local_request(const char *type, int format, void *data)
+static struct ast_channel *local_request(const char *type, int format, void *data, int *cause)
 {
 	struct local_pvt *p;
 	struct ast_channel *chan = NULL;
diff --git a/channels/chan_mgcp.c b/channels/chan_mgcp.c
index a66fb6d8dc..0b1fafd836 100755
--- a/channels/chan_mgcp.c
+++ b/channels/chan_mgcp.c
@@ -72,6 +72,7 @@
 #include <asterisk/app.h>
 #include <asterisk/musiconhold.h>
 #include <asterisk/utils.h>
+#include <asterisk/causes.h>
 #include <sys/socket.h>
 #include <sys/ioctl.h>
 #include <net/if.h>
@@ -3342,7 +3343,7 @@ static int restart_monitor(void)
 	return 0;
 }
 
-static struct ast_channel *mgcp_request(const char *type, int format, void *data)
+static struct ast_channel *mgcp_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat;
 	struct mgcp_subchannel *sub;
@@ -3364,6 +3365,7 @@ static struct ast_channel *mgcp_request(const char *type, int format, void *data
 	sub = find_subchannel(tmp, 0, NULL);
 	if (!sub) {
 		ast_log(LOG_WARNING, "Unable to find MGCP endpoint '%s'\n", tmp);
+		*cause = AST_CAUSE_UNREGISTERED;
 		return NULL;
 	}
 	
@@ -3383,6 +3385,7 @@ static struct ast_channel *mgcp_request(const char *type, int format, void *data
                  transmit_notify_request(sub,"L/vmwi(-)");
              }
          }
+		*cause = AST_CAUSE_BUSY;
 		return NULL;
     }
 	tmpc = mgcp_new(sub->owner ? sub->next : sub, AST_STATE_DOWN);
diff --git a/channels/chan_modem.c b/channels/chan_modem.c
index c6a27f1a44..41d95540b6 100755
--- a/channels/chan_modem.c
+++ b/channels/chan_modem.c
@@ -773,7 +773,7 @@ static struct ast_modem_pvt *mkif(char *iface)
 	return tmp;
 }
 
-static struct ast_channel *modem_request(const char *type, int format, void *data)
+static struct ast_channel *modem_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat;
 	struct ast_modem_pvt *p;
diff --git a/channels/chan_nbs.c b/channels/chan_nbs.c
index 6f67b3ada2..b3fdfbf936 100755
--- a/channels/chan_nbs.c
+++ b/channels/chan_nbs.c
@@ -232,7 +232,7 @@ static struct ast_channel *nbs_new(struct nbs_pvt *i, int state)
 }
 
 
-static struct ast_channel *nbs_request(const char *type, int format, void *data)
+static struct ast_channel *nbs_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat;
 	struct nbs_pvt *p;
diff --git a/channels/chan_oss.c b/channels/chan_oss.c
index c1d5ee0b35..ad87a9128b 100755
--- a/channels/chan_oss.c
+++ b/channels/chan_oss.c
@@ -26,6 +26,7 @@
 #include <asterisk/config.h>
 #include <asterisk/cli.h>
 #include <asterisk/utils.h>
+#include <asterisk/causes.h>
 #include <unistd.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -745,7 +746,7 @@ static struct ast_channel *oss_new(struct chan_oss_pvt *p, int state)
 	return tmp;
 }
 
-static struct ast_channel *oss_request(const char *type, int format, void *data)
+static struct ast_channel *oss_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat = format;
 	struct ast_channel *tmp;
@@ -756,6 +757,7 @@ static struct ast_channel *oss_request(const char *type, int format, void *data)
 	}
 	if (oss.owner) {
 		ast_log(LOG_NOTICE, "Already have a call on the OSS channel\n");
+		*cause = AST_CAUSE_BUSY;
 		return NULL;
 	}
 	tmp= oss_new(&oss, AST_STATE_DOWN);
diff --git a/channels/chan_phone.c b/channels/chan_phone.c
index b91b3657f7..b81c5b04d6 100755
--- a/channels/chan_phone.c
+++ b/channels/chan_phone.c
@@ -23,6 +23,7 @@
 #include <asterisk/options.h>
 #include <asterisk/utils.h>
 #include <asterisk/callerid.h>
+#include <asterisk/causes.h>
 #include <sys/socket.h>
 #include <sys/time.h>
 #include <errno.h>
@@ -1023,7 +1024,7 @@ static struct phone_pvt *mkif(char *iface, int mode, int txgain, int rxgain)
 	return tmp;
 }
 
-static struct ast_channel *phone_request(const char *type, int format, void *data)
+static struct ast_channel *phone_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat;
 	struct phone_pvt *p;
@@ -1047,7 +1048,8 @@ static struct ast_channel *phone_request(const char *type, int format, void *dat
 			if (!p->owner) {
 				tmp = phone_new(p, AST_STATE_DOWN, p->context);
 				break;
-			}
+			} else
+				*cause = AST_CAUSE_BUSY;
 		}
 		p = p->next;
 	}
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 56acb1683c..2181c6d872 100755
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -7946,7 +7946,7 @@ static int sip_devicestate(void *data)
 
 /*--- sip_request: PBX interface function -build SIP pvt structure ---*/
 /* SIP calls initiated by the PBX arrive here */
-static struct ast_channel *sip_request(const char *type, int format, void *data)
+static struct ast_channel *sip_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat;
 	struct sip_pvt *p;
@@ -7990,6 +7990,7 @@ static struct ast_channel *sip_request(const char *type, int format, void *data)
 	p->capability = global_capability;
 
 	if (create_addr(p, host)) {
+		*cause = AST_CAUSE_UNREGISTERED;
 		sip_destroy(p);
 		return NULL;
 	}
diff --git a/channels/chan_skinny.c b/channels/chan_skinny.c
index a77c4b18fc..dbf27977bb 100755
--- a/channels/chan_skinny.c
+++ b/channels/chan_skinny.c
@@ -2489,7 +2489,7 @@ static int restart_monitor(void)
 	return 0;
 }
 
-static struct ast_channel *skinny_request(const char *type, int format, void *data)
+static struct ast_channel *skinny_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat;
 	struct skinny_subchannel *sub;
diff --git a/channels/chan_vpb.c b/channels/chan_vpb.c
index 617367f959..82044e77c2 100755
--- a/channels/chan_vpb.c
+++ b/channels/chan_vpb.c
@@ -2115,7 +2115,7 @@ static struct ast_channel *vpb_new(struct vpb_pvt *me, int state, char *context)
 	return tmp;
 }
 
-static struct ast_channel *vpb_request(const char *type, int format, void *data) 
+static struct ast_channel *vpb_request(const char *type, int format, void *data, int *cause) 
 {
 	int oldformat;
 	struct vpb_pvt *p;
diff --git a/channels/chan_zap.c b/channels/chan_zap.c
index 07fe9ca51a..1590252cd7 100755
--- a/channels/chan_zap.c
+++ b/channels/chan_zap.c
@@ -6732,7 +6732,7 @@ static int pri_find_empty_chan(struct zt_pri *pri, int backwards)
 }
 #endif
 
-static struct ast_channel *zt_request(const char *type, int format, void *data)
+static struct ast_channel *zt_request(const char *type, int format, void *data, int *cause)
 {
 	int oldformat;
 	int groupmatch = 0;
@@ -6945,21 +6945,8 @@ next:
 	}
 	ast_mutex_unlock(lock);
 	restart_monitor();
-	if (!tmp) {
-		if (busy && (channelmatch != CHAN_PSEUDO)) {
-			tmp = zt_request("Zap", format, "pseudo");
-			if (tmp) {
-				char newname[80];
-				ast_mutex_lock(&tmp->lock);
-				snprintf(newname, sizeof(newname), "Zap/%s-busy-%d", (char *)data, rand());
-				ast_change_name(tmp, newname);
-				ast_setstate(tmp, AST_STATE_BUSY);
-				ast_mutex_unlock(&tmp->lock);
-			}
-		} else if (busy) {
-			ast_log(LOG_WARNING, "Whoa, the pseudo was busy somehow!\n");
-		}
-	}
+	if (callwait || (!tmp && busy))
+		*cause = AST_CAUSE_BUSY;
 	return tmp;
 }
 
@@ -7200,6 +7187,7 @@ static void *pri_dchannel(void *vpri)
 	time_t t;
 	int i, which=-1;
 	int numdchans;
+	int cause=0;
 	struct zt_pvt *crv;
 	pthread_t threadid;
 	pthread_attr_t attr;
@@ -7277,7 +7265,7 @@ static void *pri_dchannel(void *vpri)
 				    (tv.tv_usec - lastidle.tv_usec) / 1000) > 1000) {
 					/* Don't create a new idle call more than once per second */
 					snprintf(idlen, sizeof(idlen), "%d/%s", pri->pvts[nextidle]->channel, pri->idledial);
-					idle = zt_request("Zap", AST_FORMAT_ULAW, idlen);
+					idle = zt_request("Zap", AST_FORMAT_ULAW, idlen, &cause);
 					if (idle) {
 						pri->pvts[nextidle]->isidlecall = 1;
 						if (ast_pthread_create(&p, NULL, do_idle_thread, idle)) {
diff --git a/include/asterisk/causes.h b/include/asterisk/causes.h
index 6a6c5dc8d6..684f3d72cd 100755
--- a/include/asterisk/causes.h
+++ b/include/asterisk/causes.h
@@ -66,6 +66,8 @@
 #define AST_CAUSE_NORMAL 						AST_CAUSE_NORMAL_CLEARING
 #define AST_CAUSE_NOANSWER	 					AST_CAUSE_NO_ANSWER
 #define AST_CAUSE_CONGESTION	 				AST_CAUSE_NORMAL_CIRCUIT_CONGESTION
+#define AST_CAUSE_UNREGISTERED					AST_CAUSE_NO_ROUTE_DESTINATION
 #define AST_CAUSE_NOTDEFINED 					0
+#define AST_CAUSE_NOSUCHDRIVER					AST_CAUSE_CHAN_NOT_IMPLEMENTED
 
 #endif
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index f56f5bba0a..6b514befe3 100755
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -377,7 +377,7 @@ struct outgoing_helper {
  * by the low level module
  * Returns an ast_channel on success, NULL on failure.
  */
-struct ast_channel *ast_request(const char *type, int format, void *data);
+struct ast_channel *ast_request(const char *type, int format, void *data, int *status);
 
 //! Search the Channels by Name
 /*!
@@ -427,11 +427,11 @@ struct ast_channel *__ast_request_and_dial(const char *type, int format, void *d
  * Returns 0 on success, -1 on failure.
  */
 int ast_channel_register(const char *type, const char *description, int capabilities, 
-			struct ast_channel* (*requester)(const char *type, int format, void *data));
+			struct ast_channel* (*requester)(const char *type, int format, void *data, int *cause));
 
 /* Same like the upper function but with support for devicestate */
 int ast_channel_register_ex(const char *type, const char *description, int capabilities,
-		struct ast_channel *(*requester)(const char *type, int format, void *data),
+		struct ast_channel *(*requester)(const char *type, int format, void *data, int *cause),
 		int (*devicestate)(void *data));
 
 //! Unregister a channel class
-- 
GitLab