mirror of
https://github.com/asterisk/asterisk.git
synced 2026-04-23 15:17:10 +00:00
cli.c: Allow 'channel request hangup' to accept patterns.
This extends 'channel request hangup' to accept multiple channel names, a POSIX Extended Regular Expression, a glob-like pattern, or a combination of all of them. UserNote: The 'channel request hangup' CLI command now accepts multiple channel names, POSIX Extended Regular Expressions, glob-like patterns, or a combination of all of them. See the CLI command 'core show help channel request hangup' for full details.
This commit is contained in:
50
configure
vendored
50
configure
vendored
@@ -696,6 +696,7 @@ PBX_DAHDI_HALF_FULL
|
||||
PBX_DLADDR
|
||||
PBX_IP_MTU_DISCOVER
|
||||
PBX_RTLD_NOLOAD
|
||||
PBX_FNM_CASEFOLD
|
||||
PBX_GLOB_BRACE
|
||||
PBX_GLOB_NOMAGIC
|
||||
BIND8_CFLAGS
|
||||
@@ -32917,6 +32918,55 @@ rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
|
||||
|
||||
|
||||
|
||||
if test "x${PBX_FNM_CASEFOLD}" != "x1"; then
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for FNM_CASEFOLD in fnmatch.h" >&5
|
||||
printf %s "checking for FNM_CASEFOLD in fnmatch.h... " >&6; }
|
||||
saved_cppflags="${CPPFLAGS}"
|
||||
if test "x${FNM_CASEFOLD_DIR}" != "x"; then
|
||||
FNM_CASEFOLD_INCLUDE="-I${FNM_CASEFOLD_DIR}/include"
|
||||
fi
|
||||
CPPFLAGS="${CPPFLAGS} ${FNM_CASEFOLD_INCLUDE}"
|
||||
|
||||
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
|
||||
/* end confdefs.h. */
|
||||
#include <fnmatch.h>
|
||||
int
|
||||
main (void)
|
||||
{
|
||||
#if defined(FNM_CASEFOLD)
|
||||
int foo = 0;
|
||||
#else
|
||||
int foo = bar;
|
||||
#endif
|
||||
0
|
||||
|
||||
;
|
||||
return 0;
|
||||
}
|
||||
_ACEOF
|
||||
if ac_fn_c_try_compile "$LINENO"
|
||||
then :
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5
|
||||
printf "%s\n" "yes" >&6; }
|
||||
PBX_FNM_CASEFOLD=1
|
||||
|
||||
printf "%s\n" "#define HAVE_FNM_CASEFOLD 1" >>confdefs.h
|
||||
|
||||
|
||||
|
||||
else case e in #(
|
||||
e) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5
|
||||
printf "%s\n" "no" >&6; }
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext
|
||||
CPPFLAGS="${saved_cppflags}"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
|
||||
if test "x${PBX_RTLD_NOLOAD}" != "x1"; then
|
||||
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for RTLD_NOLOAD in dlfcn.h" >&5
|
||||
printf %s "checking for RTLD_NOLOAD in dlfcn.h... " >&6; }
|
||||
|
||||
@@ -1600,6 +1600,8 @@ AST_C_DEFINE_CHECK([GLOB_NOMAGIC], [GLOB_NOMAGIC], [glob.h])
|
||||
|
||||
AST_C_DEFINE_CHECK([GLOB_BRACE], [GLOB_BRACE], [glob.h])
|
||||
|
||||
AST_C_DEFINE_CHECK([FNM_CASEFOLD], [FNM_CASEFOLD], [fnmatch.h])
|
||||
|
||||
AST_C_DEFINE_CHECK([RTLD_NOLOAD], [RTLD_NOLOAD], [dlfcn.h])
|
||||
|
||||
AST_C_DEFINE_CHECK([IP_MTU_DISCOVER], [IP_MTU_DISCOVER], [netinet/in.h])
|
||||
|
||||
@@ -293,6 +293,9 @@
|
||||
/* Define to 1 if you have the 'fmodl' function. */
|
||||
#undef HAVE_FMODL
|
||||
|
||||
/* Define if your system has the FNM_CASEFOLD headers. */
|
||||
#undef HAVE_FNM_CASEFOLD
|
||||
|
||||
/* Define to 1 if you have the 'fork' function. */
|
||||
#undef HAVE_FORK
|
||||
|
||||
|
||||
154
main/cli.c
154
main/cli.c
@@ -45,6 +45,7 @@
|
||||
#include <regex.h>
|
||||
#include <pwd.h>
|
||||
#include <grp.h>
|
||||
#include <fnmatch.h>
|
||||
|
||||
#include "asterisk/cli.h"
|
||||
#include "asterisk/linkedlists.h"
|
||||
@@ -115,6 +116,8 @@ static AST_VECTOR(, struct ast_cli_entry *) shutdown_commands;
|
||||
/*! \brief Initial buffer size for resulting strings in ast_cli() */
|
||||
#define AST_CLI_INITLEN 256
|
||||
|
||||
#define MAX_REGEX_ERROR_LEN 128
|
||||
|
||||
void ast_cli(int fd, const char *fmt, ...)
|
||||
{
|
||||
int res;
|
||||
@@ -1227,57 +1230,162 @@ static char *handle_chanlist(struct ast_cli_entry *e, int cmd, struct ast_cli_ar
|
||||
#undef VERBOSE_FORMAT_STRING2
|
||||
}
|
||||
|
||||
static int channel_match_by_regex(const char *channel_name, const void *data)
|
||||
{
|
||||
return regexec(data, channel_name, 0, NULL, 0);
|
||||
}
|
||||
|
||||
static int channel_match_by_glob(const char *channel_name, const void *pattern)
|
||||
{
|
||||
#if defined(HAVE_FNM_CASEFOLD)
|
||||
return fnmatch(pattern, channel_name, FNM_NOESCAPE | FNM_PATHNAME | FNM_CASEFOLD);
|
||||
#else
|
||||
char *lower_channel_name = ast_str_to_lower(ast_strdup(channel_name));
|
||||
if (lower_channel_name) {
|
||||
int res = fnmatch(pattern, lower_channel_name, FNM_NOESCAPE | FNM_PATHNAME);
|
||||
ast_free(lower_channel_name);
|
||||
return res;
|
||||
}
|
||||
return FNM_NOMATCH;
|
||||
#endif
|
||||
}
|
||||
|
||||
static int channel_hangup_matches(
|
||||
struct ast_cli_args *a,
|
||||
int (*matchfn)(const char *, const void *),
|
||||
const void *data)
|
||||
{
|
||||
struct ao2_container *cached_channels;
|
||||
struct ao2_iterator iter;
|
||||
struct ast_channel_snapshot *snapshot;
|
||||
struct ast_channel *c;
|
||||
int matched = 0;
|
||||
|
||||
cached_channels = ast_channel_cache_all();
|
||||
|
||||
iter = ao2_iterator_init(cached_channels, 0);
|
||||
for (; (snapshot = ao2_iterator_next(&iter)); ao2_ref(snapshot, -1)) {
|
||||
if ((!matchfn || !matchfn(snapshot->base->name, data))
|
||||
&& (c = ast_channel_get_by_name(snapshot->base->name))) {
|
||||
ast_cli(a->fd, "Requested Hangup on channel '%s'\n", snapshot->base->name);
|
||||
ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
|
||||
c = ast_channel_unref(c);
|
||||
matched++;
|
||||
}
|
||||
}
|
||||
ao2_iterator_destroy(&iter);
|
||||
ao2_ref(cached_channels, -1);
|
||||
|
||||
return matched;
|
||||
}
|
||||
|
||||
#define arg_looks_like_regex(n) \
|
||||
(*n == '/' && n[strlen(n) - 1] == '/')
|
||||
|
||||
#define arg_looks_like_glob(n) \
|
||||
(strcspn(n, "?*[") < strlen(n))
|
||||
|
||||
static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
|
||||
{
|
||||
struct ast_channel *c = NULL;
|
||||
static const char * const completions[] = { "all", NULL };
|
||||
char *complete;
|
||||
char *complete = NULL;
|
||||
int x;
|
||||
|
||||
switch (cmd) {
|
||||
case CLI_INIT:
|
||||
e->command = "channel request hangup";
|
||||
e->usage =
|
||||
"Usage: channel request hangup <channel>|<all>\n"
|
||||
" Request that a channel be hung up. The hangup takes effect\n"
|
||||
" the next time the driver reads or writes from the channel.\n"
|
||||
"Usage: channel request hangup <all>|<channel> [<channel> ...]\n"
|
||||
" Request that one or more channels be hung up. The hangup takes\n"
|
||||
" effect the next time the driver reads or writes from the channel.\n\n"
|
||||
" If 'all' is specified instead of a channel name, all channels\n"
|
||||
" will see the hangup request.\n";
|
||||
" will see the hangup request.\n\n"
|
||||
" A POSIX Extended Regular Expression can be provided in the place\n"
|
||||
" of a fixed channel name by wrapping it in forward slashes\n"
|
||||
" (e.g. /^PJSIP.*/). Note that the expression will match any part\n"
|
||||
" of the channel name unless anchored explicitly by ^ and/or $.\n\n"
|
||||
" Finally, wildcards (*) and other glob-like characters can also\n"
|
||||
" be used to match more than one channel. Similar to how path globs\n"
|
||||
" behave, a forward slash cannot be matched with a wildcard or\n"
|
||||
" bracket expression. In other words, if you want to hangup all\n"
|
||||
" Local channels, you must specify Local/* and not Loc*.\n";
|
||||
return NULL;
|
||||
case CLI_GENERATE:
|
||||
if (a->pos != e->args) {
|
||||
if (a->pos < e->args) {
|
||||
return NULL;
|
||||
} else if (a->pos == e->args) {
|
||||
complete = ast_cli_complete(a->word, completions, a->n);
|
||||
}
|
||||
complete = ast_cli_complete(a->word, completions, a->n);
|
||||
if (!complete) {
|
||||
complete = ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
|
||||
complete = ast_complete_channels(a->line, a->word, a->pos, a->n - 1, a->pos);
|
||||
}
|
||||
return complete;
|
||||
}
|
||||
|
||||
if (a->argc != 4) {
|
||||
if (a->argc < 4) {
|
||||
return CLI_SHOWUSAGE;
|
||||
}
|
||||
|
||||
if (!strcasecmp(a->argv[3], "all")) {
|
||||
struct ast_channel_iterator *iter = NULL;
|
||||
if (!(iter = ast_channel_iterator_all_new())) {
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
for (; iter && (c = ast_channel_iterator_next(iter)); ast_channel_unref(c)) {
|
||||
channel_hangup_matches(a, NULL, NULL);
|
||||
return CLI_SUCCESS;
|
||||
}
|
||||
|
||||
for (x = e->args; x < a->argc; x++) {
|
||||
/* Try to find a literal channel name first */
|
||||
if ((c = ast_channel_get_by_name(a->argv[x]))) {
|
||||
ast_channel_lock(c);
|
||||
ast_cli(a->fd, "Requested Hangup on channel '%s'\n", ast_channel_name(c));
|
||||
ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
|
||||
ast_channel_unlock(c);
|
||||
c = ast_channel_unref(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (arg_looks_like_regex(a->argv[x])) {
|
||||
char *pattern = ast_strdup(a->argv[x]);
|
||||
regex_t re;
|
||||
int res;
|
||||
|
||||
if (!pattern) {
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
|
||||
pattern[strlen(pattern) - 1] = 0;
|
||||
res = regcomp(&re, &pattern[1], REG_EXTENDED | REG_ICASE | REG_NOSUB);
|
||||
ast_free(pattern);
|
||||
if (res) {
|
||||
char errbuf[MAX_REGEX_ERROR_LEN];
|
||||
regerror(res, &re, errbuf, sizeof(errbuf));
|
||||
ast_cli(a->fd, "%s is not a valid POSIX Extended Regular Expression: %s\n",
|
||||
a->argv[x], errbuf);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!channel_hangup_matches(a, channel_match_by_regex, &re)) {
|
||||
ast_cli(a->fd, "%s did not match a known channel\n", a->argv[x]);
|
||||
}
|
||||
regfree(&re);
|
||||
} else if (arg_looks_like_glob(a->argv[x])) {
|
||||
#if defined(HAVE_FNM_CASEFOLD)
|
||||
if (!channel_hangup_matches(a, channel_match_by_glob, a->argv[x])) {
|
||||
ast_cli(a->fd, "%s did not match a known channel\n", a->argv[x]);
|
||||
}
|
||||
#else
|
||||
char *pattern = ast_str_to_lower(ast_strdup(a->argv[x]));
|
||||
if (pattern) {
|
||||
if (!channel_hangup_matches(a, channel_match_by_glob, pattern)) {
|
||||
ast_cli(a->fd, "%s did not match a known channel\n", a->argv[x]);
|
||||
}
|
||||
ast_free(pattern);
|
||||
} else {
|
||||
return CLI_FAILURE;
|
||||
}
|
||||
#endif
|
||||
} else {
|
||||
ast_cli(a->fd, "%s is not a known channel\n", a->argv[x]);
|
||||
}
|
||||
ast_channel_iterator_destroy(iter);
|
||||
} else if ((c = ast_channel_get_by_name(a->argv[3]))) {
|
||||
ast_channel_lock(c);
|
||||
ast_cli(a->fd, "Requested Hangup on channel '%s'\n", ast_channel_name(c));
|
||||
ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
|
||||
ast_channel_unlock(c);
|
||||
c = ast_channel_unref(c);
|
||||
} else {
|
||||
ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
|
||||
}
|
||||
|
||||
return CLI_SUCCESS;
|
||||
|
||||
Reference in New Issue
Block a user