mirror of
https://github.com/asterisk/asterisk.git
synced 2025-11-02 20:08:17 +00:00
Allow the Hangup manager action to match channels by regex
* Hangup now can take a regular expression as the Channel option. If you want to hangup multiple channels, use /regex/ as the Channel option. Existing behavior to hanging up a single channel is unchanged, but if you pass a regex, the manager will send you a list of channels back that were hung up. (closes issue ASTERISK-19575) Reported by: Mark Murawski Tested by: Mark Murawski git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@361038 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
5
CHANGES
5
CHANGES
@@ -171,6 +171,11 @@ AMI (Asterisk Manager Interface) changes
|
||||
set and nat is not detected. "Y" and "N" are still returned if auto_force_rport
|
||||
is not enabled.
|
||||
|
||||
* Hangup now can take a regular expression as the Channel option. If you want
|
||||
to hangup multiple channels, use /regex/ as the Channel option. Existing
|
||||
behavior to hanging up a single channel is unchanged, but if you pass a regex,
|
||||
the manager will send you a list of channels back that were hung up.
|
||||
|
||||
FAX changes
|
||||
-----------
|
||||
* FAXOPT(faxdetect) will enable a generic fax detect framehook for dialplan
|
||||
|
||||
@@ -1382,6 +1382,14 @@ int ast_check_hangup(struct ast_channel *chan);
|
||||
|
||||
int ast_check_hangup_locked(struct ast_channel *chan);
|
||||
|
||||
/*!
|
||||
* \brief Lock the given channel, then request softhangup on the channel with the given causecode
|
||||
* \param obj channel on which to hang up
|
||||
* \param causecode cause code to use
|
||||
* \return 0
|
||||
*/
|
||||
int ast_channel_softhangup_withcause_locked(void *obj, int causecode);
|
||||
|
||||
/*!
|
||||
* \brief Compare a offset with the settings of when to hang a channel up
|
||||
* \param chan channel on which to check for hang up
|
||||
|
||||
@@ -261,6 +261,9 @@ struct ast_variable *astman_get_variables(const struct message *m);
|
||||
/*! \brief Send error in manager transaction */
|
||||
void astman_send_error(struct mansession *s, const struct message *m, char *error);
|
||||
|
||||
/*! \brief Send error in manager transaction (with va_args support) */
|
||||
void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt, ...);
|
||||
|
||||
/*! \brief Send response in manager transaction */
|
||||
void astman_send_response(struct mansession *s, const struct message *m, char *resp, char *msg);
|
||||
|
||||
|
||||
@@ -252,11 +252,26 @@ int ast_build_string(char **buffer, size_t *space, const char *fmt, ...) __attri
|
||||
*/
|
||||
int ast_build_string_va(char **buffer, size_t *space, const char *fmt, va_list ap) __attribute__((format(printf, 3, 0)));
|
||||
|
||||
/*!
|
||||
/*!
|
||||
\brief Given a string regex_string in the form of "/regex/", convert it into the form of "regex"
|
||||
|
||||
This function will trim one leading / and one trailing / from a given input string
|
||||
ast_str regex_pattern must be preallocated before calling this function
|
||||
|
||||
\return 0 on success, non-zero on failure.
|
||||
\return 1 if we only stripped a leading /
|
||||
\return 2 if we only stripped a trailing /
|
||||
\return 3 if we did not strip any / characters
|
||||
\param regex_string the string containing /regex/
|
||||
\param regex_pattern the destination ast_str which will contain "regex" after execution
|
||||
*/
|
||||
int ast_regex_string_to_regex_pattern(const char *regex_string, struct ast_str *regex_pattern);
|
||||
|
||||
/*!
|
||||
* \brief Make sure something is true.
|
||||
* Determine if a string containing a boolean value is "true".
|
||||
* This function checks to see whether a string passed to it is an indication of an "true" value.
|
||||
* It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
|
||||
* This function checks to see whether a string passed to it is an indication of an "true" value.
|
||||
* It checks to see if the string is "yes", "true", "y", "t", "on" or "1".
|
||||
*
|
||||
* \retval 0 if val is a NULL pointer.
|
||||
* \retval -1 if "true".
|
||||
|
||||
@@ -604,6 +604,26 @@ int ast_check_hangup_locked(struct ast_channel *chan)
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_channel_softhangup_withcause_locked(void *obj, int causecode)
|
||||
{
|
||||
struct ast_channel *chan = obj;
|
||||
|
||||
ast_channel_lock(chan);
|
||||
|
||||
if (causecode > 0) {
|
||||
ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n",
|
||||
ast_channel_name(chan), causecode, ast_channel_hangupcause(chan));
|
||||
|
||||
ast_channel_hangupcause_set(chan, causecode);
|
||||
}
|
||||
|
||||
ast_softhangup_nolock(chan, AST_SOFTHANGUP_EXPLICIT);
|
||||
|
||||
ast_channel_unlock(chan);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ast_channel_softhangup_cb(void *obj, void *arg, int flags)
|
||||
{
|
||||
struct ast_channel *chan = obj;
|
||||
|
||||
120
main/manager.c
120
main/manager.c
@@ -174,7 +174,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
<parameter name="Channel" required="true">
|
||||
<para>The channel name to be hangup.</para>
|
||||
<para>The exact channel name to be hungup, or to use a regular expression, set this parameter to: /regex/</para>
|
||||
<para>Example exact channel: SIP/provider-0000012a</para>
|
||||
<para>Example regular expression: /^SIP/provider-.*$/</para>
|
||||
</parameter>
|
||||
<parameter name="Cause">
|
||||
<para>Numeric hangup cause.</para>
|
||||
@@ -2034,7 +2036,7 @@ AST_THREADSTORAGE(astman_append_buf);
|
||||
|
||||
AST_THREADSTORAGE(userevent_buf);
|
||||
|
||||
/*! \brief initial allocated size for the astman_append_buf */
|
||||
/*! \brief initial allocated size for the astman_append_buf and astman_send_*_va */
|
||||
#define ASTMAN_APPEND_BUF_INITSIZE 256
|
||||
|
||||
/*!
|
||||
@@ -2109,6 +2111,23 @@ void astman_send_error(struct mansession *s, const struct message *m, char *erro
|
||||
astman_send_response_full(s, m, "Error", error, NULL);
|
||||
}
|
||||
|
||||
void astman_send_error_va(struct mansession *s, const struct message *m, const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
struct ast_str *buf;
|
||||
|
||||
if (!(buf = ast_str_thread_get(&astman_append_buf, ASTMAN_APPEND_BUF_INITSIZE))) {
|
||||
return;
|
||||
}
|
||||
|
||||
va_start(ap, fmt);
|
||||
ast_str_set_va(&buf, 0, fmt, ap);
|
||||
va_end(ap);
|
||||
|
||||
astman_send_response_full(s, m, "Error", ast_str_buffer(buf), NULL);
|
||||
ast_free(buf);
|
||||
}
|
||||
|
||||
void astman_send_ack(struct mansession *s, const struct message *m, char *msg)
|
||||
{
|
||||
astman_send_response_full(s, m, "Success", msg, NULL);
|
||||
@@ -3139,14 +3158,24 @@ static int action_hangup(struct mansession *s, const struct message *m)
|
||||
{
|
||||
struct ast_channel *c = NULL;
|
||||
int causecode = 0; /* all values <= 0 mean 'do not set hangupcause in channel' */
|
||||
const char *name = astman_get_header(m, "Channel");
|
||||
const char *id = astman_get_header(m, "ActionID");
|
||||
const char *name_or_regex = astman_get_header(m, "Channel");
|
||||
const char *cause = astman_get_header(m, "Cause");
|
||||
char idText[256] = "";
|
||||
regex_t regexbuf;
|
||||
struct ast_channel_iterator *iter = NULL;
|
||||
struct ast_str *regex_string;
|
||||
int channels_matched = 0;
|
||||
|
||||
if (ast_strlen_zero(name)) {
|
||||
if (ast_strlen_zero(name_or_regex)) {
|
||||
astman_send_error(s, m, "No channel specified");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(id)) {
|
||||
snprintf(idText, sizeof(idText), "ActionID: %s\r\n", id);
|
||||
}
|
||||
|
||||
if (!ast_strlen_zero(cause)) {
|
||||
char *endptr;
|
||||
causecode = strtol(cause, &endptr, 10);
|
||||
@@ -3157,23 +3186,84 @@ static int action_hangup(struct mansession *s, const struct message *m)
|
||||
}
|
||||
}
|
||||
|
||||
if (!(c = ast_channel_get_by_name(name))) {
|
||||
astman_send_error(s, m, "No such channel");
|
||||
/************************************************/
|
||||
/* Regular explicit match channel byname hangup */
|
||||
|
||||
if (name_or_regex[0] != '/') {
|
||||
if (!(c = ast_channel_get_by_name(name_or_regex))) {
|
||||
astman_send_error(s, m, "No such channel");
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
|
||||
(s->session->managerid ? "HTTP " : ""),
|
||||
s->session->username,
|
||||
ast_inet_ntoa(s->session->sin.sin_addr),
|
||||
ast_channel_name(c));
|
||||
|
||||
ast_channel_softhangup_withcause_locked(c, causecode);
|
||||
c = ast_channel_unref(c);
|
||||
|
||||
astman_send_ack(s, m, "Channel Hungup");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ast_channel_lock(c);
|
||||
if (causecode > 0) {
|
||||
ast_debug(1, "Setting hangupcause of channel %s to %d (is %d now)\n",
|
||||
ast_channel_name(c), causecode, ast_channel_hangupcause(c));
|
||||
ast_channel_hangupcause_set(c, causecode);
|
||||
/***********************************************/
|
||||
/* find and hangup any channels matching regex */
|
||||
|
||||
regex_string = ast_str_create(strlen(name_or_regex));
|
||||
|
||||
/* Make "/regex/" into "regex" */
|
||||
if (ast_regex_string_to_regex_pattern(name_or_regex, regex_string) != 0) {
|
||||
astman_send_error(s, m, "Regex format invalid, Channel param should be /regex/");
|
||||
ast_free(regex_string);
|
||||
return 0;
|
||||
}
|
||||
ast_softhangup_nolock(c, AST_SOFTHANGUP_EXPLICIT);
|
||||
ast_channel_unlock(c);
|
||||
|
||||
c = ast_channel_unref(c);
|
||||
/* if regex compilation fails, hangup fails */
|
||||
if (regcomp(®exbuf, ast_str_buffer(regex_string), REG_EXTENDED | REG_NOSUB)) {
|
||||
astman_send_error_va(s, m, "Regex compile failed on: %s\n", name_or_regex);
|
||||
ast_free(regex_string);
|
||||
return 0;
|
||||
}
|
||||
|
||||
astman_send_ack(s, m, "Channel Hungup");
|
||||
astman_send_listack(s, m, "Channels hung up will follow", "start");
|
||||
|
||||
for (iter = ast_channel_iterator_all_new(); iter && (c = ast_channel_iterator_next(iter)); ) {
|
||||
if (regexec(®exbuf, ast_channel_name(c), 0, NULL, 0)) {
|
||||
ast_channel_unref(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
ast_verb(3, "%sManager '%s' from %s, hanging up channel: %s\n",
|
||||
(s->session->managerid ? "HTTP " : ""),
|
||||
s->session->username,
|
||||
ast_inet_ntoa(s->session->sin.sin_addr),
|
||||
ast_channel_name(c));
|
||||
|
||||
ast_channel_softhangup_withcause_locked(c, causecode);
|
||||
channels_matched++;
|
||||
|
||||
astman_append(s,
|
||||
"Event: ChannelHungup\r\n"
|
||||
"Channel: %s\r\n"
|
||||
"%s"
|
||||
"\r\n", ast_channel_name(c), idText);
|
||||
|
||||
ast_channel_unref(c);
|
||||
}
|
||||
|
||||
ast_channel_iterator_destroy(iter);
|
||||
regfree(®exbuf);
|
||||
ast_free(regex_string);
|
||||
|
||||
astman_append(s,
|
||||
"Event: ChannelsHungupListComplete\r\n"
|
||||
"EventList: Complete\r\n"
|
||||
"ListItems: %d\r\n"
|
||||
"%s"
|
||||
"\r\n", channels_matched, idText);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
20
main/utils.c
20
main/utils.c
@@ -1350,6 +1350,26 @@ int ast_build_string(char **buffer, size_t *space, const char *fmt, ...)
|
||||
return result;
|
||||
}
|
||||
|
||||
int ast_regex_string_to_regex_pattern(const char *regex_string, struct ast_str *regex_pattern)
|
||||
{
|
||||
int regex_len = strlen(regex_string);
|
||||
int ret = 3;
|
||||
|
||||
/* Chop off the leading / if there is one */
|
||||
if ((regex_len >= 1) && (regex_string[0] == '/')) {
|
||||
ast_str_set(®ex_pattern, 0, "%s", regex_string + 1);
|
||||
ret -= 2;
|
||||
}
|
||||
|
||||
/* Chop off the ending / if there is one */
|
||||
if ((regex_len > 1) && (regex_string[regex_len - 1] == '/')) {
|
||||
ast_str_truncate(regex_pattern, -1);
|
||||
ret -= 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
int ast_true(const char *s)
|
||||
{
|
||||
if (ast_strlen_zero(s))
|
||||
|
||||
Reference in New Issue
Block a user