cli.c 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176
  1. /*
  2. * Asterisk -- A telephony toolkit for Linux.
  3. *
  4. * Standard Command Line Interface
  5. *
  6. * Copyright (C) 1999-2004, Digium, Inc.
  7. *
  8. * Mark Spencer <markster@digium.com>
  9. *
  10. * This program is free software, distributed under the terms of
  11. * the GNU General Public License
  12. */
  13. #include <unistd.h>
  14. #include <stdlib.h>
  15. #include <asterisk/logger.h>
  16. #include <asterisk/options.h>
  17. #include <asterisk/cli.h>
  18. #include <asterisk/module.h>
  19. #include <asterisk/channel.h>
  20. #include <asterisk/channel_pvt.h>
  21. #include <asterisk/manager.h>
  22. #include <asterisk/utils.h>
  23. #include <asterisk/lock.h>
  24. #include <sys/signal.h>
  25. #include <stdio.h>
  26. #include <signal.h>
  27. #include <string.h>
  28. #include <ctype.h>
  29. /* For rl_filename_completion */
  30. #include "editline/readline/readline.h"
  31. /* For module directory */
  32. #include "asterisk.h"
  33. #include "build.h"
  34. #include "astconf.h"
  35. #define VERSION_INFO "Asterisk " ASTERISK_VERSION " built by " BUILD_USER "@" BUILD_HOSTNAME \
  36. " on a " BUILD_MACHINE " running " BUILD_OS
  37. void ast_cli(int fd, char *fmt, ...)
  38. {
  39. char *stuff;
  40. int res = 0;
  41. va_list ap;
  42. va_start(ap, fmt);
  43. res = vasprintf(&stuff, fmt, ap);
  44. va_end(ap);
  45. if (res == -1) {
  46. ast_log(LOG_ERROR, "Out of memory\n");
  47. } else {
  48. ast_carefulwrite(fd, stuff, strlen(stuff), 100);
  49. free(stuff);
  50. }
  51. }
  52. AST_MUTEX_DEFINE_STATIC(clilock);
  53. struct ast_cli_entry *helpers = NULL;
  54. static char load_help[] =
  55. "Usage: load <module name>\n"
  56. " Loads the specified module into Asterisk.\n";
  57. static char unload_help[] =
  58. "Usage: unload [-f|-h] <module name>\n"
  59. " Unloads the specified module from Asterisk. The -f\n"
  60. " option causes the module to be unloaded even if it is\n"
  61. " in use (may cause a crash) and the -h module causes the\n"
  62. " module to be unloaded even if the module says it cannot, \n"
  63. " which almost always will cause a crash.\n";
  64. static char help_help[] =
  65. "Usage: help [topic]\n"
  66. " When called with a topic as an argument, displays usage\n"
  67. " information on the given command. If called without a\n"
  68. " topic, it provides a list of commands.\n";
  69. static char chanlist_help[] =
  70. "Usage: show channels [concise]\n"
  71. " Lists currently defined channels and some information about\n"
  72. " them. If 'concise' is specified, format is abridged and in\n"
  73. " a more easily machine parsable format\n";
  74. static char reload_help[] =
  75. "Usage: reload [module ...]\n"
  76. " Reloads configuration files for all listed modules which support\n"
  77. " reloading, or for all supported modules if none are listed.\n";
  78. static char set_verbose_help[] =
  79. "Usage: set verbose <level>\n"
  80. " Sets level of verbose messages to be displayed. 0 means\n"
  81. " no messages should be displayed. Equivalent to -v[v[v...]]\n"
  82. " on startup\n";
  83. static char set_debug_help[] =
  84. "Usage: set debug <level>\n"
  85. " Sets level of core debug messages to be displayed. 0 means\n"
  86. " no messages should be displayed. Equivalent to -d[d[d...]]\n"
  87. " on startup.\n";
  88. static char softhangup_help[] =
  89. "Usage: soft hangup <channel>\n"
  90. " Request that a channel be hung up. The hangup takes effect\n"
  91. " the next time the driver reads or writes from the channel\n";
  92. static int handle_load(int fd, int argc, char *argv[])
  93. {
  94. if (argc != 2)
  95. return RESULT_SHOWUSAGE;
  96. if (ast_load_resource(argv[1])) {
  97. ast_cli(fd, "Unable to load module %s\n", argv[1]);
  98. return RESULT_FAILURE;
  99. }
  100. return RESULT_SUCCESS;
  101. }
  102. static int handle_reload(int fd, int argc, char *argv[])
  103. {
  104. int x;
  105. if (argc < 1)
  106. return RESULT_SHOWUSAGE;
  107. if (argc > 1) {
  108. for (x=1;x<argc;x++)
  109. ast_module_reload(argv[x]);
  110. } else
  111. ast_module_reload(NULL);
  112. return RESULT_SUCCESS;
  113. }
  114. static int handle_set_verbose(int fd, int argc, char *argv[])
  115. {
  116. int val = 0;
  117. int oldval = 0;
  118. /* Has a hidden 'at least' argument */
  119. if ((argc != 3) && (argc != 4))
  120. return RESULT_SHOWUSAGE;
  121. if ((argc == 4) && strcasecmp(argv[2], "atleast"))
  122. return RESULT_SHOWUSAGE;
  123. oldval = option_verbose;
  124. if (argc == 3)
  125. option_verbose = atoi(argv[2]);
  126. else {
  127. val = atoi(argv[3]);
  128. if (val > option_verbose)
  129. option_verbose = val;
  130. }
  131. if (oldval != option_verbose && option_verbose > 0)
  132. ast_cli(fd, "Verbosity was %d and is now %d\n", oldval, option_verbose);
  133. else if (oldval > 0 && option_verbose > 0)
  134. ast_cli(fd, "Verbosity is at least %d\n", option_verbose);
  135. else if (oldval > 0 && option_verbose == 0)
  136. ast_cli(fd, "Verbosity is now OFF\n");
  137. return RESULT_SUCCESS;
  138. }
  139. static int handle_set_debug(int fd, int argc, char *argv[])
  140. {
  141. int val = 0;
  142. int oldval = 0;
  143. /* Has a hidden 'at least' argument */
  144. if ((argc != 3) && (argc != 4))
  145. return RESULT_SHOWUSAGE;
  146. if ((argc == 4) && strcasecmp(argv[2], "atleast"))
  147. return RESULT_SHOWUSAGE;
  148. oldval = option_debug;
  149. if (argc == 3)
  150. option_debug = atoi(argv[2]);
  151. else {
  152. val = atoi(argv[3]);
  153. if (val > option_debug)
  154. option_debug = val;
  155. }
  156. if (oldval != option_debug && option_debug > 0)
  157. ast_cli(fd, "Core debug was %d and is now %d\n", oldval, option_debug);
  158. else if (oldval > 0 && option_debug > 0)
  159. ast_cli(fd, "Core debug is at least %d\n", option_debug);
  160. else if (oldval > 0 && option_debug == 0)
  161. ast_cli(fd, "Core debug is now OFF\n");
  162. return RESULT_SUCCESS;
  163. }
  164. static int handle_unload(int fd, int argc, char *argv[])
  165. {
  166. int x;
  167. int force=AST_FORCE_SOFT;
  168. if (argc < 2)
  169. return RESULT_SHOWUSAGE;
  170. for (x=1;x<argc;x++) {
  171. if (argv[x][0] == '-') {
  172. switch(argv[x][1]) {
  173. case 'f':
  174. force = AST_FORCE_FIRM;
  175. break;
  176. case 'h':
  177. force = AST_FORCE_HARD;
  178. break;
  179. default:
  180. return RESULT_SHOWUSAGE;
  181. }
  182. } else if (x != argc - 1)
  183. return RESULT_SHOWUSAGE;
  184. else if (ast_unload_resource(argv[x], force)) {
  185. ast_cli(fd, "Unable to unload resource %s\n", argv[x]);
  186. return RESULT_FAILURE;
  187. }
  188. }
  189. return RESULT_SUCCESS;
  190. }
  191. #define MODLIST_FORMAT "%-25s %-40.40s %-10d\n"
  192. #define MODLIST_FORMAT2 "%-25s %-40.40s %-10s\n"
  193. AST_MUTEX_DEFINE_STATIC(climodentrylock);
  194. static int climodentryfd = -1;
  195. static int modlist_modentry(char *module, char *description, int usecnt)
  196. {
  197. ast_cli(climodentryfd, MODLIST_FORMAT, module, description, usecnt);
  198. return 0;
  199. }
  200. static char modlist_help[] =
  201. "Usage: show modules\n"
  202. " Shows Asterisk modules currently in use, and usage "
  203. "statistics.\n";
  204. static char version_help[] =
  205. "Usage: show version\n"
  206. " Shows Asterisk version information.\n ";
  207. static char *format_uptimestr(time_t timeval)
  208. {
  209. int years = 0, weeks = 0, days = 0, hours = 0, mins = 0, secs = 0;
  210. char timestr[256]="";
  211. int bytes = 0;
  212. int maxbytes = 0;
  213. int offset = 0;
  214. #define SECOND (1)
  215. #define MINUTE (SECOND*60)
  216. #define HOUR (MINUTE*60)
  217. #define DAY (HOUR*24)
  218. #define WEEK (DAY*7)
  219. #define YEAR (DAY*365)
  220. #define ESS(x) ((x == 1) ? "" : "s")
  221. maxbytes = sizeof(timestr);
  222. if (timeval < 0)
  223. return NULL;
  224. if (timeval > YEAR) {
  225. years = (timeval / YEAR);
  226. timeval -= (years * YEAR);
  227. if (years > 0) {
  228. snprintf(timestr + offset, maxbytes, "%d year%s, ", years, ESS(years));
  229. bytes = strlen(timestr + offset);
  230. offset += bytes;
  231. maxbytes -= bytes;
  232. }
  233. }
  234. if (timeval > WEEK) {
  235. weeks = (timeval / WEEK);
  236. timeval -= (weeks * WEEK);
  237. if (weeks > 0) {
  238. snprintf(timestr + offset, maxbytes, "%d week%s, ", weeks, ESS(weeks));
  239. bytes = strlen(timestr + offset);
  240. offset += bytes;
  241. maxbytes -= bytes;
  242. }
  243. }
  244. if (timeval > DAY) {
  245. days = (timeval / DAY);
  246. timeval -= (days * DAY);
  247. if (days > 0) {
  248. snprintf(timestr + offset, maxbytes, "%d day%s, ", days, ESS(days));
  249. bytes = strlen(timestr + offset);
  250. offset += bytes;
  251. maxbytes -= bytes;
  252. }
  253. }
  254. if (timeval > HOUR) {
  255. hours = (timeval / HOUR);
  256. timeval -= (hours * HOUR);
  257. if (hours > 0) {
  258. snprintf(timestr + offset, maxbytes, "%d hour%s, ", hours, ESS(hours));
  259. bytes = strlen(timestr + offset);
  260. offset += bytes;
  261. maxbytes -= bytes;
  262. }
  263. }
  264. if (timeval > MINUTE) {
  265. mins = (timeval / MINUTE);
  266. timeval -= (mins * MINUTE);
  267. if (mins > 0) {
  268. snprintf(timestr + offset, maxbytes, "%d minute%s, ", mins, ESS(mins));
  269. bytes = strlen(timestr + offset);
  270. offset += bytes;
  271. maxbytes -= bytes;
  272. }
  273. }
  274. secs = timeval;
  275. if (secs > 0) {
  276. snprintf(timestr + offset, maxbytes, "%d second%s", secs, ESS(secs));
  277. }
  278. return timestr ? strdup(timestr) : NULL;
  279. }
  280. static int handle_showuptime(int fd, int argc, char *argv[])
  281. {
  282. time_t curtime, tmptime;
  283. char *timestr;
  284. time(&curtime);
  285. if (ast_startuptime) {
  286. tmptime = curtime - ast_startuptime;
  287. timestr = format_uptimestr(tmptime);
  288. if (timestr) {
  289. ast_cli(fd, "System uptime: %s\n", timestr);
  290. free(timestr);
  291. }
  292. }
  293. if (ast_lastreloadtime) {
  294. tmptime = curtime - ast_lastreloadtime;
  295. timestr = format_uptimestr(tmptime);
  296. if (timestr) {
  297. ast_cli(fd, "Last reload: %s\n", timestr);
  298. free(timestr);
  299. }
  300. }
  301. return RESULT_SUCCESS;
  302. }
  303. static int handle_modlist(int fd, int argc, char *argv[])
  304. {
  305. if (argc != 2)
  306. return RESULT_SHOWUSAGE;
  307. ast_mutex_lock(&climodentrylock);
  308. climodentryfd = fd;
  309. ast_cli(fd, MODLIST_FORMAT2, "Module", "Description", "Use Count");
  310. ast_update_module_list(modlist_modentry);
  311. climodentryfd = -1;
  312. ast_mutex_unlock(&climodentrylock);
  313. return RESULT_SUCCESS;
  314. }
  315. static int handle_version(int fd, int argc, char *argv[])
  316. {
  317. if (argc != 2)
  318. return RESULT_SHOWUSAGE;
  319. ast_cli(fd, "%s\n", VERSION_INFO);
  320. return RESULT_SUCCESS;
  321. }
  322. static int handle_chanlist(int fd, int argc, char *argv[])
  323. {
  324. #define FORMAT_STRING "%15s (%-10s %-12s %-4d) %7s %-12s %-15s\n"
  325. #define FORMAT_STRING2 "%15s (%-10s %-12s %-4s) %7s %-12s %-15s\n"
  326. #define CONCISE_FORMAT_STRING "%s:%s:%s:%d:%s:%s:%s:%s:%s:%d\n"
  327. struct ast_channel *c=NULL;
  328. int numchans = 0;
  329. int concise = 0;
  330. if (argc < 2 || argc > 3)
  331. return RESULT_SHOWUSAGE;
  332. concise = (argc == 3 && (!strcasecmp(argv[2],"concise")));
  333. c = ast_channel_walk_locked(NULL);
  334. if(!concise)
  335. ast_cli(fd, FORMAT_STRING2, "Channel", "Context", "Extension", "Pri", "State", "Appl.", "Data");
  336. while(c) {
  337. if(concise)
  338. ast_cli(fd, CONCISE_FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
  339. c->appl ? c->appl : "(None)", c->data ? ( !ast_strlen_zero(c->data) ? c->data : "" ): "",
  340. (c->callerid && !ast_strlen_zero(c->callerid)) ? c->callerid : "",
  341. (c->accountcode && !ast_strlen_zero(c->accountcode)) ? c->accountcode : "",c->amaflags);
  342. else
  343. ast_cli(fd, FORMAT_STRING, c->name, c->context, c->exten, c->priority, ast_state2str(c->_state),
  344. c->appl ? c->appl : "(None)", c->data ? ( !ast_strlen_zero(c->data) ? c->data : "(Empty)" ): "(None)");
  345. numchans++;
  346. ast_mutex_unlock(&c->lock);
  347. c = ast_channel_walk_locked(c);
  348. }
  349. if(!concise)
  350. ast_cli(fd, "%d active channel(s)\n", numchans);
  351. return RESULT_SUCCESS;
  352. }
  353. static char showchan_help[] =
  354. "Usage: show channel <channel>\n"
  355. " Shows lots of information about the specified channel.\n";
  356. static char debugchan_help[] =
  357. "Usage: debug channel <channel>\n"
  358. " Enables debugging on a specific channel.\n";
  359. static char nodebugchan_help[] =
  360. "Usage: no debug channel <channel>\n"
  361. " Disables debugging on a specific channel.\n";
  362. static char commandcomplete_help[] =
  363. "Usage: _command complete \"<line>\" text state\n"
  364. " This function is used internally to help with command completion and should.\n"
  365. " never be called by the user directly.\n";
  366. static char commandnummatches_help[] =
  367. "Usage: _command nummatches \"<line>\" text \n"
  368. " This function is used internally to help with command completion and should.\n"
  369. " never be called by the user directly.\n";
  370. static char commandmatchesarray_help[] =
  371. "Usage: _command matchesarray \"<line>\" text \n"
  372. " This function is used internally to help with command completion and should.\n"
  373. " never be called by the user directly.\n";
  374. static int handle_softhangup(int fd, int argc, char *argv[])
  375. {
  376. struct ast_channel *c=NULL;
  377. if (argc != 3)
  378. return RESULT_SHOWUSAGE;
  379. c = ast_channel_walk_locked(NULL);
  380. while(c) {
  381. if (!strcasecmp(c->name, argv[2])) {
  382. ast_cli(fd, "Requested Hangup on channel '%s'\n", c->name);
  383. ast_softhangup(c, AST_SOFTHANGUP_EXPLICIT);
  384. ast_mutex_unlock(&c->lock);
  385. break;
  386. }
  387. ast_mutex_unlock(&c->lock);
  388. c = ast_channel_walk_locked(c);
  389. }
  390. if (!c)
  391. ast_cli(fd, "%s is not a known channel\n", argv[2]);
  392. return RESULT_SUCCESS;
  393. }
  394. static char *__ast_cli_generator(char *text, char *word, int state, int lock);
  395. static int handle_commandmatchesarray(int fd, int argc, char *argv[])
  396. {
  397. char *buf, *obuf;
  398. int buflen = 2048;
  399. int len = 0;
  400. char **matches;
  401. int x, matchlen;
  402. if (argc != 4)
  403. return RESULT_SHOWUSAGE;
  404. buf = malloc(buflen);
  405. if (!buf)
  406. return RESULT_FAILURE;
  407. buf[len] = '\0';
  408. matches = ast_cli_completion_matches(argv[2], argv[3]);
  409. if (matches) {
  410. for (x=0; matches[x]; x++) {
  411. #if 0
  412. printf("command matchesarray for '%s' %s got '%s'\n", argv[2], argv[3], matches[x]);
  413. #endif
  414. matchlen = strlen(matches[x]) + 1;
  415. if (len + matchlen >= buflen) {
  416. buflen += matchlen * 3;
  417. obuf = buf;
  418. buf = realloc(obuf, buflen);
  419. if (!buf)
  420. /* Out of memory... Just free old buffer and be done */
  421. free(obuf);
  422. }
  423. if (buf)
  424. len += sprintf( buf + len, "%s ", matches[x]);
  425. free(matches[x]);
  426. matches[x] = NULL;
  427. }
  428. free(matches);
  429. }
  430. #if 0
  431. printf("array for '%s' %s got '%s'\n", argv[2], argv[3], buf);
  432. #endif
  433. if (buf) {
  434. ast_cli(fd, "%s%s",buf, AST_CLI_COMPLETE_EOF);
  435. free(buf);
  436. } else
  437. ast_cli(fd, "NULL\n");
  438. return RESULT_SUCCESS;
  439. }
  440. static int handle_commandnummatches(int fd, int argc, char *argv[])
  441. {
  442. int matches = 0;
  443. if (argc != 4)
  444. return RESULT_SHOWUSAGE;
  445. matches = ast_cli_generatornummatches(argv[2], argv[3]);
  446. #if 0
  447. printf("Search for '%s' %s got '%d'\n", argv[2], argv[3], matches);
  448. #endif
  449. ast_cli(fd, "%d", matches);
  450. return RESULT_SUCCESS;
  451. }
  452. static int handle_commandcomplete(int fd, int argc, char *argv[])
  453. {
  454. char *buf;
  455. #if 0
  456. printf("Search for %d args: '%s', '%s', '%s', '%s'\n", argc, argv[0], argv[1], argv[2], argv[3]);
  457. #endif
  458. if (argc != 5)
  459. return RESULT_SHOWUSAGE;
  460. buf = __ast_cli_generator(argv[2], argv[3], atoi(argv[4]), 0);
  461. #if 0
  462. printf("Search for '%s' %s %d got '%s'\n", argv[2], argv[3], atoi(argv[4]), buf);
  463. #endif
  464. if (buf) {
  465. ast_cli(fd, buf);
  466. free(buf);
  467. } else
  468. ast_cli(fd, "NULL\n");
  469. return RESULT_SUCCESS;
  470. }
  471. static int handle_debugchan(int fd, int argc, char *argv[])
  472. {
  473. struct ast_channel *c=NULL;
  474. if (argc != 3)
  475. return RESULT_SHOWUSAGE;
  476. c = ast_channel_walk_locked(NULL);
  477. while(c) {
  478. if (!strcasecmp(c->name, argv[2])) {
  479. c->fin |= 0x80000000;
  480. c->fout |= 0x80000000;
  481. break;
  482. }
  483. ast_mutex_unlock(&c->lock);
  484. c = ast_channel_walk_locked(c);
  485. }
  486. if (c) {
  487. ast_cli(fd, "Debugging enabled on channel %s\n", c->name);
  488. ast_mutex_unlock(&c->lock);
  489. }
  490. else
  491. ast_cli(fd, "No such channel %s\n", argv[2]);
  492. return RESULT_SUCCESS;
  493. }
  494. static int handle_nodebugchan(int fd, int argc, char *argv[])
  495. {
  496. struct ast_channel *c=NULL;
  497. if (argc != 4)
  498. return RESULT_SHOWUSAGE;
  499. c = ast_channel_walk_locked(NULL);
  500. while(c) {
  501. if (!strcasecmp(c->name, argv[3])) {
  502. c->fin &= 0x7fffffff;
  503. c->fout &= 0x7fffffff;
  504. break;
  505. }
  506. ast_mutex_unlock(&c->lock);
  507. c = ast_channel_walk_locked(c);
  508. }
  509. if (c) {
  510. ast_cli(fd, "Debugging disabled on channel %s\n", c->name);
  511. ast_mutex_unlock(&c->lock);
  512. } else
  513. ast_cli(fd, "No such channel %s\n", argv[2]);
  514. return RESULT_SUCCESS;
  515. }
  516. static int handle_showchan(int fd, int argc, char *argv[])
  517. {
  518. struct ast_channel *c=NULL;
  519. struct timeval now;
  520. long elapsed_seconds=0;
  521. int hour=0, min=0, sec=0;
  522. if (argc != 3)
  523. return RESULT_SHOWUSAGE;
  524. gettimeofday(&now, NULL);
  525. c = ast_channel_walk_locked(NULL);
  526. while(c) {
  527. if (!strcasecmp(c->name, argv[2])) {
  528. if(c->cdr) {
  529. elapsed_seconds = now.tv_sec - c->cdr->start.tv_sec;
  530. hour = elapsed_seconds / 3600;
  531. min = (elapsed_seconds % 3600) / 60;
  532. sec = elapsed_seconds % 60;
  533. }
  534. ast_cli(fd,
  535. " -- General --\n"
  536. " Name: %s\n"
  537. " Type: %s\n"
  538. " UniqueID: %s\n"
  539. " Caller ID: %s\n"
  540. " DNID Digits: %s\n"
  541. " State: %s (%d)\n"
  542. " Rings: %d\n"
  543. " NativeFormat: %d\n"
  544. " WriteFormat: %d\n"
  545. " ReadFormat: %d\n"
  546. "1st File Descriptor: %d\n"
  547. " Frames in: %d%s\n"
  548. " Frames out: %d%s\n"
  549. " Time to Hangup: %ld\n"
  550. " Elapsed Time: %dh%dm%ds\n"
  551. " -- PBX --\n"
  552. " Context: %s\n"
  553. " Extension: %s\n"
  554. " Priority: %d\n"
  555. " Call Group: %d\n"
  556. " Pickup Group: %d\n"
  557. " Application: %s\n"
  558. " Data: %s\n"
  559. " Stack: %d\n"
  560. " Blocking in: %s\n",
  561. c->name, c->type, c->uniqueid,
  562. (c->callerid ? c->callerid : "(N/A)"),
  563. (c->dnid ? c->dnid : "(N/A)" ), ast_state2str(c->_state), c->_state, c->rings, c->nativeformats, c->writeformat, c->readformat,
  564. c->fds[0], c->fin & 0x7fffffff, (c->fin & 0x80000000) ? " (DEBUGGED)" : "",
  565. c->fout & 0x7fffffff, (c->fout & 0x80000000) ? " (DEBUGGED)" : "", (long)c->whentohangup,
  566. hour, min, sec,
  567. c->context, c->exten, c->priority, c->callgroup, c->pickupgroup, ( c->appl ? c->appl : "(N/A)" ),
  568. ( c-> data ? (!ast_strlen_zero(c->data) ? c->data : "(Empty)") : "(None)"),
  569. c->stack, (c->blocking ? c->blockproc : "(Not Blocking)"));
  570. ast_mutex_unlock(&c->lock);
  571. break;
  572. }
  573. ast_mutex_unlock(&c->lock);
  574. c = ast_channel_walk_locked(c);
  575. }
  576. if (!c)
  577. ast_cli(fd, "%s is not a known channel\n", argv[2]);
  578. return RESULT_SUCCESS;
  579. }
  580. static char *complete_ch_helper(char *line, char *word, int pos, int state, int rpos)
  581. {
  582. struct ast_channel *c;
  583. int which=0;
  584. char *ret;
  585. if (pos != rpos)
  586. return NULL;
  587. c = ast_channel_walk_locked(NULL);
  588. while(c) {
  589. if (!strncasecmp(word, c->name, strlen(word))) {
  590. if (++which > state)
  591. break;
  592. }
  593. ast_mutex_unlock(&c->lock);
  594. c = ast_channel_walk_locked(c);
  595. }
  596. if (c) {
  597. ret = strdup(c->name);
  598. ast_mutex_unlock(&c->lock);
  599. } else
  600. ret = NULL;
  601. return ret;
  602. }
  603. static char *complete_ch_3(char *line, char *word, int pos, int state)
  604. {
  605. return complete_ch_helper(line, word, pos, state, 2);
  606. }
  607. static char *complete_ch_4(char *line, char *word, int pos, int state)
  608. {
  609. return complete_ch_helper(line, word, pos, state, 3);
  610. }
  611. static char *complete_fn(char *line, char *word, int pos, int state)
  612. {
  613. char *c;
  614. char filename[256];
  615. if (pos != 1)
  616. return NULL;
  617. if (word[0] == '/')
  618. strncpy(filename, word, sizeof(filename)-1);
  619. else
  620. snprintf(filename, sizeof(filename), "%s/%s", (char *)ast_config_AST_MODULE_DIR, word);
  621. c = (char*)filename_completion_function(filename, state);
  622. if (c && word[0] != '/')
  623. c += (strlen((char*)ast_config_AST_MODULE_DIR) + 1);
  624. return c ? strdup(c) : c;
  625. }
  626. static int handle_help(int fd, int argc, char *argv[]);
  627. static struct ast_cli_entry builtins[] = {
  628. /* Keep alphabetized, with longer matches first (example: abcd before abc) */
  629. { { "_command", "complete", NULL }, handle_commandcomplete, "Command complete", commandcomplete_help },
  630. { { "_command", "nummatches", NULL }, handle_commandnummatches, "Returns number of command matches", commandnummatches_help },
  631. { { "_command", "matchesarray", NULL }, handle_commandmatchesarray, "Returns command matches array", commandmatchesarray_help },
  632. { { "debug", "channel", NULL }, handle_debugchan, "Enable debugging on a channel", debugchan_help, complete_ch_3 },
  633. { { "help", NULL }, handle_help, "Display help list, or specific help on a command", help_help },
  634. { { "load", NULL }, handle_load, "Load a dynamic module by name", load_help, complete_fn },
  635. { { "no", "debug", "channel", NULL }, handle_nodebugchan, "Disable debugging on a channel", nodebugchan_help, complete_ch_4 },
  636. { { "reload", NULL }, handle_reload, "Reload configuration", reload_help },
  637. { { "set", "debug", NULL }, handle_set_debug, "Set level of debug chattiness", set_debug_help },
  638. { { "set", "verbose", NULL }, handle_set_verbose, "Set level of verboseness", set_verbose_help },
  639. { { "show", "channels", NULL }, handle_chanlist, "Display information on channels", chanlist_help },
  640. { { "show", "channel", NULL }, handle_showchan, "Display information on a specific channel", showchan_help, complete_ch_3 },
  641. { { "show", "modules", NULL }, handle_modlist, "List modules and info", modlist_help },
  642. { { "show", "uptime", NULL }, handle_showuptime, "Show uptime information", modlist_help },
  643. { { "show", "version", NULL }, handle_version, "Display version info", version_help },
  644. { { "soft", "hangup", NULL }, handle_softhangup, "Request a hangup on a given channel", softhangup_help, complete_ch_3 },
  645. { { "unload", NULL }, handle_unload, "Unload a dynamic module by name", unload_help, complete_fn },
  646. { { NULL }, NULL, NULL, NULL }
  647. };
  648. static struct ast_cli_entry *find_cli(char *cmds[], int exact)
  649. {
  650. int x;
  651. int y;
  652. int match;
  653. struct ast_cli_entry *e=NULL;
  654. for (x=0;builtins[x].cmda[0];x++) {
  655. /* start optimistic */
  656. match = 1;
  657. for (y=0;match && cmds[y]; y++) {
  658. /* If there are no more words in the candidate command, then we're
  659. there. */
  660. if (!builtins[x].cmda[y] && !exact)
  661. break;
  662. /* If there are no more words in the command (and we're looking for
  663. an exact match) or there is a difference between the two words,
  664. then this is not a match */
  665. if (!builtins[x].cmda[y] || strcasecmp(builtins[x].cmda[y], cmds[y]))
  666. match = 0;
  667. }
  668. /* If more words are needed to complete the command then this is not
  669. a candidate (unless we're looking for a really inexact answer */
  670. if ((exact > -1) && builtins[x].cmda[y])
  671. match = 0;
  672. if (match)
  673. return &builtins[x];
  674. }
  675. for (e=helpers;e;e=e->next) {
  676. match = 1;
  677. for (y=0;match && cmds[y]; y++) {
  678. if (!e->cmda[y] && !exact)
  679. break;
  680. if (!e->cmda[y] || strcasecmp(e->cmda[y], cmds[y]))
  681. match = 0;
  682. }
  683. if ((exact > -1) && e->cmda[y])
  684. match = 0;
  685. if (match)
  686. break;
  687. }
  688. return e;
  689. }
  690. static void join(char *dest, size_t destsize, char *w[], int tws)
  691. {
  692. int x;
  693. /* Join words into a string */
  694. if (!dest || destsize < 1) {
  695. return;
  696. }
  697. dest[0] = '\0';
  698. for (x=0;w[x];x++) {
  699. if (x)
  700. strncat(dest, " ", destsize - strlen(dest) - 1);
  701. strncat(dest, w[x], destsize - strlen(dest) - 1);
  702. }
  703. if (tws)
  704. strncat(dest, " ", destsize - strlen(dest) - 1);
  705. }
  706. static void join2(char *dest, size_t destsize, char *w[])
  707. {
  708. int x;
  709. /* Join words into a string */
  710. if (!dest || destsize < 1) {
  711. return;
  712. }
  713. dest[0] = '\0';
  714. for (x=0;w[x];x++) {
  715. strncat(dest, w[x], destsize - strlen(dest) - 1);
  716. }
  717. }
  718. static char *find_best(char *argv[])
  719. {
  720. static char cmdline[80];
  721. int x;
  722. /* See how close we get, then print the */
  723. char *myargv[AST_MAX_CMD_LEN];
  724. for (x=0;x<AST_MAX_CMD_LEN;x++)
  725. myargv[x]=NULL;
  726. for (x=0;argv[x];x++) {
  727. myargv[x] = argv[x];
  728. if (!find_cli(myargv, -1))
  729. break;
  730. }
  731. join(cmdline, sizeof(cmdline), myargv, 0);
  732. return cmdline;
  733. }
  734. int ast_cli_unregister(struct ast_cli_entry *e)
  735. {
  736. struct ast_cli_entry *cur, *l=NULL;
  737. ast_mutex_lock(&clilock);
  738. cur = helpers;
  739. while(cur) {
  740. if (e == cur) {
  741. if (e->inuse) {
  742. ast_log(LOG_WARNING, "Can't remove command that is in use\n");
  743. } else {
  744. /* Rewrite */
  745. if (l)
  746. l->next = e->next;
  747. else
  748. helpers = e->next;
  749. e->next = NULL;
  750. break;
  751. }
  752. }
  753. l = cur;
  754. cur = cur->next;
  755. }
  756. ast_mutex_unlock(&clilock);
  757. return 0;
  758. }
  759. int ast_cli_register(struct ast_cli_entry *e)
  760. {
  761. struct ast_cli_entry *cur, *l=NULL;
  762. char fulle[80] ="", fulltst[80] ="";
  763. static int len;
  764. ast_mutex_lock(&clilock);
  765. join2(fulle, sizeof(fulle), e->cmda);
  766. if (find_cli(e->cmda, -1)) {
  767. ast_mutex_unlock(&clilock);
  768. ast_log(LOG_WARNING, "Command '%s' already registered (or something close enough)\n", fulle);
  769. return -1;
  770. }
  771. cur = helpers;
  772. while(cur) {
  773. join2(fulltst, sizeof(fulltst), cur->cmda);
  774. len = strlen(fulltst);
  775. if (strlen(fulle) < len)
  776. len = strlen(fulle);
  777. if (strncasecmp(fulle, fulltst, len) < 0) {
  778. if (l) {
  779. e->next = l->next;
  780. l->next = e;
  781. } else {
  782. e->next = helpers;
  783. helpers = e;
  784. }
  785. break;
  786. }
  787. l = cur;
  788. cur = cur->next;
  789. }
  790. if (!cur) {
  791. if (l)
  792. l->next = e;
  793. else
  794. helpers = e;
  795. e->next = NULL;
  796. }
  797. ast_mutex_unlock(&clilock);
  798. return 0;
  799. }
  800. static int help_workhorse(int fd, char *match[])
  801. {
  802. char fullcmd1[80] = "";
  803. char fullcmd2[80] = "";
  804. char matchstr[80];
  805. char *fullcmd = NULL;
  806. struct ast_cli_entry *e, *e1, *e2;
  807. e1 = builtins;
  808. e2 = helpers;
  809. if (match)
  810. join(matchstr, sizeof(matchstr), match, 0);
  811. while(e1->cmda[0] || e2) {
  812. if (e2)
  813. join(fullcmd2, sizeof(fullcmd2), e2->cmda, 0);
  814. if (e1->cmda[0])
  815. join(fullcmd1, sizeof(fullcmd1), e1->cmda, 0);
  816. if (!e1->cmda[0] ||
  817. (e2 && (strcmp(fullcmd2, fullcmd1) < 0))) {
  818. /* Use e2 */
  819. e = e2;
  820. fullcmd = fullcmd2;
  821. /* Increment by going to next */
  822. e2 = e2->next;
  823. } else {
  824. /* Use e1 */
  825. e = e1;
  826. fullcmd = fullcmd1;
  827. e1++;
  828. }
  829. /* Hide commands that start with '_' */
  830. if (fullcmd[0] == '_')
  831. continue;
  832. if (match) {
  833. if (strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
  834. continue;
  835. }
  836. }
  837. ast_cli(fd, "%25.25s %s\n", fullcmd, e->summary);
  838. }
  839. return 0;
  840. }
  841. static int handle_help(int fd, int argc, char *argv[]) {
  842. struct ast_cli_entry *e;
  843. char fullcmd[80];
  844. if ((argc < 1))
  845. return RESULT_SHOWUSAGE;
  846. if (argc > 1) {
  847. e = find_cli(argv + 1, 1);
  848. if (e) {
  849. if (e->usage)
  850. ast_cli(fd, "%s", e->usage);
  851. else {
  852. join(fullcmd, sizeof(fullcmd), argv+1, 0);
  853. ast_cli(fd, "No help text available for '%s'.\n", fullcmd);
  854. }
  855. } else {
  856. if (find_cli(argv + 1, -1)) {
  857. return help_workhorse(fd, argv + 1);
  858. } else {
  859. join(fullcmd, sizeof(fullcmd), argv+1, 0);
  860. ast_cli(fd, "No such command '%s'.\n", fullcmd);
  861. }
  862. }
  863. } else {
  864. return help_workhorse(fd, NULL);
  865. }
  866. return RESULT_SUCCESS;
  867. }
  868. static char *parse_args(char *s, int *argc, char *argv[], int max, int *trailingwhitespace)
  869. {
  870. char *dup, *cur;
  871. int x = 0;
  872. int quoted = 0;
  873. int escaped = 0;
  874. int whitespace = 1;
  875. *trailingwhitespace = 0;
  876. if (!(dup = strdup(s)))
  877. return NULL;
  878. cur = dup;
  879. while (*s) {
  880. if ((*s == '"') && !escaped) {
  881. quoted = !quoted;
  882. if (quoted & whitespace) {
  883. /* If we're starting a quoted string, coming off white space, start a new argument */
  884. if (x >= (max - 1)) {
  885. ast_log(LOG_WARNING, "Too many arguments, truncating\n");
  886. break;
  887. }
  888. argv[x++] = cur;
  889. whitespace = 0;
  890. }
  891. escaped = 0;
  892. } else if (((*s == ' ') || (*s == '\t')) && !(quoted || escaped)) {
  893. /* If we are not already in whitespace, and not in a quoted string or
  894. processing an escape sequence, and just entered whitespace, then
  895. finalize the previous argument and remember that we are in whitespace
  896. */
  897. if (!whitespace) {
  898. *(cur++) = '\0';
  899. whitespace = 1;
  900. }
  901. } else if ((*s == '\\') && !escaped) {
  902. escaped = 1;
  903. } else {
  904. if (whitespace) {
  905. /* If we are coming out of whitespace, start a new argument */
  906. if (x >= (max - 1)) {
  907. ast_log(LOG_WARNING, "Too many arguments, truncating\n");
  908. break;
  909. }
  910. argv[x++] = cur;
  911. whitespace = 0;
  912. }
  913. *(cur++) = *s;
  914. escaped = 0;
  915. }
  916. s++;
  917. }
  918. /* Null terminate */
  919. *(cur++) = '\0';
  920. argv[x] = NULL;
  921. *argc = x;
  922. *trailingwhitespace = whitespace;
  923. return dup;
  924. }
  925. /* This returns the number of unique matches for the generator */
  926. int ast_cli_generatornummatches(char *text, char *word)
  927. {
  928. int matches = 0, i = 0;
  929. char *buf = NULL, *oldbuf = NULL;
  930. while ( (buf = ast_cli_generator(text, word, i++)) ) {
  931. if (!oldbuf || strcmp(buf,oldbuf))
  932. matches++;
  933. if (oldbuf)
  934. free(oldbuf);
  935. oldbuf = buf;
  936. }
  937. if (oldbuf)
  938. free(oldbuf);
  939. return matches;
  940. }
  941. char **ast_cli_completion_matches(char *text, char *word)
  942. {
  943. char **match_list = NULL, *retstr, *prevstr;
  944. size_t match_list_len, max_equal, which, i;
  945. int matches = 0;
  946. match_list_len = 1;
  947. while ((retstr = ast_cli_generator(text, word, matches)) != NULL) {
  948. if (matches + 1 >= match_list_len) {
  949. match_list_len <<= 1;
  950. match_list = realloc(match_list, match_list_len * sizeof(char *));
  951. }
  952. match_list[++matches] = retstr;
  953. }
  954. if (!match_list)
  955. return (char **) NULL;
  956. which = 2;
  957. prevstr = match_list[1];
  958. max_equal = strlen(prevstr);
  959. for (; which <= matches; which++) {
  960. for (i = 0; i < max_equal && toupper(prevstr[i]) == toupper(match_list[which][i]); i++)
  961. continue;
  962. max_equal = i;
  963. }
  964. retstr = malloc(max_equal + 1);
  965. (void) strncpy(retstr, match_list[1], max_equal);
  966. retstr[max_equal] = '\0';
  967. match_list[0] = retstr;
  968. if (matches + 1 >= match_list_len)
  969. match_list = realloc(match_list, (match_list_len + 1) * sizeof(char *));
  970. match_list[matches + 1] = (char *) NULL;
  971. return (match_list);
  972. }
  973. static char *__ast_cli_generator(char *text, char *word, int state, int lock)
  974. {
  975. char *argv[AST_MAX_ARGS];
  976. struct ast_cli_entry *e, *e1, *e2;
  977. int x;
  978. int matchnum=0;
  979. char *dup, *res;
  980. char fullcmd1[80] = "";
  981. char fullcmd2[80] = "";
  982. char matchstr[80];
  983. char *fullcmd = NULL;
  984. int tws;
  985. if ((dup = parse_args(text, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws))) {
  986. join(matchstr, sizeof(matchstr), argv, tws);
  987. if (lock)
  988. ast_mutex_lock(&clilock);
  989. e1 = builtins;
  990. e2 = helpers;
  991. while(e1->cmda[0] || e2) {
  992. if (e2)
  993. join(fullcmd2, sizeof(fullcmd2), e2->cmda, tws);
  994. if (e1->cmda[0])
  995. join(fullcmd1, sizeof(fullcmd1), e1->cmda, tws);
  996. if (!e1->cmda[0] ||
  997. (e2 && (strcmp(fullcmd2, fullcmd1) < 0))) {
  998. /* Use e2 */
  999. e = e2;
  1000. fullcmd = fullcmd2;
  1001. /* Increment by going to next */
  1002. e2 = e2->next;
  1003. } else {
  1004. /* Use e1 */
  1005. e = e1;
  1006. fullcmd = fullcmd1;
  1007. e1++;
  1008. }
  1009. if ((fullcmd[0] != '_') && !strncasecmp(matchstr, fullcmd, strlen(matchstr))) {
  1010. /* We contain the first part of one or more commands */
  1011. matchnum++;
  1012. if (matchnum > state) {
  1013. /* Now, what we're supposed to return is the next word... */
  1014. if (!ast_strlen_zero(word) && x>0) {
  1015. res = e->cmda[x-1];
  1016. } else {
  1017. res = e->cmda[x];
  1018. }
  1019. if (res) {
  1020. if (lock)
  1021. ast_mutex_unlock(&clilock);
  1022. free(dup);
  1023. return res ? strdup(res) : NULL;
  1024. }
  1025. }
  1026. }
  1027. if (e->generator && !strncasecmp(matchstr, fullcmd, strlen(fullcmd))) {
  1028. /* We have a command in its entirity within us -- theoretically only one
  1029. command can have this occur */
  1030. fullcmd = e->generator(matchstr, word, (!ast_strlen_zero(word) ? (x - 1) : (x)), state);
  1031. if (lock)
  1032. ast_mutex_unlock(&clilock);
  1033. free(dup);
  1034. return fullcmd;
  1035. }
  1036. }
  1037. if (lock)
  1038. ast_mutex_unlock(&clilock);
  1039. free(dup);
  1040. }
  1041. return NULL;
  1042. }
  1043. char *ast_cli_generator(char *text, char *word, int state)
  1044. {
  1045. return __ast_cli_generator(text, word, state, 1);
  1046. }
  1047. int ast_cli_command(int fd, char *s)
  1048. {
  1049. char *argv[AST_MAX_ARGS];
  1050. struct ast_cli_entry *e;
  1051. int x;
  1052. char *dup;
  1053. int tws;
  1054. if ((dup = parse_args(s, &x, argv, sizeof(argv) / sizeof(argv[0]), &tws))) {
  1055. /* We need at least one entry, or ignore */
  1056. if (x > 0) {
  1057. ast_mutex_lock(&clilock);
  1058. e = find_cli(argv, 0);
  1059. if (e)
  1060. e->inuse++;
  1061. ast_mutex_unlock(&clilock);
  1062. if (e) {
  1063. switch(e->handler(fd, x, argv)) {
  1064. case RESULT_SHOWUSAGE:
  1065. ast_cli(fd, "%s", e->usage);
  1066. break;
  1067. }
  1068. } else
  1069. ast_cli(fd, "No such command '%s' (type 'help' for help)\n", find_best(argv));
  1070. if (e) {
  1071. ast_mutex_lock(&clilock);
  1072. e->inuse--;
  1073. ast_mutex_unlock(&clilock);
  1074. }
  1075. }
  1076. free(dup);
  1077. } else {
  1078. ast_log(LOG_WARNING, "Out of memory\n");
  1079. return -1;
  1080. }
  1081. return 0;
  1082. }