diff --git a/include/asterisk/res_fax.h b/include/asterisk/res_fax.h index a6b3a21c1b4959dae20b1e2a6ee0f3c06c1499d9..70715d58c37fdd3c6c89ddb092357e14605e6611 100644 --- a/include/asterisk/res_fax.h +++ b/include/asterisk/res_fax.h @@ -58,8 +58,10 @@ enum ast_fax_modems { /*! \brief current state of a fax session */ enum ast_fax_state { + /*! reserved state */ + AST_FAX_STATE_RESERVED = 0, /*! uninitialized state */ - AST_FAX_STATE_UNINITIALIZED = 0, + AST_FAX_STATE_UNINITIALIZED, /*! initialized state */ AST_FAX_STATE_INITIALIZED, /*! fax resources open state */ @@ -168,7 +170,8 @@ struct ast_fax_session_details { struct ast_fax_tech; struct ast_fax_debug_info; - +struct ast_fax_tech_token; + /*! \brief The data required to handle a fax session */ struct ast_fax_session { /*! session id */ @@ -183,6 +186,8 @@ struct ast_fax_session { unsigned long frames_sent; /*! the fax technology callbacks */ const struct ast_fax_tech *tech; + /*! the token used to reserve this session */ + struct ast_fax_tech_token *token; /*! private implementation pointer */ void *tech_pvt; /*! fax state */ @@ -197,9 +202,11 @@ struct ast_fax_session { struct ast_fax_debug_info *debug_info; /*! used to take variable-sized frames in and output frames of an expected size to the fax stack */ struct ast_smoother *smoother; -}; -struct ast_fax_tech_token; + /*! some flags to track the stat counters for this session */ + unsigned int reserved:1; + unsigned int active:1; +}; /*! \brief used to register a FAX technology module with res_fax */ struct ast_fax_tech { @@ -242,7 +249,15 @@ struct ast_fax_tech { /*! displays settings from the fax technology module */ char * (* const cli_show_settings)(int); }; - + +/*! \brief used by res_fax to reserve a FAX session */ +struct ast_fax_tech_token { + /*! the fax technology callbacks */ + const struct ast_fax_tech *tech; + /*! private implementation pointer */ + void *tech_pvt; +}; + /*! \brief register a fax technology */ int ast_fax_tech_register(struct ast_fax_tech *tech); diff --git a/res/res_fax.c b/res/res_fax.c index 3da2d02f89dd79f8009cd6ee0fab1d8579ed39cc..bfd4a51abef2f7de7621b2adb4b6835a65ac525b 100644 --- a/res/res_fax.c +++ b/res/res_fax.c @@ -220,6 +220,8 @@ static int fax_logger_level = -1; static struct { /*! The number of active FAX sessions */ int active_sessions; + /*! The number of reserved FAX sessions */ + int reserved_sessions; /*! active sessions are astobj2 objects */ struct ao2_container *container; /*! Total number of Tx FAX attempts */ @@ -631,12 +633,26 @@ static unsigned int fax_rate_str_to_int(const char *ratestr) } } +static void fax_session_release(struct ast_fax_session *s) +{ + if (s->token) { + s->tech->release_token(s->token); + s->token = NULL; + } + + if (s->reserved) { + ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, -1); + s->reserved = 0; + } +} + /*! \brief destroy a FAX session structure */ static void destroy_session(void *session) { struct ast_fax_session *s = session; if (s->tech) { + fax_session_release(s); if (s->tech_pvt) { s->tech->destroy_session(s); } @@ -656,14 +672,16 @@ static void destroy_session(void *session) ast_smoother_free(s->smoother); } - ast_atomic_fetchadd_int(&faxregistry.active_sessions, -1); + if (s->active) { + ast_atomic_fetchadd_int(&faxregistry.active_sessions, -1); + s->active = 0; + } ast_free(s->channame); ast_free(s->chan_uniqueid); } -/*! \brief create a FAX session */ -static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *details, struct ast_channel *chan) +static struct ast_fax_session *fax_session_reserve(struct ast_fax_session_details *details) { struct ast_fax_session *s; struct fax_module *faxmod; @@ -672,7 +690,68 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d return NULL; } + s->state = AST_FAX_STATE_RESERVED; + + /* locate a FAX technology module that can handle said requirements + * Note: the requirements have not yet been finalized as T.38 + * negotiation has not yet occured. */ + AST_RWLIST_RDLOCK(&faxmodules); + AST_RWLIST_TRAVERSE(&faxmodules, faxmod, list) { + if ((faxmod->tech->caps & details->caps) != details->caps) { + continue; + } + ast_debug(4, "Reserving a FAX session from '%s'.\n", faxmod->tech->description); + ast_module_ref(faxmod->tech->module); + s->tech = faxmod->tech; + break; + } + AST_RWLIST_UNLOCK(&faxmodules); + + if (!faxmod) { + ast_log(LOG_ERROR, "Could not locate a FAX technology module with capabilities (0x%X)\n", details->caps); + ao2_ref(s, -1); + return NULL; + } + + if (!s->tech->reserve_session) { + ast_debug(1, "Selected FAX technology module (%s) does not support reserving sessions.\n", s->tech->description); + return s; + } + + if (!(s->token = s->tech->reserve_session(s))) { + ao2_ref(s, -1); + return NULL; + } + + s->reserved = 1; + ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, 1); + + return s; +} + +/*! \brief create a FAX session */ +static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *details, struct ast_channel *chan, struct ast_fax_session *reserved) +{ + struct ast_fax_session *s = NULL; + struct fax_module *faxmod; + + if (reserved) { + s = reserved; + ao2_ref(reserved, +1); + + if (s->reserved) { + ast_atomic_fetchadd_int(&faxregistry.reserved_sessions, -1); + s->reserved = 0; + } + } + + if (!s && !(s = ao2_alloc(sizeof(*s), destroy_session))) { + return NULL; + } + + s->active = 1; ast_atomic_fetchadd_int(&faxregistry.active_sessions, 1); + s->state = AST_FAX_STATE_UNINITIALIZED; if (details->option.debug && (details->caps & AST_FAX_TECH_AUDIO)) { if (!(s->debug_info = ast_calloc(1, sizeof(*(s->debug_info))))) { @@ -701,39 +780,39 @@ static struct ast_fax_session *fax_session_new(struct ast_fax_session_details *d s->chan = chan; s->details = details; ao2_ref(s->details, 1); - s->state = AST_FAX_STATE_UNINITIALIZED; details->id = s->id = ast_atomic_fetchadd_int(&faxregistry.nextsessionname, 1); - /* locate a FAX technology module that can handle said requirements */ - AST_RWLIST_RDLOCK(&faxmodules); - AST_RWLIST_TRAVERSE(&faxmodules, faxmod, list) { - if ((faxmod->tech->caps & details->caps) != details->caps) { - continue; + if (!s->tech) { + /* locate a FAX technology module that can handle said requirements */ + AST_RWLIST_RDLOCK(&faxmodules); + AST_RWLIST_TRAVERSE(&faxmodules, faxmod, list) { + if ((faxmod->tech->caps & details->caps) != details->caps) { + continue; + } + ast_debug(4, "Requesting a new FAX session from '%s'.\n", faxmod->tech->description); + ast_module_ref(faxmod->tech->module); + s->tech = faxmod->tech; + break; } - ast_debug(4, "Requesting a new FAX session from '%s'.\n", faxmod->tech->description); - ast_module_ref(faxmod->tech->module); - s->tech = faxmod->tech; - break; - } - AST_RWLIST_UNLOCK(&faxmodules); + AST_RWLIST_UNLOCK(&faxmodules); - if (!faxmod) { - ast_log(LOG_ERROR, "Could not locate a FAX technology module with capabilities (0x%X)\n", details->caps); - ao2_ref(s, -1); - return NULL; + if (!faxmod) { + ast_log(LOG_ERROR, "Could not locate a FAX technology module with capabilities (0x%X)\n", details->caps); + ao2_ref(s, -1); + return NULL; + } } - if (!(s->tech_pvt = s->tech->new_session(s, NULL))) { + + if (!(s->tech_pvt = s->tech->new_session(s, s->token))) { ast_log(LOG_ERROR, "FAX session failed to initialize.\n"); ao2_ref(s, -1); - ast_module_unref(faxmod->tech->module); return NULL; } /* link the session to the session container */ if (!(ao2_link(faxregistry.container, s))) { ast_log(LOG_ERROR, "failed to add FAX session '%d' to container.\n", s->id); ao2_ref(s, -1); - ast_module_unref(faxmod->tech->module); return NULL; } ast_debug(4, "channel '%s' using FAX session '%d'\n", s->channame, s->id); @@ -930,7 +1009,7 @@ static int disable_t38(struct ast_channel *chan) ast_debug(1, "Shutting down T.38 on %s\n", chan->name); if (ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)) != 0) { - ast_log(LOG_WARNING, "error while disabling T.38 on channel '%s'\n", chan->name); + ast_debug(1, "error while disabling T.38 on channel '%s'\n", chan->name); return -1; } @@ -940,12 +1019,12 @@ static int disable_t38(struct ast_channel *chan) while (ms > 0) { ms = ast_waitfor(chan, ms); if (ms < 0) { - ast_log(LOG_WARNING, "error while disabling T.38 on channel '%s'\n", chan->name); + ast_debug(1, "error while disabling T.38 on channel '%s'\n", chan->name); return -1; } if (ms == 0) { /* all done, nothing happened */ - ast_log(LOG_WARNING, "channel '%s' timed-out during T.38 shutdown\n", chan->name); + ast_debug(1, "channel '%s' timed-out during T.38 shutdown\n", chan->name); break; } @@ -987,7 +1066,7 @@ static struct ast_control_t38_parameters our_t38_parameters = { }; /*! \brief this is the generic FAX session handling function */ -static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_details *details) +static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_details *details, struct ast_fax_session *reserved) { int ms; int timeout = RES_FAX_TIMEOUT; @@ -1005,7 +1084,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det chancount = 1; /* create the FAX session */ - if (!(fax = fax_session_new(details, chan))) { + if (!(fax = fax_session_new(details, chan, reserved))) { ast_log(LOG_ERROR, "Can't create a FAX session, FAX attempt failed.\n"); report_fax_status(chan, details, "No Available Resource"); return -1; @@ -1093,7 +1172,7 @@ static int generic_fax_exec(struct ast_channel *chan, struct ast_fax_session_det * FAX session complete before we exit the application. if needed, * send the FAX stack silence so the modems can finish their session without * any problems */ - ast_log(LOG_NOTICE, "Channel '%s' did not return a frame; probably hung up.\n", chan->name); + ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", chan->name); GENERIC_FAX_EXEC_SET_VARS(fax, chan, "HANGUP", "remote channel hungup"); c = NULL; chancount = 0; @@ -1295,7 +1374,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_ ast_playtones_stop(chan); break; case AST_T38_NEGOTIATED: - ast_log(LOG_NOTICE, "Negotiated T.38 for receive on %s\n", chan->name); + ast_debug(1, "Negotiated T.38 for receive on %s\n", chan->name); t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters); details->caps &= ~AST_FAX_TECH_AUDIO; report_fax_status(chan, details, "T.38 Negotiated"); @@ -1316,7 +1395,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_ } /* request T.38 */ - ast_log(LOG_NOTICE, "Negotiating T.38 for receive on %s\n", chan->name); + ast_debug(1, "Negotiating T.38 for receive on %s\n", chan->name); /* wait up to five seconds for negotiation to complete */ ms = 5000; @@ -1358,7 +1437,7 @@ static int receivefax_t38_init(struct ast_channel *chan, struct ast_fax_session_ ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)); break; case AST_T38_NEGOTIATED: - ast_log(LOG_NOTICE, "Negotiated T.38 for receive on %s\n", chan->name); + ast_debug(1, "Negotiated T.38 for receive on %s\n", chan->name); t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters); details->caps &= ~AST_FAX_TECH_AUDIO; report_fax_status(chan, details, "T.38 Negotiated"); @@ -1402,6 +1481,7 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) char *parse, modems[128] = ""; int channel_alive; struct ast_fax_session_details *details; + struct ast_fax_session *s; struct ast_fax_document *doc; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(filename); @@ -1498,15 +1578,6 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) return -1; } - /* make sure the channel is up */ - if (chan->_state != AST_STATE_UP) { - if (ast_answer(chan)) { - ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", chan->name); - ao2_ref(details, -1); - return -1; - } - } - ast_atomic_fetchadd_int(&faxregistry.fax_rx_attempts, 1); pbx_builtin_setvar_helper(chan, "FAXERROR", "Channel Problems"); @@ -1543,10 +1614,31 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) details->option.allow_audio = AST_FAX_OPTFLAG_TRUE; } + if (!(s = fax_session_reserve(details))) { + ast_string_field_set(details, resultstr, "error reserving fax session"); + set_channel_variables(chan, details); + ast_log(LOG_ERROR, "Unable to reserve FAX session.\n"); + ao2_ref(details, -1); + return -1; + } + + /* make sure the channel is up */ + if (chan->_state != AST_STATE_UP) { + if (ast_answer(chan)) { + ast_string_field_set(details, resultstr, "error answering channel"); + set_channel_variables(chan, details); + ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", chan->name); + ao2_ref(s, -1); + ao2_ref(details, -1); + return -1; + } + } + if (set_fax_t38_caps(chan, details)) { ast_string_field_set(details, error, "T38_NEG_ERROR"); ast_string_field_set(details, resultstr, "error negotiating T.38"); set_channel_variables(chan, details); + ao2_ref(s, -1); ao2_ref(details, -1); return -1; } @@ -1556,6 +1648,7 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, error, "T38_NEG_ERROR"); ast_string_field_set(details, resultstr, "error negotiating T.38"); set_channel_variables(chan, details); + ao2_ref(s, -1); ao2_ref(details, -1); ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", chan->name); return -1; @@ -1564,13 +1657,13 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) details->option.send_ced = 1; } - if ((channel_alive = generic_fax_exec(chan, details)) < 0) { + if ((channel_alive = generic_fax_exec(chan, details, s)) < 0) { ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); } if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) { if (disable_t38(chan)) { - ast_log(LOG_WARNING, "error disabling T.38 mode on %s\n", chan->name); + ast_debug(1, "error disabling T.38 mode on %s\n", chan->name); } } @@ -1602,6 +1695,7 @@ static int receivefax_exec(struct ast_channel *chan, const char *data) args.filename); ast_channel_unlock(chan); + ao2_ref(s, -1); ao2_ref(details, -1); /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */ @@ -1666,7 +1760,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det ast_playtones_stop(chan); break; case AST_T38_NEGOTIATED: - ast_log(LOG_NOTICE, "Negotiated T.38 for send on %s\n", chan->name); + ast_debug(1, "Negotiated T.38 for send on %s\n", chan->name); t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters); details->caps &= ~AST_FAX_TECH_AUDIO; report_fax_status(chan, details, "T.38 Negotiated"); @@ -1687,7 +1781,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det /* T.38 negotiation did not happen, initiate a switch if requested */ if (details->option.request_t38 == AST_FAX_OPTFLAG_TRUE) { - ast_log(LOG_NOTICE, "Negotiating T.38 for send on %s\n", chan->name); + ast_debug(1, "Negotiating T.38 for send on %s\n", chan->name); /* wait up to five seconds for negotiation to complete */ ms = 5000; @@ -1729,7 +1823,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det ast_indicate_data(chan, AST_CONTROL_T38_PARAMETERS, &t38_parameters, sizeof(t38_parameters)); break; case AST_T38_NEGOTIATED: - ast_log(LOG_NOTICE, "Negotiated T.38 for receive on %s\n", chan->name); + ast_debug(1, "Negotiated T.38 for receive on %s\n", chan->name); t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters); details->caps &= ~AST_FAX_TECH_AUDIO; report_fax_status(chan, details, "T.38 Negotiated"); @@ -1798,7 +1892,7 @@ static int sendfax_t38_init(struct ast_channel *chan, struct ast_fax_session_det ast_playtones_stop(chan); break; case AST_T38_NEGOTIATED: - ast_log(LOG_NOTICE, "Negotiated T.38 for send on %s\n", chan->name); + ast_debug(1, "Negotiated T.38 for send on %s\n", chan->name); t38_parameters_ast_to_fax(&details->their_t38_parameters, parameters); details->caps &= ~AST_FAX_TECH_AUDIO; report_fax_status(chan, details, "T.38 Negotiated"); @@ -1839,6 +1933,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) char *parse, *filenames, *c, modems[128] = ""; int channel_alive, file_count; struct ast_fax_session_details *details; + struct ast_fax_session *s; struct ast_fax_document *doc; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(filenames); @@ -1936,15 +2031,6 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) return -1; } - /* make sure the channel is up */ - if (chan->_state != AST_STATE_UP) { - if (ast_answer(chan)) { - ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", chan->name); - ao2_ref(details, -1); - return -1; - } - } - ast_atomic_fetchadd_int(&faxregistry.fax_tx_attempts, 1); file_count = 0; @@ -2003,10 +2089,31 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) details->option.request_t38 = AST_FAX_OPTFLAG_TRUE; } + if (!(s = fax_session_reserve(details))) { + ast_string_field_set(details, resultstr, "error reserving fax session"); + set_channel_variables(chan, details); + ast_log(LOG_ERROR, "Unable to reserve FAX session.\n"); + ao2_ref(details, -1); + return -1; + } + + /* make sure the channel is up */ + if (chan->_state != AST_STATE_UP) { + if (ast_answer(chan)) { + ast_string_field_set(details, resultstr, "error answering channel"); + set_channel_variables(chan, details); + ast_log(LOG_WARNING, "Channel '%s' failed answer attempt.\n", chan->name); + ao2_ref(s, -1); + ao2_ref(details, -1); + return -1; + } + } + if (set_fax_t38_caps(chan, details)) { ast_string_field_set(details, error, "T38_NEG_ERROR"); ast_string_field_set(details, resultstr, "error negotiating T.38"); set_channel_variables(chan, details); + ao2_ref(s, -1); ao2_ref(details, -1); return -1; } @@ -2016,6 +2123,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_string_field_set(details, error, "T38_NEG_ERROR"); ast_string_field_set(details, resultstr, "error negotiating T.38"); set_channel_variables(chan, details); + ao2_ref(s, -1); ao2_ref(details, -1); ast_log(LOG_ERROR, "error initializing channel '%s' in T.38 mode\n", chan->name); return -1; @@ -2024,18 +2132,19 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) details->option.send_cng = 1; } - if ((channel_alive = generic_fax_exec(chan, details)) < 0) { + if ((channel_alive = generic_fax_exec(chan, details, s)) < 0) { ast_atomic_fetchadd_int(&faxregistry.fax_failures, 1); } if (ast_channel_get_t38_state(chan) == T38_STATE_NEGOTIATED) { if (disable_t38(chan)) { - ast_log(LOG_WARNING, "error disabling T.38 mode on %s\n", chan->name); + ast_debug(1, "error disabling T.38 mode on %s\n", chan->name); } } if (!(filenames = generate_filenames_string(details, "FileName: ", "\r\n"))) { ast_log(LOG_ERROR, "Error generating SendFAX manager event\n"); + ao2_ref(s, -1); ao2_ref(details, -1); return (!channel_alive) ? -1 : 0; } @@ -2069,6 +2178,7 @@ static int sendfax_exec(struct ast_channel *chan, const char *data) ast_free(filenames); + ao2_ref(s, -1); ao2_ref(details, -1); /* If the channel hungup return -1; otherwise, return 0 to continue in the dialplan */ @@ -2307,6 +2417,7 @@ static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli ast_cli(a->fd, "\nFAX Statistics:\n---------------\n\n"); ast_cli(a->fd, "%-20.20s : %d\n", "Current Sessions", faxregistry.active_sessions); + ast_cli(a->fd, "%-20.20s : %d\n", "Reserved Sessions", faxregistry.reserved_sessions); ast_cli(a->fd, "%-20.20s : %d\n", "Transmit Attempts", faxregistry.fax_tx_attempts); ast_cli(a->fd, "%-20.20s : %d\n", "Receive Attempts", faxregistry.fax_rx_attempts); ast_cli(a->fd, "%-20.20s : %d\n", "Completed FAXes", faxregistry.fax_complete); @@ -2616,6 +2727,7 @@ static int load_module(void) /* initialize the registry */ faxregistry.active_sessions = 0; + faxregistry.reserved_sessions = 0; if (!(faxregistry.container = ao2_container_alloc(FAX_MAXBUCKETS, session_hash_cb, session_cmp_cb))) { return AST_MODULE_LOAD_DECLINE; }