Newer
Older
" 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;
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
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
/*! \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)
Mark Spencer
committed
char cdrtime[256];
char nf[256];
struct ast_str *write_transpath = ast_str_alloca(256);
struct ast_str *read_transpath = ast_str_alloca(256);
Richard Mudgett
committed
struct ast_str *obuf;/*!< Buffer for variable, CDR variable, and trace output. */
struct ast_str *output;/*!< Accumulation buffer for all output. */
int hour=0, min=0, sec=0;
Jonathan Rose
committed
struct ast_callid *callid;
char call_identifier_str[AST_CALLID_BUFFER_LENGTH] = "";
Richard Mudgett
committed
struct ast_party_id effective_connected_id;
#ifdef CHANNEL_TRACE
int trace_enabled;
#endif
Richard Mudgett
committed
struct ast_bridge *bridge;
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 (!(c = ast_channel_get_by_name(a->argv[3]))) {
ast_cli(a->fd, "%s is not a known channel\n", a->argv[3]);
return CLI_SUCCESS;
Kevin P. Fleming
committed
}
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;
}
ast_channel_lock(c);
if (ast_channel_cdr(c)) {
elapsed_seconds = now.tv_sec - ast_channel_cdr(c)->start.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 {
}
Jonathan Rose
committed
/* Construct the call identifier string based on the status of the channel's call identifier */
if ((callid = ast_channel_callid(c))) {
ast_callid_strnprint(call_identifier_str, sizeof(call_identifier_str), callid);
ast_callid_unref(callid);
}
Richard Mudgett
committed
effective_connected_id = ast_channel_connected_effective_id(c);
Richard Mudgett
committed
bridge = ast_channel_get_bridge(c);
Richard Mudgett
committed
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"
" Rings: %d\n"
" NativeFormats: %s\n"
" WriteFormat: %s\n"
" ReadFormat: %s\n"
" WriteTranscode: %s %s\n"
" ReadTranscode: %s %s\n"
Kevin P. Fleming
committed
"1st File Descriptor: %d\n"
" Frames in: %d%s\n"
" Frames out: %d%s\n"
" 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
" Blocking in: %s\n"
" Call Identifer: %s\n",
Richard Mudgett
committed
ast_channel_name(c),
ast_channel_tech(c)->type,
ast_channel_uniqueid(c),
ast_channel_linkedid(c),
S_COR(ast_channel_caller(c)->id.number.valid, ast_channel_caller(c)->id.number.str, "(N/A)"),
S_COR(ast_channel_caller(c)->id.name.valid, ast_channel_caller(c)->id.name.str, "(N/A)"),
S_COR(ast_channel_connected(c)->id.number.valid, ast_channel_connected(c)->id.number.str, "(N/A)"),
S_COR(ast_channel_connected(c)->id.name.valid, ast_channel_connected(c)->id.name.str, "(N/A)"),
Richard Mudgett
committed
S_COR(effective_connected_id.number.valid, effective_connected_id.number.str, "(N/A)"),
S_COR(effective_connected_id.name.valid, effective_connected_id.name.str, "(N/A)"),
S_OR(ast_channel_dialed(c)->number.str, "(N/A)"),
Richard Mudgett
committed
ast_state2str(ast_channel_state(c)),
ast_channel_state(c),
ast_channel_rings(c),
ast_getformatname_multiple(nf, sizeof(nf), ast_channel_nativeformats(c)),
ast_getformatname(ast_channel_writeformat(c)),
ast_getformatname(ast_channel_readformat(c)),
ast_channel_writetrans(c) ? "Yes" : "No",
ast_translate_path_to_str(ast_channel_writetrans(c), &write_transpath),
ast_channel_readtrans(c) ? "Yes" : "No",
ast_translate_path_to_str(ast_channel_readtrans(c), &read_transpath),
ast_channel_fd(c, 0),
Richard Mudgett
committed
ast_channel_fin(c) & ~DEBUGCHAN_FLAG,
(ast_channel_fin(c) & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
ast_channel_fout(c) & ~DEBUGCHAN_FLAG,
(ast_channel_fout(c) & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
(long) ast_channel_whentohangup(c)->tv_sec,
cdrtime,
bridge ? bridge->uniqueid : "(Not bridged)",
ast_channel_context(c),
ast_channel_exten(c),
ast_channel_priority(c),
ast_channel_callgroup(c),
ast_channel_pickupgroup(c),
(ast_channel_appl(c) ? ast_channel_appl(c) : "(N/A)" ),
(ast_channel_data(c) ? S_OR(ast_channel_data(c), "(Empty)") : "(None)"),
Jonathan Rose
committed
(ast_test_flag(ast_channel_flags(c), AST_FLAG_BLOCKING) ? ast_channel_blockproc(c) : "(Not Blocking)"),
S_OR(call_identifier_str, "(None)"));
Richard Mudgett
committed
if (pbx_builtin_serialize_variables(c, &obuf)) {
ast_str_append(&output, 0, " Variables:\n%s\n", ast_str_buffer(obuf));
}
if (ast_channel_cdr(c) && ast_cdr_serialize_variables(ast_channel_cdr(c), &obuf, '=', '\n', 1)) {
Richard Mudgett
committed
ast_str_append(&output, 0, " CDR Variables:\n%s\n", ast_str_buffer(obuf));
}
#ifdef CHANNEL_TRACE
trace_enabled = ast_channel_trace_is_enabled(c);
Richard Mudgett
committed
ast_str_append(&output, 0, " Context Trace: %s\n",
trace_enabled ? "Enabled" : "Disabled");
if (trace_enabled && ast_channel_trace_serialize(c, &obuf)) {
ast_str_append(&output, 0, " Trace:\n%s\n", ast_str_buffer(obuf));
}
ast_channel_unlock(c);
c = ast_channel_unref(c);
Richard Mudgett
committed
ao2_cleanup(bridge);
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)
Kevin P. Fleming
committed
struct ast_channel *c = NULL;
int which = 0;
char notfound = '\0';
char *ret = ¬found; /* so NULL can break the loop */
struct ast_channel_iterator *iter;
Kevin P. Fleming
committed
if (pos != rpos) {
}
if (ast_strlen_zero(word)) {
iter = ast_channel_iterator_all_new();
} else {
iter = ast_channel_iterator_by_name_new(word, strlen(word));
}
if (!iter) {
return NULL;
}
while (ret == ¬found && (c = ast_channel_iterator_next(iter))) {
if (++which > state) {
ast_channel_lock(c);
ret = ast_strdup(ast_channel_name(c));
ast_channel_unlock(c);
}
ast_channel_unref(c);
Kevin P. Fleming
committed
ast_channel_iterator_destroy(iter);
return ret == ¬found ? NULL : 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
}
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
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)
{
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
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;
1819
1820
1821
1822
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
/* 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
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
1883
1884
1885
1886
1887
1888
1889
1890
1891
1892
1893
1894
1895
1896
1897
1898
1899
1900
1901
1902
1903
1904
1905
1906
1907
1908
1909
1910
1911
1912
1913
1914
1915
1916
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;
1973
1974
1975
1976
1977
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
}
/*! \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 */