cli.c 38 KB

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