• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* ----------------------------------------------------------------------- *
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 
37 struct 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  */
58 const char *exit_aliases[] = { "q", "quit" };
59 const char *help_aliases[] = { "h", "?" };
60 
61 /* List of aliases */
62 struct 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 
75 struct cli_mode_descr *current_mode;
76 int autocomplete_backlog;
77 
78 struct autocomplete_list {
79     char autocomplete_token[MAX_LINE_SIZE];
80     struct autocomplete_list *next;
81 };
82 struct autocomplete_list *autocomplete_head = NULL;
83 struct autocomplete_list *autocomplete_tail = NULL;
84 struct autocomplete_list *autocomplete_last_seen = NULL;
85 
autocomplete_add_token_to_list(const char * token)86 static 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 
autocomplete_destroy_list(void)101 static 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  **/
set_mode(cli_mode_t mode,struct s_hardware * hardware)121 void 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  **/
mode_s_to_mode_t(char * name)209 cli_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  **/
find_cli_mode_descr(cli_mode_t mode,struct cli_mode_descr ** mode_found)234 void 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  **/
expand_aliases(char * line __unused,char ** command,char ** module,int * argc,char ** argv)259 static 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 
303 out:
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  **/
parse_command_line(char * line,char ** command,char ** module,int * argc,char ** argv)325 static 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  **/
find_cli_callback_descr(const char * module_name,struct cli_module_descr * modules_list,struct cli_callback_descr ** module_found)416 void 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 
436 not_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  **/
autocomplete_command(char * command)449 static 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  **/
autocomplete_module(char * command,char * module)538 static 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  **/
autocomplete(char * line)579 static 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 
607 out:
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  **/
exec_command(char * line,struct s_hardware * hardware)619 static 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 
reset_prompt(void)748 static 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 
start_auto_mode(struct s_hardware * hardware)758 void 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 
print_history(int argc,char ** argv,struct s_hardware * hardware)801 void 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 */
start_cli_mode(struct s_hardware * hardware)820 void 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 }
1142