1 /*
2  * Copyright (c) 2010, Sangoma Technologies
3  * Moises Silva <moy@sangoma.com>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * * Redistributions of source code must retain the above copyright
11  * notice, this list of conditions and the following disclaimer.
12  *
13  * * Redistributions in binary form must reproduce the above copyright
14  * notice, this list of conditions and the following disclaimer in the
15  * documentation and/or other materials provided with the distribution.
16  *
17  * * Neither the name of the original author; nor the names of any contributors
18  * may be used to endorse or promote products derived from this software
19  * without specific prior written permission.
20  *
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
25  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER
26  * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
27  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
28  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
29  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
30  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
31  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
32  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
33  */
34 
35 #include "private/ftdm_core.h"
36 
37 FTDM_ENUM_NAMES(CHANNEL_STATE_NAMES, CHANNEL_STATE_STRINGS)
38 FTDM_STR2ENUM(ftdm_str2ftdm_channel_state, ftdm_channel_state2str, ftdm_channel_state_t, CHANNEL_STATE_NAMES, FTDM_CHANNEL_STATE_INVALID)
39 
40 FTDM_ENUM_NAMES(CHANNEL_STATE_STATUS_NAMES, CHANNEL_STATE_STATUS_STRINGS)
41 FTDM_STR2ENUM(ftdm_str2ftdm_state_status, ftdm_state_status2str, ftdm_state_status_t, CHANNEL_STATE_STATUS_NAMES, FTDM_STATE_STATUS_INVALID)
42 
43 static ftdm_status_t ftdm_core_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq);
44 
_ftdm_channel_complete_state(const char * file,const char * func,int line,ftdm_channel_t * fchan)45 FT_DECLARE(ftdm_status_t) _ftdm_channel_complete_state(const char *file, const char *func, int line, ftdm_channel_t *fchan)
46 {
47 	uint8_t hindex = 0;
48 	ftdm_time_t diff = 0;
49 	ftdm_channel_state_t state = fchan->state;
50 
51 #if 0
52 	/*  I could not perform this sanity check without more disruptive changes. Ideally we should check here if the signaling module completing the state
53 		executed a state processor (called ftdm_channel_advance_states() which call fchan->span->state_processor(fchan)) for the state. That is just a
54 		sanity check, as in the past we had at least one bug where the signaling module set the state and then accidentally changed the state to a new one
55 		without calling ftdm_channel_advance_states(), meaning the state processor for the first state was not executed and that lead to unexpected behavior.
56 
57 		If we want to be able to perform this kind of sanity check it would be nice to add a new state status (FTDM_STATE_STATUS_PROCESSING), the function
58 		ftdm_channel_advance_states() would set the state_status to PROCESSING and then the check below for STATUS_NEW would be valid. Currently is not
59 		valid because the signaling module may be completing the state at the end of the state_processor callback and therefore the state will still be
60 		in STATUS_NEW, and is perfectly valid ... */
61 
62 	if (fchan->state_status == FTDM_STATE_STATUS_NEW) {
63 		ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_CRIT,
64 						"Asking to complete state change from %s to %s in %llums, but the state is still unprocessed (this might be a bug!)\n",
65 						ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff);
66 	}
67 #endif
68 
69 	if (fchan->state_status == FTDM_STATE_STATUS_COMPLETED) {
70 		ftdm_assert_return(!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE), FTDM_FAIL, "State change flag set but state is already completed\n");
71 		return FTDM_SUCCESS;
72 	}
73 
74 	ftdm_usrmsg_free(&fchan->usrmsg);
75 
76 	ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
77 
78 	if (state == FTDM_CHANNEL_STATE_PROGRESS) {
79 		ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
80 	} else if (state == FTDM_CHANNEL_STATE_PROGRESS_MEDIA) {
81 		ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
82 		ftdm_test_and_set_media(fchan);
83 	} else if (state == FTDM_CHANNEL_STATE_UP) {
84 		ftdm_set_flag(fchan, FTDM_CHANNEL_PROGRESS);
85 		ftdm_set_flag(fchan, FTDM_CHANNEL_ANSWERED);
86 		ftdm_test_and_set_media(fchan);
87 	} else if (state == FTDM_CHANNEL_STATE_DIALING) {
88 		ftdm_sigmsg_t msg;
89 		memset(&msg, 0, sizeof(msg));
90 		msg.channel = fchan;
91 		msg.event_id = FTDM_SIGEVENT_DIALING;
92 		ftdm_span_send_signal(fchan->span, &msg);
93 	} else if (state == FTDM_CHANNEL_STATE_HANGUP) {
94 		ftdm_set_echocancel_call_end(fchan);
95 	}
96 
97 	/* MAINTENANCE WARNING
98 	 * we're assuming an indication performed
99 	 * via state change will involve a single state change */
100 	ftdm_ack_indication(fchan, fchan->indication, FTDM_SUCCESS);
101 
102 	hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1);
103 
104 	ftdm_assert(!fchan->history[hindex].end_time, "End time should be zero!\n");
105 
106 	fchan->history[hindex].end_time = ftdm_current_time_in_ms();
107 	fchan->last_state_change_time = ftdm_current_time_in_ms();
108 
109 	fchan->state_status = FTDM_STATE_STATUS_COMPLETED;
110 
111 	diff = fchan->history[hindex].end_time - fchan->history[hindex].time;
112 
113 	ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Completed state change from %s to %s in %"FTDM_TIME_FMT" ms\n",
114 			ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(state), diff);
115 
116 
117 	if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) {
118 		ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING);
119 		ftdm_interrupt_signal(fchan->state_completed_interrupt);
120 	}
121 
122 	return FTDM_SUCCESS;
123 }
124 
_ftdm_set_state(const char * file,const char * func,int line,ftdm_channel_t * fchan,ftdm_channel_state_t state)125 FT_DECLARE(ftdm_status_t) _ftdm_set_state(const char *file, const char *func, int line,
126 		ftdm_channel_t *fchan, ftdm_channel_state_t state)
127 {
128 	if (fchan->state_status != FTDM_STATE_STATUS_COMPLETED) {
129 		/* the current state is not completed, setting a new state from a signaling module
130 		   when the current state is not completed is equivalent to implicitly acknowledging
131 		   the current state */
132 		_ftdm_channel_complete_state(file, func, line, fchan);
133 	}
134 	return ftdm_core_set_state(file, func, line, fchan, state, 0);
135 }
136 
ftdm_parse_state_map(ftdm_channel_t * ftdmchan,ftdm_channel_state_t state,ftdm_state_map_t * state_map)137 static int ftdm_parse_state_map(ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, ftdm_state_map_t *state_map)
138 {
139 	int x = 0, ok = 0;
140 	ftdm_state_direction_t direction = ftdm_test_flag(ftdmchan, FTDM_CHANNEL_OUTBOUND) ? ZSD_OUTBOUND : ZSD_INBOUND;
141 
142 	for(x = 0; x < FTDM_MAP_NODE_SIZE; x++) {
143 		int i = 0, proceed = 0;
144 		if (!state_map->nodes[x].type) {
145 			break;
146 		}
147 
148 		if (state_map->nodes[x].direction != direction) {
149 			continue;
150 		}
151 
152 		if (state_map->nodes[x].check_states[0] == FTDM_CHANNEL_STATE_ANY) {
153 			proceed = 1;
154 		} else {
155 			for(i = 0; i < FTDM_MAP_MAX; i++) {
156 				if (state_map->nodes[x].check_states[i] == ftdmchan->state) {
157 					proceed = 1;
158 					break;
159 				}
160 			}
161 		}
162 
163 		if (!proceed) {
164 			continue;
165 		}
166 
167 		for(i = 0; i < FTDM_MAP_MAX; i++) {
168 			ok = (state_map->nodes[x].type == ZSM_ACCEPTABLE);
169 			if (state_map->nodes[x].states[i] == FTDM_CHANNEL_STATE_END) {
170 				break;
171 			}
172 			if (state_map->nodes[x].states[i] == state) {
173 				ok = !ok;
174 				goto end;
175 			}
176 		}
177 	}
178  end:
179 
180 	return ok;
181 }
182 
ftdm_channel_cancel_state(const char * file,const char * func,int line,ftdm_channel_t * fchan)183 FT_DECLARE(ftdm_status_t) ftdm_channel_cancel_state(const char *file, const char *func, int line, ftdm_channel_t *fchan)
184 {
185 	ftdm_time_t diff;
186 	ftdm_channel_state_t state;
187 	ftdm_channel_state_t last_state;
188 	uint8_t hindex = 0;
189 
190 	if (!ftdm_test_flag(fchan, FTDM_CHANNEL_STATE_CHANGE)) {
191 		ftdm_log_chan(fchan, FTDM_LOG_WARNING, "Cannot cancel state change from %s to %s, it was already processed\n",
192 				ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(fchan->state));
193 		return FTDM_FAIL;
194 	}
195 
196 	if (fchan->state_status != FTDM_STATE_STATUS_NEW) {
197 		ftdm_log_chan(fchan, FTDM_LOG_WARNING, "Failed to cancel state change from %s to %s, state is not new anymore\n",
198 				ftdm_channel_state2str(fchan->last_state), ftdm_channel_state2str(fchan->state));
199 		return FTDM_FAIL;
200 	}
201 
202 	/* compute the last history index */
203 	hindex = (fchan->hindex == 0) ? (ftdm_array_len(fchan->history) - 1) : (fchan->hindex - 1);
204 	diff = fchan->history[hindex].end_time - fchan->history[hindex].time;
205 
206 	/* go back in time and revert the state to the previous state */
207 	state = fchan->state;
208 	last_state = fchan->last_state;
209 
210 	fchan->state = fchan->last_state;
211 	fchan->state_status = FTDM_STATE_STATUS_COMPLETED;
212 	fchan->last_state = fchan->history[hindex].last_state;
213 	fchan->hindex = hindex;
214 
215 	/* clear the state change flag */
216 	ftdm_clear_flag(fchan, FTDM_CHANNEL_STATE_CHANGE);
217 
218 	/* ack any pending indications as cancelled */
219 	ftdm_ack_indication(fchan, fchan->indication, FTDM_ECANCELED);
220 
221 	/* wake up anyone sleeping waiting for the state change to complete, it won't ever be completed */
222 	if (ftdm_test_flag(fchan, FTDM_CHANNEL_BLOCKING)) {
223 		ftdm_clear_flag(fchan, FTDM_CHANNEL_BLOCKING);
224 		ftdm_interrupt_signal(fchan->state_completed_interrupt);
225 	}
226 
227 	/* NOTE
228 	 * we could potentially also take out the channel from the pendingchans queue, but I believe is easier just leave it,
229 	 * the only side effect will be a call to ftdm_channel_advance_states() for a channel that has nothing to advance */
230 	ftdm_log_chan_ex(fchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Cancelled state change from %s to %s in %"FTDM_TIME_FMT" ms\n",
231 			ftdm_channel_state2str(last_state), ftdm_channel_state2str(state), diff);
232 
233 	return FTDM_SUCCESS;
234 }
235 
236 
ftdm_channel_set_state(const char * file,const char * func,int line,ftdm_channel_t * ftdmchan,ftdm_channel_state_t state,int waitrq,ftdm_usrmsg_t * usrmsg)237 FT_DECLARE(ftdm_status_t) ftdm_channel_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq, ftdm_usrmsg_t *usrmsg)
238 {
239 	ftdm_channel_save_usrmsg(ftdmchan, usrmsg);
240 
241 	if (ftdm_core_set_state(file, func, line, ftdmchan, state, waitrq) != FTDM_SUCCESS) {
242 		ftdm_usrmsg_free(&ftdmchan->usrmsg);
243 	}
244 	return FTDM_SUCCESS;
245 }
246 
247 /* this function MUST be called with the channel lock held. If waitrq == 1, the channel will be unlocked/locked (never call it with waitrq == 1 with an lock recursivity > 1) */
248 #define DEFAULT_WAIT_TIME 1000
ftdm_core_set_state(const char * file,const char * func,int line,ftdm_channel_t * ftdmchan,ftdm_channel_state_t state,int waitrq)249 static ftdm_status_t ftdm_core_set_state(const char *file, const char *func, int line, ftdm_channel_t *ftdmchan, ftdm_channel_state_t state, int waitrq)
250 {
251 	ftdm_status_t status;
252 	int ok = 1;
253 	int waitms = DEFAULT_WAIT_TIME;
254 
255 	if (!ftdm_test_flag(ftdmchan, FTDM_CHANNEL_READY)) {
256 		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR, "Ignored state change request from %s to %s, the channel is not ready\n",
257 				ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
258 		return FTDM_FAIL;
259 	}
260 
261 	if (ftdmchan->state_status != FTDM_STATE_STATUS_COMPLETED) {
262 		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_ERROR,
263 		"Ignored state change request from %s to %s, the previous state change has not been processed yet (status = %s)\n",
264 		ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state),
265 		ftdm_state_status2str(ftdmchan->state_status));
266 		return FTDM_FAIL;
267 	}
268 
269 	if (ftdmchan->state == state) {
270 		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "Why bother changing state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
271 		return FTDM_FAIL;
272 	}
273 
274 	if (!ftdmchan->state_completed_interrupt) {
275 		status = ftdm_interrupt_create(&ftdmchan->state_completed_interrupt, FTDM_INVALID_SOCKET, FTDM_NO_FLAGS);
276 		if (status != FTDM_SUCCESS) {
277 			ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_CRIT,
278 					"Failed to create state change interrupt when moving from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
279 			return status;
280 		}
281 	}
282 
283 	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NATIVE_SIGBRIDGE)) {
284 		goto perform_state_change;
285 	}
286 
287 	if (ftdmchan->span->state_map) {
288 		ok = ftdm_parse_state_map(ftdmchan, state, ftdmchan->span->state_map);
289 		goto end;
290 	}
291 
292 	/* basic core state validation (by-passed if the signaling module provides a state_map) */
293 	switch(ftdmchan->state) {
294 	case FTDM_CHANNEL_STATE_HANGUP:
295 	case FTDM_CHANNEL_STATE_TERMINATING:
296 		{
297 			ok = 0;
298 			switch(state) {
299 			case FTDM_CHANNEL_STATE_DOWN:
300 			case FTDM_CHANNEL_STATE_BUSY:
301 			case FTDM_CHANNEL_STATE_RESTART:
302 				ok = 1;
303 				break;
304 			default:
305 				break;
306 			}
307 		}
308 		break;
309 	case FTDM_CHANNEL_STATE_UP:
310 		{
311 			ok = 1;
312 			switch(state) {
313 			case FTDM_CHANNEL_STATE_PROGRESS:
314 			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
315 			case FTDM_CHANNEL_STATE_RING:
316 				ok = 0;
317 				break;
318 			default:
319 				break;
320 			}
321 		}
322 		break;
323 	case FTDM_CHANNEL_STATE_DOWN:
324 		{
325 			ok = 0;
326 
327 			switch(state) {
328 			case FTDM_CHANNEL_STATE_DIALTONE:
329 			case FTDM_CHANNEL_STATE_COLLECT:
330 			case FTDM_CHANNEL_STATE_DIALING:
331 			case FTDM_CHANNEL_STATE_RING:
332 			case FTDM_CHANNEL_STATE_PROGRESS_MEDIA:
333 			case FTDM_CHANNEL_STATE_PROGRESS:
334 			case FTDM_CHANNEL_STATE_IDLE:
335 			case FTDM_CHANNEL_STATE_GET_CALLERID:
336 			case FTDM_CHANNEL_STATE_GENRING:
337 				ok = 1;
338 				break;
339 			default:
340 				break;
341 			}
342 		}
343 		break;
344 	case FTDM_CHANNEL_STATE_BUSY:
345 		{
346 			switch(state) {
347 			case FTDM_CHANNEL_STATE_UP:
348 				ok = 0;
349 				break;
350 			default:
351 				break;
352 			}
353 		}
354 		break;
355 	case FTDM_CHANNEL_STATE_RING:
356 		{
357 			switch(state) {
358 			case FTDM_CHANNEL_STATE_UP:
359 				ok = 1;
360 				break;
361 			default:
362 				break;
363 			}
364 		}
365 		break;
366 	default:
367 		break;
368 	}
369 
370 end:
371 
372 	if (!ok) {
373 		ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_WARNING, "VETO state change from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
374 		goto done;
375 	}
376 
377 perform_state_change:
378 
379 	ftdm_log_chan_ex(ftdmchan, file, func, line, FTDM_LOG_LEVEL_DEBUG, "Changed state from %s to %s\n", ftdm_channel_state2str(ftdmchan->state), ftdm_channel_state2str(state));
380 	ftdmchan->last_state = ftdmchan->state;
381 	ftdmchan->state = state;
382 	ftdmchan->state_status = FTDM_STATE_STATUS_NEW;
383 	ftdmchan->history[ftdmchan->hindex].file = file;
384 	ftdmchan->history[ftdmchan->hindex].func = func;
385 	ftdmchan->history[ftdmchan->hindex].line = line;
386 	ftdmchan->history[ftdmchan->hindex].state = ftdmchan->state;
387 	ftdmchan->history[ftdmchan->hindex].last_state = ftdmchan->last_state;
388 	ftdmchan->history[ftdmchan->hindex].time = ftdm_current_time_in_ms();
389 	ftdmchan->history[ftdmchan->hindex].end_time = 0;
390 	ftdmchan->hindex++;
391 	if (ftdmchan->hindex == ftdm_array_len(ftdmchan->history)) {
392 		ftdmchan->hindex = 0;
393 	}
394 	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_STATE_CHANGE);
395 
396 	if (ftdmchan->span->pendingchans) {
397 		ftdm_queue_enqueue(ftdmchan->span->pendingchans, ftdmchan);
398 	} else {
399 		/* there is a potential deadlock here, if a signaling module is processing
400 		 * state changes while the ftdm_span_stop() function is called, the signaling
401 		 * thread will block until it can acquire the span lock, but the thread calling
402 		 * ftdm_span_stop() which holds the span lock is waiting on the signaling thread
403 		 * to finish ... The only reason to acquire the span lock is this flag, new
404 		 * signaling modules should use the pendingchans queue instead of this flag,
405 		 * as of today a few modules need still to be updated before we can get rid of
406 		 * this flag (ie, ftmod_libpri, ftmod_isdn, ftmod_analog) */
407 		ftdm_set_flag_locked(ftdmchan->span, FTDM_SPAN_STATE_CHANGE);
408 	}
409 
410 	if (ftdm_test_flag(ftdmchan, FTDM_CHANNEL_NONBLOCK)) {
411 		/* the channel should not block waiting for state processing */
412 		goto done;
413 	}
414 
415 	if (!waitrq) {
416 		/* no waiting was requested */
417 		goto done;
418 	}
419 
420 	/* let's wait for the state change to be completed by the signaling stack */
421 	ftdm_set_flag(ftdmchan, FTDM_CHANNEL_BLOCKING);
422 
423 	ftdm_mutex_unlock(ftdmchan->mutex);
424 
425 	status = ftdm_interrupt_wait(ftdmchan->state_completed_interrupt, waitms);
426 
427 	ftdm_mutex_lock(ftdmchan->mutex);
428 
429 	if (status != FTDM_SUCCESS) {
430 		ftdm_clear_flag(ftdmchan, FTDM_CHANNEL_BLOCKING);
431 		ftdm_log_chan_ex(ftdmchan, file, func, line,
432 				FTDM_LOG_LEVEL_WARNING, "state change from %s to %s was most likely not completed after aprox %dms\n",
433 				ftdm_channel_state2str(ftdmchan->last_state), ftdm_channel_state2str(state), DEFAULT_WAIT_TIME);
434 		ok = 0;
435 		goto done;
436 	}
437 done:
438 	return ok ? FTDM_SUCCESS : FTDM_FAIL;
439 }
440 
ftdm_channel_get_state(const ftdm_channel_t * ftdmchan)441 FT_DECLARE(int) ftdm_channel_get_state(const ftdm_channel_t *ftdmchan)
442 {
443 	int state;
444 	ftdm_channel_lock(ftdmchan);
445 	state = ftdmchan->state;
446 	ftdm_channel_unlock(ftdmchan);
447 	return state;
448 }
449 
ftdm_channel_get_state_str(const ftdm_channel_t * ftdmchan)450 FT_DECLARE(const char *) ftdm_channel_get_state_str(const ftdm_channel_t *ftdmchan)
451 {
452 	const char *state;
453 	ftdm_channel_lock(ftdmchan);
454 	state = ftdm_channel_state2str(ftdmchan->state);
455 	ftdm_channel_unlock(ftdmchan);
456 	return state;
457 }
458 
ftdm_channel_get_last_state(const ftdm_channel_t * ftdmchan)459 FT_DECLARE(int) ftdm_channel_get_last_state(const ftdm_channel_t *ftdmchan)
460 {
461 	int last_state;
462 	ftdm_channel_lock(ftdmchan);
463 	last_state = ftdmchan->last_state;
464 	ftdm_channel_unlock(ftdmchan);
465 	return last_state;
466 }
467 
ftdm_channel_get_last_state_str(const ftdm_channel_t * ftdmchan)468 FT_DECLARE(const char *) ftdm_channel_get_last_state_str(const ftdm_channel_t *ftdmchan)
469 {
470 	const char *state;
471 	ftdm_channel_lock(ftdmchan);
472 	state = ftdm_channel_state2str(ftdmchan->last_state);
473 	ftdm_channel_unlock(ftdmchan);
474 	return state;
475 }
476 
write_history_entry(const ftdm_channel_t * fchan,ftdm_stream_handle_t * stream,int i,ftdm_time_t * prevtime)477 static void write_history_entry(const ftdm_channel_t *fchan, ftdm_stream_handle_t *stream, int i, ftdm_time_t *prevtime)
478 {
479 	char func[255];
480 	char line[255];
481 	char states[255];
482 	const char *filename = NULL;
483 	snprintf(states, sizeof(states), "%-5.15s => %-5.15s", ftdm_channel_state2str(fchan->history[i].last_state), ftdm_channel_state2str(fchan->history[i].state));
484 	snprintf(func, sizeof(func), "[%s]", fchan->history[i].func);
485 	filename = strrchr(fchan->history[i].file, *FTDM_PATH_SEPARATOR);
486 	if (!filename) {
487 		filename = fchan->history[i].file;
488 	} else {
489 		filename++;
490 	}
491 	if (!(*prevtime)) {
492 		*prevtime = fchan->history[i].time;
493 	}
494 	snprintf(line, sizeof(func), "[%s:%d]", filename, fchan->history[i].line);
495 	stream->write_function(stream, "%-30.30s %-30.30s %-30.30s %lums\n", states, func, line, (fchan->history[i].time - *prevtime));
496 	*prevtime = fchan->history[i].time;
497 }
498 
ftdm_channel_get_history_str(const ftdm_channel_t * fchan)499 FT_DECLARE(char *) ftdm_channel_get_history_str(const ftdm_channel_t *fchan)
500 {
501 	uint8_t i = 0;
502 	ftdm_time_t currtime = 0;
503 	ftdm_time_t prevtime = 0;
504 
505 	ftdm_stream_handle_t stream = { 0 };
506 	FTDM_STANDARD_STREAM(stream);
507 	if (!fchan->history[0].file) {
508 		stream.write_function(&stream, "-- No state history --\n");
509 		return stream.data;
510 	}
511 
512 	stream.write_function(&stream, "%-30.30s %-30.30s %-30.30s %s",
513 			"-- States --", "-- Function --", "-- Location --", "-- Time Offset --\n");
514 
515 	for (i = fchan->hindex; i < ftdm_array_len(fchan->history); i++) {
516 		if (!fchan->history[i].file) {
517 			break;
518 		}
519 		write_history_entry(fchan, &stream, i, &prevtime);
520 	}
521 
522 	for (i = 0; i < fchan->hindex; i++) {
523 		write_history_entry(fchan, &stream, i, &prevtime);
524 	}
525 
526 	currtime = ftdm_current_time_in_ms();
527 
528 	stream.write_function(&stream, "\nTime since last state change: %lums\n", (currtime - prevtime));
529 
530 	return stream.data;
531 }
532 
ftdm_channel_advance_states(ftdm_channel_t * fchan)533 FT_DECLARE(ftdm_status_t) ftdm_channel_advance_states(ftdm_channel_t *fchan)
534 {
535 	ftdm_channel_state_t state;
536 
537 	ftdm_assert_return(fchan->span->state_processor, FTDM_FAIL, "Cannot process states without a state processor!\n");
538 
539 	while (fchan->state_status == FTDM_STATE_STATUS_NEW) {
540 		state = fchan->state;
541 		ftdm_log_chan(fchan, FTDM_LOG_DEBUG, "Executing state processor for %s\n", ftdm_channel_state2str(fchan->state));
542 		fchan->span->state_processor(fchan);
543 		if (state == fchan->state && fchan->state_status == FTDM_STATE_STATUS_NEW) {
544 			/* if the state did not change and is still NEW, the state status must go to PROCESSED
545 			 * otherwise we don't touch it since is a new state and the old state was
546 			 * already completed implicitly by the state_processor() function via some internal
547 			 * call to ftdm_set_state() */
548 			fchan->state_status = FTDM_STATE_STATUS_PROCESSED;
549 		}
550 	}
551 
552 	return FTDM_SUCCESS;
553 }
554 
ftdm_check_state_all(ftdm_span_t * span,ftdm_channel_state_t state)555 FT_DECLARE(int) ftdm_check_state_all(ftdm_span_t *span, ftdm_channel_state_t state)
556 {
557 	uint32_t j;
558 	for(j = 1; j <= span->chan_count; j++) {
559 		if (span->channels[j]->state != state || ftdm_test_flag(span->channels[j], FTDM_CHANNEL_STATE_CHANGE)) {
560 			return 0;
561 		}
562 	}
563 	return 1;
564 }
565 
566 /* For Emacs:
567  * Local Variables:
568  * mode:c
569  * indent-tabs-mode:t
570  * tab-width:4
571  * c-basic-offset:4
572  * End:
573  * For VIM:
574  * vim:set softtabstop=4 shiftwidth=4 tabstop=4 noet:
575  */
576