bootloader: usability improvements

- add "edit" menu option, to edit menu commands before executing them;
- add "menu" boot command, to return to the menu from the prompt;
- provide more line editing features when getting input;
- fix a few potential buffer overflows as a side effect.
This commit is contained in:
David van Moolenbroek 2012-10-11 00:09:19 +02:00
parent 52cf951fdc
commit 9733fcdb43
8 changed files with 202 additions and 22 deletions

View file

@ -4,4 +4,5 @@ default=2
menu=Start MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel rootdevname=$rootdevname menu=Start MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel rootdevname=$rootdevname
menu=Start latest MINIX 3:load_mods /boot/minix_latest/mod*;multiboot /boot/minix_latest/kernel rootdevname=$rootdevname menu=Start latest MINIX 3:load_mods /boot/minix_latest/mod*;multiboot /boot/minix_latest/kernel rootdevname=$rootdevname
menu=Start latest MINIX 3 in single user mode:load_mods /boot/minix_latest/mod*;multiboot /boot/minix_latest/kernel rootdevname=$rootdevname bootopts=-s menu=Start latest MINIX 3 in single user mode:load_mods /boot/minix_latest/mod*;multiboot /boot/minix_latest/kernel rootdevname=$rootdevname bootopts=-s
menu=Edit menu option:edit
menu=Drop to boot prompt:prompt menu=Drop to boot prompt:prompt

View file

@ -130,6 +130,9 @@ Each command is executed just as though the user had typed it in
and so can be any valid command that would be accepted at the and so can be any valid command that would be accepted at the
normal boot prompt. normal boot prompt.
In addition, In addition,
.Dq Ic edit
can be used to put the menu in editing mode, allowing the user to modify the
next selected option before executing it, and
.Dq Ic prompt .Dq Ic prompt
can be used to drop to the normal boot prompt. can be used to drop to the normal boot prompt.
.It Sy timeout .It Sy timeout

View file

@ -61,6 +61,7 @@ banner=Welcome to the MINIX 3 installation CD
banner================================================================================ banner================================================================================
banner= banner=
menu=Regular MINIX 3:multiboot /kernel bootcd=1 cdproberoot=1 rootdevname=ram disable=inet menu=Regular MINIX 3:multiboot /kernel bootcd=1 cdproberoot=1 rootdevname=ram disable=inet
menu=Edit menu option:edit
menu=Drop to boot prompt:prompt menu=Drop to boot prompt:prompt
clear=1 clear=1
timeout=10 timeout=10
@ -106,6 +107,7 @@ hdemu_root_changes()
cat >$RELEASEMNTDIR/boot.cfg <<END_BOOT_CFG cat >$RELEASEMNTDIR/boot.cfg <<END_BOOT_CFG
menu=Regular MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel bootcd=2 disable=inet bios_wini=yes bios_remap_first=1 ramimagedev=c0d7p0s0 menu=Regular MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel bootcd=2 disable=inet bios_wini=yes bios_remap_first=1 ramimagedev=c0d7p0s0
menu=Edit menu option:edit
menu=Drop to boot prompt:prompt menu=Drop to boot prompt:prompt
clear=1 clear=1
timeout=10 timeout=10
@ -120,6 +122,7 @@ usb_root_changes()
echo \ echo \
cat >$RELEASEMNTDIR/boot.cfg <<END_BOOT_CFG cat >$RELEASEMNTDIR/boot.cfg <<END_BOOT_CFG
menu=Regular MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel bios_wini=yes bios_remap_first=1 rootdevname=c0d7p0s0 menu=Regular MINIX 3:load_mods /boot/minix_default/mod*;multiboot /boot/minix_default/kernel bios_wini=yes bios_remap_first=1 rootdevname=c0d7p0s0
menu=Edit menu option:edit
menu=Drop to boot prompt:prompt menu=Drop to boot prompt:prompt
clear=1 clear=1
timeout=10 timeout=10

View file

@ -297,11 +297,10 @@ getchoicefrominput(char *input, int def)
return choice; return choice;
} }
void static void
doboottypemenu(void) showmenu(void)
{ {
int choice; int choice;
char input[80], *ic, *oc;
printf("\n"); printf("\n");
/* Display menu */ /* Display menu */
@ -317,19 +316,31 @@ doboottypemenu(void)
choice + 1, choice + 1,
bootconf.desc[choice]); bootconf.desc[choice]);
} }
}
void
doboottypemenu(void)
{
int choice, editing;
char input[256], *ic, *oc;
showmenu();
choice = -1; choice = -1;
editing = 0;
for (;;) { for (;;) {
input[0] = '\0'; input[0] = '\0';
if (bootconf.timeout < 0) { if (bootconf.timeout < 0) {
if (bootconf.menuformat == MENUFORMAT_LETTER) if (bootconf.menuformat == MENUFORMAT_LETTER)
printf("\nOption: [%c]:", printf("\nOption%s: [%c]:",
editing ? " (edit)" : "",
bootconf.def + 'A'); bootconf.def + 'A');
else else
printf("\nOption: [%d]:", printf("\nOption%s: [%d]:",
editing ? " (edit)" : "",
bootconf.def + 1); bootconf.def + 1);
gets(input); editline(input, sizeof(input), NULL);
choice = getchoicefrominput(input, bootconf.def); choice = getchoicefrominput(input, bootconf.def);
} else if (bootconf.timeout == 0) } else if (bootconf.timeout == 0)
choice = bootconf.def; choice = bootconf.def;
@ -351,27 +362,45 @@ doboottypemenu(void)
} }
if (choice < 0) if (choice < 0)
continue; continue;
if (!strcmp(bootconf.command[choice], "prompt") && ic = bootconf.command[choice];
if (editing) {
printf("> ");
editline(input, sizeof(input), ic);
ic = input;
}
if (!strcmp(ic, "edit") &&
((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 || ((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 ||
check_password((char *)boot_params.bp_password))) { check_password((char *)boot_params.bp_password))) {
printf("type \"?\" or \"help\" for help.\n"); editing = 1;
bootmenu(); /* does not return */ bootconf.timeout = -1;
} else if (!strcmp(ic, "prompt") &&
((boot_params.bp_flags & X86_BP_FLAGS_PASSWORD) == 0 ||
check_password((char *)boot_params.bp_password))) {
printf("type \"?\" or \"help\" for help, "
"or \"menu\" to return to the menu.\n");
prompt(1);
showmenu();
editing = 0;
bootconf.timeout = -1;
} else { } else {
ic = bootconf.command[choice];
/* Split command string at ; into separate commands */ /* Split command string at ; into separate commands */
do { do {
/*
* This must support inline editing, since ic
* may also point to input.
*/
oc = input; oc = input;
/* Look for ; separator */ /* Look for ; separator */
for (; *ic && *ic != COMMAND_SEPARATOR; ic++) for (; *ic && *ic != COMMAND_SEPARATOR; ic++)
*oc++ = *ic; *oc++ = *ic;
if (*input == '\0') if (*ic == COMMAND_SEPARATOR)
ic++;
if (oc == input)
continue; continue;
/* Strip out any trailing spaces */ /* Strip out any trailing spaces */
oc--; oc--;
for (; *oc == ' ' && oc > input; oc--); for (; *oc == ' ' && oc > input; oc--);
*++oc = '\0'; *++oc = '\0';
if (*ic == COMMAND_SEPARATOR)
ic++;
/* Stop silly command strings like ;;; */ /* Stop silly command strings like ;;; */
if (*input != '\0') if (*input != '\0')
docommand(input); docommand(input);

View file

@ -45,9 +45,30 @@ ENTRY(conputc)
call _C_LABEL(prot_to_real) # enter real mode call _C_LABEL(prot_to_real) # enter real mode
.code16 .code16
cmp $0x08, %al # backspace?
jne print_char
# Multiline backspace support.
push %ax
movb $0x3, %ah # get cursor position
xorw %bx, %bx
int $0x10
pop %ax
testb %dl, %dl # cursor on first column?
jnz print_char
testb %dh, %dh # cursor not on first row?
jz print_char
decb %dh # move up one row
movb $0x4f, %dl # move to last column
xorw %bx, %bx
movb $0x02, %ah # set cursor position
jmp do_int
print_char:
movw $1,%bx movw $1,%bx
movb $0x0e,%ah movb $0x0e,%ah # print character
movb %al, %cl movb %al, %cl
do_int:
int $0x10 int $0x10
calll _C_LABEL(real_to_prot) # back to protected mode calll _C_LABEL(real_to_prot) # back to protected mode
@ -68,12 +89,12 @@ ENTRY(congetc)
movb $0x0,%ah movb $0x0,%ah
int $0x16 int $0x16
movb %al,%bl movw %ax,%bx
calll _C_LABEL(real_to_prot) # back to protected mode calll _C_LABEL(real_to_prot) # back to protected mode
.code32 .code32
movb %bl, 28(%esp) movw %bx, 28(%esp)
popa popa
ret ret

View file

@ -91,7 +91,9 @@ struct bootblk_command {
void (*c_fn)(char *); void (*c_fn)(char *);
}; };
void bootmenu(void); void bootmenu(void);
void prompt(int);
void docommand(char *); void docommand(char *);
void editline(char *, size_t, char *);
/* in "user code": */ /* in "user code": */
void command_help(char *); void command_help(char *);
@ -114,6 +116,8 @@ int coniskey(void);
__compactcall void conputc(int); __compactcall void conputc(int);
void conclr(void); void conclr(void);
int getchar_ex(void);
int getextmem2(int *); int getextmem2(int *);
__compactcall int getextmemps2(void *); __compactcall int getextmemps2(void *);
int getmementry(int *, int *); int getmementry(int *, int *);

View file

@ -62,23 +62,136 @@ docommand(char *arg)
} }
void void
bootmenu(void) prompt(int allowreturn)
{ {
char input[80]; char input[80];
for (;;) { for (;;) {
char *c = input; char *c = input;
input[0] = '\0';
printf("> "); printf("> ");
gets(input); editline(input, sizeof(input), NULL);
/* /*
* Skip leading whitespace. * Skip leading whitespace.
*/ */
while (*c == ' ') while (*c == ' ')
c++; c++;
if (allowreturn && !strcmp(c, "menu"))
break;
if (*c) if (*c)
docommand(c); docommand(c);
} }
} }
void
bootmenu(void)
{
prompt(0);
}
/* Derived from libsa gets(). */
void
editline(char *buf, size_t size, char *input)
{
int c, i, pos, len = 0;
/* If an initial input has been given, copy and print this first. */
if (input != NULL) {
while (*input && len < size - 1)
putchar(buf[len++] = *input++);
}
pos = len;
for (;;) {
c = getchar_ex();
switch (c & 0177) {
case '\0':
switch (c) {
case 0x4b00: /* Left arrow: move cursor to left. */
if (pos > 0) {
putchar('\b');
pos--;
}
break;
case 0x4d00: /* Right arrow: move cursor to right. */
if (pos < len) putchar(buf[pos++]);
break;
}
break;
case 'b' & 037: /* Ctrl+B: move cursor to left. */
if (pos > 0) {
putchar('\b');
pos--;
}
break;
case 'f' & 037: /* Ctrl+F: move cursor to right. */
if (pos < len) putchar(buf[pos++]);
break;
case 'a' & 037: /* Ctrl+A: move cursor to start of line. */
for ( ; pos > 0; pos--) putchar('\b');
break;
case 'e' & 037: /* Ctrl+E: move cursor to end of line. */
for ( ; pos < len; pos++) putchar(buf[pos]);
break;
case '\n': /* Enter: return line. */
case '\r':
for ( ; pos < len; pos++) putchar(buf[pos]);
buf[len] = '\0';
putchar('\n');
return;
#if HASH_ERASE
case '#':
#endif
case '\b': /* Backspace: erase character before cursor. */
case '\177':
if (pos > 0) {
pos--;
len--;
putchar('\b');
for (i = pos; i < len; i++)
putchar(buf[i] = buf[i + 1]);
putchar(' ');
for (i = pos; i < len; i++) putchar('\b');
putchar('\b');
}
break;
case 'r' & 037: /* Ctrl+R: reprint line. */
putchar('\n');
for (i = 0; i < len; i++) putchar(buf[i]);
for (i = len; i > pos; i--) putchar('\b');
break;
#if AT_ERASE
case '@':
#endif
case 'u' & 037: /* Ctrl+U: clear entire line. */
case 'w' & 037:
for ( ; pos > 0; pos--) putchar('\b');
for ( ; pos < len; pos++) putchar(' ');
for ( ; pos > 0; pos--) putchar('\b');
len = 0;
break;
case '\a': /* Ctrl+G: sound bell but do not store character. */
putchar(c);
break;
case '\t': /* Tab: convert to single space. */
c = ' ';
/*FALLTHROUGH*/
default: /* Insert character at cursor position. */
if (len < size - 1) {
for (i = len; i > pos; i--)
buf[i] = buf[i - 1];
buf[pos] = c;
pos++;
len++;
putchar(c);
for (i = pos; i < len; i++) putchar(buf[i]);
for (i = pos; i < len; i++) putchar('\b');
} else {
putchar('\a');
}
break;
}
}
/*NOTREACHED*/
}

View file

@ -259,7 +259,7 @@ putchar(int c)
} }
int int
getchar(void) getchar_ex(void)
{ {
int c; int c;
#ifdef SUPPORT_SERIAL #ifdef SUPPORT_SERIAL
@ -272,9 +272,9 @@ getchar(void)
c = congetc(); c = congetc();
#ifdef CONSOLE_KEYMAP #ifdef CONSOLE_KEYMAP
{ {
char *cp = strchr(CONSOLE_KEYMAP, c); char *cp = strchr(CONSOLE_KEYMAP, c & 0xff);
if (cp != 0 && cp[1] != 0) if (cp != 0 && cp[1] != 0)
c = cp[1]; c = cp[1] | (c & 0xff00);
} }
#endif #endif
return c; return c;
@ -302,6 +302,12 @@ getchar(void)
#endif /* SUPPORT_SERIAL */ #endif /* SUPPORT_SERIAL */
} }
int
getchar(void)
{
return getchar_ex() & 0xff;
}
int int
iskey(int intr) iskey(int intr)
{ {