2 * Asterisk -- A telephony toolkit for Linux.
4 * Standard Command Line Interface
6 * Copyright (C) 1999, Mark Spencer
8 * Mark Spencer <markster@linux-support.net>
10 * This program is free software, distributed under the terms of
11 * the GNU General Public License
16 #include <asterisk/logger.h>
17 #include <asterisk/options.h>
18 #include <asterisk/cli.h>
19 #include <asterisk/module.h>
20 #include <asterisk/channel.h>
21 #include <sys/signal.h>
26 /* For rl_filename_completion */
27 #include <readline/readline.h>
28 /* For module directory */
31 void ast_cli(int fd, char *fmt, ...)
36 vsnprintf(stuff, sizeof(stuff), fmt, ap);
38 write(fd, stuff, strlen(stuff));
41 pthread_mutex_t clilock = PTHREAD_MUTEX_INITIALIZER;
44 struct ast_cli_entry *helpers = NULL;
46 static char load_help[] =
47 "Usage: load <module name>\n"
48 " Loads the specified module into Asterisk.\n";
50 static char unload_help[] =
51 "Usage: unload [-f|-h] <module name>\n"
52 " Unloads the specified module from Asterisk. The -f\n"
53 " option causes the module to be unloaded even if it is\n"
54 " in use (may cause a crash) and the -h module causes the\n"
55 " module to be unloaded even if the module says it cannot, \n"
56 " which almost always will cause a crash.\n";
58 static char help_help[] =
59 "Usage: help [topic]\n"
60 " When called with a topic as an argument, displays usage\n"
61 " information on the given command. If called without a\n"
62 " topic, it provides a list of commands.\n";
64 static char chanlist_help[] =
65 "Usage: show channels\n"
66 " Lists currently defined channels and some information about\n"
69 static int handle_load(int fd, int argc, char *argv[])
72 return RESULT_SHOWUSAGE;
73 if (ast_load_resource(argv[1])) {
74 ast_cli(fd, "Unable to load module %s\n", argv[1]);
75 return RESULT_FAILURE;
77 return RESULT_SUCCESS;
80 static int handle_unload(int fd, int argc, char *argv[])
83 int force=AST_FORCE_SOFT;
85 return RESULT_SHOWUSAGE;
86 for (x=1;x<argc;x++) {
87 if (argv[x][0] == '-') {
90 force = AST_FORCE_FIRM;
93 force = AST_FORCE_HARD;
96 return RESULT_SHOWUSAGE;
98 } else if (x != argc - 1)
99 return RESULT_SHOWUSAGE;
100 else if (ast_unload_resource(argv[x], force)) {
101 ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
102 return RESULT_FAILURE;
105 return RESULT_SUCCESS;
108 #define MODLIST_FORMAT "%-20s %-40.40s %-10d\n"
109 #define MODLIST_FORMAT2 "%-20s %-40.40s %-10s\n"
111 static pthread_mutex_t climodentrylock = PTHREAD_MUTEX_INITIALIZER;
112 static int climodentryfd = -1;
114 static int modlist_modentry(char *module, char *description, int usecnt)
116 ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
120 static char modlist_help[] =
121 "Usage: show modules\n"
122 " Shows Asterisk modules currently in use, and usage "
125 static int handle_modlist(int fd, int argc, char *argv[])
128 return RESULT_SHOWUSAGE;
129 pthread_mutex_lock(&climodentrylock);
131 ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
132 ast_update_module_list(modlist_modentry);
134 pthread_mutex_unlock(&climodentrylock);
135 return RESULT_SUCCESS;
138 static int handle_chanlist(int fd, int argc, char *argv[])
140 #define FORMAT_STRING "%15s (%-10s %-12s %-4d) %-12s %-15s\n"
141 #define FORMAT_STRING2 "%15s (%-10s %-12s %-4s) %-12s %-15s\n"
142 struct ast_channel *c=NULL;
144 return RESULT_SHOWUSAGE;
145 c = ast_channel_walk(NULL);
146 ast_cli(fd, FORMAT_STRING2, "Channel", "Context", "Extension", "Pri", "Appl.", "Data");
148 ast_cli(fd, FORMAT_STRING, c->name, c->context, c->exten, c->priority,
149 c->appl ? c->appl : "(None)", c->data ? ( strlen(c->data) ? c->data : "(Empty)" ): "(None)");
150 c = ast_channel_walk(c);
152 return RESULT_SUCCESS;
155 static char showchan_help[] =
156 "Usage: show channel <channel>\n"
157 " Shows lots of information about the specified channel.\n";
159 static int handle_showchan(int fd, int argc, char *argv[])
161 struct ast_channel *c=NULL;
163 return RESULT_SHOWUSAGE;
164 c = ast_channel_walk(NULL);
166 if (!strcasecmp(c->name, argv[2])) {
177 " NativeFormat: %d\n"
178 "File Descriptor: %d\n"
186 " Blocking in: %s\n",
188 (c->callerid ? c->callerid : "(N/A)"),
189 (c->dnid ? c->dnid : "(N/A)" ), c->state, c->rings, c->nativeformats, c->writeformat, c->readformat,
190 c->fd, c->context, c->exten, c->priority, ( c->appl ? c->appl : "(N/A)" ),
191 ( c-> data ? (strlen(c->data) ? c->data : "(Empty)") : "(None)"),
192 c->stack, (c->blocking ? c->blockproc : "(Not Blocking)"));
196 c = ast_channel_walk(c);
199 ast_cli(fd, "%s is not a known channel\n", argv[2]);
200 return RESULT_SUCCESS;
203 static char *complete_ch(char *line, char *word, int pos, int state)
205 struct ast_channel *c;
207 c = ast_channel_walk(NULL);
211 c = ast_channel_walk(c);
213 return c ? strdup(c->name) : NULL;
216 static char *complete_fn(char *line, char *word, int pos, int state)
223 strncpy(filename, word, sizeof(filename));
225 snprintf(filename, sizeof(filename), "%s/%s", AST_MODULE_DIR, word);
226 c = filename_completion_function(filename, state);
227 if (c && word[0] != '/')
228 c += (strlen(AST_MODULE_DIR) + 1);
229 return c ? strdup(c) : c;
232 static int handle_help(int fd, int argc, char *argv[]);
234 static struct ast_cli_entry builtins[] = {
235 /* Keep alphabetized */
236 { { "help", NULL }, handle_help, "Display help list, or specific help on a command", help_help },
237 { { "load", NULL }, handle_load, "Load a dynamic module by name", load_help, complete_fn },
238 { { "show", "channel", NULL }, handle_showchan, "Display information on a specific channel", showchan_help, complete_ch },
239 { { "show", "channels", NULL }, handle_chanlist, "Display information on channels", chanlist_help },
240 { { "show", "modules", NULL }, handle_modlist, "List modules and info", modlist_help },
241 { { "unload", NULL }, handle_unload, "Unload a dynamic module by name", unload_help, complete_fn },
242 { { NULL }, NULL, NULL, NULL }
245 static struct ast_cli_entry *find_cli(char *cmds[], int exact)
250 struct ast_cli_entry *e=NULL;
251 for (x=0;builtins[x].cmda[0];x++) {
252 /* start optimistic */
254 for (y=0;match && cmds[y]; y++) {
255 /* If there are no more words in the candidate command, then we're
257 if (!builtins[x].cmda[y] && !exact)
259 /* If there are no more words in the command (and we're looking for
260 an exact match) or there is a difference between the two words,
261 then this is not a match */
262 if (!builtins[x].cmda[y] || strcasecmp(builtins[x].cmda[y], cmds[y]))
265 /* If more words are needed to complete the command then this is not
266 a candidate (unless we're looking for a really inexact answer */
267 if ((exact > -1) && builtins[x].cmda[y])
272 for (e=helpers;e;e=e->next) {
274 for (y=0;match && cmds[y]; y++) {
275 if (!e->cmda[y] && !exact)
277 if (!e->cmda[y] || strcasecmp(e->cmda[y], cmds[y]))
280 if ((exact > -1) && e->cmda[y])
288 static void join(char *s, int len, char *w[])
291 /* Join words into a string */
295 strncat(s, " ", len - strlen(s));
296 strncat(s, w[x], len - strlen(s));
300 static void join2(char *s, int len, char *w[])
303 /* Join words into a string */
306 strncat(s, w[x], len - strlen(s));
310 static char *find_best(char *argv[])
312 static char cmdline[80];
314 /* See how close we get, then print the */
315 char *myargv[AST_MAX_CMD_LEN];
316 for (x=0;x<AST_MAX_CMD_LEN;x++)
318 for (x=0;argv[x];x++) {
320 if (!find_cli(myargv, -1))
323 join(cmdline, sizeof(cmdline), myargv);
327 int ast_cli_unregister(struct ast_cli_entry *e)
329 struct ast_cli_entry *cur, *l=NULL;
330 pthread_mutex_lock(&clilock);
345 pthread_mutex_unlock(&clilock);
349 int ast_cli_register(struct ast_cli_entry *e)
351 struct ast_cli_entry *cur, *l=NULL;
352 char fulle[80], fulltst[80];
354 pthread_mutex_lock(&clilock);
355 join2(fulle, sizeof(fulle), e->cmda);
356 if (find_cli(e->cmda, -1)) {
357 ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
358 pthread_mutex_unlock(&clilock);
363 join2(fulltst, sizeof(fulltst), cur->cmda);
364 len = strlen(fulltst);
365 if (strlen(fulle) < len)
367 if (strncasecmp(fulle, fulltst, len) < 0) {
387 pthread_mutex_unlock(&clilock);
391 static int help_workhorse(int fd, char *match[])
397 struct ast_cli_entry *e, *e1, *e2;
401 join(matchstr, sizeof(matchstr), match);
402 while(e1->cmda[0] || e2) {
404 join(fullcmd2, sizeof(fullcmd2), e2->cmda);
406 join(fullcmd1, sizeof(fullcmd1), e1->cmda);
408 (e2 && (strcmp(fullcmd2, fullcmd1) < 0))) {
412 /* Increment by going to next */
421 if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
425 ast_cli(fd, "%20.20s %s\n", fullcmd, e->summary);
430 static int handle_help(int fd, int argc, char *argv[]) {
431 struct ast_cli_entry *e;
434 return RESULT_SHOWUSAGE;
436 e = find_cli(argv + 1, 1);
438 ast_cli(fd, e->usage);
440 if (find_cli(argv + 1, -1)) {
441 return help_workhorse(fd, argv + 1);
443 join(fullcmd, sizeof(fullcmd), argv+1);
444 ast_cli(fd, "No such command '%s'.\n", fullcmd);
448 return help_workhorse(fd, NULL);
450 return RESULT_SUCCESS;
453 static char *parse_args(char *s, int *max, char *argv[])
467 /* If it's escaped, put a literal quote */
476 if (!quoted && !escaped) {
477 /* If we're not quoted, mark this as whitespace, and
478 end the previous argument */
482 /* Otherwise, just treat it as anything else */
486 /* If we're escaped, print a literal, otherwise enable escaping */
496 if (x >= AST_MAX_ARGS -1) {
497 ast_log(LOG_WARNING, "Too many arguments, truncating\n");
500 /* Coming off of whitespace, start the next argument */
517 char *ast_cli_generator(char *text, char *word, int state)
519 char *argv[AST_MAX_ARGS];
520 struct ast_cli_entry *e, *e1, *e2;
529 if ((dup = parse_args(text, &x, argv))) {
530 join(matchstr, sizeof(matchstr), argv);
531 pthread_mutex_lock(&clilock);
534 while(e1->cmda[0] || e2) {
536 join(fullcmd2, sizeof(fullcmd2), e2->cmda);
538 join(fullcmd1, sizeof(fullcmd1), e1->cmda);
540 (e2 && (strcmp(fullcmd2, fullcmd1) < 0))) {
544 /* Increment by going to next */
552 if (!strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
553 /* We contain the first part of one or more commands */
555 if (matchnum > state) {
556 /* Now, what we're supposed to return is the next word... */
563 pthread_mutex_unlock(&clilock);
564 return res ? strdup(res) : NULL;
568 if (e->generator && !strncasecmp(matchstr, fullcmd, strlen(fullcmd))) {
569 /* We have a command in its entirity within us -- theoretically only one
570 command can have this occur */
571 fullcmd = e->generator(text, word, (strlen(word) ? (x - 1) : (x)), state);
572 pthread_mutex_unlock(&clilock);
577 pthread_mutex_unlock(&clilock);
583 int ast_cli_command(int fd, char *s)
585 char *argv[AST_MAX_ARGS];
586 struct ast_cli_entry *e;
590 if ((dup = parse_args(s, &x, argv))) {
591 /* We need at least one entry, or ignore */
593 pthread_mutex_lock(&clilock);
594 e = find_cli(argv, 0);
596 switch(e->handler(fd, x, argv)) {
597 case RESULT_SHOWUSAGE:
598 ast_cli(fd, e->usage);
603 ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv));
604 pthread_mutex_unlock(&clilock);
608 ast_log(LOG_WARNING, "Out of memory\n");