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 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 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 **/ 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, ¤t_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 **/ 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 **/ 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 **/ 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 **/ 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 **/ 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 **/ 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 **/ 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 **/ 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 **/ 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 ¤t_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 ¤t_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 ¤t_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 ¤t_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 ¤t_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 ¤t_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 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 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 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 */ 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, ¤t_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