[Freeswitch-svn] [commit] r4785 - in freeswitch/trunk/src: . include mod/applications/mod_commands mod/applications/mod_dptools mod/applications/mod_ivrtest
Author: anthm Date: Wed Mar 28 19:37:12 2007 New Revision: 4785 Modified: freeswitch/trunk/src/include/switch_core.h freeswitch/trunk/src/include/switch_ivr.h freeswitch/trunk/src/include/switch_types.h freeswitch/trunk/src/mod/applications/mod_commands/mod_commands.c freeswitch/trunk/src/mod/applications/mod_dptools/mod_dptools.c freeswitch/trunk/src/mod/applications/mod_ivrtest/mod_ivrtest.c freeswitch/trunk/src/switch_channel.c freeswitch/trunk/src/switch_core.c freeswitch/trunk/src/switch_ivr.c Log: Addition of scheduler engine and a few applications to use it. This patch adds a scheduler thread to the core and moves the heartbeat event to use the new scheduler as an example. Also The following features are implemented that use this scheduler: sched_hangup dialplan application: <action application="sched_hangup" data="+10 normal_clearing bleg"/> ** The cause code is optional and the optional bleg keyword will only hangup the channel the current channel is bridged to if the call is in a bridge. sched_transfer dialplan application: <action application="sched_transfer" data="+10 1000 XML default"/> ** The last 2 args (dialplan and context) are optional sched_broadcast dialplan application: <action application="sched_broadcast" data="+10 playback:/tmp/foo.wav"/> <action application="sched_broadcast" data="+10 playback!normal_clearing:/tmp/foo.wav"/> ** The optional !<cause_code> can be added to make the channel hangup after broadcasting the file. sched_hangup api function: sched_hangup +10 <uuid_string> normal_clearing ** The cause code is optional sched_transfer api function: sched_transfer +10 <uuid_string> 1000 XML default ** The last 2 args (dialplan and context) are optional sched_broadcast api function: sched_broadcast +10 <uuid_str> playback:/tmp/foo.wav sched_broadcast +10 <uuid_str> playback!normal_clearing:/tmp/foo.wav ** The optional !<cause_code> can be added to make the channel hangup after broadcasting the file. The new C functions in the core are documented in the doxeygen. *NOTE* This commit should satisfy at least 2 bounties on the wiki Modified: freeswitch/trunk/src/include/switch_core.h ============================================================================== --- freeswitch/trunk/src/include/switch_core.h (original) +++ freeswitch/trunk/src/include/switch_core.h Wed Mar 28 19:37:12 2007 @@ -104,17 +104,68 @@ struct switch_core_runtime; struct switch_core_port_allocator; +struct switch_core_scheduler_task { + time_t created; + time_t runtime; + uint32_t cmd_id; + char *group; + void *cmd_arg; + uint32_t task_id; +}; + + /*! \defgroup core1 Core Library \ingroup FREESWITCH \{ */ - ///\defgroup mb1 Media Bugs ///\ingroup core1 ///\{ + +///\defgroup sched1 Scheduler +///\ingroup core1 +///\{ + + +/*! + \brief Schedule a task in the future + \param runtime the time in epoch seconds to execute the task. + \param func the callback function to execute when the task is executed. + \param desc an arbitrary description of the task. + \param group a group id tag to link multiple tasks to a single entity. + \param cmd_id an arbitrary index number be used in the callback. + \param cmd_arg user data to be passed to the callback. + \param flags flags to alter behaviour + \return the id of the task +*/ +SWITCH_DECLARE(uint32_t) switch_core_scheduler_add_task(time_t task_runtime, + switch_core_scheduler_func_t func, + char *desc, + char *group, + uint32_t cmd_id, + void *cmd_arg, + switch_scheduler_flag_t flags); + +/*! + \brief Delete a scheduled task + \param task_id the id of the task + \return SWITCH_STATUS_SUCCESS if the task was deleted. +*/ +SWITCH_DECLARE(switch_status_t) switch_core_scheduler_del_task_id(uint32_t task_id); + +/*! + \brief Delete a scheduled task based on the group name + \param group the group name + \return SWITCH_STATUS_SUCCESS if any tasks were deleted +*/ +SWITCH_DECLARE(switch_status_t) switch_core_scheduler_del_task_group(char *group); + + +///\} + /*! \brief Add a media bug to the session \param session the session to add the bug to Modified: freeswitch/trunk/src/include/switch_ivr.h ============================================================================== --- freeswitch/trunk/src/include/switch_ivr.h (original) +++ freeswitch/trunk/src/include/switch_ivr.h Wed Mar 28 19:37:12 2007 @@ -332,6 +332,28 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_session_transfer(switch_core_session_t *session, char *extension, char *dialplan, char *context); /*! + \brief Transfer an existing session to another location in the future + \param runtime the time (int epoch seconds) to transfer the call + \param uuid the uuid of the session to transfer + \param extension the new extension + \param dialplan the new dialplan (OPTIONAL, may be NULL) + \param context the new context (OPTIONAL, may be NULL) + \return the id of the task +*/ +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_transfer(time_t runtime, char *uuid, char *extension, char *dialplan, char *context); + + +/*! + \brief Hangup an existing session in the future + \param runtime the time (int epoch seconds) to transfer the call + \param uuid the uuid of the session to hangup + \param cause the hanup cause code + \param bleg hangup up the B-Leg if possible + \return the id of the task +*/ +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_hangup(time_t runtime, char *uuid, switch_call_cause_t cause, switch_bool_t bleg); + +/*! \brief Bridge two existing sessions \param originator_uuid the uuid of the originator \param originatee_uuid the uuid of the originator @@ -384,6 +406,16 @@ SWITCH_DECLARE(switch_status_t) switch_ivr_unhold(switch_core_session_t *session); /*! + \brief Signal the session to broadcast audio in the future + \param runtime when (in epoch time) to run the broadcast + \param uuid the uuid of the session to broadcast on + \param path the path data of the broadcast "/path/to/file.wav [<timer name>]" or "speak:<engine>|<voice>|<Text to say>" + \param flags flags to send to the request (SMF_ECHO_BRIDGED to send the broadcast to both sides of the call) + \return the id of the task +*/ +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_broadcast(time_t runtime, char *uuid, char *path, switch_media_flag_t flags); + +/*! \brief Signal the session to broadcast audio \param uuid the uuid of the session to broadcast on \param path the path data of the broadcast "/path/to/file.wav [<timer name>]" or "speak:<engine>|<voice>|<Text to say>" Modified: freeswitch/trunk/src/include/switch_types.h ============================================================================== --- freeswitch/trunk/src/include/switch_types.h (original) +++ freeswitch/trunk/src/include/switch_types.h Wed Mar 28 19:37:12 2007 @@ -168,6 +168,12 @@ } switch_management_action_t; typedef enum { + SSHF_NONE = 0, + SSHF_OWN_THREAD = (1 << 0), + SSHF_FREE_ARG = (1 << 1) +} switch_scheduler_flag_t; + +typedef enum { SMF_NONE = 0, SMF_REBRIDGE = (1 << 0), SMF_ECHO_ALEG = (1 << 1), @@ -910,7 +916,8 @@ SWITCH_CAUSE_LOSE_RACE = 502, SWITCH_CAUSE_MANAGER_REQUEST = 503, SWITCH_CAUSE_BLIND_TRANSFER = 600, - SWITCH_CAUSE_ATTENDED_TRANSFER = 601 + SWITCH_CAUSE_ATTENDED_TRANSFER = 601, + SWITCH_CAUSE_ALLOTTED_TIMEOUT = 602 } switch_call_cause_t; typedef enum { @@ -979,6 +986,8 @@ typedef void (*switch_application_function_t)(switch_core_session_t *, char *); typedef void (*switch_event_callback_t)(switch_event_t *); typedef switch_caller_extension_t *(*switch_dialplan_hunt_function_t)(switch_core_session_t *, void *); +typedef struct switch_core_scheduler_task switch_core_scheduler_task_t; +typedef void (*switch_core_scheduler_func_t)(switch_core_scheduler_task_t *task); typedef switch_status_t (*switch_state_handler_t)(switch_core_session_t *); typedef switch_status_t (*switch_outgoing_channel_hook_t)(switch_core_session_t *, switch_caller_profile_t *, switch_core_session_t *); typedef switch_status_t (*switch_answer_channel_hook_t)(switch_core_session_t *); Modified: freeswitch/trunk/src/mod/applications/mod_commands/mod_commands.c ============================================================================== --- freeswitch/trunk/src/mod/applications/mod_commands/mod_commands.c (original) +++ freeswitch/trunk/src/mod/applications/mod_commands/mod_commands.c Wed Mar 28 19:37:12 2007 @@ -51,6 +51,9 @@ static switch_api_interface_t media_api_interface; static switch_api_interface_t hold_api_interface; static switch_api_interface_t broadcast_api_interface; +static switch_api_interface_t sched_broadcast_api_interface; +static switch_api_interface_t sched_transfer_api_interface; +static switch_api_interface_t sched_hangup_api_interface; static switch_status_t status_function(char *cmd, switch_core_session_t *session, switch_stream_handle_t *stream) { @@ -245,6 +248,89 @@ return SWITCH_STATUS_SUCCESS; } + +static switch_status_t sched_transfer_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + switch_core_session_t *session = NULL; + char *argv[6] = {0}; + int argc = 0; + + if (isession) { + return SWITCH_STATUS_FALSE; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (switch_strlen_zero(cmd) || argc < 2 || argc > 5) { + stream->write_function(stream, "USAGE: %s\n", sched_transfer_api_interface.syntax); + } else { + char *uuid = argv[1]; + char *dest = argv[2]; + char *dp = argv[3]; + char *context = argv[4]; + time_t when; + + if (*argv[0] == '+') { + when = time (NULL) + atol(argv[0] + 1); + } else { + when = atol(argv[0]); + } + + if ((session = switch_core_session_locate(uuid))) { + switch_ivr_schedule_transfer(when, uuid, dest, dp, context); + stream->write_function(stream, "OK\n"); + switch_core_session_rwunlock(session); + } else { + stream->write_function(stream, "No Such Channel!\n"); + } + } + + return SWITCH_STATUS_SUCCESS; +} + +static switch_status_t sched_hangup_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + switch_core_session_t *session = NULL; + char *argv[4] = {0}; + int argc = 0; + + if (isession) { + return SWITCH_STATUS_FALSE; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (switch_strlen_zero(cmd) || argc < 1) { + stream->write_function(stream, "USAGE: %s\n", sched_hangup_api_interface.syntax); + } else { + char *uuid = argv[1]; + char *cause_str = argv[2]; + time_t when; + switch_call_cause_t cause = SWITCH_CAUSE_ALLOTTED_TIMEOUT; + + if (*argv[0] == '+') { + when = time (NULL) + atol(argv[0] + 1); + } else { + when = atol(argv[0]); + } + + if (cause_str) { + cause = switch_channel_str2cause(cause_str); + } + + if ((session = switch_core_session_locate(uuid))) { + switch_ivr_schedule_hangup(when, uuid, cause, SWITCH_FALSE); + stream->write_function(stream, "OK\n"); + switch_core_session_rwunlock(session); + } else { + stream->write_function(stream, "No Such Channel!\n"); + } + } + + return SWITCH_STATUS_SUCCESS; +} + + static switch_status_t uuid_media_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) { char *argv[4] = {0}; @@ -313,6 +399,50 @@ return SWITCH_STATUS_SUCCESS; } + +static switch_status_t sched_broadcast_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) +{ + char *argv[4] = {0}; + int argc = 0; + switch_status_t status = SWITCH_STATUS_FALSE; + + if (isession) { + return status; + } + + argc = switch_separate_string(cmd, ' ', argv, (sizeof(argv) / sizeof(argv[0]))); + + if (switch_strlen_zero(cmd) || argc < 3) { + stream->write_function(stream, "USAGE: %s\n", sched_broadcast_api_interface.syntax); + } else { + switch_media_flag_t flags = SMF_NONE; + time_t when; + + if (*argv[0] == '+') { + when = time (NULL) + atol(argv[0] + 1); + } else { + when = atol(argv[0]); + } + + if (argv[3]) { + if (!strcmp(argv[3], "both")) { + flags |= (SMF_ECHO_ALEG | SMF_ECHO_BLEG); + } else if (!strcmp(argv[3], "aleg")) { + flags |= SMF_ECHO_ALEG; + } else if (!strcmp(argv[3], "bleg")) { + flags |= SMF_ECHO_BLEG; + } + } else { + flags |= SMF_ECHO_ALEG; + } + + status = switch_ivr_schedule_broadcast(when, argv[1], argv[2], flags); + stream->write_function(stream, "+OK Message Scheduled\n"); + } + + return SWITCH_STATUS_SUCCESS; +} + static switch_status_t uuid_hold_function(char *cmd, switch_core_session_t *isession, switch_stream_handle_t *stream) { char *argv[4] = {0}; @@ -707,12 +837,36 @@ return SWITCH_STATUS_SUCCESS; } +static switch_api_interface_t sched_transfer_api_interface = { + /*.interface_name */ "sched_transfer", + /*.desc */ "Schedule a broadcast event to a running call", + /*.function */ sched_transfer_function, + /*.syntax */ "[+]<time> <uuid> <extension> [<dialplan>] [<context>]", + /*.next */ NULL +}; + +static switch_api_interface_t sched_broadcast_api_interface = { + /*.interface_name */ "sched_broadcast", + /*.desc */ "Schedule a broadcast event to a running call", + /*.function */ sched_broadcast_function, + /*.syntax */ "[+]<time> <uuid> <path> [aleg|bleg|both]", + /*.next */ &sched_transfer_api_interface +}; + +static switch_api_interface_t sched_hangup_api_interface = { + /*.interface_name */ "sched_hangup", + /*.desc */ "Schedule a running call to hangup", + /*.function */ sched_hangup_function, + /*.syntax */ "[+]<time> <uuid> [<cause>]", + /*.next */ &sched_broadcast_api_interface +}; + static switch_api_interface_t version_api_interface = { /*.interface_name */ "version", /*.desc */ "Show version of the switch", /*.function */ version_function, /*.syntax */ "", - /*.next */ NULL + /*.next */ &sched_hangup_api_interface }; static switch_api_interface_t help_api_interface = { Modified: freeswitch/trunk/src/mod/applications/mod_dptools/mod_dptools.c ============================================================================== --- freeswitch/trunk/src/mod/applications/mod_dptools/mod_dptools.c (original) +++ freeswitch/trunk/src/mod/applications/mod_dptools/mod_dptools.c Wed Mar 28 19:37:12 2007 @@ -96,6 +96,99 @@ } } +static void sched_transfer_function(switch_core_session_t *session, char *data) +{ + int argc; + char *argv[4] = {0}; + char *mydata; + + if (data && (mydata = switch_core_session_strdup(session, data))) { + if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) >= 2) { + time_t when; + + if (*argv[0] == '+') { + when = time (NULL) + atol(argv[0] + 1); + } else { + when = atol(argv[0]); + } + + switch_ivr_schedule_transfer(when, switch_core_session_get_uuid(session), argv[1], argv[2], argv[3]); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Args\n"); + } + } +} + +static void sched_hangup_function(switch_core_session_t *session, char *data) +{ + int argc; + char *argv[5] = {0}; + char *mydata; + + if (data && (mydata = switch_core_session_strdup(session, data))) { + if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) >= 1) { + time_t when; + switch_call_cause_t cause = SWITCH_CAUSE_ALLOTTED_TIMEOUT; + switch_bool_t bleg = SWITCH_FALSE; + + if (*argv[0] == '+') { + when = time (NULL) + atol(argv[0] + 1); + } else { + when = atol(argv[0]); + } + + if (argv[1]) { + cause = switch_channel_str2cause(argv[1]); + } + + if (argv[2] && !strcasecmp(argv[2], "bleg")) { + bleg = SWITCH_TRUE; + } + + switch_ivr_schedule_hangup(when, switch_core_session_get_uuid(session), cause, bleg); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No time specified.\n"); + } + } +} + + +static void sched_broadcast_function(switch_core_session_t *session, char *data) +{ + int argc; + char *argv[6] = {0}; + char *mydata; + + if (data && (mydata = switch_core_session_strdup(session, data))) { + if ((argc = switch_separate_string(mydata, ' ', argv, (sizeof(argv) / sizeof(argv[0])))) >= 2) { + time_t when; + switch_media_flag_t flags = SMF_NONE; + + if (*argv[0] == '+') { + when = time (NULL) + atol(argv[0] + 1); + } else { + when = atol(argv[0]); + } + + if (argv[2]) { + if (!strcmp(argv[2], "both")) { + flags |= (SMF_ECHO_ALEG | SMF_ECHO_BLEG); + } else if (!strcmp(argv[2], "aleg")) { + flags |= SMF_ECHO_ALEG; + } else if (!strcmp(argv[2], "bleg")) { + flags |= SMF_ECHO_BLEG; + } + } else { + flags |= SMF_ECHO_ALEG; + } + + switch_ivr_schedule_broadcast(when, switch_core_session_get_uuid(session), argv[1], flags); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid Args\n"); + } + } +} + static void sleep_function(switch_core_session_t *session, char *data) { @@ -509,6 +602,7 @@ } } + static switch_api_interface_t strepoch_api_interface = { /*.interface_name */ "strepoch", /*.desc */ "Convert a date string into epoch time", @@ -541,6 +635,39 @@ /*.next */ &dptools_api_interface }; + + +static switch_application_interface_t sched_transfer_application_interface = { + /*.interface_name */ "sched_transfer", + /*.application_function */ sched_transfer_function, + /*.long_desc */ "Schedule a transfer in the future", + /*.short_desc */ "Schedule a transfer in the future", + /*.syntax */ "[+]<time> <extension> <dialplan> <context>", + /* flags */ SAF_SUPPORT_NOMEDIA, + /*.next */ NULL +}; + +static switch_application_interface_t sched_broadcast_application_interface = { + /*.interface_name */ "sched_broadcast", + /*.application_function */ sched_broadcast_function, + /*.long_desc */ "Schedule a broadcast in the future", + /*.short_desc */ "Schedule a broadcast in the future", + /*.syntax */ "[+]<time> <path> [aleg|bleg|both]", + /* flags */ SAF_SUPPORT_NOMEDIA, + /*.next */ &sched_transfer_application_interface +}; + +static switch_application_interface_t sched_hangup_application_interface = { + /*.interface_name */ "sched_hangup", + /*.application_function */ sched_hangup_function, + /*.long_desc */ "Schedule a hangup in the future", + /*.short_desc */ "Schedule a hangup in the future", + /*.syntax */ "[+]<time> [<cause>]", + /* flags */ SAF_SUPPORT_NOMEDIA, + /*.next */ &sched_broadcast_application_interface +}; + + static const switch_application_interface_t queuedtmf_application_interface = { /*.interface_name */ "queue_dtmf", /*.application_function */ queue_dtmf_function, @@ -548,7 +675,7 @@ /* short_desc */ "Queue dtmf to be sent", /* syntax */ "<dtmf_data>", /* flags */ SAF_SUPPORT_NOMEDIA, - /*.next */ NULL + /*.next */ &sched_hangup_application_interface }; static const switch_application_interface_t redirect_application_interface = { Modified: freeswitch/trunk/src/mod/applications/mod_ivrtest/mod_ivrtest.c ============================================================================== --- freeswitch/trunk/src/mod/applications/mod_ivrtest/mod_ivrtest.c (original) +++ freeswitch/trunk/src/mod/applications/mod_ivrtest/mod_ivrtest.c Wed Mar 28 19:37:12 2007 @@ -233,6 +233,7 @@ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Done\n"); } +#ifdef BUGTEST static switch_bool_t bug_callback(switch_media_bug_t *bug, void *user_data, switch_abc_type_t type) { switch_frame_t *frame; @@ -249,9 +250,11 @@ return SWITCH_TRUE; } +#endif static void bugtest_function(switch_core_session_t *session, char *data) { +#ifdef BUGTEST switch_media_bug_t *bug; switch_channel_t *channel = switch_core_session_get_channel(session); switch_status_t status; @@ -264,6 +267,11 @@ switch_channel_hangup(channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); return; } +#endif + //switch_ivr_schedule_broadcast(time(NULL) + 10, switch_core_session_get_uuid(session), "/Users/anthm/sr8k.wav", SMF_ECHO_ALEG); + //switch_ivr_schedule_transfer(time(NULL) + 10, switch_core_session_get_uuid(session), "2000", NULL, NULL); + //switch_ivr_schedule_hangup(time(NULL) + 10, switch_core_session_get_uuid(session), SWITCH_CAUSE_ALLOTTED_TIMEOUT); + switch_ivr_play_file(session, NULL, data, NULL); } Modified: freeswitch/trunk/src/switch_channel.c ============================================================================== --- freeswitch/trunk/src/switch_channel.c (original) +++ freeswitch/trunk/src/switch_channel.c Wed Mar 28 19:37:12 2007 @@ -95,6 +95,7 @@ { "MANAGER_REQUEST", SWITCH_CAUSE_MANAGER_REQUEST }, { "BLIND_TRANSFER", SWITCH_CAUSE_BLIND_TRANSFER }, { "ATTENDED_TRANSFER", SWITCH_CAUSE_ATTENDED_TRANSFER }, + { "ALLOTTED_TIMEOUT", SWITCH_CAUSE_ALLOTTED_TIMEOUT}, { NULL, 0 } }; Modified: freeswitch/trunk/src/switch_core.c ============================================================================== --- freeswitch/trunk/src/switch_core.c (original) +++ freeswitch/trunk/src/switch_core.c Wed Mar 28 19:37:12 2007 @@ -147,6 +147,19 @@ SWITCH_DECLARE_DATA switch_directories SWITCH_GLOBAL_dirs = {0}; +struct switch_core_scheduler_task_container { + switch_core_scheduler_task_t task; + time_t executed; + int in_thread; + int destroyed; + switch_core_scheduler_func_t func; + switch_memory_pool_t *pool; + uint32_t flags; + char *desc; + struct switch_core_scheduler_task_container *next; +}; +typedef struct switch_core_scheduler_task_container switch_core_scheduler_task_container_t; + struct switch_core_runtime { switch_time_t initiated; uint32_t session_id; @@ -169,6 +182,10 @@ uint32_t shutting_down; uint8_t running; char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + switch_core_scheduler_task_container_t *task_list; + switch_mutex_t *task_mutex; + uint32_t task_id; + int task_thread_running; }; /* Prototypes */ @@ -196,6 +213,238 @@ } } +static void send_heartbeat(void) +{ + switch_event_t *event; + switch_core_time_duration_t duration; + + switch_core_measure_time(switch_core_uptime(), &duration); + + if (switch_event_create(&event, SWITCH_EVENT_HEARTBEAT) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Info", "System Ready"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Up-Time", + "%u year%s, " + "%u day%s, " + "%u hour%s, " + "%u minute%s, " + "%u second%s, " + "%u millisecond%s, " + "%u microsecond%s\n", + duration.yr, duration.yr == 1 ? "" : "s", + duration.day, duration.day == 1 ? "" : "s", + duration.hr, duration.hr == 1 ? "" : "s", + duration.min, duration.min == 1 ? "" : "s", + duration.sec, duration.sec == 1 ? "" : "s", + duration.ms, duration.ms == 1 ? "" : "s", + duration.mms, duration.mms == 1 ? "" : "s" + ); + + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Count", "%u", switch_core_session_count()); + switch_event_fire(&event); + } +} + +static void heartbeat_callback(switch_core_scheduler_task_t *task) +{ + send_heartbeat(); + + /* reschedule this task */ + task->runtime += 20; +} + +static void switch_core_scheduler_execute(switch_core_scheduler_task_container_t *tp) +{ + //switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Executing task %u %s (%s)\n", tp->task.task_id, tp->desc, switch_str_nil(tp->task.group)); + + tp->func(&tp->task); + + if (tp->task.runtime > tp->executed) { + tp->executed = 0; + } else { + tp->destroyed = 1; + } +} + +static void *SWITCH_THREAD_FUNC task_own_thread(switch_thread_t *thread, void *obj) +{ + switch_core_scheduler_task_container_t *tp = (switch_core_scheduler_task_container_t *) obj; + switch_memory_pool_t *pool; + + pool = tp->pool; + tp->pool = NULL; + + switch_core_scheduler_execute(tp); + switch_core_destroy_memory_pool(&pool); + tp->in_thread = 0; + + return NULL; +} + +static int task_thread_loop(int done) +{ + switch_core_scheduler_task_container_t *tofree, *tp, *last = NULL; + + + switch_mutex_lock(runtime.task_mutex); + for (tp = runtime.task_list; tp; tp = tp->next) { + if (done) { + tp->destroyed = 1; + } else { + time_t now = time(NULL); + if (now >= tp->task.runtime && !tp->in_thread) { + tp->executed = now; + if (switch_test_flag(tp, SSHF_OWN_THREAD)) { + switch_thread_t *thread; + switch_threadattr_t *thd_attr; + assert(switch_core_new_memory_pool(&tp->pool) == SWITCH_STATUS_SUCCESS); + switch_threadattr_create(&thd_attr, tp->pool); + switch_threadattr_detach_set(thd_attr, 1); + tp->in_thread = 1; + switch_thread_create(&thread, thd_attr, task_own_thread, tp, tp->pool); + } else { + switch_core_scheduler_execute(tp); + } + } + } + } + switch_mutex_unlock(runtime.task_mutex); + switch_mutex_lock(runtime.task_mutex); + for (tp = runtime.task_list; tp; ) { + if (tp->destroyed && !tp->in_thread) { + tofree = tp; + tp = tp->next; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Deleting task %u %s (%s)\n", + tofree->task.task_id, tofree->desc, switch_str_nil(tofree->task.group)); + if (last) { + last->next = tofree->next; + } else { + runtime.task_list = tofree->next; + } + switch_safe_free(tofree->task.group); + if (tofree->task.cmd_arg && switch_test_flag(tofree, SSHF_FREE_ARG)) { + free(tofree->task.cmd_arg); + } + switch_safe_free(tofree->desc); + free(tofree); + } else { + last = tp; + tp = tp->next; + } + } + switch_mutex_unlock(runtime.task_mutex); + + return done; +} + +static void *SWITCH_THREAD_FUNC switch_core_task_thread(switch_thread_t *thread, void *obj) +{ + + runtime.task_thread_running = 1; + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Starting task thread\n"); + while(runtime.task_thread_running == 1) { + if (task_thread_loop(0)) { + break; + } + switch_yield(500000); + } + + task_thread_loop(1); + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Task thread ending\n"); + runtime.task_thread_running = 0; + + return NULL; +} + +static void switch_core_task_thread_launch(void) +{ + switch_thread_t *thread; + switch_threadattr_t *thd_attr; + switch_threadattr_create(&thd_attr, runtime.memory_pool); + switch_threadattr_detach_set(thd_attr, 1); + switch_thread_create(&thread, thd_attr, switch_core_task_thread, NULL, runtime.memory_pool); +} + +SWITCH_DECLARE(uint32_t) switch_core_scheduler_add_task(time_t task_runtime, + switch_core_scheduler_func_t func, + char *desc, + char *group, + uint32_t cmd_id, + void *cmd_arg, + switch_scheduler_flag_t flags) +{ + switch_core_scheduler_task_container_t *container, *tp; + + switch_mutex_lock(runtime.task_mutex); + assert((container = malloc(sizeof(*container)))); + memset(container, 0, sizeof(*container)); + assert(func); + container->func = func; + time(&container->task.created); + container->task.runtime = task_runtime; + container->task.group = strdup(group ? group : "none"); + container->task.cmd_id = cmd_id; + container->task.cmd_arg = cmd_arg; + container->flags = flags; + container->desc = strdup(desc ? desc : "none"); + + for (tp = runtime.task_list; tp && tp->next; tp = tp->next); + + if (tp) { + tp->next = container; + } else { + runtime.task_list = container; + } + + for (container->task.task_id = 0; !container->task.task_id; container->task.task_id = ++runtime.task_id); + + switch_mutex_unlock(runtime.task_mutex); + + tp = container; + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Added task %u %s (%s) to run at %ld\n", + tp->task.task_id, tp->desc, switch_str_nil(tp->task.group), task_runtime); + + + return container->task.task_id; +} + +SWITCH_DECLARE(switch_status_t) switch_core_scheduler_del_task_id(uint32_t task_id) +{ + switch_core_scheduler_task_container_t *tp; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_mutex_lock(runtime.task_mutex); + for (tp = runtime.task_list; tp; tp = tp->next) { + if (tp->task.task_id == task_id) { + tp->destroyed++; + status = SWITCH_STATUS_SUCCESS; + break; + } + } + switch_mutex_unlock(runtime.task_mutex); + + return status; +} + +SWITCH_DECLARE(switch_status_t) switch_core_scheduler_del_task_group(char *group) +{ + switch_core_scheduler_task_container_t *tp; + switch_status_t status = SWITCH_STATUS_FALSE; + + switch_mutex_lock(runtime.task_mutex); + for (tp = runtime.task_list; tp; tp = tp->next) { + if (!strcmp(tp->task.group, group)) { + tp->destroyed++; + status = SWITCH_STATUS_SUCCESS; + } + } + switch_mutex_unlock(runtime.task_mutex); + + return status; +} + + static void switch_core_media_bug_destroy(switch_media_bug_t *bug) { switch_buffer_destroy(&bug->raw_read_buffer); @@ -2148,7 +2397,8 @@ } if (!session->read_codec && (*frame)->codec) { - need_codec = TRUE; + status = SWITCH_STATUS_FALSE; + goto done; } if (session->bugs && !need_codec) { @@ -2422,7 +2672,7 @@ } if (!session->write_codec && frame->codec) { - need_codec = TRUE; + return SWITCH_STATUS_FALSE; } if (session->bugs && !need_codec) { @@ -3153,11 +3403,10 @@ switch_event_t *event; const switch_application_interface_t *application_interface; - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_DEBUG, "Standard EXECUTE\n"); + if ((extension = switch_channel_get_caller_extension(session->channel)) == 0) { - switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "No Extension!\n"); - switch_channel_hangup(session->channel, SWITCH_CAUSE_DESTINATION_OUT_OF_ORDER); + switch_channel_hangup(session->channel, SWITCH_CAUSE_NORMAL_CLEARING); return; } @@ -3731,6 +3980,8 @@ switch_event_t *event; switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_NOTICE, "Close Channel %s\n", switch_channel_get_name((*session)->channel)); + + switch_core_scheduler_del_task_group((*session)->uuid_str); switch_mutex_lock(runtime.session_table_mutex); switch_core_hash_delete(runtime.session_table, (*session)->uuid_str); @@ -4143,7 +4394,6 @@ char *sqlbuf = (char *) malloc(sql_len); char *sql; switch_size_t newlen; - uint32_t loops = 0; if (!runtime.event_db) { runtime.event_db = switch_core_db_handle(); @@ -4198,38 +4448,6 @@ *sqlbuf = '\0'; } - if (loops++ >= 5000) { - switch_event_t *event; - switch_core_time_duration_t duration; - - switch_core_measure_time(switch_core_uptime(), &duration); - - if (switch_event_create(&event, SWITCH_EVENT_HEARTBEAT) == SWITCH_STATUS_SUCCESS) { - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Event-Info", "System Ready"); - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Up-Time", - "%u year%s, " - "%u day%s, " - "%u hour%s, " - "%u minute%s, " - "%u second%s, " - "%u millisecond%s, " - "%u microsecond%s\n", - duration.yr, duration.yr == 1 ? "" : "s", - duration.day, duration.day == 1 ? "" : "s", - duration.hr, duration.hr == 1 ? "" : "s", - duration.min, duration.min == 1 ? "" : "s", - duration.sec, duration.sec == 1 ? "" : "s", - duration.ms, duration.ms == 1 ? "" : "s", - duration.mms, duration.mms == 1 ? "" : "s" - ); - - switch_event_add_header(event, SWITCH_STACK_BOTTOM, "Session-Count", "%u", switch_core_session_count()); - switch_event_fire(&event); - } - - loops = 0; - } - if (nothing_in_queue) { switch_yield(1000); } @@ -4672,11 +4890,17 @@ runtime.running = 1; switch_core_hash_init(&runtime.session_table, runtime.memory_pool); switch_mutex_init(&runtime.session_table_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool); + switch_mutex_init(&runtime.task_mutex, SWITCH_MUTEX_NESTED, runtime.memory_pool); #ifdef CRASH_PROT switch_core_hash_init(&runtime.stack_table, runtime.memory_pool); #endif + + + switch_core_task_thread_launch(); runtime.initiated = switch_time_now(); + switch_core_scheduler_add_task(time(NULL), heartbeat_callback, "heartbeat", "core", 0, NULL, SSHF_NONE); + switch_uuid_get(&uuid); switch_uuid_format(runtime.uuid_str, &uuid); @@ -4836,11 +5060,28 @@ while (switch_queue_size(runtime.sql_queue) > 0) { switch_yield(10000); } + + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Stopping Task Thread\n"); + if (runtime.task_thread_running == 1) { + int sanity = 0; + + runtime.task_thread_running = -1; + + while(runtime.task_thread_running) { + switch_yield(100000); + if (++sanity > 10) { + break; + } + } + } + switch_core_db_close(runtime.db); switch_core_db_close(runtime.event_db); switch_xml_destroy(); switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_CONSOLE, "Finalizing Shutdown.\n"); switch_log_shutdown(); + + if(runtime.console != stdout && runtime.console != stderr) { fclose(runtime.console); Modified: freeswitch/trunk/src/switch_ivr.c ============================================================================== --- freeswitch/trunk/src/switch_ivr.c (original) +++ freeswitch/trunk/src/switch_ivr.c Wed Mar 28 19:37:12 2007 @@ -130,7 +130,7 @@ if (cause_name) { cause = switch_channel_str2cause(cause_name); } - + switch_channel_hangup(channel, cause); } else if (cmd_hash == CMD_NOMEDIA) { char *uuid = switch_event_get_header(event, "nomedia-uuid"); @@ -3240,6 +3240,157 @@ return SWITCH_STATUS_SUCCESS; } +struct hangup_helper { + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + switch_bool_t bleg; + switch_call_cause_t cause; +}; + +static void sch_hangup_callback(switch_core_scheduler_task_t *task) +{ + struct hangup_helper *helper; + switch_core_session_t *session, *other_session; + char *other_uuid; + + assert(task); + + helper = (struct hangup_helper *) task->cmd_arg; + + if ((session = switch_core_session_locate(helper->uuid_str))) { + switch_channel_t *channel = switch_core_session_get_channel(session); + + if (helper->bleg) { + if ((other_uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) && + (other_session = switch_core_session_locate(other_uuid))) { + switch_channel_t *other_channel = switch_core_session_get_channel(other_session); + switch_channel_hangup(other_channel, helper->cause); + switch_core_session_rwunlock(other_session); + } else { + switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING, "No channel to hangup\n"); + } + } else { + switch_channel_hangup(channel, helper->cause); + } + + switch_core_session_rwunlock(session); + } +} + +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_hangup(time_t runtime, char *uuid, switch_call_cause_t cause, switch_bool_t bleg) +{ + struct hangup_helper *helper; + size_t len = sizeof(*helper); + + assert((helper = malloc(len))); + memset(helper, 0, len); + + switch_copy_string(helper->uuid_str, uuid, sizeof(helper->uuid_str)); + helper->cause = cause; + helper->bleg = bleg; + + return switch_core_scheduler_add_task(runtime, sch_hangup_callback, (char *)__SWITCH_FUNC__, uuid, 0, helper, SSHF_FREE_ARG); +} + +struct transfer_helper { + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + char *extension; + char *dialplan; + char *context; +}; + +static void sch_transfer_callback(switch_core_scheduler_task_t *task) +{ + struct transfer_helper *helper; + switch_core_session_t *session; + + assert(task); + + helper = (struct transfer_helper *) task->cmd_arg; + + if ((session = switch_core_session_locate(helper->uuid_str))) { + switch_ivr_session_transfer(session, helper->extension, helper->dialplan, helper->context); + switch_core_session_rwunlock(session); + } + +} + +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_transfer(time_t runtime, char *uuid, char *extension, char *dialplan, char *context) +{ + struct transfer_helper *helper; + size_t len = sizeof(*helper); + char *cur = NULL; + + if (extension) { + len += strlen(extension) + 1; + } + + if (dialplan) { + len += strlen(dialplan) + 1; + } + + if (context) { + len += strlen(context) + 1; + } + + assert((helper = malloc(len))); + memset(helper, 0, len); + + switch_copy_string(helper->uuid_str, uuid, sizeof(helper->uuid_str)); + + cur = (char *) helper + sizeof(*helper); + + if (extension) { + helper->extension = cur; + switch_copy_string(helper->extension, extension, strlen(extension) + 1); + cur += strlen(helper->extension) + 1; + } + + if (dialplan) { + helper->dialplan = cur; + switch_copy_string(helper->dialplan, dialplan, strlen(dialplan) + 1); + cur += strlen(helper->dialplan) + 1; + } + + if (context) { + helper->context = cur; + switch_copy_string(helper->context, context, strlen(context)+1); + } + + return switch_core_scheduler_add_task(runtime, sch_transfer_callback, (char *)__SWITCH_FUNC__, uuid, 0, helper, SSHF_FREE_ARG); +} + + +struct broadcast_helper { + char uuid_str[SWITCH_UUID_FORMATTED_LENGTH + 1]; + char *path; + switch_media_flag_t flags; +}; + +static void sch_broadcast_callback(switch_core_scheduler_task_t *task) +{ + struct broadcast_helper *helper; + assert(task); + + helper = (struct broadcast_helper *) task->cmd_arg; + switch_ivr_broadcast(helper->uuid_str, helper->path, helper->flags); +} + +SWITCH_DECLARE(uint32_t) switch_ivr_schedule_broadcast(time_t runtime, char *uuid, char *path, switch_media_flag_t flags) +{ + struct broadcast_helper *helper; + size_t len = sizeof(*helper) + strlen(path) + 1; + + assert((helper = malloc(len))); + memset(helper, 0, len); + + switch_copy_string(helper->uuid_str, uuid, sizeof(helper->uuid_str)); + helper->flags = flags; + helper->path = (char *) helper + sizeof(*helper); + switch_copy_string(helper->path, path, len - sizeof(helper)); + + + return switch_core_scheduler_add_task(runtime, sch_broadcast_callback, (char *)__SWITCH_FUNC__, uuid, 0, helper, SSHF_FREE_ARG); +} SWITCH_DECLARE(switch_status_t) switch_ivr_broadcast(char *uuid, char *path, switch_media_flag_t flags) { @@ -3249,9 +3400,15 @@ switch_event_t *event; switch_core_session_t *other_session = NULL; char *other_uuid = NULL; + char *app = "playback"; + + assert(path); if ((session = switch_core_session_locate(uuid))) { - char *app; + char *cause = NULL; + char *mypath = strdup(path); + char *p; + master = session; channel = switch_core_session_get_channel(session); @@ -3261,11 +3418,17 @@ switch_ivr_media(uuid, SMF_REBRIDGE); } - if (!strncasecmp(path, "speak:", 6)) { - path += 6; - app = "speak"; - } else { - app = "playback"; + if ((p = strchr(mypath, ':'))) { + app = mypath; + *p++ = '\0'; + path = p; + } + + if ((cause = strchr(app, '!'))) { + *cause++ = '\0'; + if (!cause) { + cause = "normal_clearing"; + } } if ((flags & SMF_ECHO_BLEG) && (other_uuid = switch_channel_get_variable(channel, SWITCH_BRIDGE_VARIABLE)) @@ -3299,8 +3462,21 @@ } } + if (cause) { + if (switch_event_create(&event, SWITCH_EVENT_MESSAGE) == SWITCH_STATUS_SUCCESS) { + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "call-command", "execute"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-name", "hangup"); + switch_event_add_header(event, SWITCH_STACK_BOTTOM, "execute-app-arg", "%s", cause); + + switch_core_session_queue_private_event(session, &event); + } + } + switch_core_session_rwunlock(session); + switch_safe_free(mypath); } + + return SWITCH_STATUS_SUCCESS; } @@ -3449,11 +3625,16 @@ switch_channel_set_variable(channel, SWITCH_BRIDGE_VARIABLE, NULL); switch_channel_set_variable(other_channel, SWITCH_BRIDGE_VARIABLE, NULL); - switch_channel_hangup(other_channel, switch_channel_get_cause(channel)); + if (switch_channel_get_state(other_channel) < CS_HANGUP && + switch_true(switch_channel_get_variable(other_channel, SWITCH_HANGUP_AFTER_BRIDGE_VARIABLE))) { + switch_channel_hangup(other_channel, switch_channel_get_cause(channel)); + } else { + switch_channel_set_state(other_channel, CS_EXECUTE); + } switch_core_session_rwunlock(other_session); } - + return SWITCH_STATUS_SUCCESS; }
participants (1)
-
Freeswitch SVN