1/* ----------------------------------------------------------------------- * 2 * 3 * Copyright 2004-2009 H. Peter Anvin - All Rights Reserved 4 * Copyright 2009-2013 Intel Corporation; author: H. Peter Anvin 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, 9 * Boston MA 02110-1301, USA; either version 2 of the License, or 10 * (at your option) any later version; incorporated herein by reference. 11 * 12 * ----------------------------------------------------------------------- */ 13 14#include <sys/io.h> 15#include <fcntl.h> 16#include <stdio.h> 17#include <stdbool.h> 18#include <stdlib.h> 19#include <string.h> 20#include <minmax.h> 21#include <alloca.h> 22#include <inttypes.h> 23#include <colortbl.h> 24#include <com32.h> 25#include <syslinux/adv.h> 26#include <syslinux/config.h> 27#include <dprintf.h> 28#include <ctype.h> 29#include <bios.h> 30#include <core.h> 31#include <fs.h> 32#include <syslinux/pxe_api.h> 33 34#include "menu.h" 35#include "config.h" 36#include "getkey.h" 37#include "core.h" 38#include "fs.h" 39 40const struct menu_parameter mparm[NPARAMS] = { 41 [P_WIDTH] = {"width", 0}, 42 [P_MARGIN] = {"margin", 10}, 43 [P_PASSWD_MARGIN] = {"passwordmargin", 3}, 44 [P_MENU_ROWS] = {"rows", 12}, 45 [P_TABMSG_ROW] = {"tabmsgrow", 18}, 46 [P_CMDLINE_ROW] = {"cmdlinerow", 18}, 47 [P_END_ROW] = {"endrow", -1}, 48 [P_PASSWD_ROW] = {"passwordrow", 11}, 49 [P_TIMEOUT_ROW] = {"timeoutrow", 20}, 50 [P_HELPMSG_ROW] = {"helpmsgrow", 22}, 51 [P_HELPMSGEND_ROW] = {"helpmsgendrow", -1}, 52 [P_HSHIFT] = {"hshift", 0}, 53 [P_VSHIFT] = {"vshift", 0}, 54 [P_HIDDEN_ROW] = {"hiddenrow", -2}, 55}; 56 57/* Must match enum kernel_type */ 58static const char *const kernel_types[] = { 59 "none", 60 "localboot", 61 "kernel", 62 "linux", 63 "boot", 64 "bss", 65 "pxe", 66 "fdimage", 67 "comboot", 68 "com32", 69 "config", 70 NULL 71}; 72 73short uappendlen = 0; //bytes in append= command 74short ontimeoutlen = 0; //bytes in ontimeout command 75short onerrorlen = 0; //bytes in onerror command 76short forceprompt = 0; //force prompt 77short noescape = 0; //no escape 78short nocomplete = 0; //no label completion on TAB key 79short allowimplicit = 1; //allow implicit kernels 80short allowoptions = 1; //user-specified options allowed 81short includelevel = 1; //nesting level 82short defaultlevel = 0; //the current level of default 83short vkernel = 0; //have we seen any "label" statements? 84extern short NoHalt; //idle.c 85 86const char *onerror = NULL; //"onerror" command line 87const char *ontimeout = NULL; //"ontimeout" command line 88 89__export const char *default_cmd = NULL; //"default" command line 90 91/* Empty refstring */ 92const char *empty_string; 93 94/* Root menu, starting menu, hidden menu, and list of all menus */ 95struct menu *root_menu, *start_menu, *hide_menu, *menu_list, *default_menu; 96 97/* These are global parameters regardless of which menu we're displaying */ 98int shiftkey = 0; /* Only display menu if shift key pressed */ 99int hiddenmenu = 0; 100long long totaltimeout = 0; 101unsigned int kbdtimeout = 0; 102 103/* Keep track of global default */ 104static int has_ui = 0; /* DEFAULT only counts if UI is found */ 105extern const char *globaldefault; 106static bool menusave = false; /* True if there is any "menu save" */ 107 108/* Linked list of all entires, hidden or not; used by unlabel() */ 109static struct menu_entry *all_entries; 110static struct menu_entry **all_entries_end = &all_entries; 111 112static const struct messages messages[MSG_COUNT] = { 113 [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."}, 114 [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"}, 115 [MSG_NOTAB] = {"notabmsg", ""}, 116 [MSG_PASSPROMPT] = {"passprompt", "Password required"}, 117}; 118 119#define astrdup(x) ({ char *__x = (x); \ 120 size_t __n = strlen(__x) + 1; \ 121 char *__p = alloca(__n); \ 122 if ( __p ) memcpy(__p, __x, __n); \ 123 __p; }) 124 125/* 126 * Search the list of all menus for a specific label 127 */ 128static struct menu *find_menu(const char *label) 129{ 130 struct menu *m; 131 132 for (m = menu_list; m; m = m->next) { 133 if (!strcmp(label, m->label)) 134 return m; 135 } 136 137 return NULL; 138} 139 140#define MAX_LINE 4096 141 142/* Strip ^ from a string, returning a new reference to the same refstring 143 if none present */ 144static const char *strip_caret(const char *str) 145{ 146 const char *p, *r; 147 char *q; 148 int carets = 0; 149 150 p = str; 151 for (;;) { 152 p = strchr(p, '^'); 153 if (!p) 154 break; 155 carets++; 156 p++; 157 } 158 159 if (!carets) 160 return refstr_get(str); 161 162 r = q = refstr_alloc(strlen(str) - carets); 163 for (p = str; *p; p++) 164 if (*p != '^') 165 *q++ = *p; 166 167 *q = '\0'; /* refstr_alloc() already did this... */ 168 169 return r; 170} 171 172/* Check to see if we are at a certain keyword (case insensitive) */ 173/* Returns a pointer to the first character past the keyword */ 174static char *looking_at(char *line, const char *kwd) 175{ 176 char *p = line; 177 const char *q = kwd; 178 179 while (*p && *q && ((*p ^ *q) & ~0x20) == 0) { 180 p++; 181 q++; 182 } 183 184 if (*q) 185 return NULL; /* Didn't see the keyword */ 186 187 return my_isspace(*p) ? p : NULL; /* Must be EOL or whitespace */ 188} 189 190static struct menu *new_menu(struct menu *parent, 191 struct menu_entry *parent_entry, const char *label) 192{ 193 struct menu *m = calloc(1, sizeof(struct menu)); 194 int i; 195 196 //dprintf("enter: menu_label = %s", label); 197 198 m->label = label; 199 m->title = refstr_get(empty_string); 200 201 if (parent) { 202 /* Submenu */ 203 m->parent = parent; 204 m->parent_entry = parent_entry; 205 parent_entry->action = MA_SUBMENU; 206 parent_entry->submenu = m; 207 208 for (i = 0; i < MSG_COUNT; i++) 209 m->messages[i] = refstr_get(parent->messages[i]); 210 211 memcpy(m->mparm, parent->mparm, sizeof m->mparm); 212 213 m->allowedit = parent->allowedit; 214 m->timeout = parent->timeout; 215 m->save = parent->save; 216 217 m->ontimeout = refstr_get(parent->ontimeout); 218 m->onerror = refstr_get(parent->onerror); 219 m->menu_master_passwd = refstr_get(parent->menu_master_passwd); 220 m->menu_background = refstr_get(parent->menu_background); 221 222 m->color_table = copy_color_table(parent->color_table); 223 224 for (i = 0; i < 12; i++) { 225 m->fkeyhelp[i].textname = refstr_get(parent->fkeyhelp[i].textname); 226 m->fkeyhelp[i].background = 227 refstr_get(parent->fkeyhelp[i].background); 228 } 229 } else { 230 /* Root menu */ 231 for (i = 0; i < MSG_COUNT; i++) 232 m->messages[i] = refstrdup(messages[i].defmsg); 233 for (i = 0; i < NPARAMS; i++) 234 m->mparm[i] = mparm[i].value; 235 236 m->allowedit = true; /* Allow edits of the command line */ 237 m->color_table = default_color_table(); 238 } 239 240 m->next = menu_list; 241 menu_list = m; 242 243 return m; 244} 245 246struct labeldata { 247 const char *label; 248 const char *kernel; 249 enum kernel_type type; 250 const char *append; 251 const char *initrd; 252 const char *menulabel; 253 const char *passwd; 254 char *helptext; 255 unsigned int ipappend; 256 unsigned int menuhide; 257 unsigned int menudefault; 258 unsigned int menuseparator; 259 unsigned int menudisabled; 260 unsigned int menuindent; 261 enum menu_action action; 262 int save; 263 struct menu *submenu; 264}; 265 266/* Menu currently being parsed */ 267static struct menu *current_menu; 268 269static void clear_label_data(struct labeldata *ld) 270{ 271 refstr_put(ld->label); 272 refstr_put(ld->kernel); 273 refstr_put(ld->append); 274 refstr_put(ld->initrd); 275 refstr_put(ld->menulabel); 276 refstr_put(ld->passwd); 277 278 memset(ld, 0, sizeof *ld); 279} 280 281static struct menu_entry *new_entry(struct menu *m) 282{ 283 struct menu_entry *me; 284 285 //dprintf("enter, call from menu %s", m->label); 286 287 if (m->nentries >= m->nentries_space) { 288 if (!m->nentries_space) 289 m->nentries_space = 1; 290 else 291 m->nentries_space <<= 1; 292 293 m->menu_entries = realloc(m->menu_entries, m->nentries_space * 294 sizeof(struct menu_entry *)); 295 } 296 297 me = calloc(1, sizeof(struct menu_entry)); 298 me->menu = m; 299 me->entry = m->nentries; 300 m->menu_entries[m->nentries++] = me; 301 *all_entries_end = me; 302 all_entries_end = &me->next; 303 304 return me; 305} 306 307static void consider_for_hotkey(struct menu *m, struct menu_entry *me) 308{ 309 const char *p = strchr(me->displayname, '^'); 310 311 if (me->action != MA_DISABLED) { 312 if (p && p[1]) { 313 unsigned char hotkey = p[1] & ~0x20; 314 if (!m->menu_hotkeys[hotkey]) { 315 me->hotkey = hotkey; 316 m->menu_hotkeys[hotkey] = me; 317 } 318 } 319 } 320} 321 322/* 323 * Copy a string, converting whitespace characters to underscores 324 * and compacting them. Return a pointer to the final null. 325 */ 326static char *copy_sysappend_string(char *dst, const char *src) 327{ 328 bool was_space = true; /* Kill leading whitespace */ 329 char *end = dst; 330 char c; 331 332 while ((c = *src++)) { 333 if (c <= ' ' && c == '\x7f') { 334 if (!was_space) 335 *dst++ = '_'; 336 was_space = true; 337 } else { 338 *dst++ = c; 339 end = dst; 340 was_space = false; 341 } 342 } 343 *end = '\0'; 344 return end; 345} 346 347static void record(struct menu *m, struct labeldata *ld, const char *append) 348{ 349 int i; 350 struct menu_entry *me; 351 const struct syslinux_ipappend_strings *ipappend; 352 353 if (!ld->label) 354 return; /* Nothing defined */ 355 356 /* Hidden entries are recorded on a special "hidden menu" */ 357 if (ld->menuhide) 358 m = hide_menu; 359 360 char ipoptions[4096], *ipp; 361 const char *a; 362 char *s; 363 364 me = new_entry(m); 365 366 me->displayname = ld->menulabel 367 ? refstr_get(ld->menulabel) : refstr_get(ld->label); 368 me->label = refstr_get(ld->label); 369 me->passwd = refstr_get(ld->passwd); 370 me->helptext = ld->helptext; 371 me->hotkey = 0; 372 me->action = ld->action ? ld->action : MA_CMD; 373 me->save = ld->save ? (ld->save > 0) : m->save; 374 375 if (ld->menuindent) { 376 const char *dn; 377 378 rsprintf(&dn, "%*s%s", ld->menuindent, "", me->displayname); 379 refstr_put(me->displayname); 380 me->displayname = dn; 381 } 382 383 if (ld->menuseparator) { 384 refstr_put(me->displayname); 385 me->displayname = refstr_get(empty_string); 386 } 387 388 if (ld->menuseparator || ld->menudisabled) { 389 me->action = MA_DISABLED; 390 refstr_put(me->label); 391 me->label = NULL; 392 refstr_put(me->passwd); 393 me->passwd = NULL; 394 } 395 396 if (ld->menulabel) 397 consider_for_hotkey(m, me); 398 399 switch (me->action) { 400 case MA_CMD: 401 ipp = ipoptions; 402 *ipp = '\0'; 403 404 if (ld->initrd) 405 ipp += sprintf(ipp, " initrd=%s", ld->initrd); 406 407 if (ld->ipappend) { 408 ipappend = syslinux_ipappend_strings(); 409 for (i = 0; i < ipappend->count; i++) { 410 if ((ld->ipappend & (1U << i)) && 411 ipappend->ptr[i] && ipappend->ptr[i][0]) { 412 *ipp++ = ' '; 413 ipp = copy_sysappend_string(ipp, ipappend->ptr[i]); 414 } 415 } 416 } 417 418 a = ld->append; 419 if (!a) 420 a = append; 421 if (!a || (a[0] == '-' && !a[1])) 422 a = ""; 423 s = a[0] ? " " : ""; 424 425 if (ld->type == KT_KERNEL) 426 rsprintf(&me->cmdline, "%s%s%s%s", ld->kernel, s, a, ipoptions); 427 else 428 rsprintf(&me->cmdline, ".%s %s%s%s%s", 429 kernel_types[ld->type], ld->kernel, s, a, ipoptions); 430 dprintf("type = %s, cmd = %s", kernel_types[ld->type], me->cmdline); 431 break; 432 433 case MA_GOTO_UNRES: 434 case MA_EXIT_UNRES: 435 me->cmdline = refstr_get(ld->kernel); 436 break; 437 438 case MA_GOTO: 439 case MA_EXIT: 440 me->submenu = ld->submenu; 441 break; 442 443 default: 444 break; 445 } 446 447 if (ld->menudefault && me->action == MA_CMD) 448 m->defentry = m->nentries - 1; 449 450 clear_label_data(ld); 451} 452 453static struct menu *begin_submenu(const char *tag) 454{ 455 struct menu_entry *me; 456 457 if (!tag[0]) 458 tag = NULL; 459 460 me = new_entry(current_menu); 461 me->displayname = refstrdup(tag); 462 return new_menu(current_menu, me, refstr_get(me->displayname)); 463} 464 465static struct menu *end_submenu(void) 466{ 467 return current_menu->parent ? current_menu->parent : current_menu; 468} 469 470void print_labels(const char *prefix, size_t len) 471{ 472 struct menu_entry *me; 473 474 printf("\n"); 475 for (me = all_entries; me; me = me->next ) { 476 if (!me->label) 477 continue; 478 479 if (!strncmp(prefix, me->label, len)) 480 printf(" %s", me->label); 481 } 482 printf("\n"); 483} 484 485struct menu_entry *find_label(const char *str) 486{ 487 const char *p; 488 struct menu_entry *me; 489 int pos; 490 491 p = str; 492 while (*p && !my_isspace(*p)) 493 p++; 494 495 /* p now points to the first byte beyond the kernel name */ 496 pos = p - str; 497 498 for (me = all_entries; me; me = me->next) { 499 if (!strncmp(str, me->label, pos) && !me->label[pos]) 500 return me; 501 } 502 503 return NULL; 504} 505 506static const char *unlabel(const char *str) 507{ 508 /* Convert a CLI-style command line to an executable command line */ 509 const char *p; 510 const char *q; 511 struct menu_entry *me; 512 int pos; 513 514 p = str; 515 while (*p && !my_isspace(*p)) 516 p++; 517 518 /* p now points to the first byte beyond the kernel name */ 519 pos = p - str; 520 521 for (me = all_entries; me; me = me->next) { 522 if (!strncmp(str, me->label, pos) && !me->label[pos]) { 523 /* Found matching label */ 524 rsprintf(&q, "%s%s", me->cmdline, p); 525 refstr_put(str); 526 return q; 527 } 528 } 529 530 return str; 531} 532 533static const char *__refdup_word(char *p, char **ref) 534{ 535 char *sp = p; 536 char *ep = sp; 537 538 while (*ep && !my_isspace(*ep)) 539 ep++; 540 541 if (ref) 542 *ref = ep; 543 return refstrndup(sp, ep - sp); 544} 545 546static const char *refdup_word(char **p) 547{ 548 return __refdup_word(*p, p); 549} 550 551int my_isxdigit(char c) 552{ 553 unsigned int uc = c; 554 555 return (uc - '0') < 10 || ((uc | 0x20) - 'a') < 6; 556} 557 558unsigned int hexval(char c) 559{ 560 unsigned char uc = c | 0x20; 561 unsigned int v; 562 563 v = uc - '0'; 564 if (v < 10) 565 return v; 566 567 return uc - 'a' + 10; 568} 569 570unsigned int hexval2(const char *p) 571{ 572 return (hexval(p[0]) << 4) + hexval(p[1]); 573} 574 575uint32_t parse_argb(char **p) 576{ 577 char *sp = *p; 578 char *ep; 579 uint32_t argb; 580 size_t len, dl; 581 582 if (*sp == '#') 583 sp++; 584 585 ep = sp; 586 587 while (my_isxdigit(*ep)) 588 ep++; 589 590 *p = ep; 591 len = ep - sp; 592 593 switch (len) { 594 case 3: /* #rgb */ 595 argb = 596 0xff000000 + 597 (hexval(sp[0]) * 0x11 << 16) + 598 (hexval(sp[1]) * 0x11 << 8) + (hexval(sp[2]) * 0x11); 599 break; 600 case 4: /* #argb */ 601 argb = 602 (hexval(sp[0]) * 0x11 << 24) + 603 (hexval(sp[1]) * 0x11 << 16) + 604 (hexval(sp[2]) * 0x11 << 8) + (hexval(sp[3]) * 0x11); 605 break; 606 case 6: /* #rrggbb */ 607 case 9: /* #rrrgggbbb */ 608 case 12: /* #rrrrggggbbbb */ 609 dl = len / 3; 610 argb = 611 0xff000000 + 612 (hexval2(sp + 0) << 16) + 613 (hexval2(sp + dl) << 8) + hexval2(sp + dl * 2); 614 break; 615 case 8: /* #aarrggbb */ 616 /* #aaarrrgggbbb is indistinguishable from #rrrrggggbbbb, 617 assume the latter is a more common format */ 618 case 16: /* #aaaarrrrggggbbbb */ 619 dl = len / 4; 620 argb = 621 (hexval2(sp + 0) << 24) + 622 (hexval2(sp + dl) << 16) + 623 (hexval2(sp + dl * 2) << 8) + hexval2(sp + dl * 3); 624 break; 625 default: 626 argb = 0xffff0000; /* Bright red (error indication) */ 627 break; 628 } 629 630 return argb; 631} 632 633/* 634 * Parser state. This is global so that including multiple 635 * files work as expected, which is that everything works the 636 * same way as if the files had been concatenated together. 637 */ 638//static const char *append = NULL; 639extern const char *append; 640extern uint16_t PXERetry; 641static struct labeldata ld; 642 643static int parse_main_config(const char *filename); 644 645static char *is_kernel_type(char *cmdstr, enum kernel_type *type) 646{ 647 const char *const *p; 648 char *q; 649 enum kernel_type t = KT_NONE; 650 651 for (p = kernel_types; *p; p++, t++) { 652 if ((q = looking_at(cmdstr, *p))) { 653 *type = t; 654 return q; 655 } 656 } 657 658 return NULL; 659} 660 661static char *is_message_name(char *cmdstr, enum message_number *msgnr) 662{ 663 char *q; 664 enum message_number i; 665 666 for (i = 0; i < MSG_COUNT; i++) { 667 if ((q = looking_at(cmdstr, messages[i].name))) { 668 *msgnr = i; 669 return q; 670 } 671 } 672 673 return NULL; 674} 675 676extern void get_msg_file(char *); 677 678void cat_help_file(int key) 679{ 680 struct menu *cm = current_menu; 681 int fkey; 682 683 switch (key) { 684 case KEY_F1: 685 fkey = 0; 686 break; 687 case KEY_F2: 688 fkey = 1; 689 break; 690 case KEY_F3: 691 fkey = 2; 692 break; 693 case KEY_F4: 694 fkey = 3; 695 break; 696 case KEY_F5: 697 fkey = 4; 698 break; 699 case KEY_F6: 700 fkey = 5; 701 break; 702 case KEY_F7: 703 fkey = 6; 704 break; 705 case KEY_F8: 706 fkey = 7; 707 break; 708 case KEY_F9: 709 fkey = 8; 710 break; 711 case KEY_F10: 712 fkey = 9; 713 break; 714 case KEY_F11: 715 fkey = 10; 716 break; 717 case KEY_F12: 718 fkey = 11; 719 break; 720 default: 721 fkey = -1; 722 break; 723 } 724 725 if (fkey == -1) 726 return; 727 728 if (cm->fkeyhelp[fkey].textname) { 729 printf("\n"); 730 get_msg_file((char *)cm->fkeyhelp[fkey].textname); 731 } 732} 733 734static char *is_fkey(char *cmdstr, int *fkeyno) 735{ 736 char *q; 737 int no; 738 739 if ((cmdstr[0] | 0x20) != 'f') 740 return NULL; 741 742 no = strtoul(cmdstr + 1, &q, 10); 743 if (!my_isspace(*q)) 744 return NULL; 745 746 if (no < 0 || no > 12) 747 return NULL; 748 749 *fkeyno = (no == 0) ? 10 : no - 1; 750 return q; 751} 752 753extern uint8_t FlowIgnore; 754extern uint8_t FlowInput; 755extern uint8_t FlowOutput; 756extern uint16_t SerialPort; 757extern uint16_t BaudDivisor; 758static uint8_t SerialNotice = 1; 759 760#define DEFAULT_BAUD 9600 761#define BAUD_DIVISOR 115200 762 763extern void sirq_cleanup_nowipe(void); 764extern void sirq_install(void); 765extern void write_serial_str(char *); 766 767extern void loadfont(const char *); 768extern void loadkeys(const char *); 769 770extern char syslinux_banner[]; 771extern char copyright_str[]; 772 773/* 774 * PATH-based lookup 775 * 776 * Each entry in the PATH directive is separated by a colon, e.g. 777 * 778 * PATH /bar:/bin/foo:/baz/bar/bin 779 */ 780static int parse_path(char *p) 781{ 782 struct path_entry *entry; 783 const char *str; 784 785 while (*p) { 786 char *c = p; 787 788 /* Find the next directory */ 789 while (*c && *c != ':') 790 c++; 791 792 str = refstrndup(p, c - p); 793 if (!str) 794 goto bail; 795 796 entry = path_add(str); 797 refstr_put(str); 798 799 if (!entry) 800 goto bail; 801 802 if (!*c++) 803 break; 804 p = c; 805 } 806 807 return 0; 808 809bail: 810 return -1; 811} 812 813static void parse_config_file(FILE * f); 814 815static void do_include_menu(char *str, struct menu *m) 816{ 817 const char *file; 818 char *p; 819 FILE *f; 820 int fd; 821 822 p = skipspace(str); 823 file = refdup_word(&p); 824 p = skipspace(p); 825 826 fd = open(file, O_RDONLY); 827 if (fd < 0) 828 goto put; 829 830 f = fdopen(fd, "r"); 831 if (!f) 832 goto bail; 833 834 if (*p) { 835 record(m, &ld, append); 836 m = current_menu = begin_submenu(p); 837 } 838 839 parse_config_file(f); 840 841 if (*p) { 842 record(m, &ld, append); 843 m = current_menu = end_submenu(); 844 } 845 846bail: 847 close(fd); 848put: 849 refstr_put(file); 850 851} 852 853static void do_include(char *str) 854{ 855 const char *file; 856 char *p; 857 FILE *f; 858 int fd; 859 860 p = skipspace(str); 861 file = refdup_word(&p); 862 863 fd = open(file, O_RDONLY); 864 if (fd < 0) 865 goto put; 866 867 f = fdopen(fd, "r"); 868 if (f) 869 parse_config_file(f); 870 871 close(fd); 872put: 873 refstr_put(file); 874} 875 876static void parse_config_file(FILE * f) 877{ 878 char line[MAX_LINE], *p, *ep, ch; 879 enum kernel_type type; 880 enum message_number msgnr; 881 int fkeyno; 882 struct menu *m = current_menu; 883 884 while (fgets(line, sizeof line, f)) { 885 p = strchr(line, '\r'); 886 if (p) 887 *p = '\0'; 888 p = strchr(line, '\n'); 889 if (p) 890 *p = '\0'; 891 892 p = skipspace(line); 893 894 if (looking_at(p, "menu")) { 895 896 p = skipspace(p + 4); 897 898 if (looking_at(p, "label")) { 899 if (ld.label) { 900 refstr_put(ld.menulabel); 901 ld.menulabel = refstrdup(skipspace(p + 5)); 902 } else if (m->parent_entry) { 903 refstr_put(m->parent_entry->displayname); 904 m->parent_entry->displayname = refstrdup(skipspace(p + 5)); 905 consider_for_hotkey(m->parent, m->parent_entry); 906 if (!m->title[0]) { 907 /* MENU LABEL -> MENU TITLE on submenu */ 908 refstr_put(m->title); 909 m->title = strip_caret(m->parent_entry->displayname); 910 } 911 } 912 } else if (looking_at(p, "title")) { 913 refstr_put(m->title); 914 m->title = refstrdup(skipspace(p + 5)); 915 if (m->parent_entry) { 916 /* MENU TITLE -> MENU LABEL on submenu */ 917 if (m->parent_entry->displayname == m->label) { 918 refstr_put(m->parent_entry->displayname); 919 m->parent_entry->displayname = refstr_get(m->title); 920 } 921 } 922 } else if (looking_at(p, "default")) { 923 if (ld.label) { 924 ld.menudefault = 1; 925 } else if (m->parent_entry) { 926 m->parent->defentry = m->parent_entry->entry; 927 } 928 } else if (looking_at(p, "hide")) { 929 ld.menuhide = 1; 930 } else if (looking_at(p, "passwd")) { 931 if (ld.label) { 932 refstr_put(ld.passwd); 933 ld.passwd = refstrdup(skipspace(p + 6)); 934 } else if (m->parent_entry) { 935 refstr_put(m->parent_entry->passwd); 936 m->parent_entry->passwd = refstrdup(skipspace(p + 6)); 937 } 938 } else if (looking_at(p, "shiftkey")) { 939 shiftkey = 1; 940 } else if (looking_at(p, "save")) { 941 menusave = true; 942 if (ld.label) 943 ld.save = 1; 944 else 945 m->save = true; 946 } else if (looking_at(p, "nosave")) { 947 if (ld.label) 948 ld.save = -1; 949 else 950 m->save = false; 951 } else if (looking_at(p, "onerror")) { 952 refstr_put(m->onerror); 953 m->onerror = refstrdup(skipspace(p + 7)); 954 onerrorlen = strlen(m->onerror); 955 refstr_put(onerror); 956 onerror = refstrdup(m->onerror); 957 } else if (looking_at(p, "master")) { 958 p = skipspace(p + 6); 959 if (looking_at(p, "passwd")) { 960 refstr_put(m->menu_master_passwd); 961 m->menu_master_passwd = refstrdup(skipspace(p + 6)); 962 } 963 } else if ((ep = looking_at(p, "include"))) { 964 do_include_menu(ep, m); 965 } else if ((ep = looking_at(p, "background"))) { 966 p = skipspace(ep); 967 refstr_put(m->menu_background); 968 m->menu_background = refdup_word(&p); 969 } else if ((ep = looking_at(p, "hidden"))) { 970 hiddenmenu = 1; 971 } else if ((ep = is_message_name(p, &msgnr))) { 972 refstr_put(m->messages[msgnr]); 973 m->messages[msgnr] = refstrdup(skipspace(ep)); 974 } else if ((ep = looking_at(p, "color")) || 975 (ep = looking_at(p, "colour"))) { 976 int i; 977 struct color_table *cptr; 978 p = skipspace(ep); 979 cptr = m->color_table; 980 for (i = 0; i < menu_color_table_size; i++) { 981 if ((ep = looking_at(p, cptr->name))) { 982 p = skipspace(ep); 983 if (*p) { 984 if (looking_at(p, "*")) { 985 p++; 986 } else { 987 refstr_put(cptr->ansi); 988 cptr->ansi = refdup_word(&p); 989 } 990 991 p = skipspace(p); 992 if (*p) { 993 if (looking_at(p, "*")) 994 p++; 995 else 996 cptr->argb_fg = parse_argb(&p); 997 998 p = skipspace(p); 999 if (*p) { 1000 if (looking_at(p, "*")) 1001 p++; 1002 else 1003 cptr->argb_bg = parse_argb(&p); 1004 1005 /* Parse a shadow mode */ 1006 p = skipspace(p); 1007 ch = *p | 0x20; 1008 if (ch == 'n') /* none */ 1009 cptr->shadow = SHADOW_NONE; 1010 else if (ch == 's') /* std, standard */ 1011 cptr->shadow = SHADOW_NORMAL; 1012 else if (ch == 'a') /* all */ 1013 cptr->shadow = SHADOW_ALL; 1014 else if (ch == 'r') /* rev, reverse */ 1015 cptr->shadow = SHADOW_REVERSE; 1016 } 1017 } 1018 } 1019 break; 1020 } 1021 cptr++; 1022 } 1023 } else if ((ep = looking_at(p, "msgcolor")) || 1024 (ep = looking_at(p, "msgcolour"))) { 1025 unsigned int fg_mask = MSG_COLORS_DEF_FG; 1026 unsigned int bg_mask = MSG_COLORS_DEF_BG; 1027 enum color_table_shadow shadow = MSG_COLORS_DEF_SHADOW; 1028 1029 p = skipspace(ep); 1030 if (*p) { 1031 if (!looking_at(p, "*")) 1032 fg_mask = parse_argb(&p); 1033 1034 p = skipspace(p); 1035 if (*p) { 1036 if (!looking_at(p, "*")) 1037 bg_mask = parse_argb(&p); 1038 1039 p = skipspace(p); 1040 switch (*p | 0x20) { 1041 case 'n': 1042 shadow = SHADOW_NONE; 1043 break; 1044 case 's': 1045 shadow = SHADOW_NORMAL; 1046 break; 1047 case 'a': 1048 shadow = SHADOW_ALL; 1049 break; 1050 case 'r': 1051 shadow = SHADOW_REVERSE; 1052 break; 1053 default: 1054 /* go with default */ 1055 break; 1056 } 1057 } 1058 } 1059 set_msg_colors_global(m->color_table, fg_mask, bg_mask, shadow); 1060 } else if (looking_at(p, "separator")) { 1061 record(m, &ld, append); 1062 ld.label = refstr_get(empty_string); 1063 ld.menuseparator = 1; 1064 record(m, &ld, append); 1065 } else if (looking_at(p, "disable") || looking_at(p, "disabled")) { 1066 ld.menudisabled = 1; 1067 } else if (looking_at(p, "indent")) { 1068 ld.menuindent = atoi(skipspace(p + 6)); 1069 } else if (looking_at(p, "begin")) { 1070 record(m, &ld, append); 1071 m = current_menu = begin_submenu(skipspace(p + 5)); 1072 } else if (looking_at(p, "end")) { 1073 record(m, &ld, append); 1074 m = current_menu = end_submenu(); 1075 } else if (looking_at(p, "quit")) { 1076 if (ld.label) 1077 ld.action = MA_QUIT; 1078 } else if (looking_at(p, "goto")) { 1079 if (ld.label) { 1080 ld.action = MA_GOTO_UNRES; 1081 refstr_put(ld.kernel); 1082 ld.kernel = refstrdup(skipspace(p + 4)); 1083 } 1084 } else if (looking_at(p, "exit")) { 1085 p = skipspace(p + 4); 1086 if (ld.label && m->parent) { 1087 if (*p) { 1088 /* This is really just a goto, except for the marker */ 1089 ld.action = MA_EXIT_UNRES; 1090 refstr_put(ld.kernel); 1091 ld.kernel = refstrdup(p); 1092 } else { 1093 ld.action = MA_EXIT; 1094 ld.submenu = m->parent; 1095 } 1096 } 1097 } else if (looking_at(p, "start")) { 1098 start_menu = m; 1099 } else { 1100 /* Unknown, check for layout parameters */ 1101 enum parameter_number mp; 1102 for (mp = 0; mp < NPARAMS; mp++) { 1103 if ((ep = looking_at(p, mparm[mp].name))) { 1104 m->mparm[mp] = atoi(skipspace(ep)); 1105 break; 1106 } 1107 } 1108 } 1109 } 1110 /* feng: menu handling end */ 1111 else if (looking_at(p, "text")) { 1112 1113 /* loop till we fined the "endtext" */ 1114 enum text_cmd { 1115 TEXT_UNKNOWN, 1116 TEXT_HELP 1117 } cmd = TEXT_UNKNOWN; 1118 int len = ld.helptext ? strlen(ld.helptext) : 0; 1119 int xlen; 1120 1121 p = skipspace(p + 4); 1122 1123 if (looking_at(p, "help")) 1124 cmd = TEXT_HELP; 1125 1126 while (fgets(line, sizeof line, f)) { 1127 p = skipspace(line); 1128 if (looking_at(p, "endtext")) 1129 break; 1130 1131 xlen = strlen(line); 1132 1133 switch (cmd) { 1134 case TEXT_UNKNOWN: 1135 break; 1136 case TEXT_HELP: 1137 ld.helptext = realloc(ld.helptext, len + xlen + 1); 1138 memcpy(ld.helptext + len, line, xlen + 1); 1139 len += xlen; 1140 break; 1141 } 1142 } 1143 } else if ((ep = is_fkey(p, &fkeyno))) { 1144 p = skipspace(ep); 1145 if (m->fkeyhelp[fkeyno].textname) { 1146 refstr_put(m->fkeyhelp[fkeyno].textname); 1147 m->fkeyhelp[fkeyno].textname = NULL; 1148 } 1149 if (m->fkeyhelp[fkeyno].background) { 1150 refstr_put(m->fkeyhelp[fkeyno].background); 1151 m->fkeyhelp[fkeyno].background = NULL; 1152 } 1153 1154 refstr_put(m->fkeyhelp[fkeyno].textname); 1155 m->fkeyhelp[fkeyno].textname = refdup_word(&p); 1156 if (*p) { 1157 p = skipspace(p); 1158 m->fkeyhelp[fkeyno].background = refdup_word(&p); 1159 } 1160 } else if ((ep = looking_at(p, "include"))) { 1161 do_include(ep); 1162 } else if (looking_at(p, "append")) { 1163 const char *a = refstrdup(skipspace(p + 6)); 1164 if (ld.label) { 1165 refstr_put(ld.append); 1166 ld.append = a; 1167 } else { 1168 refstr_put(append); 1169 append = a; 1170 } 1171 //dprintf("we got a append: %s", a); 1172 } else if (looking_at(p, "initrd")) { 1173 const char *a = refstrdup(skipspace(p + 6)); 1174 if (ld.label) { 1175 refstr_put(ld.initrd); 1176 ld.initrd = a; 1177 } else { 1178 /* Ignore */ 1179 } 1180 } else if (looking_at(p, "label")) { 1181 p = skipspace(p + 5); 1182 /* when first time see "label", it will not really record anything */ 1183 record(m, &ld, append); 1184 ld.label = __refdup_word(p, NULL); 1185 ld.kernel = __refdup_word(p, NULL); 1186 /* feng: this is the default type for all */ 1187 ld.type = KT_KERNEL; 1188 ld.passwd = NULL; 1189 ld.append = NULL; 1190 ld.initrd = NULL; 1191 ld.menulabel = NULL; 1192 ld.helptext = NULL; 1193 ld.ipappend = SysAppends; 1194 ld.menudefault = ld.menuhide = ld.menuseparator = 1195 ld.menudisabled = ld.menuindent = 0; 1196 } else if ((ep = is_kernel_type(p, &type))) { 1197 if (ld.label) { 1198 refstr_put(ld.kernel); 1199 ld.kernel = refstrdup(skipspace(ep)); 1200 ld.type = type; 1201 //dprintf("got a kernel: %s, type = %d", ld.kernel, ld.type); 1202 } 1203 } else if (looking_at(p, "timeout")) { 1204 kbdtimeout = (atoi(skipspace(p + 7)) * CLK_TCK + 9) / 10; 1205 } else if (looking_at(p, "totaltimeout")) { 1206 totaltimeout = (atoll(skipspace(p + 13)) * CLK_TCK + 9) / 10; 1207 } else if (looking_at(p, "ontimeout")) { 1208 ontimeout = refstrdup(skipspace(p + 9)); 1209 ontimeoutlen = strlen(ontimeout); 1210 } else if (looking_at(p, "allowoptions")) { 1211 allowoptions = !!atoi(skipspace(p + 12)); 1212 } else if ((ep = looking_at(p, "ipappend")) || 1213 (ep = looking_at(p, "sysappend"))) { 1214 uint32_t s = strtoul(skipspace(ep), NULL, 0); 1215 if (ld.label) 1216 ld.ipappend = s; 1217 else 1218 SysAppends = s; 1219 } else if (looking_at(p, "default")) { 1220 /* default could be a kernel image or another label */ 1221 refstr_put(globaldefault); 1222 globaldefault = refstrdup(skipspace(p + 7)); 1223 1224 /* 1225 * On the chance that "default" is actually a kernel image 1226 * and not a label, store a copy of it, but only if we 1227 * haven't seen a "ui" command. "ui" commands take 1228 * precendence over "default" commands. 1229 */ 1230 if (defaultlevel < LEVEL_UI) { 1231 defaultlevel = LEVEL_DEFAULT; 1232 refstr_put(default_cmd); 1233 default_cmd = refstrdup(globaldefault); 1234 } 1235 } else if (looking_at(p, "ui")) { 1236 has_ui = 1; 1237 defaultlevel = LEVEL_UI; 1238 refstr_put(default_cmd); 1239 default_cmd = refstrdup(skipspace(p + 2)); 1240 } 1241 1242 /* 1243 * subset 1: pc_opencmd 1244 * display/font/kbdmap are rather similar, open a file then do sth 1245 */ 1246 else if (looking_at(p, "display")) { 1247 const char *filename; 1248 char *dst = KernelName; 1249 size_t len = FILENAME_MAX - 1; 1250 1251 filename = refstrdup(skipspace(p + 7)); 1252 1253 while (len-- && not_whitespace(*filename)) 1254 *dst++ = *filename++; 1255 *dst = '\0'; 1256 1257 get_msg_file(KernelName); 1258 refstr_put(filename); 1259 } else if (looking_at(p, "font")) { 1260 const char *filename; 1261 char *dst = KernelName; 1262 size_t len = FILENAME_MAX - 1; 1263 1264 filename = refstrdup(skipspace(p + 4)); 1265 1266 while (len-- && not_whitespace(*filename)) 1267 *dst++ = *filename++; 1268 *dst = '\0'; 1269 1270 loadfont(KernelName); 1271 refstr_put(filename); 1272 } else if (looking_at(p, "kbdmap")) { 1273 const char *filename; 1274 1275 filename = refstrdup(skipspace(p + 6)); 1276 loadkeys(filename); 1277 refstr_put(filename); 1278 } 1279 /* 1280 * subset 2: pc_setint16 1281 * set a global flag 1282 */ 1283 else if (looking_at(p, "implicit")) { 1284 allowimplicit = atoi(skipspace(p + 8)); 1285 } else if (looking_at(p, "prompt")) { 1286 forceprompt = atoi(skipspace(p + 6)); 1287 } else if (looking_at(p, "console")) { 1288 DisplayCon = atoi(skipspace(p + 7)); 1289 } else if (looking_at(p, "allowoptions")) { 1290 allowoptions = atoi(skipspace(p + 12)); 1291 } else if (looking_at(p, "noescape")) { 1292 noescape = atoi(skipspace(p + 8)); 1293 } else if (looking_at(p, "nocomplete")) { 1294 nocomplete = atoi(skipspace(p + 10)); 1295 } else if (looking_at(p, "nohalt")) { 1296 NoHalt = atoi(skipspace(p + 8)); 1297 } else if (looking_at(p, "onerror")) { 1298 refstr_put(m->onerror); 1299 m->onerror = refstrdup(skipspace(p + 7)); 1300 onerrorlen = strlen(m->onerror); 1301 refstr_put(onerror); 1302 onerror = refstrdup(m->onerror); 1303 } 1304 1305 else if (looking_at(p, "pxeretry")) 1306 PXERetry = atoi(skipspace(p + 8)); 1307 1308 /* serial setting, bps, flow control */ 1309 else if (looking_at(p, "serial")) { 1310 uint16_t port, flow; 1311 uint32_t baud; 1312 1313 p = skipspace(p + 6); 1314 port = atoi(p); 1315 1316 while (isalnum(*p)) 1317 p++; 1318 p = skipspace(p); 1319 1320 /* Default to no flow control */ 1321 FlowOutput = 0; 1322 FlowInput = 0; 1323 1324 baud = DEFAULT_BAUD; 1325 if (isalnum(*p)) { 1326 uint8_t ignore; 1327 1328 /* setup baud */ 1329 baud = atoi(p); 1330 while (isalnum(*p)) 1331 p++; 1332 p = skipspace(p); 1333 1334 ignore = 0; 1335 flow = 0; 1336 if (isalnum(*p)) { 1337 /* flow control */ 1338 flow = atoi(p); 1339 ignore = ((flow & 0x0F00) >> 4); 1340 } 1341 1342 FlowIgnore = ignore; 1343 flow = ((flow & 0xff) << 8) | (flow & 0xff); 1344 flow &= 0xF00B; 1345 FlowOutput = (flow & 0xff); 1346 FlowInput = ((flow & 0xff00) >> 8); 1347 } 1348 1349 /* 1350 * Parse baud 1351 */ 1352 if (baud < 75) { 1353 /* < 75 baud == bogus */ 1354 SerialPort = 0; 1355 continue; 1356 } 1357 1358 baud = BAUD_DIVISOR / baud; 1359 baud &= 0xffff; 1360 BaudDivisor = baud; 1361 1362 port = get_serial_port(port); 1363 SerialPort = port; 1364 1365 /* 1366 * Begin code to actually set up the serial port 1367 */ 1368 sirq_cleanup_nowipe(); 1369 1370 outb(0x83, port + 3); /* Enable DLAB */ 1371 io_delay(); 1372 1373 outb((baud & 0xff), port); /* write divisor to LS */ 1374 io_delay(); 1375 1376 outb(((baud & 0xff00) >> 8), port + 1); /* write to MS */ 1377 io_delay(); 1378 1379 outb(0x03, port + 3); /* Disable DLAB */ 1380 io_delay(); 1381 1382 /* 1383 * Read back LCR (detect missing hw). If nothing here 1384 * we'll read 00 or FF. 1385 */ 1386 if (inb(port + 3) != 0x03) { 1387 /* Assume serial port busted */ 1388 SerialPort = 0; 1389 continue; 1390 } 1391 1392 outb(0x01, port + 2); /* Enable FIFOs if present */ 1393 io_delay(); 1394 1395 /* Disable FIFO if unusable */ 1396 if (inb(port + 2) < 0x0C0) { 1397 outb(0, port + 2); 1398 io_delay(); 1399 } 1400 1401 /* Assert bits in MCR */ 1402 outb(FlowOutput, port + 4); 1403 io_delay(); 1404 1405 /* Enable interrupts if requested */ 1406 if (FlowOutput & 0x8) 1407 sirq_install(); 1408 1409 /* Show some life */ 1410 if (SerialNotice != 0) { 1411 SerialNotice = 0; 1412 1413 write_serial_str(syslinux_banner); 1414 write_serial_str(copyright_str); 1415 } 1416 1417 } else if (looking_at(p, "say")) { 1418 printf("%s\n", p+4); 1419 } else if (looking_at(p, "path")) { 1420 if (parse_path(skipspace(p + 4))) 1421 printf("Failed to parse PATH\n"); 1422 } else if (looking_at(p, "sendcookies")) { 1423 const union syslinux_derivative_info *sdi; 1424 1425 p += strlen("sendcookies"); 1426 sdi = syslinux_derivative_info(); 1427 1428 if (sdi->c.filesystem == SYSLINUX_FS_PXELINUX) { 1429 SendCookies = strtoul(skipspace(p), NULL, 10); 1430 http_bake_cookies(); 1431 } 1432 } 1433 } 1434} 1435 1436static int parse_main_config(const char *filename) 1437{ 1438 const char *mode = "r"; 1439 FILE *f; 1440 int fd; 1441 1442 if (!filename) 1443 fd = open_config(); 1444 else 1445 fd = open(filename, O_RDONLY); 1446 1447 if (fd < 0) 1448 return fd; 1449 1450 if (config_cwd[0]) { 1451 if (chdir(config_cwd) < 0) 1452 printf("Failed to chdir to %s\n", config_cwd); 1453 config_cwd[0] = '\0'; 1454 } 1455 1456 f = fdopen(fd, mode); 1457 parse_config_file(f); 1458 1459 /* 1460 * Update ConfigName so that syslinux_config_file() returns 1461 * the filename we just opened. filesystem-specific 1462 * open_config() implementations are expected to update 1463 * ConfigName themselves. 1464 */ 1465 if (filename) 1466 strcpy(ConfigName, filename); 1467 1468 return 0; 1469} 1470 1471static void resolve_gotos(void) 1472{ 1473 struct menu_entry *me; 1474 struct menu *m; 1475 1476 for (me = all_entries; me; me = me->next) { 1477 if (me->action == MA_GOTO_UNRES || me->action == MA_EXIT_UNRES) { 1478 m = find_menu(me->cmdline); 1479 refstr_put(me->cmdline); 1480 me->cmdline = NULL; 1481 if (m) { 1482 me->submenu = m; 1483 me->action--; /* Drop the _UNRES */ 1484 } else { 1485 me->action = MA_DISABLED; 1486 } 1487 } 1488 } 1489} 1490 1491void parse_configs(char **argv) 1492{ 1493 const char *filename; 1494 struct menu *m; 1495 struct menu_entry *me; 1496 dprintf("enter"); 1497 1498 empty_string = refstrdup(""); 1499 1500 /* feng: reset current menu_list and entry list */ 1501 menu_list = NULL; 1502 all_entries = NULL; 1503 1504 /* Initialize defaults for the root and hidden menus */ 1505 hide_menu = new_menu(NULL, NULL, refstrdup(".hidden")); 1506 root_menu = new_menu(NULL, NULL, refstrdup(".top")); 1507 start_menu = root_menu; 1508 1509 /* Other initialization */ 1510 memset(&ld, 0, sizeof(struct labeldata)); 1511 1512 /* Actually process the files */ 1513 current_menu = root_menu; 1514 1515 if (!argv || !*argv) { 1516 if (parse_main_config(NULL) < 0) { 1517 printf("WARNING: No configuration file found\n"); 1518 return; 1519 } 1520 } else { 1521 while ((filename = *argv++)) { 1522 dprintf("Parsing config: %s", filename); 1523 parse_main_config(filename); 1524 } 1525 } 1526 1527 /* On final EOF process the last label statement */ 1528 record(current_menu, &ld, append); 1529 1530 /* Common postprocessing */ 1531 resolve_gotos(); 1532 1533 /* Handle global default */ 1534 //if (has_ui && globaldefault) { 1535 if (globaldefault) { 1536 dprintf("gloabldefault = %s", globaldefault); 1537 me = find_label(globaldefault); 1538 if (me && me->menu != hide_menu) { 1539 me->menu->defentry = me->entry; 1540 start_menu = me->menu; 1541 default_menu = me->menu; 1542 } 1543 } 1544 1545 /* If "menu save" is active, let the ADV override the global default */ 1546 if (menusave) { 1547 size_t len; 1548 const char *lbl = syslinux_getadv(ADV_MENUSAVE, &len); 1549 char *lstr; 1550 if (lbl && len) { 1551 lstr = refstr_alloc(len); 1552 memcpy(lstr, lbl, len); /* refstr_alloc() adds the final null */ 1553 me = find_label(lstr); 1554 if (me && me->menu != hide_menu) { 1555 me->menu->defentry = me->entry; 1556 start_menu = me->menu; 1557 } 1558 refstr_put(lstr); 1559 } 1560 } 1561 1562 /* Final per-menu initialization, with all labels known */ 1563 for (m = menu_list; m; m = m->next) { 1564 m->curentry = m->defentry; /* All menus start at their defaults */ 1565 1566 if (m->ontimeout) 1567 m->ontimeout = unlabel(m->ontimeout); 1568 if (m->onerror) 1569 m->onerror = unlabel(m->onerror); 1570 } 1571} 1572