Newer
Older
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
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
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";
e->usage =
"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;
Russell Bryant
committed
if (!(buf = ast_realloc(obuf, buflen)))
/* 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";
e->usage =
"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";
e->usage =
"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;
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
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 (!(chan->fin & DEBUGCHAN_FLAG) || !(chan->fout & DEBUGCHAN_FLAG)) {
if (args->is_off) {
chan->fin &= ~DEBUGCHAN_FLAG;
chan->fout &= ~DEBUGCHAN_FLAG;
} else {
chan->fin |= DEBUGCHAN_FLAG;
chan->fout |= DEBUGCHAN_FLAG;
}
ast_cli(args->fd, "Debugging %s on channel %s\n", args->is_off ? "disabled" : "enabled",
chan->name);
}
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)
struct ast_str *out = ast_str_thread_get(&ast_str_thread_global_buf, 16);
Mark Spencer
committed
char cdrtime[256];
char nf[256], wf[256], rf[256];
int hour=0, min=0, sec=0;
#ifdef CHANNEL_TRACE
int trace_enabled;
#endif
switch (cmd) {
case CLI_INIT:
e->command = "core show channel";
e->usage =
"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
}
ast_channel_lock(c);
Joshua Colp
committed
if (c->cdr) {
Kevin P. Fleming
committed
elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
hour = elapsed_seconds / 3600;
min = (elapsed_seconds % 3600) / 60;
sec = elapsed_seconds % 60;
snprintf(cdrtime, sizeof(cdrtime), "%dh%dm%ds", hour, min, sec);
} else {
}
ast_cli(a->fd,
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"
" 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\n"
" ReadTranscode: %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"
" Direct Bridge: %s\n"
"Indirect Bridge: %s\n"
" -- 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"
" Blocking in: %s\n",
c->name, c->tech->type, c->uniqueid, c->linkedid,
S_OR(c->cid.cid_num, "(N/A)"),
S_OR(c->cid.cid_name, "(N/A)"),
Russell Bryant
committed
S_OR(c->cid.cid_dnid, "(N/A)"),
c->language,
ast_state2str(c->_state), c->_state, c->rings,
ast_getformatname_multiple(nf, sizeof(nf), c->nativeformats),
ast_getformatname_multiple(wf, sizeof(wf), c->writeformat),
ast_getformatname_multiple(rf, sizeof(rf), c->readformat),
c->writetrans ? "Yes" : "No",
c->readtrans ? "Yes" : "No",
c->fds[0],
c->fin & ~DEBUGCHAN_FLAG, (c->fin & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
c->fout & ~DEBUGCHAN_FLAG, (c->fout & DEBUGCHAN_FLAG) ? " (DEBUGGED)" : "",
(long)c->whentohangup.tv_sec,
Kevin P. Fleming
committed
cdrtime, c->_bridge ? c->_bridge->name : "<none>", ast_bridged_channel(c) ? ast_bridged_channel(c)->name : "<none>",
c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
( c-> data ? S_OR(c->data, "(Empty)") : "(None)"),
Kevin P. Fleming
committed
(ast_test_flag(c, AST_FLAG_BLOCKING) ? c->blockproc : "(Not Blocking)"));
if (pbx_builtin_serialize_variables(c, &out)) {
ast_cli(a->fd," Variables:\n%s\n", ast_str_buffer(out));
}
if (c->cdr && ast_cdr_serialize_variables(c->cdr, &out, '=', '\n', 1)) {
ast_cli(a->fd," CDR Variables:\n%s\n", ast_str_buffer(out));
}
#ifdef CHANNEL_TRACE
trace_enabled = ast_channel_trace_is_enabled(c);
ast_cli(a->fd, " Context Trace: %s\n", trace_enabled ? "Enabled" : "Disabled");
if (trace_enabled && ast_channel_trace_serialize(c, &out))
ast_cli(a->fd, " Trace:\n%s\n", ast_str_buffer(out));
ast_channel_unlock(c);
c = ast_channel_unref(c);
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) {
}
Kevin P. Fleming
committed
if (!(iter = ast_channel_iterator_by_name_new(word, strlen(word)))) {
return NULL;
}
while (ret == ¬found && (c = ast_channel_iterator_next(iter))) {
if (++which > state) {
ast_channel_lock(c);
ret = ast_strdup(c->name);
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";
e->usage =
"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;
Russell Bryant
committed
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, gi->chan->name, gi->group, (ast_strlen_zero(gi->category) ? "(default)" : gi->category));
Russell Bryant
committed
}
gi = AST_LIST_NEXT(gi, group_list);
Russell Bryant
committed
}
ast_app_group_list_unlock();
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
}
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"),
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"),
/*!
* 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)
{
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
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;
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
/* 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
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;
}
/*! \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));
/*!
* 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;
/* regexp match, takes [foo|bar] or {foo|bar} */
l = strlen(cmd);
/* wildcard match - will extend in the future */
if (l > 0 && cli_word[0] == '%') {
return 1; /* wildcard */
}
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
pos = strcasestr(cli_word, cmd);
if (pos == NULL) /* not found, say ok if optional */
return cli_word[0] == '[' ? 0 : -1;
if (pos == cli_word) /* no valid match at the beginning */
return -1;
if (strchr(cli_rsvd, pos[-1]) && strchr(cli_rsvd, pos[l]))
return 1; /* valid match */
return -1; /* not found */
}
/*! \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)) ) {
/* word-by word regexp comparison */
const char * const *src = cmds;
const char * const *dst = e->cmda;
int n = 0;
for (;; dst++, src += n) {
n = word_match(*src, *dst);
if (n < 0)
if (ast_strlen_zero(*dst) || ((*dst)[0] == '[' && ast_strlen_zero(dst[1]))) {
/* no more words in 'e' */
if (ast_strlen_zero(*src)) /* exact match, cannot do better */
/* Here, cmds has more words than the entry 'e' */
if (match_type != 0) /* but we look for almost exact match... */
continue; /* so we skip this one. */
/* otherwise we like it (case 0) */
} else { /* still words in 'e' */
if (ast_strlen_zero(*src))
continue; /* cmds is shorter than 'e', not good */
/* Here we have leftover words in cmds and 'e',
* but there is a mismatch. We only accept this one if match_type == -1
* and this is the last word for both.
*/
if (match_type != -1 || !ast_strlen_zero(src[1]) ||
!ast_strlen_zero(dst[1])) /* not the one we look for */
/* good, we are in case match_type == -1 and mismatch on last word */
if (src - cmds > matchlen) { /* remember the candidate */
matchlen = src - cmds;
return e ? e : cand;
static char *find_best(const char *argv[])
/* See how close we get, then print the candidate */
const char *myargv[AST_MAX_CMD_LEN] = { NULL, };
for (x = 0; argv[x]; x++) {
myargv[x] = argv[x];
if (!find_cli(myargv, -1))
break;
}
ast_join(cmdline, sizeof(cmdline), myargv);
static int __ast_cli_unregister(struct ast_cli_entry *e, struct ast_cli_entry *ed)
Kevin P. Fleming
committed
if (e->inuse) {
ast_log(LOG_WARNING, "Can't remove command that is in use\n");
} else {
AST_RWLIST_WRLOCK(&helpers);
AST_RWLIST_REMOVE(&helpers, e, list);
AST_RWLIST_UNLOCK(&helpers);
Tilghman Lesher
committed
ast_free(e->_full_cmd);
if (e->handler) {
/* this is a new-style entry. Reset fields and free memory. */
char *cmda = (char *) e->cmda;
memset(cmda, '\0', sizeof(e->cmda));
Tilghman Lesher
committed
ast_free(e->command);
e->command = NULL;
e->usage = NULL;
}
static int __ast_cli_register(struct ast_cli_entry *e, struct ast_cli_entry *ed)
Kevin P. Fleming
committed
struct ast_cli_entry *cur;
int i, lf, ret = -1;
struct ast_cli_args a; /* fake argument */
char **dst = (char **)e->cmda; /* need to cast as the entry is readonly */
char *s;
e->handler(e, CLI_INIT, &a);
/* XXX check that usage and command are filled up */
s = ast_skip_blanks(e->command);
s = e->command = ast_strdup(s);
for (i=0; !ast_strlen_zero(s) && i < AST_MAX_CMD_LEN-1; i++) {
*dst++ = s; /* store string */
s = ast_skip_nonblanks(s);
if (*s == '\0') /* we are done */
break;
*s++ = '\0';
s = ast_skip_blanks(s);
*dst++ = NULL;
if (find_cli(e->cmda, 1)) {
ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", S_OR(e->_full_cmd, e->command));
if (set_full_cmd(e))
goto done;
lf = e->cmdlen;
AST_RWLIST_TRAVERSE_SAFE_BEGIN(&helpers, cur, list) {
int len = cur->cmdlen;
if (lf < len)
len = lf;
if (strncasecmp(e->_full_cmd, cur->_full_cmd, len) < 0) {
AST_RWLIST_INSERT_BEFORE_CURRENT(e, list);
Kevin P. Fleming
committed
if (!cur)
AST_RWLIST_INSERT_TAIL(&helpers, e, list);
ret = 0; /* success */