Newer
Older
Mark Spencer
committed
#undef VERBOSE_FORMAT_STRING2
static char *handle_softhangup(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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"
" If 'all' is specified instead of a channel name, all channels\n"
" will see the hangup request.\n";
return NULL;
case CLI_GENERATE:
return ast_complete_channels(a->line, a->word, a->pos, a->n, e->args);
}
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)) {
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);
}
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));
Kevin P. Fleming
committed
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;
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
/*! \brief handles CLI command 'cli show permissions' */
static char *handle_cli_show_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct usergroup_cli_perm *cp;
struct cli_perm *perm;
struct passwd *pw = NULL;
struct group *gr = NULL;
switch (cmd) {
case CLI_INIT:
e->command = "cli show permissions";
e->usage =
"Usage: cli show permissions\n"
" Shows CLI configured permissions.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
AST_RWLIST_RDLOCK(&cli_perms);
AST_LIST_TRAVERSE(&cli_perms, cp, list) {
if (cp->uid >= 0) {
pw = getpwuid(cp->uid);
if (pw) {
ast_cli(a->fd, "user: %s [uid=%d]\n", pw->pw_name, cp->uid);
}
} else {
gr = getgrgid(cp->gid);
if (gr) {
ast_cli(a->fd, "group: %s [gid=%d]\n", gr->gr_name, cp->gid);
}
}
ast_cli(a->fd, "Permissions:\n");
if (cp->perms) {
AST_LIST_TRAVERSE(cp->perms, perm, list) {
ast_cli(a->fd, "\t%s -> %s\n", perm->permit ? "permit" : "deny", perm->command);
}
}
ast_cli(a->fd, "\n");
}
AST_RWLIST_UNLOCK(&cli_perms);
return CLI_SUCCESS;
}
/*! \brief handles CLI command 'cli reload permissions' */
static char *handle_cli_reload_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "cli reload permissions";
e->usage =
"Usage: cli reload permissions\n"
" Reload the 'cli_permissions.conf' file.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
ast_cli_perms_init(1);
return CLI_SUCCESS;
}
/*! \brief handles CLI command 'cli check permissions' */
static char *handle_cli_check_permissions(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct passwd *pw = NULL;
struct group *gr;
int gid = -1, uid = -1;
char command[AST_MAX_ARGS] = "";
struct ast_cli_entry *ce = NULL;
int found = 0;
char *group, *tmp;
switch (cmd) {
case CLI_INIT:
e->command = "cli check permissions";
e->usage =
"Usage: cli check permissions {<username>|@<groupname>|<username>@<groupname>} [<command>]\n"
" Check permissions config for a user@group or list the allowed commands for the specified user.\n"
" The username or the groupname may be omitted.\n";
return NULL;
case CLI_GENERATE:
if (a->pos >= 4) {
return ast_cli_generator(a->line + strlen("cli check permissions") + strlen(a->argv[3]) + 1, a->word, a->n);
}
return NULL;
}
if (a->argc < 4) {
return CLI_SHOWUSAGE;
}
tmp = ast_strdupa(a->argv[3]);
group = strchr(tmp, '@');
if (group) {
gr = getgrnam(&group[1]);
if (!gr) {
ast_cli(a->fd, "Unknown group '%s'\n", &group[1]);
return CLI_FAILURE;
}
group[0] = '\0';
gid = gr->gr_gid;
}
if (!group && ast_strlen_zero(tmp)) {
ast_cli(a->fd, "You didn't supply a username\n");
} else if (!ast_strlen_zero(tmp) && !(pw = getpwnam(tmp))) {
ast_cli(a->fd, "Unknown user '%s'\n", tmp);
return CLI_FAILURE;
} else if (pw) {
uid = pw->pw_uid;
}
if (a->argc == 4) {
while ((ce = cli_next(ce))) {
/* Hide commands that start with '_' */
if (ce->_full_cmd[0] == '_') {
continue;
}
if (cli_has_permissions(uid, gid, ce->_full_cmd)) {
ast_cli(a->fd, "%30.30s %s\n", ce->_full_cmd, S_OR(ce->summary, "<no description available>"));
found++;
}
}
if (!found) {
ast_cli(a->fd, "You are not allowed to run any command on Asterisk\n");
}
} else {
ast_join(command, sizeof(command), a->argv + 4);
ast_cli(a->fd, "%s '%s%s%s' is %s to run command: '%s'\n", uid >= 0 ? "User" : "Group", tmp,
group && uid >= 0 ? "@" : "",
group ? &group[1] : "",
cli_has_permissions(uid, gid, command) ? "allowed" : "not allowed", command);
}
return CLI_SUCCESS;
}
Russell Bryant
committed
static char *__ast_cli_generator(const char *text, const char *word, int state, int lock);
static char *handle_commandmatchesarray(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
int buflen = 2048;
switch (cmd) {
case CLI_INIT:
e->command = "_command matchesarray";
"Usage: _command matchesarray \"<line>\" text \n"
" This function is used internally to help with command completion and should.\n"
" never be called by the user directly.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 4)
return CLI_SHOWUSAGE;
Russell Bryant
committed
if (!(buf = ast_malloc(buflen)))
return CLI_FAILURE;
matches = ast_cli_completion_matches(a->argv[2], a->argv[3]);
matchlen = strlen(matches[x]) + 1;
if (len + matchlen >= buflen) {
buflen += matchlen * 3;
obuf = buf;
if (!(buf = ast_realloc(obuf, buflen)))
Russell Bryant
committed
/* Memory allocation failure... Just free old buffer and be done */
Tilghman Lesher
committed
ast_free(obuf);
}
if (buf)
len += sprintf( buf + len, "%s ", matches[x]);
Tilghman Lesher
committed
ast_free(matches[x]);
Tilghman Lesher
committed
ast_free(matches);
ast_cli(a->fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
Tilghman Lesher
committed
ast_free(buf);
ast_cli(a->fd, "NULL\n");
return CLI_SUCCESS;
static char *handle_commandnummatches(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
switch (cmd) {
case CLI_INIT:
e->command = "_command nummatches";
"Usage: _command nummatches \"<line>\" text \n"
" This function is used internally to help with command completion and should.\n"
" never be called by the user directly.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 4)
return CLI_SHOWUSAGE;
matches = ast_cli_generatornummatches(a->argv[2], a->argv[3]);
ast_cli(a->fd, "%d", matches);
return CLI_SUCCESS;
static char *handle_commandcomplete(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
switch (cmd) {
case CLI_INIT:
e->command = "_command complete";
"Usage: _command complete \"<line>\" text state\n"
" This function is used internally to help with command completion and should.\n"
" never be called by the user directly.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc != 5)
return CLI_SHOWUSAGE;
buf = __ast_cli_generator(a->argv[2], a->argv[3], atoi(a->argv[4]), 0);
ast_cli(a->fd, "%s", buf);
Tilghman Lesher
committed
ast_free(buf);
ast_cli(a->fd, "NULL\n");
return CLI_SUCCESS;
struct channel_set_debug_args {
int fd;
int is_off;
};
static int channel_set_debug(void *obj, void *arg, void *data, int flags)
{
struct ast_channel *chan = obj;
struct channel_set_debug_args *args = data;
ast_channel_lock(chan);
if (!(ast_channel_fin(chan) & DEBUGCHAN_FLAG) || !(ast_channel_fout(chan) & DEBUGCHAN_FLAG)) {
if (args->is_off) {
ast_channel_fin_set(chan, ast_channel_fin(chan) & ~DEBUGCHAN_FLAG);
ast_channel_fout_set(chan, ast_channel_fout(chan) & ~DEBUGCHAN_FLAG);
} else {
ast_channel_fin_set(chan, ast_channel_fin(chan) | DEBUGCHAN_FLAG);
ast_channel_fout_set(chan, ast_channel_fout(chan) | DEBUGCHAN_FLAG);
}
ast_cli(args->fd, "Debugging %s on channel %s\n", args->is_off ? "disabled" : "enabled",
ast_channel_name(chan));
}
ast_channel_unlock(chan);
return 0;
}
static char *handle_core_set_debug_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_channel *c = NULL;
struct channel_set_debug_args args = {
.fd = a->fd,
};
switch (cmd) {
case CLI_INIT:
e->command = "core set debug channel";
e->usage =
"Usage: core set debug channel <all|channel> [off]\n"
" Enables/disables debugging on all or on a specific channel.\n";
return NULL;
case CLI_GENERATE:
/* XXX remember to handle the optional "off" */
if (a->pos != e->args)
return NULL;
return a->n == 0 ? ast_strdup("all") : ast_complete_channels(a->line, a->word, a->pos, a->n - 1, e->args);
if (cmd == (CLI_HANDLER + 1000)) {
/* called from handle_nodebugchan_deprecated */
args.is_off = 1;
} else if (a->argc == e->args + 2) {
/* 'core set debug channel {all|chan_id}' */
if (!strcasecmp(a->argv[e->args + 1], "off"))
args.is_off = 1;
else
return CLI_SHOWUSAGE;
} else if (a->argc != e->args + 1) {
return CLI_SHOWUSAGE;
}
if (!strcasecmp("all", a->argv[e->args])) {
if (args.is_off) {
global_fin &= ~DEBUGCHAN_FLAG;
global_fout &= ~DEBUGCHAN_FLAG;
} else {
global_fin |= DEBUGCHAN_FLAG;
global_fout |= DEBUGCHAN_FLAG;
}
ast_channel_callback(channel_set_debug, NULL, &args, OBJ_NODATA | OBJ_MULTIPLE);
if ((c = ast_channel_get_by_name(a->argv[e->args]))) {
channel_set_debug(c, NULL, &args, 0);
ast_channel_unref(c);
} else {
ast_cli(a->fd, "No such channel %s\n", a->argv[e->args]);
ast_cli(a->fd, "Debugging on new channels is %s\n", args.is_off ? "disabled" : "enabled");
return CLI_SUCCESS;
static char *handle_nodebugchan_deprecated(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
switch (cmd) {
case CLI_INIT:
e->command = "no debug channel";
return NULL;
case CLI_HANDLER:
/* exit out of switch statement */
break;
default:
return NULL;
}
if (a->argc != e->args + 1)
return CLI_SHOWUSAGE;
/* add a 'magic' value to the CLI_HANDLER command so that
* handle_core_set_debug_channel() will act as if 'off'
* had been specified as part of the command
*/
res = handle_core_set_debug_channel(e, CLI_HANDLER + 1000, a);
return res;
static char *handle_showchan(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
struct ast_channel_snapshot *snapshot;
Mark Spencer
committed
char cdrtime[256];
struct ast_str *obuf;/*!< Buffer for CDR variables. */
Richard Mudgett
committed
struct ast_str *output;/*!< Accumulation buffer for all output. */
int hour=0, min=0, sec=0;
struct ast_var_t *var;
switch (cmd) {
case CLI_INIT:
e->command = "core show channel";
"Usage: core show channel <channel>\n"
" Shows lots of information about the specified channel.\n";
return NULL;
case CLI_GENERATE:
return ast_complete_channels(a->line, a->word, a->pos, a->n, 3);
}
if (a->argc != 4) {
return CLI_SHOWUSAGE;
}
Kevin P. Fleming
committed
now = ast_tvnow();
if (!(msg = stasis_cache_get(ast_channel_cache_by_name(), ast_channel_snapshot_type(), a->argv[3]))) {
ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
return CLI_SUCCESS;
Kevin P. Fleming
committed
}
snapshot = stasis_message_data(msg);
Richard Mudgett
committed
obuf = ast_str_thread_get(&ast_str_thread_global_buf, 16);
if (!obuf) {
return CLI_FAILURE;
}
output = ast_str_create(8192);
if (!output) {
return CLI_FAILURE;
}
if (!ast_tvzero(snapshot->creationtime)) {
elapsed_seconds = now.tv_sec - snapshot->creationtime.tv_sec;
Kevin P. Fleming
committed
hour = elapsed_seconds / 3600;
min = (elapsed_seconds % 3600) / 60;
sec = elapsed_seconds % 60;
snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
} else {
}
Richard Mudgett
committed
ast_str_append(&output, 0,
Kevin P. Fleming
committed
" -- General --\n"
" Name: %s\n"
" Type: %s\n"
" UniqueID: %s\n"
" LinkedID: %s\n"
Kevin P. Fleming
committed
" Caller ID: %s\n"
" Caller ID Name: %s\n"
"Connected Line ID: %s\n"
"Connected Line ID Name: %s\n"
Richard Mudgett
committed
"Eff. Connected Line ID: %s\n"
"Eff. Connected Line ID Name: %s\n"
Kevin P. Fleming
committed
" DNID Digits: %s\n"
Russell Bryant
committed
" Language: %s\n"
Kevin P. Fleming
committed
" State: %s (%d)\n"
" NativeFormats: %s\n"
" WriteFormat: %s\n"
" ReadFormat: %s\n"
" WriteTranscode: %s %s\n"
" ReadTranscode: %s %s\n"
Kevin P. Fleming
committed
" Time to Hangup: %ld\n"
" Elapsed Time: %s\n"
Richard Mudgett
committed
" Bridge ID: %s\n"
Kevin P. Fleming
committed
" -- PBX --\n"
" Context: %s\n"
" Extension: %s\n"
" Priority: %d\n"
" Call Group: %llu\n"
" Pickup Group: %llu\n"
Kevin P. Fleming
committed
" Application: %s\n"
" Data: %s\n"
Jonathan Rose
committed
" Call Identifer: %s\n",
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
snapshot->name,
snapshot->type,
snapshot->uniqueid,
snapshot->linkedid,
S_OR(snapshot->caller_number, "(N/A)"),
S_OR(snapshot->caller_name, "(N/A)"),
S_OR(snapshot->connected_number, "(N/A)"),
S_OR(snapshot->connected_name, "(N/A)"),
S_OR(snapshot->effective_number, "(N/A)"),
S_OR(snapshot->effective_name, "(N/A)"),
S_OR(snapshot->caller_dnid, "(N/A)"),
snapshot->language,
ast_state2str(snapshot->state),
snapshot->state,
snapshot->nativeformats,
snapshot->writeformat,
snapshot->readformat,
!ast_strlen_zero(snapshot->writetrans) ? "Yes" : "No",
snapshot->writetrans,
!ast_strlen_zero(snapshot->readtrans) ? "Yes" : "No",
snapshot->readtrans,
(long) snapshot->hanguptime.tv_sec,
Richard Mudgett
committed
cdrtime,
S_OR(snapshot->bridgeid, "(Not bridged)"),
snapshot->context,
snapshot->exten,
snapshot->priority,
snapshot->callgroup,
snapshot->pickupgroup,
S_OR(snapshot->appl, "(N/A)"),
S_OR(snapshot->data, "(Empty)"),
S_OR(snapshot->callid, "(None)"));
ast_str_append(&output, 0, " Variables:\n");
AST_LIST_TRAVERSE(snapshot->channel_vars, var, entries) {
ast_str_append(&output, 0, "%s=%s\n", ast_var_name(var), ast_var_value(var));
Richard Mudgett
committed
}
if (ast_cdr_serialize_variables(snapshot->name, &obuf, '=', '\n')) {
ast_str_append(&output, 0, " CDR Variables:\n%s\n", ast_str_buffer(obuf));
}
Richard Mudgett
committed
ast_cli(a->fd, "%s", ast_str_buffer(output));
ast_free(output);
return CLI_SUCCESS;
/*
* helper function to generate CLI matches from a fixed set of values.
* A NULL word is acceptable.
*/
char *ast_cli_complete(const char *word, const char * const choices[], int state)
Mark Spencer
committed
{
int i, which = 0, len;
len = ast_strlen_zero(word) ? 0 : strlen(word);
for (i = 0; choices[i]; i++) {
if ((!len || !strncasecmp(word, choices[i], len)) && ++which > state)
return ast_strdup(choices[i]);
Mark Spencer
committed
}
return NULL;
}
char *ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
int wordlen = strlen(word), which = 0;
RAII_VAR(struct ao2_container *, cached_channels, NULL, ao2_cleanup);
char *ret = NULL;
struct ao2_iterator iter;
struct stasis_message *msg;
Kevin P. Fleming
committed
if (pos != rpos) {
}
if (!(cached_channels = stasis_cache_dump(ast_channel_cache(), ast_channel_snapshot_type()))) {
return NULL;
}
iter = ao2_iterator_init(cached_channels, 0);
for (; (msg = ao2_iterator_next(&iter)); ao2_ref(msg, -1)) {
struct ast_channel_snapshot *snapshot = stasis_message_data(msg);
if (!strncasecmp(word, snapshot->name, wordlen) && (++which > state)) {
ret = ast_strdup(snapshot->name);
break;
}
ao2_iterator_destroy(&iter);
return ret;
static char *group_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
Russell Bryant
committed
{
#define FORMAT_STRING "%-25s %-20s %-20s\n"
struct ast_group_info *gi = NULL;
Russell Bryant
committed
int numchans = 0;
regex_t regexbuf;
int havepattern = 0;
switch (cmd) {
case CLI_INIT:
e->command = "group show channels";
"Usage: group show channels [pattern]\n"
" Lists all currently active channels with channel group(s) specified.\n"
" Optional regular expression pattern is matched to group names for each\n"
" channel.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
if (a->argc < 3 || a->argc > 4)
return CLI_SHOWUSAGE;
if (a->argc == 4) {
if (regcomp(®exbuf, a->argv[3], REG_EXTENDED | REG_NOSUB))
return CLI_SHOWUSAGE;
Russell Bryant
committed
havepattern = 1;
}
ast_cli(a->fd, FORMAT_STRING, "Channel", "Group", "Category");
ast_app_group_list_rdlock();
gi = ast_app_group_list_head();
while (gi) {
if (!havepattern || !regexec(®exbuf, gi->group, 0, NULL, 0)) {
ast_cli(a->fd, FORMAT_STRING, ast_channel_name(gi->chan), gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
Russell Bryant
committed
}
gi = AST_LIST_NEXT(gi, group_list);
Russell Bryant
committed
}
Russell Bryant
committed
if (havepattern)
regfree(®exbuf);
ast_cli(a->fd, "%d active channel%s\n", numchans, ESS(numchans));
return CLI_SUCCESS;
Russell Bryant
committed
#undef FORMAT_STRING
}
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
static char *handle_cli_wait_fullybooted(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
switch (cmd) {
case CLI_INIT:
e->command = "core waitfullybooted";
e->usage =
"Usage: core waitfullybooted\n"
" Wait until Asterisk has fully booted.\n";
return NULL;
case CLI_GENERATE:
return NULL;
}
while (!ast_test_flag(&ast_options, AST_OPT_FLAG_FULLY_BOOTED)) {
usleep(100);
}
ast_cli(a->fd, "Asterisk has fully booted.\n");
return CLI_SUCCESS;
}
static char *handle_help(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static struct ast_cli_entry cli_cli[] = {
/* Deprecated, but preferred command is now consolidated (and already has a deprecated command for it). */
Jason Parker
committed
AST_CLI_DEFINE(handle_commandcomplete, "Command complete"),
AST_CLI_DEFINE(handle_commandnummatches, "Returns number of command matches"),
AST_CLI_DEFINE(handle_commandmatchesarray, "Returns command matches array"),
Russell Bryant
committed
Jason Parker
committed
AST_CLI_DEFINE(handle_nodebugchan_deprecated, "Disable debugging on channel(s)"),
Jason Parker
committed
AST_CLI_DEFINE(handle_chanlist, "Display information on channels"),
Jason Parker
committed
AST_CLI_DEFINE(handle_showcalls, "Display information on calls"),
Jason Parker
committed
AST_CLI_DEFINE(handle_showchan, "Display information on a specific channel"),
AST_CLI_DEFINE(handle_core_set_debug_channel, "Enable/disable debugging on a channel"),
Jason Parker
committed
AST_CLI_DEFINE(handle_verbose, "Set level of debug/verbose chattiness"),
Jason Parker
committed
AST_CLI_DEFINE(group_show_channels, "Display active channels with group(s)"),
Jason Parker
committed
AST_CLI_DEFINE(handle_help, "Display help list, or specific help on a command"),
Jason Parker
committed
AST_CLI_DEFINE(handle_logger_mute, "Toggle logging output to a console"),
Jason Parker
committed
AST_CLI_DEFINE(handle_modlist, "List modules and info"),
AST_CLI_DEFINE(handle_load, "Load a module by name"),
AST_CLI_DEFINE(handle_reload, "Reload configuration for a module"),
AST_CLI_DEFINE(handle_core_reload, "Global reload"),
AST_CLI_DEFINE(handle_unload, "Unload a module by name"),
Jason Parker
committed
AST_CLI_DEFINE(handle_showuptime, "Show uptime information"),
Jason Parker
committed
AST_CLI_DEFINE(handle_softhangup, "Request a hangup on a given channel"),
AST_CLI_DEFINE(handle_cli_reload_permissions, "Reload CLI permissions config"),
AST_CLI_DEFINE(handle_cli_show_permissions, "Show CLI permissions"),
AST_CLI_DEFINE(handle_cli_check_permissions, "Try a permissions config for a user"),
AST_CLI_DEFINE(handle_cli_wait_fullybooted, "Wait for Asterisk to be fully booted"),
/*!
* Some regexp characters in cli arguments are reserved and used as separators.
*/
static const char cli_rsvd[] = "[]{}|*%";
/*!
* initialize the _full_cmd string and related parameters,
* return 0 on success, -1 on error.
*/
static int set_full_cmd(struct ast_cli_entry *e)
{
int i;
char buf[80];
ast_join(buf, sizeof(buf), e->cmda);
e->_full_cmd = ast_strdup(buf);
if (!e->_full_cmd) {
ast_log(LOG_WARNING, "-- cannot allocate <%s>\n", buf);
return -1;
}
e->cmdlen = strcspn(e->_full_cmd, cli_rsvd);
for (i = 0; e->cmda[i]; i++)
;
e->args = i;
return 0;
}
/*! \brief cleanup (free) cli_perms linkedlist. */
static void destroy_user_perms(void)
struct cli_perm *perm;
struct usergroup_cli_perm *user_perm;
AST_RWLIST_WRLOCK(&cli_perms);
while ((user_perm = AST_LIST_REMOVE_HEAD(&cli_perms, list))) {
while ((perm = AST_LIST_REMOVE_HEAD(user_perm->perms, list))) {
ast_free(perm->command);
ast_free(perm);
}
ast_free(user_perm);
}
AST_RWLIST_UNLOCK(&cli_perms);
int ast_cli_perms_init(int reload)
{
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
struct ast_flags config_flags = { reload ? CONFIG_FLAG_FILEUNCHANGED : 0 };
struct ast_config *cfg;
char *cat = NULL;
struct ast_variable *v;
struct usergroup_cli_perm *user_group, *cp_entry;
struct cli_perm *perm = NULL;
struct passwd *pw;
struct group *gr;
if (ast_mutex_trylock(&permsconfiglock)) {
ast_log(LOG_NOTICE, "You must wait until last 'cli reload permissions' command finish\n");
return 1;
}
cfg = ast_config_load2(perms_config, "" /* core, can't reload */, config_flags);
if (!cfg) {
ast_mutex_unlock(&permsconfiglock);
return 1;
} else if (cfg == CONFIG_STATUS_FILEUNCHANGED) {
ast_mutex_unlock(&permsconfiglock);
return 0;
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
/* free current structures. */
destroy_user_perms();
while ((cat = ast_category_browse(cfg, cat))) {
if (!strcasecmp(cat, "general")) {
/* General options */
for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
if (!strcasecmp(v->name, "default_perm")) {
cli_default_perm = (!strcasecmp(v->value, "permit")) ? 1: 0;
}
}
continue;
}
/* users or groups */
gr = NULL, pw = NULL;
if (cat[0] == '@') {
/* This is a group */
gr = getgrnam(&cat[1]);
if (!gr) {
ast_log (LOG_WARNING, "Unknown group '%s'\n", &cat[1]);
continue;
}
} else {
/* This is a user */
pw = getpwnam(cat);
if (!pw) {
ast_log (LOG_WARNING, "Unknown user '%s'\n", cat);
continue;
}
}
user_group = NULL;
/* Check for duplicates */
AST_RWLIST_WRLOCK(&cli_perms);
AST_LIST_TRAVERSE(&cli_perms, cp_entry, list) {
if ((pw && cp_entry->uid == pw->pw_uid) || (gr && cp_entry->gid == gr->gr_gid)) {
/* if it is duplicated, just added this new settings, to
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
1865
1866
1867
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1880
1881
1882
the current list. */
user_group = cp_entry;
break;
}
}
AST_RWLIST_UNLOCK(&cli_perms);
if (!user_group) {
/* alloc space for the new user config. */
user_group = ast_calloc(1, sizeof(*user_group));
if (!user_group) {
continue;
}
user_group->uid = (pw ? pw->pw_uid : -1);
user_group->gid = (gr ? gr->gr_gid : -1);
user_group->perms = ast_calloc(1, sizeof(*user_group->perms));
if (!user_group->perms) {
ast_free(user_group);
continue;
}
}
for (v = ast_variable_browse(cfg, cat); v; v = v->next) {
if (ast_strlen_zero(v->value)) {
/* we need to check this condition cause it could break security. */
ast_log(LOG_WARNING, "Empty permit/deny option in user '%s'\n", cat);
continue;
}
if (!strcasecmp(v->name, "permit")) {
perm = ast_calloc(1, sizeof(*perm));
if (perm) {
perm->permit = 1;
perm->command = ast_strdup(v->value);
}
} else if (!strcasecmp(v->name, "deny")) {
perm = ast_calloc(1, sizeof(*perm));
if (perm) {
perm->permit = 0;
perm->command = ast_strdup(v->value);
}
} else {
/* up to now, only 'permit' and 'deny' are possible values. */
ast_log(LOG_WARNING, "Unknown '%s' option\n", v->name);
continue;
}
if (perm) {
/* Added the permission to the user's list. */
AST_LIST_INSERT_TAIL(user_group->perms, perm, list);
perm = NULL;
}
}
AST_RWLIST_WRLOCK(&cli_perms);
AST_RWLIST_INSERT_TAIL(&cli_perms, user_group, list);
AST_RWLIST_UNLOCK(&cli_perms);
}
ast_config_destroy(cfg);
ast_mutex_unlock(&permsconfiglock);
return 0;
}
static void cli_shutdown(void)
{
ast_cli_unregister_multiple(cli_cli, ARRAY_LEN(cli_cli));
}
/*! \brief initialize the _full_cmd string in * each of the builtins. */
void ast_builtins_init(void)
{
ast_cli_register_multiple(cli_cli, ARRAY_LEN(cli_cli));
ast_register_atexit(cli_shutdown);
/*!
* match a word in the CLI entry.
* returns -1 on mismatch, 0 on match of an optional word,
* 1 on match of a full word.
*
* The pattern can be
* any_word match for equal
* [foo|bar|baz] optionally, one of these words
* {foo|bar|baz} exactly, one of these words
* % any word
*/
static int word_match(const char *cmd, const char *cli_word)
{
int l;
char *pos;
if (ast_strlen_zero(cmd) || ast_strlen_zero(cli_word))
return -1;
if (!strchr(cli_rsvd, cli_word[0])) /* normal match */
return (strcasecmp(cmd, cli_word) == 0) ? 1 : -1;
l = strlen(cmd);
/* wildcard match - will extend in the future */
if (l > 0 && cli_word[0] == '%') {
return 1; /* wildcard */
}
/* Start a search for the command entered against the cli word in question */
pos = strcasestr(cli_word, cmd);
while (pos) {
/*
*Check if the word matched with is surrounded by reserved characters on both sides
* and isn't at the beginning of the cli_word since that would make it check in a location we shouldn't know about.
* If it is surrounded by reserved chars and isn't at the beginning, it's a match.
*/
if (pos != cli_word && strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l])) {
return 1; /* valid match */
}
/* Ok, that one didn't match, strcasestr to the next appearance of the command and start over.*/
pos = strcasestr(pos + 1, cmd);
}
/* If no matches were found over the course of the while loop, we hit the end of the string. It's a mismatch. */
return -1;
1939
1940
1941
1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957
1958
1959
1960
1961
1962
}
/*! \brief if word is a valid prefix for token, returns the pos-th
* match as a malloced string, or NULL otherwise.
* Always tell in *actual how many matches we got.
*/
static char *is_prefix(const char *word, const char *token,
int pos, int *actual)
{
int lw;
char *s, *t1;
*actual = 0;
if (ast_strlen_zero(token))
return NULL;
if (ast_strlen_zero(word))
word = ""; /* dummy */
lw = strlen(word);
if (strcspn(word, cli_rsvd) != lw)
return NULL; /* no match if word has reserved chars */
if (strchr(cli_rsvd, token[0]) == NULL) { /* regular match */
if (strncasecmp(token, word, lw)) /* no match */
return NULL;
*actual = 1;
return (pos != 0) ? NULL : ast_strdup(token);
}
/* now handle regexp match */
/* Wildcard always matches, so we never do is_prefix on them */
t1 = ast_strdupa(token + 1); /* copy, skipping first char */
while (pos >= 0 && (s = strsep(&t1, cli_rsvd)) && *s) {
if (*s == '%') /* wildcard */
continue;
if (strncasecmp(s, word, lw)) /* no match */
continue;
(*actual)++;
if (pos-- == 0)
}
return NULL;
}
* \brief locate a cli command in the 'helpers' list (which must be locked).
* The search compares word by word taking care of regexps in e->cmda
* This function will return NULL when nothing is matched, or the ast_cli_entry that matched.
* \param cmds
* \param match_type has 3 possible values:
* 0 returns if the search key is equal or longer than the entry.
* note that trailing optional arguments are skipped.
* -1 true if the mismatch is on the last word XXX not true!
* 1 true only on complete, exact match.
static struct ast_cli_entry *find_cli(const char * const cmds[], int match_type)
int matchlen = -1; /* length of longest match so far */
struct ast_cli_entry *cand = NULL, *e=NULL;
Russell Bryant
committed
while ( (e = cli_next(e)) ) {