You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

622 lines
14 KiB

  1. /***************************************************************************
  2. * Copyright (C) 2005 by Dominic Rath *
  3. * Dominic.Rath@gmx.de *
  4. * *
  5. * part of this file is taken from libcli (libcli.sourceforge.net) *
  6. * Copyright (C) David Parrish (david@dparrish.com) *
  7. * *
  8. * This program is free software; you can redistribute it and/or modify *
  9. * it under the terms of the GNU General Public License as published by *
  10. * the Free Software Foundation; either version 2 of the License, or *
  11. * (at your option) any later version. *
  12. * *
  13. * This program is distributed in the hope that it will be useful, *
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of *
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
  16. * GNU General Public License for more details. *
  17. * *
  18. * You should have received a copy of the GNU General Public License *
  19. * along with this program; if not, write to the *
  20. * Free Software Foundation, Inc., *
  21. * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
  22. ***************************************************************************/
  23. #ifdef HAVE_CONFIG_H
  24. #include "config.h"
  25. #endif
  26. #include "replacements.h"
  27. #include "command.h"
  28. #include "log.h"
  29. #include "time_support.h"
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <ctype.h>
  33. #include <stdarg.h>
  34. #include <stdio.h>
  35. #include <unistd.h>
  36. int fast_and_dangerous = 0;
  37. void command_print_help_line(command_context_t* context, struct command_s *command, int indent);
  38. int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
  39. int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
  40. int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc);
  41. int build_unique_lengths(command_context_t *context, command_t *commands)
  42. {
  43. command_t *c, *p;
  44. /* iterate through all commands */
  45. for (c = commands; c; c = c->next)
  46. {
  47. /* find out how many characters are required to uniquely identify a command */
  48. for (c->unique_len = 1; c->unique_len <= strlen(c->name); c->unique_len++)
  49. {
  50. int foundmatch = 0;
  51. /* for every command, see if the current length is enough */
  52. for (p = commands; p; p = p->next)
  53. {
  54. /* ignore the command itself */
  55. if (c == p)
  56. continue;
  57. /* compare commands up to the current length */
  58. if (strncmp(p->name, c->name, c->unique_len) == 0)
  59. foundmatch++;
  60. }
  61. /* when none of the commands matched, we've found the minimum length required */
  62. if (!foundmatch)
  63. break;
  64. }
  65. /* if the current command has children, build the unique lengths for them */
  66. if (c->children)
  67. build_unique_lengths(context, c->children);
  68. }
  69. return ERROR_OK;
  70. }
  71. /* Avoid evaluating this each time we add a command. Reduces overhead from O(n^2) to O(n).
  72. * Makes a difference on ARM7 types machines and is not observable on GHz machines.
  73. */
  74. static int unique_length_dirty = 1;
  75. command_t* register_command(command_context_t *context, command_t *parent, char *name, int (*handler)(struct command_context_s *context, char* name, char** args, int argc), enum command_mode mode, char *help)
  76. {
  77. command_t *c, *p;
  78. unique_length_dirty = 1;
  79. if (!context || !name)
  80. return NULL;
  81. c = malloc(sizeof(command_t));
  82. c->name = strdup(name);
  83. c->parent = parent;
  84. c->children = NULL;
  85. c->handler = handler;
  86. c->mode = mode;
  87. if (help)
  88. c->help = strdup(help);
  89. else
  90. c->help = NULL;
  91. c->unique_len = 0;
  92. c->next = NULL;
  93. /* place command in tree */
  94. if (parent)
  95. {
  96. if (parent->children)
  97. {
  98. /* find last child */
  99. for (p = parent->children; p && p->next; p = p->next);
  100. if (p)
  101. p->next = c;
  102. }
  103. else
  104. {
  105. parent->children = c;
  106. }
  107. }
  108. else
  109. {
  110. if (context->commands)
  111. {
  112. /* find last command */
  113. for (p = context->commands; p && p->next; p = p->next);
  114. if (p)
  115. p->next = c;
  116. }
  117. else
  118. {
  119. context->commands = c;
  120. }
  121. }
  122. return c;
  123. }
  124. int unregister_command(command_context_t *context, char *name)
  125. {
  126. command_t *c, *p = NULL, *c2;
  127. unique_length_dirty = 1;
  128. if ((!context) || (!name))
  129. return ERROR_INVALID_ARGUMENTS;
  130. /* find command */
  131. for (c = context->commands; c; c = c->next)
  132. {
  133. if (strcmp(name, c->name) == 0)
  134. {
  135. /* unlink command */
  136. if (p)
  137. {
  138. p->next = c->next;
  139. }
  140. else
  141. {
  142. context->commands = c->next;
  143. }
  144. /* unregister children */
  145. if (c->children)
  146. {
  147. for (c2 = c->children; c2; c2 = c2->next)
  148. {
  149. free(c2->name);
  150. if (c2->help)
  151. free(c2->help);
  152. free(c2);
  153. }
  154. }
  155. /* delete command */
  156. free(c->name);
  157. if (c->help)
  158. free(c->help);
  159. free(c);
  160. }
  161. /* remember the last command for unlinking */
  162. p = c;
  163. }
  164. return ERROR_OK;
  165. }
  166. int parse_line(char *line, char *words[], int max_words)
  167. {
  168. int nwords = 0;
  169. char *p = line;
  170. char *word_start = line;
  171. int inquote = 0;
  172. while (nwords < max_words - 1)
  173. {
  174. /* check if we reached
  175. * a terminating NUL
  176. * a matching closing quote character " or '
  177. * we're inside a word but not a quote, and the current character is whitespace
  178. */
  179. if (!*p || *p == inquote || (word_start && !inquote && isspace(*p)))
  180. {
  181. /* we're inside a word or quote, and reached its end*/
  182. if (word_start)
  183. {
  184. int len;
  185. char *word_end=p;
  186. /* This will handle extra whitespace within quotes */
  187. while (isspace(*word_start)&&(word_start<word_end))
  188. word_start++;
  189. while (isspace(*(word_end-1))&&(word_start<word_end))
  190. word_end--;
  191. len = word_end - word_start;
  192. if (len>0)
  193. {
  194. /* copy the word */
  195. memcpy(words[nwords] = malloc(len + 1), word_start, len);
  196. /* add terminating NUL */
  197. words[nwords++][len] = 0;
  198. }
  199. }
  200. /* we're done parsing the line */
  201. if (!*p)
  202. break;
  203. /* skip over trailing quote or whitespace*/
  204. if (inquote || isspace(*p))
  205. p++;
  206. while (isspace(*p))
  207. p++;
  208. inquote = 0;
  209. word_start = 0;
  210. }
  211. else if (*p == '"' || *p == '\'')
  212. {
  213. /* we've reached the beginning of a quote */
  214. inquote = *p++;
  215. word_start = p;
  216. }
  217. else
  218. {
  219. /* we've reached the beginning of a new word */
  220. if (!word_start)
  221. word_start = p;
  222. /* normal character, skip */
  223. p++;
  224. }
  225. }
  226. return nwords;
  227. }
  228. void command_print_n(command_context_t *context, char *format, ...)
  229. {
  230. char *string;
  231. va_list ap;
  232. va_start(ap, format);
  233. string = alloc_vprintf(format, ap);
  234. if (string != NULL)
  235. {
  236. context->output_handler(context, string);
  237. free(string);
  238. }
  239. va_end(ap);
  240. }
  241. void command_print(command_context_t *context, char *format, ...)
  242. {
  243. char *string;
  244. va_list ap;
  245. va_start(ap, format);
  246. string = alloc_vprintf(format, ap);
  247. if (string != NULL)
  248. {
  249. strcat(string, "\n"); /* alloc_vprintf guaranteed the buffer to be at least one char longer */
  250. context->output_handler(context, string);
  251. free(string);
  252. }
  253. va_end(ap);
  254. }
  255. int find_and_run_command(command_context_t *context, command_t *commands, char *words[], int num_words, int start_word)
  256. {
  257. command_t *c;
  258. int retval = ERROR_COMMAND_SYNTAX_ERROR;
  259. if (unique_length_dirty)
  260. {
  261. unique_length_dirty = 0;
  262. /* update unique lengths */
  263. build_unique_lengths(context, context->commands);
  264. }
  265. for (c = commands; c; c = c->next)
  266. {
  267. if (strncasecmp(c->name, words[start_word], c->unique_len))
  268. continue;
  269. if (strncasecmp(c->name, words[start_word], strlen(words[start_word])))
  270. continue;
  271. if ((context->mode == COMMAND_CONFIG) || (c->mode == COMMAND_ANY) || (c->mode == context->mode) )
  272. {
  273. if (!c->children)
  274. {
  275. if (!c->handler)
  276. {
  277. command_print(context, "No handler for command");
  278. retval = ERROR_COMMAND_SYNTAX_ERROR;
  279. break;
  280. }
  281. else
  282. {
  283. int retval = c->handler(context, c->name, words + start_word + 1, num_words - start_word - 1);
  284. if (retval == ERROR_COMMAND_SYNTAX_ERROR)
  285. {
  286. command_print(context, "Syntax error:");
  287. command_print_help_line(context, c, 0);
  288. } else if (retval != ERROR_OK)
  289. {
  290. /* we do not print out an error message because the command *should*
  291. * have printed out an error
  292. */
  293. LOG_DEBUG("Command failed with error code %d", retval);
  294. }
  295. return retval;
  296. }
  297. }
  298. else
  299. {
  300. if (start_word == num_words - 1)
  301. {
  302. command_print(context, "Incomplete command");
  303. break;
  304. }
  305. return find_and_run_command(context, c->children, words, num_words, start_word + 1);
  306. }
  307. }
  308. }
  309. command_print(context, "Command %s not found", words[start_word]);
  310. return retval;
  311. }
  312. int command_run_line(command_context_t *context, char *line)
  313. {
  314. int nwords;
  315. char *words[128] = {0};
  316. int retval;
  317. int i;
  318. if ((!context) || (!line))
  319. return ERROR_INVALID_ARGUMENTS;
  320. /* skip preceding whitespace */
  321. while (isspace(*line))
  322. line++;
  323. /* empty line, ignore */
  324. if (!*line)
  325. return ERROR_OK;
  326. /* ignore comments */
  327. if (*line && (line[0] == '#'))
  328. return ERROR_OK;
  329. LOG_DEBUG("%s", line);
  330. nwords = parse_line(line, words, sizeof(words) / sizeof(words[0]));
  331. if (nwords > 0)
  332. retval = find_and_run_command(context, context->commands, words, nwords, 0);
  333. else
  334. return ERROR_INVALID_ARGUMENTS;
  335. for (i = 0; i < nwords; i++)
  336. free(words[i]);
  337. return retval;
  338. }
  339. int command_run_file(command_context_t *context, FILE *file, enum command_mode mode)
  340. {
  341. int retval = ERROR_OK;
  342. int old_command_mode;
  343. char *buffer=malloc(4096);
  344. if (buffer==NULL)
  345. {
  346. return ERROR_INVALID_ARGUMENTS;
  347. }
  348. old_command_mode = context->mode;
  349. context->mode = mode;
  350. while (fgets(buffer, 4096, file))
  351. {
  352. char *p;
  353. char *cmd, *end;
  354. /* stop processing line after a comment (#, !) or a LF, CR were encountered */
  355. if ((p = strpbrk(buffer, "#!\r\n")))
  356. *p = 0;
  357. /* skip over leading whitespace */
  358. cmd = buffer;
  359. while (isspace(*cmd))
  360. cmd++;
  361. /* empty (all whitespace) line? */
  362. if (!*cmd)
  363. continue;
  364. /* search the end of the current line, ignore trailing whitespace */
  365. for (p = end = cmd; *p; p++)
  366. if (!isspace(*p))
  367. end = p;
  368. /* terminate end */
  369. *++end = 0;
  370. if (strcasecmp(cmd, "quit") == 0)
  371. break;
  372. /* run line */
  373. if ((retval = command_run_line(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION)
  374. break;
  375. }
  376. context->mode = old_command_mode;
  377. free(buffer);
  378. return retval;
  379. }
  380. void command_print_help_line(command_context_t* context, struct command_s *command, int indent)
  381. {
  382. command_t *c;
  383. char *indent_text=malloc(indent + 2);
  384. char *help = "no help available";
  385. char name_buf[64];
  386. if (indent)
  387. {
  388. indent_text[0] = ' ';
  389. memset(indent_text + 1, '-', indent);
  390. indent_text[indent + 1] = 0;
  391. }
  392. if (command->help)
  393. help = command->help;
  394. snprintf(name_buf, 64, command->name);
  395. if (indent)
  396. strncat(name_buf, indent_text, 64);
  397. command_print(context, "%20s\t%s", name_buf, help, indent);
  398. if (command->children)
  399. {
  400. for (c = command->children; c; c = c->next)
  401. {
  402. command_print_help_line(context, c, indent + 1);
  403. }
  404. }
  405. free(indent_text);
  406. }
  407. int command_print_help_match(command_context_t* context, command_t* c_first, char* name, char** args, int argc)
  408. {
  409. command_t * c;
  410. for (c = c_first; c; c = c->next)
  411. {
  412. if (argc > 0)
  413. {
  414. if (strncasecmp(c->name, args[0], c->unique_len))
  415. continue;
  416. if (strncasecmp(c->name, args[0], strlen(args[0])))
  417. continue;
  418. if (argc > 1)
  419. {
  420. command_print_help_match(context, c->children, name, args + 1, argc - 1);
  421. continue;
  422. }
  423. }
  424. command_print_help_line(context, c, 0);
  425. }
  426. return ERROR_OK;
  427. }
  428. int command_print_help(command_context_t* context, char* name, char** args, int argc)
  429. {
  430. return command_print_help_match(context, context->commands, name, args, argc);
  431. }
  432. void command_set_output_handler(command_context_t* context, int (*output_handler)(struct command_context_s *context, char* line), void *priv)
  433. {
  434. context->output_handler = output_handler;
  435. context->output_handler_priv = priv;
  436. }
  437. command_context_t* copy_command_context(command_context_t* context)
  438. {
  439. command_context_t* copy_context = malloc(sizeof(command_context_t));
  440. *copy_context = *context;
  441. return copy_context;
  442. }
  443. int command_done(command_context_t *context)
  444. {
  445. free(context);
  446. context = NULL;
  447. return ERROR_OK;
  448. }
  449. command_context_t* command_init()
  450. {
  451. command_context_t* context = malloc(sizeof(command_context_t));
  452. context->mode = COMMAND_EXEC;
  453. context->commands = NULL;
  454. context->current_target = 0;
  455. context->output_handler = NULL;
  456. context->output_handler_priv = NULL;
  457. register_command(context, NULL, "help", command_print_help,
  458. COMMAND_EXEC, "display this help");
  459. register_command(context, NULL, "sleep", handle_sleep_command,
  460. COMMAND_ANY, "sleep for <n> milliseconds");
  461. register_command(context, NULL, "time", handle_time_command,
  462. COMMAND_ANY, "time <cmd + args> - execute <cmd + args> and print time it took");
  463. register_command(context, NULL, "fast", handle_fast_command,
  464. COMMAND_ANY, "fast <enable/disable> - place at beginning of config files. Sets defaults to fast and dangerous.");
  465. return context;
  466. }
  467. /* sleep command sleeps for <n> miliseconds
  468. * this is useful in target startup scripts
  469. */
  470. int handle_sleep_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
  471. {
  472. unsigned long duration = 0;
  473. if (argc == 1)
  474. {
  475. duration = strtoul(args[0], NULL, 0);
  476. usleep(duration * 1000);
  477. }
  478. return ERROR_OK;
  479. }
  480. int handle_fast_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
  481. {
  482. if (argc!=1)
  483. return ERROR_COMMAND_SYNTAX_ERROR;
  484. fast_and_dangerous = strcmp("enable", args[0])==0;
  485. return ERROR_OK;
  486. }
  487. int handle_time_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
  488. {
  489. duration_t duration;
  490. char *duration_text;
  491. int retval;
  492. float t;
  493. if (argc<1)
  494. return ERROR_COMMAND_SYNTAX_ERROR;
  495. duration_start_measure(&duration);
  496. retval = find_and_run_command(cmd_ctx, cmd_ctx->commands, args, argc, 0);
  497. duration_stop_measure(&duration, &duration_text);
  498. t=duration.duration.tv_sec;
  499. t+=((float)duration.duration.tv_usec / 1000000.0);
  500. command_print(cmd_ctx, "%s took %fs", args[0], t);
  501. free(duration_text);
  502. return retval;
  503. }