res_fax: Provide AMI equivalents for fax CLI commands

Specifically the following equivalents were created:
fax show session -> FAXSession
fax show sessions -> FAXSessions
fax show stats -> FAXStats

Review: https://reviewboard.asterisk.org/r/3666/


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@418911 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Jonathan Rose 2014-07-18 15:49:46 +00:00
parent dd23637195
commit 5c988cc4e6
5 changed files with 452 additions and 5 deletions

View File

@ -42,6 +42,10 @@ AMI
* New AMI action LoggerRotate reloads and rotates logger in the same manner
as CLI command 'logger rotate'
* New AMI Actions FAXSessions, FAXSession, and FAXStats replicate the
functionality of CLI commands 'fax show sessions', 'fax show session',
and fax show stats' respectively.
* New AMI actions PRIDebugSet, PRIDebugFileSet, and PRIDebugFileUnset
enable manager control over PRI debugging levels and file output.

View File

@ -29,6 +29,7 @@
#include <asterisk/frame.h>
#include <asterisk/cli.h>
#include <asterisk/stringfields.h>
#include <asterisk/manager.h>
/*! \brief capabilities for res_fax to locate a fax technology module */
enum ast_fax_capabilities {
@ -255,6 +256,9 @@ struct ast_fax_tech {
char * (* const cli_show_capabilities)(int);
/*! displays details about the fax session */
char * (* const cli_show_session)(struct ast_fax_session *, int);
/*! Generates manager event detailing the fax session */
void (* const manager_fax_session)(struct mansession *,
const char *, struct ast_fax_session *);
/*! displays statistics from the fax technology module */
char * (* const cli_show_stats)(int);
/*! displays settings from the fax technology module */
@ -276,6 +280,9 @@ unsigned int ast_fax_maxrate(void);
/*! \brief convert an ast_fax_state to a string */
const char *ast_fax_state_to_str(enum ast_fax_state state);
/*! \brief get string representation of a FAX session's operation */
const char *ast_fax_session_operation_str(struct ast_fax_session *s);
/*!
* \brief Log message at FAX or recommended level
*

View File

@ -235,6 +235,188 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<ref type="application">SendFax</ref>
</see-also>
</function>
<manager name="FAXSessions" language="en_US">
<synopsis>
Lists active FAX sessions
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
<para>Will generate a series of FAXSession events with information about each FAXSession. Closes with
a FAXSessionsComplete event which includes a count of the included FAX sessions. This action works in
the same manner as the CLI command 'fax show sessions'</para>
</description>
</manager>
<managerEvent language="en_US" name="FAXSessionsEntry">
<managerEventInstance class="EVENT_FLAG_REPORTING">
<synopsis>A single list item for the FAXSessions AMI command</synopsis>
<syntax>
<parameter name="ActionID" required="false"/>
<parameter name="Channel">
<para>Name of the channel responsible for the FAX session</para>
</parameter>
<parameter name="Technology">
<para>The FAX technology that the FAX session is using</para>
</parameter>
<parameter name="SessionNumber">
<para>The numerical identifier for this particular session</para>
</parameter>
<parameter name="SessionType">
<para>FAX session passthru/relay type</para>
<enumlist>
<enum name="G.711" />
<enum name="T.38" />
</enumlist>
</parameter>
<parameter name="Operation">
<para>FAX session operation type</para>
<enumlist>
<enum name="gateway" />
<enum name="V.21" />
<enum name="send" />
<enum name="receive" />
<enum name="none" />
</enumlist>
</parameter>
<parameter name="State">
<para>Current state of the FAX session</para>
<enumlist>
<enum name="Uninitialized" />
<enum name="Initialized" />
<enum name="Open" />
<enum name="Active" />
<enum name="Complete" />
<enum name="Reserved" />
<enum name="Inactive" />
<enum name="Unknown" />
</enumlist>
</parameter>
<parameter name="Files">
<para>File or list of files associated with this FAX session</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<managerEvent language="en_US" name="FAXSessionsComplete">
<managerEventInstance class="EVENT_FLAG_CALL">
<synopsis>Raised when all FAXSession events are completed for a FAXSessions command</synopsis>
<syntax>
<parameter name="ActionID" required="false"/>
<parameter name="Total">
<para>Count of FAXSession events sent in response to FAXSessions action</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<manager name="FAXSession" language="en_US">
<synopsis>
Responds with a detailed description of a single FAX session
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
<parameter name="SessionNumber" required="true">
<para>The session ID of the fax the user is interested in.</para>
</parameter>
</syntax>
<description>
<para>Provides details about a specific FAX session. The response will include a common subset of
the output from the CLI command 'fax show session &lt;session_number&gt;' for each technology. If the
FAX technolgy used by this session does not include a handler for FAXSession, then this action
will fail.</para>
</description>
</manager>
<managerEvent language="en_US" name="FAXSession">
<managerEventInstance class="EVENT_FLAG_REPORTING">
<synopsis>Raised in response to FAXSession manager command</synopsis>
<syntax>
<parameter name="ActionID" required="false"/>
<parameter name="SessionNumber">
<para>The numerical identifier for this particular session</para>
</parameter>
<xi:include xpointer="xpointer(/docs/managerEvent[@name='FAXSessionsEntry']/managerEventInstance/syntax/parameter[@name='Operation'])" />
<xi:include xpointer="xpointer(/docs/managerEvent[@name='FAXSessionsEntry']/managerEventInstance/syntax/parameter[@name='State'])" />
<parameter name="ErrorCorrectionMode" required="false">
<para>Whether error correcting mode is enabled for the FAX session. This field is not
included when operation is 'V.21 Detect' or if operation is 'gateway' and state is
'Uninitialized'
</para>
<enumlist>
<enum name="yes" />
<enum name="no" />
</enumlist>
</parameter>
<parameter name="DataRate" required="false">
<para>Bit rate of the FAX. This field is not included when operation is 'V.21 Detect' or
if operation is 'gateway' and state is 'Uninitialized'.</para>
</parameter>
<parameter name="ImageResolution" required="false">
<para>Resolution of each page of the FAX. Will be in the format of X_RESxY_RES. This field
is not included if the operation is anything other than Receive/Transmit.</para>
</parameter>
<parameter name="PageNumber" required="false">
<para>Current number of pages transferred during this FAX session. May change as the FAX
progresses. This field is not included when operation is 'V.21 Detect' or if operation is
'gateway' and state is 'Uninitialized'.</para>
</parameter>
<parameter name="FileName" required="false">
<para>Filename of the image being sent/recieved for this FAX session. This field is not
included if Operation isn't 'send' or 'receive'.</para>
</parameter>
<parameter name="PagesTransmitted" required="false">
<para>Total number of pages sent during this session. This field is not included if
Operation isn't 'send' or 'receive'. Will always be 0 for 'receive'.</para>
</parameter>
<parameter name="PagesReceived" required="false">
<para>Total number of pages received during this session. This field is not included if
Operation is not 'send' or 'receive'. Will be 0 for 'send'.</para>
</parameter>
<parameter name="TotalBadLines" required="false">
<para>Total number of bad lines sent/recieved during this session. This field is not
included if Operation is not 'send' or 'received'.</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
<manager name="FAXStats" language="en_US">
<synopsis>
Responds with fax statistics
</synopsis>
<syntax>
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
</syntax>
<description>
<para>Provides FAX statistics including the number of active sessions, reserved sessions, completed
sessions, failed sessions, and the number of receive/transmit attempts. This command provides all
of the non-technology specific information provided by the CLI command 'fax show stats'</para>
</description>
</manager>
<managerEvent language="en_US" name="FAXStats">
<managerEventInstance class="EVENT_FLAG_REPORTING">
<synopsis>Raised in response to FAXStats manager command</synopsis>
<syntax>
<parameter name="ActionID" required="false"/>
<parameter name="CurrentSessions" required="true">
<para>Number of active FAX sessions</para>
</parameter>
<parameter name="ReservedSessions" required="true">
<para>Number of reserved FAX sessions</para>
</parameter>
<parameter name="TransmitAttempts" required="true">
<para>Total FAX sessions for which Asterisk is/was the transmitter</para>
</parameter>
<parameter name="ReceiveAttempts" required="true">
<para>Total FAX sessions for which Asterisk is/was the recipient</para>
</parameter>
<parameter name="CompletedFAXes" required="true">
<para>Total FAX sessions which have been completed successfully</para>
</parameter>
<parameter name="FailedFAXes" required="true">
<para>Total FAX sessions which failed to complete successfully</para>
</parameter>
</syntax>
</managerEventInstance>
</managerEvent>
***/
static const char app_receivefax[] = "ReceiveFAX";
@ -1144,7 +1326,7 @@ static char *generate_filenames_string(struct ast_fax_session_details *details,
/* don't process empty lists */
if (AST_LIST_EMPTY(&details->documents)) {
return NULL;
return ast_strdup("");
}
/* Calculate the total length of all of the file names */
@ -3742,6 +3924,43 @@ static char *cli_fax_show_session(struct ast_cli_entry *e, int cmd, struct ast_c
return CLI_SUCCESS;
}
static int manager_fax_session(struct mansession *s, const struct message *m)
{
const char *action_id = astman_get_header(m, "ActionID");
const char *session_number = astman_get_header(m, "SessionNumber");
char id_text[256] = "";
struct ast_fax_session *session;
struct ast_fax_session find_session;
if (sscanf(session_number, "%30u", &find_session.id) != 1) {
astman_send_error(s, m, "Invalid session ID");
return 0;
}
session = ao2_find(faxregistry.container, &find_session, OBJ_POINTER);
if (!session) {
astman_send_error(s, m, "Session not found");
return 0;
}
if (!session->tech->manager_fax_session) {
astman_send_error(s, m, "Fax technology doesn't provide a handler for FAXSession");
ao2_ref(session, -1);
return 0;
}
if (!ast_strlen_zero(action_id)) {
snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", action_id);
}
astman_send_ack(s, m, "FAXSession event will follow");
session->tech->manager_fax_session(s, id_text, session);
ao2_ref(session, -1);
return 0;
}
/*! \brief display fax stats */
static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
@ -3775,7 +3994,36 @@ static char *cli_fax_show_stats(struct ast_cli_entry *e, int cmd, struct ast_cli
return CLI_SUCCESS;
}
static const char *cli_session_type(struct ast_fax_session *s)
static int manager_fax_stats(struct mansession *s, const struct message *m)
{
const char *action_id = astman_get_header(m, "ActionID");
char id_text[256] = "";
astman_send_ack(s, m, "FAXStats event will follow");
if (!ast_strlen_zero(action_id)) {
snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", action_id);
}
astman_append(s, "Event: FAXStats\r\n"
"%s"
"CurrentSessions: %d\r\n"
"ReservedSessions: %d\r\n"
"TransmitAttempts: %d\r\n"
"ReceiveAttempts: %d\r\n"
"CompletedFAXes: %d\r\n"
"FailedFAXes: %d\r\n"
"\r\n",
id_text,
faxregistry.active_sessions, faxregistry.reserved_sessions,
faxregistry.fax_tx_attempts, faxregistry.fax_rx_attempts,
faxregistry.fax_complete, faxregistry.fax_failures);
return 0;
}
static const char *fax_session_type(struct ast_fax_session *s)
{
if (s->details->caps & AST_FAX_TECH_AUDIO) {
return "G.711";
@ -3787,7 +4035,7 @@ static const char *cli_session_type(struct ast_fax_session *s)
return "none";
}
static const char *cli_session_operation(struct ast_fax_session *s)
const char *ast_fax_session_operation_str(struct ast_fax_session *s)
{
if (s->details->caps & AST_FAX_TECH_GATEWAY) {
return "gateway";
@ -3835,8 +4083,8 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
ast_cli(a->fd, "%-20.20s %-10.10s %-10u %-5.5s %-10.10s %-15.15s %-30s\n",
s->channame, s->tech->type, s->id,
cli_session_type(s),
cli_session_operation(s),
fax_session_type(s),
ast_fax_session_operation_str(s),
ast_fax_state_to_str(s->state), S_OR(filenames, ""));
ast_free(filenames);
@ -3850,6 +4098,72 @@ static char *cli_fax_show_sessions(struct ast_cli_entry *e, int cmd, struct ast_
return CLI_SUCCESS;
}
static int manager_fax_sessions_entry(struct mansession *s,
struct ast_fax_session *session, const char *id_text)
{
char *filenames;
ao2_lock(session);
filenames = generate_filenames_string(session->details, "", ",");
if (!filenames) {
ast_log(LOG_ERROR, "Error generating Files string");
ao2_unlock(session);
return -1;
}
astman_append(s, "Event: FAXSessionsEntry\r\n"
"%s" /* ActionID if present */
"Channel: %s\r\n" /* Channel name */
"Technology: %s\r\n" /* Fax session technology */
"SessionNumber: %u\r\n" /* Session ID */
"SessionType: %s\r\n" /* G711 or T38 */
"Operation: %s\r\n"
"State: %s\r\n"
"Files: %s\r\n"
"\r\n",
id_text, session->channame, session->tech->type, session->id,
fax_session_type(session), ast_fax_session_operation_str(session),
ast_fax_state_to_str(session->state), S_OR(filenames, ""));
ast_free(filenames);
ao2_unlock(session);
return 0;
}
static int manager_fax_sessions(struct mansession *s, const struct message *m)
{
const char *action_id = astman_get_header(m, "ActionID");
char id_text[256] = "";
struct ast_fax_session *session;
struct ao2_iterator iter;
int session_count = 0;
astman_send_listack(s, m, "FAXSessionsEntry event list will follow", "Start");
if (!ast_strlen_zero(action_id)) {
snprintf(id_text, sizeof(id_text), "ActionID: %s\r\n", action_id);
}
iter = ao2_iterator_init(faxregistry.container, 0);
while ((session = ao2_iterator_next(&iter))) {
if (!manager_fax_sessions_entry(s, session, id_text)) {
session_count++;
}
ao2_ref(session, -1);
}
ao2_iterator_destroy(&iter);
astman_append(s, "Event: FAXSessionsComplete\r\n"
"%s"
"EventList: Complete\r\n"
"Total: %d\r\n"
"\r\n",
id_text,
session_count);
return 0;
}
static struct ast_cli_entry fax_cli[] = {
AST_CLI_DEFINE(cli_fax_show_version, "Show versions of FAX For Asterisk components"),
AST_CLI_DEFINE(cli_fax_set_debug, "Enable/Disable FAX debugging on new FAX sessions"),
@ -4246,6 +4560,33 @@ static int load_module(void)
return AST_MODULE_LOAD_DECLINE;
}
if (ast_manager_register_xml("FAXSessions", EVENT_FLAG_CALL, manager_fax_sessions)) {
ast_log(LOG_WARNING, "failed to register 'FAXSessions' AMI command.\n");
ast_unregister_application(app_receivefax);
ast_unregister_application(app_sendfax);
ao2_ref(faxregistry.container, -1);
return AST_MODULE_LOAD_DECLINE;
}
if (ast_manager_register_xml("FAXSession", EVENT_FLAG_CALL, manager_fax_session)) {
ast_log(LOG_WARNING, "failed to register 'FAXSession' AMI command.\n");
ast_manager_unregister("FAXSession");
ast_unregister_application(app_receivefax);
ast_unregister_application(app_sendfax);
ao2_ref(faxregistry.container, -1);
return AST_MODULE_LOAD_DECLINE;
}
if (ast_manager_register_xml("FAXStats", EVENT_FLAG_REPORTING, manager_fax_stats)) {
ast_log(LOG_WARNING, "failed to register 'FAXStats' AMI command.\n");
ast_manager_unregister("FAXSession");
ast_manager_unregister("FAXSessions");
ast_unregister_application(app_receivefax);
ast_unregister_application(app_sendfax);
ao2_ref(faxregistry.container, -1);
return AST_MODULE_LOAD_DECLINE;
}
ast_cli_register_multiple(fax_cli, ARRAY_LEN(fax_cli));
res = ast_custom_function_register(&acf_faxopt);
fax_logger_level = ast_logger_register_level("FAX");

View File

@ -6,6 +6,7 @@
LINKER_SYMBOL_PREFIXast_fax_minrate;
LINKER_SYMBOL_PREFIXast_fax_maxrate;
LINKER_SYMBOL_PREFIXast_fax_state_to_str;
LINKER_SYMBOL_PREFIXast_fax_session_operation_str;
LINKER_SYMBOL_PREFIXast_fax_log;
local:
*;

View File

@ -86,6 +86,8 @@ static void spandsp_v21_tone(void *data, int code, int level, int delay);
static char *spandsp_fax_cli_show_capabilities(int fd);
static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd);
static void spandsp_manager_fax_session(struct mansession *s,
const char *id_text, struct ast_fax_session *session);
static char *spandsp_fax_cli_show_stats(int fd);
static char *spandsp_fax_cli_show_settings(int fd);
@ -113,6 +115,7 @@ static struct ast_fax_tech spandsp_fax_tech = {
.switch_to_t38 = spandsp_fax_switch_to_t38,
.cli_show_capabilities = spandsp_fax_cli_show_capabilities,
.cli_show_session = spandsp_fax_cli_show_session,
.manager_fax_session = spandsp_manager_fax_session,
.cli_show_stats = spandsp_fax_cli_show_stats,
.cli_show_settings = spandsp_fax_cli_show_settings,
};
@ -1073,6 +1076,97 @@ static char *spandsp_fax_cli_show_session(struct ast_fax_session *s, int fd)
return CLI_SUCCESS;
}
static void spandsp_manager_fax_session(struct mansession *s,
const char *id_text, struct ast_fax_session *session)
{
struct ast_str *message_string;
struct spandsp_pvt *span_pvt = session->tech_pvt;
int res;
message_string = ast_str_create(128);
if (!message_string) {
return;
}
ao2_lock(session);
res = ast_str_append(&message_string, 0, "SessionNumber: %d\r\n", session->id);
res |= ast_str_append(&message_string, 0, "Operation: %s\r\n", ast_fax_session_operation_str(session));
res |= ast_str_append(&message_string, 0, "State: %s\r\n", ast_fax_state_to_str(session->state));
if (session->details->caps & AST_FAX_TECH_GATEWAY) {
t38_stats_t stats;
if (session->state == AST_FAX_STATE_UNINITIALIZED) {
goto skip_cap_additions;
}
t38_gateway_get_transfer_statistics(&span_pvt->t38_gw_state, &stats);
res |= ast_str_append(&message_string, 0, "ErrorCorrectionMode: %s\r\n",
stats.error_correcting_mode ? "yes" : "no");
res |= ast_str_append(&message_string, 0, "DataRate: %d\r\n",
stats.bit_rate);
res |= ast_str_append(&message_string, 0, "PageNumber: %d\r\n",
stats.pages_transferred + 1);
} else if (!(session->details->caps & AST_FAX_TECH_V21_DETECT)) { /* caps is SEND/RECEIVE */
t30_stats_t stats;
if (session->state == AST_FAX_STATE_UNINITIALIZED) {
goto skip_cap_additions;
}
t30_get_transfer_statistics(span_pvt->t30_state, &stats);
res |= ast_str_append(&message_string, 0, "ErrorCorrectionMode: %s\r\n",
stats.error_correcting_mode ? "Yes" : "No");
res |= ast_str_append(&message_string, 0, "DataRate: %d\r\n",
stats.bit_rate);
res |= ast_str_append(&message_string, 0, "ImageResolution: %dx%d\r\n",
stats.x_resolution, stats.y_resolution);
#if SPANDSP_RELEASE_DATE >= 20090220
res |= ast_str_append(&message_string, 0, "PageNumber: %d\r\n",
((session->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_rx : stats.pages_tx) + 1);
#else
res |= ast_str_append(&message_string, 0, "PageNumber: %d\r\n",
stats.pages_transferred + 1);
#endif
res |= ast_str_append(&message_string, 0, "FileName: %s\r\n",
session->details->caps & AST_FAX_TECH_RECEIVE ? span_pvt->t30_state->rx_file :
span_pvt->t30_state->tx_file);
#if SPANDSP_RELEASE_DATE >= 20090220
res |= ast_str_append(&message_string, 0, "PagesTransmitted: %d\r\n",
stats.pages_tx);
res |= ast_str_append(&message_string, 0, "PagesReceived: %d\r\n",
stats.pages_rx);
#else
res |= ast_str_append(&message_string, 0, "PagesTransmitted: %d\r\n",
(session->details->caps & AST_FAX_TECH_SEND) ? stats.pages_transferred : 0);
res |= ast_str_append(&message_string, 0, "PagesReceived: %d\r\n",
(session->details->caps & AST_FAX_TECH_RECEIVE) ? stats.pages_transferred : 0);
#endif
res |= ast_str_append(&message_string, 0, "TotalBadLines: %d\r\n",
stats.bad_rows);
}
skip_cap_additions:
ao2_unlock(session);
if (res < 0) {
/* One or more of the ast_str_append attempts failed, cancel the message */
ast_free(message_string);
return;
}
astman_append(s, "Event: FAXSession\r\n"
"%s"
"%s"
"\r\n",
id_text,
ast_str_buffer(message_string));
ast_free(message_string);
}
/*! \brief */
static char *spandsp_fax_cli_show_stats(int fd)
{