blob: 2895b13ce73bc1a473a0934914c677042e8b9c14 [file] [log] [blame]
Greg Hartman76d05dc2016-11-23 15:51:27 -08001/* ----------------------------------------------------------------------- *
2 *
3 * Copyright 2009 Erwan Velu - All Rights Reserved
4 *
5 * Permission is hereby granted, free of charge, to any person
6 * obtaining a copy of this software and associated documentation
7 * files (the "Software"), to deal in the Software without
8 * restriction, including without limitation the rights to use,
9 * copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom
11 * the Software is furnished to do so, subject to the following
12 * conditions:
13 *
14 * The above copyright notice and this permission notice shall
15 * be included in all copies or substantial portions of the Software.
16 *
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
18 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
19 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
20 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
21 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
22 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
24 * OTHER DEALINGS IN THE SOFTWARE.
25 *
26 * -----------------------------------------------------------------------
27 */
28
29#include <stdlib.h>
30#include <string.h>
31#include <syslinux/config.h>
32#include <getkey.h>
33#include <acpi/acpi.h>
34#include "hdt-cli.h"
35#include "hdt-common.h"
36
37struct cli_mode_descr *list_modes[] = {
38 &hdt_mode,
39 &dmi_mode,
40 &syslinux_mode,
41 &pxe_mode,
42 &kernel_mode,
43 &cpu_mode,
44 &pci_mode,
45 &vesa_mode,
46 &disk_mode,
47 &vpd_mode,
48 &memory_mode,
49 &acpi_mode,
50 NULL,
51};
52
53/*
54 * .aliases = {"q", "quit"} won't work since it is an array of pointers, not an
55 * array of variables. There is no easy way around it besides declaring the arrays of
56 * strings first.
57 */
58const char *exit_aliases[] = { "q", "quit" };
59const char *help_aliases[] = { "h", "?" };
60
61/* List of aliases */
62struct cli_alias hdt_aliases[] = {
63 {
64 .command = CLI_EXIT,
65 .nb_aliases = 2,
66 .aliases = exit_aliases,
67 },
68 {
69 .command = CLI_HELP,
70 .nb_aliases = 2,
71 .aliases = help_aliases,
72 },
73};
74
75struct cli_mode_descr *current_mode;
76int autocomplete_backlog;
77
78struct autocomplete_list {
79 char autocomplete_token[MAX_LINE_SIZE];
80 struct autocomplete_list *next;
81};
82struct autocomplete_list *autocomplete_head = NULL;
83struct autocomplete_list *autocomplete_tail = NULL;
84struct autocomplete_list *autocomplete_last_seen = NULL;
85
86static void autocomplete_add_token_to_list(const char *token)
87{
88 struct autocomplete_list *new = malloc(sizeof(struct autocomplete_list));
89
90 strlcpy(new->autocomplete_token, token, sizeof(new->autocomplete_token));
91 new->next = NULL;
92 autocomplete_backlog++;
93
94 if (autocomplete_tail != NULL)
95 autocomplete_tail->next = new;
96 if (autocomplete_head == NULL)
97 autocomplete_head = new;
98 autocomplete_tail = new;
99}
100
101static void autocomplete_destroy_list(void)
102{
103 struct autocomplete_list *tmp = NULL;
104
105 while (autocomplete_head != NULL) {
106 tmp = autocomplete_head->next;
107 free(autocomplete_head);
108 autocomplete_head = tmp;
109 }
110 autocomplete_backlog = 0;
111 autocomplete_tail = NULL;
112 autocomplete_last_seen = NULL;
113}
114
115/**
116 * set_mode - set the current mode of the cli
117 * @mode: mode to set
118 *
119 * Unlike cli_set_mode, this function is not used by the cli directly.
120 **/
121void set_mode(cli_mode_t mode, struct s_hardware *hardware)
122{
123 int i = 0;
124
125 switch (mode) {
126 case EXIT_MODE:
127 hdt_cli.mode = mode;
128 break;
129 case HDT_MODE:
130 hdt_cli.mode = mode;
131 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_HDT);
132 break;
133 case PXE_MODE:
134 if (hardware->sv->filesystem != SYSLINUX_FS_PXELINUX) {
135 more_printf("You are not currently using PXELINUX\n");
136 break;
137 }
138 hdt_cli.mode = mode;
139 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_PXE);
140 break;
141 case KERNEL_MODE:
142 hdt_cli.mode = mode;
143 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_KERNEL);
144 break;
145 case SYSLINUX_MODE:
146 hdt_cli.mode = mode;
147 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_SYSLINUX);
148 break;
149 case VESA_MODE:
150 hdt_cli.mode = mode;
151 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_VESA);
152 break;
153 case PCI_MODE:
154 hdt_cli.mode = mode;
155 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_PCI);
156 break;
157 case CPU_MODE:
158 hdt_cli.mode = mode;
159 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_CPU);
160 break;
161 case DMI_MODE:
162 if (!hardware->is_dmi_valid) {
163 more_printf("No valid DMI table found, exiting.\n");
164 break;
165 }
166 hdt_cli.mode = mode;
167 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_DMI);
168 break;
169 case DISK_MODE:
170 hdt_cli.mode = mode;
171 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_DISK);
172 break;
173 case VPD_MODE:
174 if (!hardware->is_vpd_valid) {
175 more_printf("No valid VPD table found, exiting.\n");
176 break;
177 }
178 hdt_cli.mode = mode;
179 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_VPD);
180 break;
181 case MEMORY_MODE:
182 hdt_cli.mode = mode;
183 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_MEMORY);
184 break;
185 case ACPI_MODE:
186 hdt_cli.mode = mode;
187 snprintf(hdt_cli.prompt, sizeof(hdt_cli.prompt), "%s> ", CLI_ACPI);
188 break;
189 default:
190 /* Invalid mode */
191 more_printf("Unknown mode, please choose among:\n");
192 while (list_modes[i]) {
193 more_printf("\t%s\n", list_modes[i]->name);
194 i++;
195 }
196 }
197
198 find_cli_mode_descr(hdt_cli.mode, &current_mode);
199 /* There is not cli_mode_descr struct for the exit mode */
200 if (current_mode == NULL && hdt_cli.mode != EXIT_MODE) {
201 /* Shouldn't get here... */
202 more_printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode);
203 }
204}
205
206/**
207 * mode_s_to_mode_t - given a mode string, return the cli_mode_t representation
208 **/
209cli_mode_t mode_s_to_mode_t(char *name)
210{
211 int i = 0;
212
213 while (list_modes[i]) {
214 if (!strncmp(name, list_modes[i]->name, sizeof(list_modes[i]->name)))
215 break;
216 i++;
217 }
218
219 if (!list_modes[i])
220 return INVALID_MODE;
221 else
222 return list_modes[i]->mode;
223}
224
225/**
226 * find_cli_mode_descr - find the cli_mode_descr struct associated to a mode
227 * @mode: mode to look for
228 * @mode_found: store the mode if found, NULL otherwise
229 *
230 * Given a mode name, return a pointer to the associated cli_mode_descr
231 * structure.
232 * Note: the current mode name is stored in hdt_cli.mode.
233 **/
234void find_cli_mode_descr(cli_mode_t mode, struct cli_mode_descr **mode_found)
235{
236 int i = 0;
237
238 while (list_modes[i] && list_modes[i]->mode != mode)
239 i++;
240
241 /* Shouldn't get here... */
242 if (!list_modes[i])
243 *mode_found = NULL;
244 else
245 *mode_found = list_modes[i];
246}
247
248/**
249 * expand_aliases - resolve aliases mapping
250 * @line: command line to parse
251 * @command: first token in the line
252 * @module: second token in the line
253 * @argc: number of arguments
254 * @argv: array of arguments
255 *
256 * We maintain a small list of static alises to enhance user experience.
257 * Only commands can be aliased (first token). Otherwise it can become really hairy...
258 **/
259static void expand_aliases(char *line __unused, char **command, char **module,
260 int *argc, char **argv)
261{
262 struct cli_mode_descr *mode;
263 int i, j;
264
265 find_cli_mode_descr(mode_s_to_mode_t(*command), &mode);
266 if (mode != NULL && *module == NULL) {
267 /*
268 * The user specified a mode instead of `set mode...', e.g.
269 * `dmi' instead of `set mode dmi'
270 */
271
272 /* *argv is NULL since *module is NULL */
273 *argc = 1;
274 *argv = malloc(*argc * sizeof(char *));
275 argv[0] = malloc((sizeof(*command) + 1) * sizeof(char));
276 strlcpy(argv[0], *command, sizeof(*command) + 1);
277 dprintf("CLI DEBUG: ALIAS %s ", *command);
278
279 strlcpy(*command, CLI_SET, sizeof(CLI_SET)); /* set */
280
281 *module = malloc(sizeof(CLI_MODE) * sizeof(char));
282 strlcpy(*module, CLI_MODE, sizeof(CLI_MODE)); /* mode */
283
284 dprintf("--> %s %s %s\n", *command, *module, argv[0]);
285 goto out;
286 }
287
288 /* Simple aliases mapping a single command to another one */
289 for (i = 0; i < MAX_ALIASES; i++) {
290 for (j = 0; j < hdt_aliases[i].nb_aliases; j++) {
291 if (!strncmp(*command, hdt_aliases[i].aliases[j],
292 sizeof(hdt_aliases[i].aliases[j]))) {
293 dprintf("CLI DEBUG: ALIAS %s ", *command);
294 strlcpy(*command, hdt_aliases[i].command,
295 sizeof(hdt_aliases[i].command) + 1);
296 dprintf("--> %s\n", *command);
297 goto out; /* Don't allow chaining aliases */
298 }
299 }
300 }
301 return;
302
303out:
304 dprintf("CLI DEBUG: New parameters:\n");
305 dprintf("CLI DEBUG: command = %s\n", *command);
306 dprintf("CLI DEBUG: module = %s\n", *module);
307 dprintf("CLI DEBUG: argc = %d\n", *argc);
308 for (i = 0; i < *argc; i++)
309 dprintf("CLI DEBUG: argv[%d] = %s\n", i, argv[0]);
310 return;
311}
312
313/**
314 * parse_command_line - low level parser for the command line
315 * @line: command line to parse
316 * @command: first token in the line
317 * @module: second token in the line
318 * @argc: number of arguments
319 * @argv: array of arguments
320 *
321 * The format of the command line is:
322 * <main command> [<module on which to operate> [<args>]]
323 * command is always malloc'ed (even for an empty line)
324 **/
325static void parse_command_line(char *line, char **command, char **module,
326 int *argc, char **argv)
327{
328 int argc_iter = 0, args_pos = 0, token_found = 0, token_len = 0;
329 int args_len = 0;
330 char *pch = NULL, *pch_next = NULL, *tmp_pch_next = NULL;
331
332 *command = NULL;
333 *module = NULL;
334 *argc = 0;
335
336 pch = line;
337 while (pch != NULL) {
338 pch_next = strchr(pch + 1, ' ');
339 tmp_pch_next = pch_next;
340
341 /*
342 * Skip whitespaces if the user entered
343 * 'set mode foo' for 'set mode foo'
344 * ^ ^
345 * |___|___ pch
346 * |___ pch_next <- wrong!
347 *
348 * We still keep the position into tmp_pch_next to compute
349 * the lenght of the current token.
350 */
351 while (pch_next != NULL && !strncmp(pch_next, CLI_SPACE, 1))
352 pch_next++;
353
354 /* End of line guaranteed to be zeroed */
355 if (pch_next == NULL) {
356 token_len = (int)(strchr(pch + 1, '\0') - pch);
357 args_len = token_len;
358 } else {
359 token_len = (int)(tmp_pch_next - pch);
360 args_len = (int)(pch_next - pch);
361 }
362
363 if (token_found == 0) {
364 /* Main command to execute */
365 *command = malloc((token_len + 1) * sizeof(char));
366 strlcpy(*command, pch, token_len);
367 (*command)[token_len] = '\0';
368 dprintf("CLI DEBUG parse: command = %s\n", *command);
369 args_pos += args_len;
370 } else if (token_found == 1) {
371 /* Module */
372 *module = malloc((token_len + 1) * sizeof(char));
373 strlcpy(*module, pch, token_len);
374 (*module)[token_len] = '\0';
375 dprintf("CLI DEBUG parse: module = %s\n", *module);
376 args_pos += args_len;
377 } else
378 (*argc)++;
379
380 token_found++;
381 pch = pch_next;
382 }
383 dprintf("CLI DEBUG parse: argc = %d\n", *argc);
384
385 /* Skip arguments handling if none is supplied */
386 if (!*argc)
387 return;
388
389 /* Transform the arguments string into an array */
390 *argv = malloc(*argc * sizeof(char *));
391 pch = strtok(line + args_pos, CLI_SPACE);
392 while (pch != NULL) {
393 dprintf("CLI DEBUG parse: argv[%d] = %s\n", argc_iter, pch);
394 argv[argc_iter] = malloc(strlen(pch) * sizeof(char));
395 strlcpy(argv[argc_iter], pch, strlen(pch));
396 argc_iter++;
397 pch = strtok(NULL, CLI_SPACE);
398 /*
399 * strtok(NULL, CLI_SPACE) over a stream of spaces
400 * will return an empty string
401 */
402 while (pch != NULL && !strncmp(pch, "", 1))
403 pch = strtok(NULL, CLI_SPACE);
404 }
405}
406
407/**
408 * find_cli_callback_descr - find a callback in a list of modules
409 * @module_name: Name of the module to find
410 * @modules_list: Lits of modules among which to find @module_name
411 * @module_found: Pointer to the matched module, NULL if not found
412 *
413 * Given a module name and a list of possible modules, find the corresponding
414 * module structure that matches the module name and store it in @module_found.
415 **/
416void find_cli_callback_descr(const char *module_name,
417 struct cli_module_descr *modules_list,
418 struct cli_callback_descr **module_found)
419{
420 int modules_iter = 0;
421
422 if (modules_list == NULL)
423 goto not_found;
424
425 /* Find the callback to execute */
426 while (modules_list->modules[modules_iter].name &&
427 strcmp(module_name, modules_list->modules[modules_iter].name) != 0)
428 modules_iter++;
429
430 if (modules_list->modules[modules_iter].name) {
431 *module_found = &(modules_list->modules[modules_iter]);
432 dprintf("CLI DEBUG: module %s found\n", (*module_found)->name);
433 return;
434 }
435
436not_found:
437 *module_found = NULL;
438 return;
439}
440
441/**
442 * autocomplete_command - print matching commands
443 * @command: Beginning of the command
444 *
445 * Given a string @command, print all availables commands starting with
446 * @command. Commands are found within the list of commands for the current
447 * mode and the hdt mode (if the current mode is not hdt).
448 **/
449static void autocomplete_command(char *command)
450{
451 int j = 0;
452 struct cli_callback_descr *associated_module = NULL;
453
454 /* First take care of the two special commands: 'show' and 'set' */
455 if (strncmp(CLI_SHOW, command, strlen(command)) == 0) {
456 printf("%s\n", CLI_SHOW);
457 autocomplete_add_token_to_list(CLI_SHOW);
458 }
459 if (strncmp(CLI_SET, command, strlen(command)) == 0) {
460 printf("%s\n", CLI_SET);
461 autocomplete_add_token_to_list(CLI_SET);
462 }
463
464 /*
465 * Then, go through the modes for the special case
466 * '<mode>' -> 'set mode <mode>'
467 */
468 while (list_modes[j]) {
469 if (strncmp(list_modes[j]->name, command, strlen(command)) == 0) {
470 printf("%s\n", list_modes[j]->name);
471 autocomplete_add_token_to_list(list_modes[j]->name);
472 }
473 j++;
474 }
475
476 /*
477 * Let's go now through the list of default_modules for the current mode
478 * (single token commands for the current_mode)
479 */
480 j = 0;
481 if (current_mode->default_modules && current_mode->default_modules->modules) {
482 while (current_mode->default_modules->modules[j].name) {
483 if (strncmp(current_mode->default_modules->modules[j].name,
484 command, strlen(command)) == 0) {
485 printf("%s\n", current_mode->default_modules->modules[j].name);
486 autocomplete_add_token_to_list(current_mode->default_modules->
487 modules[j].name);
488 }
489 j++;
490 }
491 }
492
493 /*
494 * Finally, if the current_mode is not hdt, list the available
495 * default_modules of hdt (these are always available from any mode).
496 */
497 if (current_mode->mode == HDT_MODE)
498 return;
499
500 if (!hdt_mode.default_modules || !hdt_mode.default_modules->modules)
501 return;
502
503 j = 0;
504 while (hdt_mode.default_modules &&
505 hdt_mode.default_modules->modules[j].name) {
506 /*
507 * Any default command that is present in hdt mode but
508 * not in the current mode is available. A default
509 * command can be redefined in the current mode though.
510 * This next call tests this use case: if it is
511 * overwritten, do not print it again.
512 */
513 find_cli_callback_descr(hdt_mode.default_modules->modules[j].name,
514 current_mode->default_modules,
515 &associated_module);
516 if (associated_module == NULL &&
517 strncmp(command,
518 hdt_mode.default_modules->modules[j].name,
519 strlen(command)) == 0) {
520 printf("%s\n", hdt_mode.default_modules->modules[j].name);
521 autocomplete_add_token_to_list(hdt_mode.default_modules->modules[j].
522 name);
523 }
524 j++;
525 }
526}
527
528/**
529 * autocomplete_module - print matching modules
530 * @command: Command on the command line (not NULL)
531 * @module: Beginning of the module
532 *
533 * Given a command @command and a string @module, print all availables modules
534 * starting with @module for command @command. Commands are found within the
535 * list of commands for the current mode and the hdt mode (if the current mode
536 * is not hdt).
537 **/
538static void autocomplete_module(char *command, char *module)
539{
540 int j = 0;
541 char autocomplete_full_line[MAX_LINE_SIZE];
542
543 if (strncmp(CLI_SHOW, command, strlen(command)) == 0) {
544 if (!current_mode->show_modules || !current_mode->show_modules->modules)
545 return;
546
547 while (current_mode->show_modules->modules[j].name) {
548 if (strncmp(current_mode->show_modules->modules[j].name,
549 module, strlen(module)) == 0) {
550 printf("%s\n", current_mode->show_modules->modules[j].name);
551 sprintf(autocomplete_full_line, "%s %s",
552 CLI_SHOW, current_mode->show_modules->modules[j].name);
553 autocomplete_add_token_to_list(autocomplete_full_line);
554 }
555 j++;
556 }
557 } else if (strncmp(CLI_SET, command, strlen(command)) == 0) {
558 j = 0;
559 if (!current_mode->set_modules || !current_mode->set_modules->modules)
560 return;
561
562 while (current_mode->set_modules->modules[j].name) {
563 if (strncmp(current_mode->set_modules->modules[j].name,
564 module, strlen(module)) == 0) {
565 printf("%s\n", current_mode->set_modules->modules[j].name);
566 sprintf(autocomplete_full_line, "%s %s",
567 CLI_SET, current_mode->set_modules->modules[j].name);
568 autocomplete_add_token_to_list(autocomplete_full_line);
569 }
570 j++;
571 }
572 }
573}
574
575/**
576 * autocomplete - find possible matches for a command line
577 * @line: command line to parse
578 **/
579static void autocomplete(char *line)
580{
581 int i;
582 int argc = 0;
583 char *command = NULL, *module = NULL;
584 char **argv = NULL;
585
586 parse_command_line(line, &command, &module, &argc, argv);
587
588 dprintf("CLI DEBUG autocomplete: before checking args\n");
589 /* If the user specified arguments, there is nothing we can complete */
590 if (argc != 0)
591 goto out;
592
593 /* No argument, (the start of) a module has been specified */
594 if (module != NULL) {
595 autocomplete_module(command, module);
596 free(module);
597 goto out;
598 }
599
600 /* No argument, no module, (the start of) a command has been specified */
601 if (command != NULL) {
602 autocomplete_command(command);
603 free(command);
604 goto out;
605 }
606
607out:
608 /* Let's not forget to clean ourselves */
609 for (i = 0; i < argc; i++)
610 free(argv[i]);
611 if (argc > 0)
612 free(argv);
613 return;
614}
615
616/**
617 * exec_command - main logic to map the command line to callbacks
618 **/
619static void exec_command(char *line, struct s_hardware *hardware)
620{
621 int argc, i = 0;
622 char *command = NULL, *module = NULL;
623 char **argv = NULL;
624 struct cli_callback_descr *current_module = NULL;
625
626 /* This will allocate memory for command and module */
627 parse_command_line(line, &command, &module, &argc, argv);
628
629 dprintf("CLI DEBUG exec: Checking for aliases\n");
630 /*
631 * Expand shortcuts, if needed
632 * This will allocate memory for argc/argv
633 */
634 expand_aliases(line, &command, &module, &argc, argv);
635
636 find_cli_callback_descr(command, current_mode->default_modules,
637 &current_module);
638
639 if ((module == NULL) || (current_module->nomodule == true)) {
640 dprintf("CLI DEBUG exec : single command detected\n");
641 /*
642 * A single word was specified: look at the list of default
643 * commands in the current mode to see if there is a match.
644 * If not, it may be a generic function (exit, help, ...). These
645 * are stored in the list of default commands of the hdt mode.
646 */
647
648 /* First of all it the command doesn't need module, let's rework the arguments */
649 if ((current_module->nomodule == true) && ( module != NULL)) {
650 dprintf("CLI_DEBUG exec: Reworking arguments with argc=%d\n",argc);
651 char **new_argv=NULL;
652 new_argv=malloc((argc + 2)*sizeof(char *));
653 for (int argc_iter=0; argc_iter<argc; argc_iter++) {
654 dprintf("CLI_DEBUG exec rework : copy %d to %d (%s)\n",argc_iter,argc_iter+1,argv[argc_iter]);
655 new_argv[argc_iter+1] = malloc(strlen(argv[argc_iter]));
656 strlcpy(new_argv[argc_iter+1], argv[argc_iter], strlen(argv[argc_iter]));
657 free(argv[argc_iter]);
658 }
659 new_argv[0] = malloc(strlen(module)*sizeof(char));
660 strlcpy(new_argv[0], module, strlen(module));
661 argc++;
662 free(argv);
663 argv=new_argv;
664 }
665
666 if (current_module != NULL)
667 current_module->exec(argc, argv, hardware);
668 else if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1) &&
669 current_mode->show_modules != NULL &&
670 current_mode->show_modules->default_callback != NULL)
671 current_mode->show_modules->default_callback(argc, argv, hardware);
672 else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1) &&
673 current_mode->set_modules != NULL &&
674 current_mode->set_modules->default_callback != NULL)
675 current_mode->set_modules->default_callback(argc, argv, hardware);
676 else {
677 find_cli_callback_descr(command, hdt_mode.default_modules,
678 &current_module);
679 if (current_module != NULL)
680 current_module->exec(argc, argv, hardware);
681 else
682 more_printf("unknown command: '%s'\n", command);
683 }
684 } else {
685 /*
686 * A module has been specified! We now need to find the type of command.
687 *
688 * The syntax of the cli is the following:
689 * <type of command> <module on which to operate> <args>
690 * e.g.
691 * dmi> show system
692 * dmi> show bank 1
693 * dmi> show memory 0 1
694 * pci> show device 12
695 * hdt> set mode dmi
696 */
697 if (!strncmp(command, CLI_SHOW, sizeof(CLI_SHOW) - 1)) {
698 dprintf("CLI DEBUG exec: %s command detected\n", CLI_SHOW);
699 /* Look first for a 'show' callback in the current mode */
700 find_cli_callback_descr(module, current_mode->show_modules,
701 &current_module);
702 /* Execute the callback, if found */
703 if (current_module != NULL)
704 current_module->exec(argc, argv, hardware);
705 else {
706 dprintf("CLI DEBUG exec: Looking for callback\n");
707 /* Look now for a 'show' callback in the hdt mode */
708 find_cli_callback_descr(module, hdt_mode.show_modules,
709 &current_module);
710 /* Execute the callback, if found */
711 if (current_module != NULL)
712 current_module->exec(argc, argv, hardware);
713 else
714 printf("unknown module: '%s'\n", module);
715 }
716 } else if (!strncmp(command, CLI_SET, sizeof(CLI_SET) - 1)) {
717 dprintf("CLI DEBUG exec : %s command detected\n", CLI_SET);
718 /* Look now for a 'set' callback in the hdt mode */
719 find_cli_callback_descr(module, current_mode->set_modules,
720 &current_module);
721 /* Execute the callback, if found */
722 if (current_module != NULL)
723 current_module->exec(argc, argv, hardware);
724 else {
725 /* Look now for a 'set' callback in the hdt mode */
726 find_cli_callback_descr(module, hdt_mode.set_modules,
727 &current_module);
728 /* Execute the callback, if found */
729 if (current_module != NULL)
730 current_module->exec(argc, argv, hardware);
731 else
732 printf("unknown module: '%s'\n", module);
733 }
734 }
735 }
736
737 /* Let's not forget to clean ourselves */
738 if (command != NULL)
739 free(command);
740 if (module != NULL)
741 free(module);
742 for (i = 0; i < argc; i++)
743 free(argv[i]);
744 if (argc > 0)
745 free(argv);
746}
747
748static void reset_prompt(void)
749{
750 /* No need to display the prompt if we exit */
751 if (hdt_cli.mode != EXIT_MODE) {
752 printf("%s", hdt_cli.prompt);
753 /* Reset the line */
754 hdt_cli.cursor_pos = 0;
755 }
756}
757
758void start_auto_mode(struct s_hardware *hardware)
759{
760 char *mypch;
761 int nb_commands = 0;
762 char *commands[MAX_NB_AUTO_COMMANDS];
763
764 more_printf("\nEntering Auto mode\n");
765
766 /* Protecting the auto_label from the strtok modifications */
767 char *temp = strdup(hardware->auto_label);
768
769 /* Searching & saving all commands */
770 mypch = strtok(temp, AUTO_SEPARATOR);
771 while (mypch != NULL) {
772 if ((strlen(remove_spaces(mypch)) > 0) &&
773 (remove_spaces(mypch)[0] != AUTO_SEPARATOR[0])) {
774 nb_commands++;
775 if ((commands[nb_commands] = malloc(AUTO_COMMAND_SIZE)) != NULL) {
776 sprintf(commands[nb_commands], "%s", remove_spaces(mypch));
777 } else
778 nb_commands--;
779 }
780 mypch = strtok(NULL, AUTO_SEPARATOR);
781 }
782
783 free(temp);
784
785 /* Executing found commands */
786 for (int i = 1; i <= nb_commands; i++) {
787 if (commands[i]) {
788 if (!quiet)
789 more_printf("%s%s\n", hdt_cli.prompt, commands[i]);
790 exec_command(commands[i], hardware);
791 free(commands[i]);
792 }
793 }
794
795 if (!quiet)
796 more_printf("\nExiting Auto mode\n");
797
798 more_printf("\n");
799}
800
801void print_history(int argc, char **argv, struct s_hardware * hardware)
802{
803 (void)argc;
804 (void)argv;
805 (void)hardware;
806
807 reset_more_printf();
808 for (int i = 1; i <= MAX_HISTORY_SIZE; i++) {
809 if (i == hdt_cli.history_pos) {
810 more_printf("*%d:'%s'\n", i, hdt_cli.history[i]);
811 continue;
812 }
813 if (strlen(hdt_cli.history[i]) == 0)
814 continue;
815 more_printf(" %d:'%s'\n", i, hdt_cli.history[i]);
816 }
817}
818
819/* Code that manages the cli mode */
820void start_cli_mode(struct s_hardware *hardware)
821{
822 int current_key = 0;
823 int future_history_pos = 1; /* position of the next position in the history */
824 int current_future_history_pos = 1; /* Temp variable */
825 bool display_history = true; /* Temp Variable */
826 char temp_command[MAX_LINE_SIZE];
827
828 hdt_cli.cursor_pos = 0;
829 memset(hdt_cli.history, 0, sizeof(hdt_cli.history));
830 hdt_cli.history_pos = 1;
831 hdt_cli.max_history_pos = 1;
832
833 /* Find the mode selected */
834 set_mode(HDT_MODE, hardware);
835 find_cli_mode_descr(hdt_cli.mode, &current_mode);
836 if (current_mode == NULL) {
837 /* Shouldn't get here... */
838 more_printf("!!! BUG: Mode '%d' unknown.\n", hdt_cli.mode);
839 return;
840 }
841
842 /* Start the auto mode if the command line is set */
843 if (strlen(hardware->auto_label) > 0) {
844 start_auto_mode(hardware);
845 }
846
847 more_printf("Entering CLI mode\n");
848
849 reset_prompt();
850
851 while (hdt_cli.mode != EXIT_MODE) {
852
853 /* Display the cursor */
854 display_cursor(true);
855
856 /* Let's put the cursor blinking until we get an input */
857 set_cursor_blink(true);
858
859 /* We wait endlessly for a keyboard input */
860 current_key = get_key(stdin, 0);
861
862 /* We have to cancel the blinking mode to prevent
863 * input text to blink */
864 set_cursor_blink(false);
865
866 /* Reset autocomplete buffer unless TAB is pressed */
867 if (current_key != KEY_TAB)
868 autocomplete_destroy_list();
869
870 switch (current_key) {
871 /* clear until then end of line */
872 case KEY_CTRL('k'):
873 /* Clear the end of the line */
874 clear_end_of_line();
875 memset(&INPUT[hdt_cli.cursor_pos], 0,
876 strlen(INPUT) - hdt_cli.cursor_pos);
877 break;
878
879 case KEY_CTRL('c'):
880 printf("\n");
881 reset_prompt();
882 break;
883
884 case KEY_LEFT:
885 if (hdt_cli.cursor_pos > 0) {
886 move_cursor_left(1);
887 hdt_cli.cursor_pos--;
888 }
889 break;
890
891 case KEY_RIGHT:
892 if (hdt_cli.cursor_pos < (int)strlen(INPUT)) {
893 move_cursor_right(1);
894 hdt_cli.cursor_pos++;
895 }
896 break;
897
898 case KEY_CTRL('e'):
899 case KEY_END:
900 /* Calling with a 0 value will make the cursor move */
901 /* So, let's move the cursor only if needed */
902 if ((strlen(INPUT) - hdt_cli.cursor_pos) > 0) {
903 /* Return to the begining of line */
904 move_cursor_right(strlen(INPUT) - hdt_cli.cursor_pos);
905 hdt_cli.cursor_pos = strlen(INPUT);
906 }
907 break;
908
909 case KEY_CTRL('a'):
910 case KEY_HOME:
911 /* Calling with a 0 value will make the cursor move */
912 /* So, let's move the cursor only if needed */
913 if (hdt_cli.cursor_pos > 0) {
914 /* Return to the begining of line */
915 move_cursor_left(hdt_cli.cursor_pos);
916 hdt_cli.cursor_pos = 0;
917 }
918 break;
919
920 case KEY_UP:
921
922 /* Saving future position */
923 current_future_history_pos = future_history_pos;
924
925 /* We have to compute the next position */
926 if (future_history_pos == 1) {
927 future_history_pos = MAX_HISTORY_SIZE;
928 } else {
929 future_history_pos--;
930 }
931
932 /* Does the next position is valid */
933 if (strlen(hdt_cli.history[future_history_pos]) == 0) {
934 /* Position is invalid, restoring position */
935 future_history_pos = current_future_history_pos;
936 break;
937 }
938
939 /* Let's make that future position the one we use */
940 memset(INPUT, 0, sizeof(INPUT));
941 strlcpy(INPUT, hdt_cli.history[future_history_pos], sizeof(INPUT));
942
943 /* Clear the line */
944 clear_line();
945
946 /* Move to the begining of line */
947 move_cursor_to_column(0);
948
949 reset_prompt();
950 printf("%s", INPUT);
951 hdt_cli.cursor_pos = strlen(INPUT);
952 break;
953
954 case KEY_DOWN:
955 display_history = true;
956
957 /* Saving future position */
958 current_future_history_pos = future_history_pos;
959
960 if (future_history_pos == MAX_HISTORY_SIZE) {
961 future_history_pos = 1;
962 } else {
963 future_history_pos++;
964 }
965
966 /* Does the next position is valid */
967 if (strlen(hdt_cli.history[future_history_pos]) == 0)
968 display_history = false;
969
970 /* An exception is made to reach the last empty line */
971 if (future_history_pos == hdt_cli.max_history_pos)
972 display_history = true;
973
974 if (display_history == false) {
975 /* Position is invalid, restoring position */
976 future_history_pos = current_future_history_pos;
977 break;
978 }
979
980 /* Let's make that future position the one we use */
981 memset(INPUT, 0, sizeof(INPUT));
982 strlcpy(INPUT, hdt_cli.history[future_history_pos], sizeof(INPUT));
983
984 /* Clear the line */
985 clear_line();
986
987 /* Move to the begining of line */
988 move_cursor_to_column(0);
989
990 reset_prompt();
991 printf("%s", INPUT);
992 hdt_cli.cursor_pos = strlen(INPUT);
993 break;
994
995 case KEY_TAB:
996 if (autocomplete_backlog) {
997 clear_line();
998 /* Move to the begining of line */
999 move_cursor_to_column(0);
1000 reset_prompt();
1001 printf("%s", autocomplete_last_seen->autocomplete_token);
1002 strlcpy(INPUT,
1003 autocomplete_last_seen->autocomplete_token,
1004 sizeof(INPUT));
1005 hdt_cli.cursor_pos = strlen(INPUT);
1006
1007 /* Cycle through the list */
1008 autocomplete_last_seen = autocomplete_last_seen->next;
1009 if (autocomplete_last_seen == NULL)
1010 autocomplete_last_seen = autocomplete_head;
1011 } else {
1012 printf("\n");
1013 autocomplete(skip_spaces(INPUT));
1014 autocomplete_last_seen = autocomplete_head;
1015
1016 printf("%s%s", hdt_cli.prompt, INPUT);
1017 }
1018 break;
1019
1020 case KEY_ENTER:
1021 printf("\n");
1022 if (strlen(remove_spaces(INPUT)) < 1) {
1023 reset_prompt();
1024 break;
1025 }
1026 exec_command(remove_spaces(INPUT), hardware);
1027 hdt_cli.history_pos++;
1028
1029 /* Did we reach the end of the history ?*/
1030 if (hdt_cli.history_pos > MAX_HISTORY_SIZE) {
1031 /* Let's return at the beginning */
1032 hdt_cli.history_pos = 1;
1033 }
1034
1035 /* Does the next position is already used ?
1036 * If yes, we are cycling in history */
1037 if (strlen(INPUT) > 0) {
1038 /* Let's clean that entry */
1039 memset(&INPUT,0,sizeof(INPUT));
1040 }
1041
1042 future_history_pos = hdt_cli.history_pos;
1043 if (hdt_cli.history_pos > hdt_cli.max_history_pos)
1044 hdt_cli.max_history_pos = hdt_cli.history_pos;
1045 reset_prompt();
1046 break;
1047
1048 case KEY_CTRL('d'):
1049 case KEY_DELETE:
1050 /* No need to delete when input is empty */
1051 if (strlen(INPUT) == 0)
1052 break;
1053 /* Don't delete when cursor is at the end of the line */
1054 if (hdt_cli.cursor_pos >= strlen(INPUT))
1055 break;
1056
1057 for (int c = hdt_cli.cursor_pos; c < (int)strlen(INPUT) - 1; c++)
1058 INPUT[c] = INPUT[c + 1];
1059 INPUT[strlen(INPUT) - 1] = '\0';
1060
1061 /* Clear the end of the line */
1062 clear_end_of_line();
1063
1064 /* Print the resulting buffer */
1065 printf("%s", INPUT + hdt_cli.cursor_pos);
1066
1067 /* Replace the cursor at the proper place */
1068 if (strlen(INPUT + hdt_cli.cursor_pos) > 0)
1069 move_cursor_left(strlen(INPUT + hdt_cli.cursor_pos));
1070 break;
1071
1072 case KEY_DEL:
1073 case KEY_BACKSPACE:
1074 /* Don't delete prompt */
1075 if (hdt_cli.cursor_pos == 0)
1076 break;
1077
1078 for (int c = hdt_cli.cursor_pos - 1;
1079 c < (int)strlen(INPUT) - 1; c++)
1080 INPUT[c] = INPUT[c + 1];
1081 INPUT[strlen(INPUT) - 1] = '\0';
1082
1083 /* Get one char back */
1084 move_cursor_left(1);
1085
1086 /* Clear the end of the line */
1087 clear_end_of_line();
1088
1089 /* Print the resulting buffer */
1090 printf("%s", INPUT + hdt_cli.cursor_pos - 1);
1091
1092 /* Realing to a char before the place we were */
1093 hdt_cli.cursor_pos--;
1094 move_cursor_to_column(strlen(hdt_cli.prompt) + hdt_cli.cursor_pos +
1095 1);
1096
1097 break;
1098
1099 case KEY_F1:
1100 printf("\n");
1101 exec_command(CLI_HELP, hardware);
1102 reset_prompt();
1103 break;
1104
1105 default:
1106 if ((current_key < 0x20) || (current_key > 0x7e))
1107 break;
1108 /* Prevent overflow */
1109 if (hdt_cli.cursor_pos > MAX_LINE_SIZE - 2)
1110 break;
1111 /* If we aren't at the end of the input line, let's insert */
1112 if (hdt_cli.cursor_pos < (int)strlen(INPUT)) {
1113 char key[2];
1114 int trailing_chars = strlen(INPUT) - hdt_cli.cursor_pos;
1115 memset(temp_command, 0, sizeof(temp_command));
1116 strlcpy(temp_command, INPUT, hdt_cli.cursor_pos);
1117 sprintf(key, "%c", current_key);
1118 strncat(temp_command, key, 1);
1119 strncat(temp_command,
1120 INPUT + hdt_cli.cursor_pos, trailing_chars);
1121 memset(INPUT, 0, sizeof(INPUT));
1122 snprintf(INPUT, sizeof(INPUT), "%s", temp_command);
1123
1124 /* Clear the end of the line */
1125 clear_end_of_line();
1126
1127 /* Print the resulting buffer */
1128 printf("%s", INPUT + hdt_cli.cursor_pos);
1129
1130 /* Return where we must put the new char */
1131 move_cursor_left(trailing_chars);
1132
1133 } else {
1134 putchar(current_key);
1135 INPUT[hdt_cli.cursor_pos] = current_key;
1136 }
1137 hdt_cli.cursor_pos++;
1138 break;
1139 }
1140 }
1141}