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