Review Board 1.7.16


Backport of state_interface for app_queue in Asterisk 1.4

Review Request #116 - Created Jan. 5, 2009 and submitted

Mark Michelson
1.4
Reviewers
asterisk-dev
Asterisk
This is a backport of functionality that is already in all releases of Asterisk 1.6.

The functionality being added here is the ability to associate an interface with a queue member which is used to determine that member's device state.
A common usage for such functionality is to accurately determine the device state of a queue member who is called via the Agent or Local channel 
technologies. By specifying a more "concrete" interface to watch for device state, the queue application can more accurately decide the availability
of the queue member.

The policy for Asterisk 1.4 is to not introduce new functionality, and only make bug fixes. This case is one where the line has been blurred significantly
and so community feedback would be helpful. From a development standpoint, this violates the policy since most of what is being added is a new option for
queue members. From many users' perspectives, however, this is a means of solving a deficiency in the basic operations of queues. Please take a look at the
diff attached and comment on whether this would be problematic. It is implemented in such a way that any old dialplans, CLI commands, manager commands, etc.
will not need to be updated unless you are making use of this new functionality.

In internal discussions, the only point that was brought up was a possible problem that could aris if using persistent members. If you upgraded to a version 
that had the state interface functionality in it and then downgraded to a version which did not have state interface functionality, then the queue members' 
names loaded from the database would be wrong (they would have a semicolon and the state interface appended). Since the members' names are only used for display 
purposes, since the error may be easily corrected by reloading the queue configuration,and since the number of people likely to be affected by this is very 
small, I feel it's a risk worth taking.

Another potential problem I have thought of is that Asterisk 1.4 does not contain func_devstate. This means that this solution will only work if
you know for sure what endpoint will be dialed when a call is placed from a queue. To give an example, if you know that calling Local/2000 will always 
end up dialing SIP/2000, then this functionality will work great for you. If calling Local/2000 may end up dialing SIP/2000 or potentially SIP/3000 based 
on some dialplan logic, then this new functionality will not help you since you cannot use custom device states in the dialplan.
I used the scenario of issue 12970 as a basis for creating a test.

For the test, I created an Agent channel, Agent/1000, and had this line in the
dialplan:

exten => 1006,1,AgentCallbackLogin(1000,,1000@internal)

In the context "internal," extension 1000 is as follows:

exten => 1000,1,Dial(SIP/1000)

If it is not clear, All calls to Agent/1000 will eventually go to SIP/1000. I added
a line to my queues.conf file for my queue:

member => Agent/1000,,,SIP/1000

When I place an outbound call from SIP/1000, issuing the "queue show" command shows that
Agent/1000 is "in use." Attempting to place a call to the queue will not cause SIP/1000 to
ring since I have ringinuse=no for my queue. When SIP/1000 hangs up, the status returns to 
"not in use."



As another test, I tried implementing a common problem. In my queues.conf file, I added this
to my queue:

member => Local/1000,,,SIP/1000

As you may be able to guess, Local/1000 always dials SIP/1000. I placed a call to my queue
and answered with SIP/1000. At this point, issuing a "queue show" command showed that the
member Local/1000 was "in use." Then, from SIP/1000 I transferred the call to another endpoint.
Issuing a "queue show" command then showed Local/1000 as being "not in use."

Changes between revision 1 and 2

1 2 3
1 2 3

  1. /branches/1.4/apps/app_queue.c: Loading...
/branches/1.4/apps/app_queue.c
Diff Revision 1 Diff Revision 2
[20] 1502 lines
[+20] [+] static int join_queue(char *queuename, struct queue_ent *qe, enum queue_result *reason)
1503

    
   
1503

   
1504
static int play_file(struct ast_channel *chan, char *filename)
1504
static int play_file(struct ast_channel *chan, char *filename)
1505
{
1505
{
1506
	int res;
1506
	int res;
1507

    
   
1507

   

    
   
1508
	if (ast_strlen_zero(filename)) {

    
   
1509
		return 0;

    
   
1510
	}

    
   
1511

   
1508
	ast_stopstream(chan);
1512
	ast_stopstream(chan);
1509

    
   
1513

   
1510
	res = ast_streamfile(chan, filename, chan->language);
1514
	res = ast_streamfile(chan, filename, chan->language);
1511
	if (!res)
1515
	if (!res)
1512
		res = ast_waitstream(chan, AST_DIGIT_ANY);
1516
		res = ast_waitstream(chan, AST_DIGIT_ANY);
[+20] [20] 573 lines
[+20] [+] static void record_abandoned(struct queue_ent *qe)
2086
	qe->parent->callsabandoned++;
2090
	qe->parent->callsabandoned++;
2087
	ast_mutex_unlock(&qe->parent->lock);
2091
	ast_mutex_unlock(&qe->parent->lock);
2088
}
2092
}
2089

    
   
2093

   
2090
/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2094
/*! \brief RNA == Ring No Answer. Common code that is executed when we try a queue member and they don't answer. */
2091
static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername)
2095
static void rna(int rnatime, struct queue_ent *qe, char *interface, char *membername, int pause)
2092
{
2096
{
2093
	if (option_verbose > 2)
2097
	if (option_verbose > 2)
2094
		ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
2098
		ast_verbose( VERBOSE_PREFIX_3 "Nobody picked up in %d ms\n", rnatime);
2095
	ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
2099
	ast_queue_log(qe->parent->name, qe->chan->uniqueid, membername, "RINGNOANSWER", "%d", rnatime);
2096
	if (qe->parent->autopause) {
2100
	if (qe->parent->autopause && pause) {
2097
		if (!set_member_paused(qe->parent->name, interface, 1)) {
2101
		if (!set_member_paused(qe->parent->name, interface, 1)) {
2098
			if (option_verbose > 2)
2102
			if (option_verbose > 2)
2099
				ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
2103
				ast_verbose( VERBOSE_PREFIX_3 "Auto-Pausing Queue Member %s in queue %s since they failed to answer.\n", interface, qe->parent->name);
2100
		} else {
2104
		} else {
2101
			if (option_verbose > 2)
2105
			if (option_verbose > 2)
[+20] [20] 166 lines
[+20] [+] static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
2268
							if (in->cdr)
2272
							if (in->cdr)
2269
								ast_cdr_busy(in->cdr);
2273
								ast_cdr_busy(in->cdr);
2270
							do_hang(o);
2274
							do_hang(o);
2271
							endtime = (long)time(NULL);
2275
							endtime = (long)time(NULL);
2272
							endtime -= starttime;
2276
							endtime -= starttime;
2273
							rna(endtime*1000, qe, on, membername);
2277
							rna(endtime * 1000, qe, on, membername, 0);
2274
							if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2278
							if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2275
								if (qe->parent->timeoutrestart)
2279
								if (qe->parent->timeoutrestart)
2276
									*to = orig;
2280
									*to = orig;
2277
								ring_one(qe, outgoing, &numbusies);
2281
								ring_one(qe, outgoing, &numbusies);
2278
							}
2282
							}
[+20] [20] 4 lines
[+20] static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
2283
								ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
2287
								ast_verbose( VERBOSE_PREFIX_3 "%s is circuit-busy\n", o->chan->name);
2284
							if (in->cdr)
2288
							if (in->cdr)
2285
								ast_cdr_busy(in->cdr);
2289
								ast_cdr_busy(in->cdr);
2286
							endtime = (long)time(NULL);
2290
							endtime = (long)time(NULL);
2287
							endtime -= starttime;
2291
							endtime -= starttime;
2288
							rna(endtime*1000, qe, on, membername);
2292
							rna(endtime * 1000, qe, on, membername, 0);
2289
							do_hang(o);
2293
							do_hang(o);
2290
							if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2294
							if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2291
								if (qe->parent->timeoutrestart)
2295
								if (qe->parent->timeoutrestart)
2292
									*to = orig;
2296
									*to = orig;
2293
								ring_one(qe, outgoing, &numbusies);
2297
								ring_one(qe, outgoing, &numbusies);
[+20] [20] 12 lines
[+20] static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
2306
						}
2310
						}
2307
					}
2311
					}
2308
					ast_frfree(f);
2312
					ast_frfree(f);
2309
				} else {
2313
				} else {
2310
					endtime = (long) time(NULL) - starttime;
2314
					endtime = (long) time(NULL) - starttime;
2311
					rna(endtime * 1000, qe, on, membername);
2315
					rna(endtime * 1000, qe, on, membername, 1);
2312
					do_hang(o);
2316
					do_hang(o);
2313
					if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2317
					if (qe->parent->strategy != QUEUE_STRATEGY_RINGALL) {
2314
						if (qe->parent->timeoutrestart)
2318
						if (qe->parent->timeoutrestart)
2315
							*to = orig;
2319
							*to = orig;
2316
						ring_one(qe, outgoing, &numbusies);
2320
						ring_one(qe, outgoing, &numbusies);
[+20] [20] 27 lines
[+20] static struct callattempt *wait_for_answer(struct queue_ent *qe, struct callattempt *outgoing, int *to, char *digit, int prebusies, int caller_disconnect, int forwardsallowed)
2344
			}
2348
			}
2345
			ast_frfree(f);
2349
			ast_frfree(f);
2346
		}
2350
		}
2347
		if (!*to) {
2351
		if (!*to) {
2348
			for (o = start; o; o = o->call_next)
2352
			for (o = start; o; o = o->call_next)
2349
				rna(orig, qe, o->interface, o->member->membername);
2353
				rna(orig, qe, o->interface, o->member->membername, 1);
2350
		}
2354
		}
2351
	}
2355
	}
2352

    
   
2356

   
2353
	return peer;
2357
	return peer;
2354
}
2358
}
[+20] [20] 282 lines
[+20] [+] static void queue_transfer_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
2637
				new_chan->exten, new_chan->context, (long) (callstart - qe->start),
2641
				new_chan->exten, new_chan->context, (long) (callstart - qe->start),
2638
				(long) (time(NULL) - callstart));
2642
				(long) (time(NULL) - callstart));
2639

    
   
2643

   
2640
	update_queue(qe->parent, member, callcompletedinsl);
2644
	update_queue(qe->parent, member, callcompletedinsl);
2641
	
2645
	
2642
	if ((datastore = ast_channel_datastore_find(new_chan, &queue_transfer_info, NULL))) {
2646
	/* No need to lock the channels because they are already locked in ast_do_masquerade */
2643
		ast_channel_datastore_remove(new_chan, datastore);
2647
	if ((datastore = ast_channel_datastore_find(old_chan, &queue_transfer_info, NULL))) {

    
   
2648
		ast_channel_datastore_remove(old_chan, datastore);
2644
	} else {
2649
	} else {
2645
		ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
2650
		ast_log(LOG_WARNING, "Can't find the queue_transfer datastore.\n");
2646
	}
2651
	}
2647
}
2652
}
2648

    
   
2653

   
2649
/*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
2654
/*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
2650
 *
2655
 *
2651
 * When a caller is atxferred, then the queue_transfer_info datastore
2656
 * When a caller is atxferred, then the queue_transfer_info datastore
2652
 * is removed from the channel. If it's still there after the bridge is
2657
 * is removed from the channel. If it's still there after the bridge is
2653
 * broken, then the caller was not atxferred.
2658
 * broken, then the caller was not atxferred.

    
   
2659
 *

    
   
2660
 * \note Only call this with chan locked
2654
 */
2661
 */
2655
static int attended_transfer_occurred(struct ast_channel *chan)
2662
static int attended_transfer_occurred(struct ast_channel *chan)
2656
{
2663
{
2657
	return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
2664
	return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
2658
}
2665
}
[+20] [20] 264 lines
[+20] [+] static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
2923
	 * datastore was moved to another channel during a masquerade. If this is
2930
	 * datastore was moved to another channel during a masquerade. If this is
2924
	 * the case, don't free the datastore here because later, when the channel
2931
	 * the case, don't free the datastore here because later, when the channel
2925
	 * to which the datastore was moved hangs up, it will attempt to free this
2932
	 * to which the datastore was moved hangs up, it will attempt to free this
2926
	 * datastore again, causing a crash
2933
	 * datastore again, causing a crash
2927
	 */
2934
	 */

    
   
2935
	ast_channel_lock(qe->chan);
2928
	if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
2936
	if (datastore && !ast_channel_datastore_remove(qe->chan, datastore)) {
2929
		ast_channel_datastore_free(datastore);
2937
		ast_channel_datastore_free(datastore);
2930
	}
2938
	}

    
   
2939
	ast_channel_unlock(qe->chan);
2931
	ast_mutex_lock(&qe->parent->lock);
2940
	ast_mutex_lock(&qe->parent->lock);
2932
	if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
2941
	if (qe->parent->strategy == QUEUE_STRATEGY_RRMEMORY) {
2933
		store_next(qe, outgoing);
2942
		store_next(qe, outgoing);
2934
	}
2943
	}
2935
	ast_mutex_unlock(&qe->parent->lock);
2944
	ast_mutex_unlock(&qe->parent->lock);
[+20] [20] 236 lines
[+20] static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
3172
			ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername);
3181
			ast_log(LOG_WARNING, "The device state of this queue member, %s, is still 'Not in Use' when it probably should not be! Please check UPGRADE.txt for correct configuration settings.\n", member->membername);
3173
			
3182
			
3174
		transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
3183
		transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
3175
		bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
3184
		bridge = ast_bridge_call(qe->chan,peer, &bridge_config);
3176

    
   
3185

   

    
   
3186
		ast_channel_lock(qe->chan);
3177
		if (!attended_transfer_occurred(qe->chan)) {
3187
		if (!attended_transfer_occurred(qe->chan)) {
3178
			struct ast_datastore *tds;
3188
			struct ast_datastore *tds;
3179
			if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
3189
			if (strcasecmp(oldcontext, qe->chan->context) || strcasecmp(oldexten, qe->chan->exten)) {
3180
				ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
3190
				ast_queue_log(queuename, qe->chan->uniqueid, member->membername, "TRANSFER", "%s|%s|%ld|%ld",
3181
					qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
3191
					qe->chan->exten, qe->chan->context, (long) (callstart - qe->start),
[+20] [20] 30 lines
[+20] static int try_calling(struct queue_ent *qe, const char *options, char *announceoverride, const char *url, int *tries, int *noption, const char *agi)
3212
							"%s",
3222
							"%s",
3213
							queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
3223
							queuename, qe->chan->uniqueid, peer->name, member->membername, (long)(callstart - qe->start),
3214
							(long)(time(NULL) - callstart),
3224
							(long)(time(NULL) - callstart),
3215
							qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3225
							qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, sizeof(vars)) : "");
3216
			}
3226
			}
3217
			ast_channel_lock(qe->chan);
3227
			if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {	
3218
			if ((tds = ast_channel_datastore_find(qe->chan, &queue_transfer_info, NULL))) {

   
3219
				ast_channel_datastore_remove(qe->chan, tds);
3228
				ast_channel_datastore_remove(qe->chan, tds);
3220
			}
3229
			}
3221
			ast_channel_unlock(qe->chan);

   
3222
			update_queue(qe->parent, member, callcompletedinsl);
3230
			update_queue(qe->parent, member, callcompletedinsl);
3223
		}
3231
		}
3224

    
   
3232

   
3225
		if (transfer_ds) {
3233
		if (transfer_ds) {
3226
			ast_channel_datastore_free(transfer_ds);
3234
			ast_channel_datastore_free(transfer_ds);
3227
		}
3235
		}

    
   
3236
		ast_channel_unlock(qe->chan);
3228
		ast_hangup(peer);
3237
		ast_hangup(peer);
3229
		res = bridge ? bridge : 1;
3238
		res = bridge ? bridge : 1;
3230
		ao2_ref(member, -1);
3239
		ao2_ref(member, -1);
3231
	}
3240
	}
3232
out:
3241
out:
[+20] [20] 1185 lines
[+20] [+] static int reload_queues(void)
4418
						if (cur && strcasecmp(cur->state_interface, state_interface)) {
4427
						if (cur && strcasecmp(cur->state_interface, state_interface)) {
4419
							remove_from_interfaces(cur->state_interface);
4428
							remove_from_interfaces(cur->state_interface);
4420
						}
4429
						}
4421

    
   
4430

   
4422
						newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
4431
						newm = create_queue_member(interface, membername, penalty, cur ? cur->paused : 0, state_interface);
4423
						if (!cur || (cur && strcasecmp(cur->state_interface, state_interface)))
4432
						if (!cur || (cur && strcasecmp(cur->state_interface, state_interface))) {
4424
							add_to_interfaces(state_interface);
4433
							add_to_interfaces(state_interface);

    
   
4434
						}
4425
						ao2_link(q->members, newm);
4435
						ao2_link(q->members, newm);
4426
						ao2_ref(newm, -1);
4436
						ao2_ref(newm, -1);
4427
						newm = NULL;
4437
						newm = NULL;
4428

    
   
4438

   
4429
						if (cur)
4439
						if (cur)
4430
							ao2_ref(cur, -1);
4440
							ao2_ref(cur, -1);
4431
						else {
4441
						else {
4432
							/* Add them to the master int list if necessary */

   
4433
							q->membercount++;
4442
							q->membercount++;
4434
						}
4443
						}
4435
					} else {
4444
					} else {
4436
						queue_set_param(q, var->name, var->value, var->lineno, 1);
4445
						queue_set_param(q, var->name, var->value, var->lineno, 1);
4437
					}
4446
					}
[+20] [20] 740 lines
  1. /branches/1.4/apps/app_queue.c: Loading...

https://reviewboard.asterisk.org/ runs on a server provided by Digium, Inc. and uses bandwidth donated to the open source Asterisk community by API Digital Communications in Huntsville, AL USA.
Please report problems with this site to asteriskteam@digium.com.