From da20539a79fe070350a43503b8db954c50980e18 Mon Sep 17 00:00:00 2001 From: hpa Date: Tue, 30 Aug 2005 22:36:49 +0000 Subject: [PATCH] New "totaltimeout" option; change the menu behaviour on timeout. --- NEWS | 2 + com32/include/klibc/archsetjmp.h | 19 ++++++++ com32/include/setjmp.h | 43 ++++++++++++++++++ com32/lib/Makefile | 2 +- com32/lib/setjmp.S | 5 +++ com32/modules/menu.c | 97 ++++++++++++++++++++++++++++++---------- com32/modules/menu.h | 11 +++++ com32/modules/readconfig.c | 13 +++--- keywords | 1 + keywords.inc | 3 +- parseconfig.inc | 27 +++++++---- syslinux.doc | 16 ++++++- ui.inc | 89 +++++++++++++++++++++++------------- 13 files changed, 256 insertions(+), 72 deletions(-) create mode 100644 com32/include/klibc/archsetjmp.h create mode 100644 com32/include/setjmp.h diff --git a/NEWS b/NEWS index ccec82a..b175768 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,8 @@ Changes in 3.11: physical floppy disk is present in the system. * Enable the 16550A FIFOs, if present, when doing serial console. + * New "totaltimeout" command establishes a timeout without + regard for any user input. Changes in 3.10: * gcc 4.0.1 compilation fixes. diff --git a/com32/include/klibc/archsetjmp.h b/com32/include/klibc/archsetjmp.h new file mode 100644 index 0000000..db04314 --- /dev/null +++ b/com32/include/klibc/archsetjmp.h @@ -0,0 +1,19 @@ +/* + * arch/i386/include/klibc/archsetjmp.h + */ + +#ifndef _KLIBC_ARCHSETJMP_H +#define _KLIBC_ARCHSETJMP_H + +struct __jmp_buf { + unsigned int __ebx; + unsigned int __esp; + unsigned int __ebp; + unsigned int __esi; + unsigned int __edi; + unsigned int __eip; +}; + +typedef struct __jmp_buf jmp_buf[1]; + +#endif /* _SETJMP_H */ diff --git a/com32/include/setjmp.h b/com32/include/setjmp.h new file mode 100644 index 0000000..b504eb6 --- /dev/null +++ b/com32/include/setjmp.h @@ -0,0 +1,43 @@ +/* + * setjmp.h + */ + +#ifndef _SETJMP_H +#define _SETJMP_H + +#include +#include +#include +#include + +#include + +__extern int setjmp(jmp_buf); +__extern __noreturn longjmp(jmp_buf, int); + +/* + Whose bright idea was it to add unrelated functionality to just about + the only function in the standard C library (setjmp) which cannot be + wrapped by an ordinary function wrapper? Anyway, the damage is done, + and therefore, this wrapper *must* be inline. However, gcc will + complain if this is an inline function for unknown reason, and + therefore sigsetjmp() needs to be a macro. +*/ + +struct __sigjmp_buf { + jmp_buf __jmpbuf; + sigset_t __sigs; +}; + +typedef struct __sigjmp_buf sigjmp_buf[1]; + +#define sigsetjmp(__env, __save) \ +({ \ + struct __sigjmp_buf *__e = (__env); \ + sigprocmask(0, NULL, &__e->__sigs); \ + setjmp(__e->__jmpbuf); \ +}) + +__extern __noreturn siglongjmp(sigjmp_buf, int); + +#endif /* _SETJMP_H */ diff --git a/com32/lib/Makefile b/com32/lib/Makefile index af64950..3aa0a62 100644 --- a/com32/lib/Makefile +++ b/com32/lib/Makefile @@ -4,7 +4,7 @@ include MCONFIG LIBOBJS = \ abort.o atexit.o atoi.o atol.o atoll.o calloc.o creat.o \ ctypes.o errno.o fgetc.o fgets.o fopen.o fprintf.o fputc.o \ - putchar.o \ + putchar.o setjmp.o \ fputs.o fread2.o fread.o free.o fwrite2.o fwrite.o getopt.o \ lrand48.o malloc.o stack.o memccpy.o memchr.o memcmp.o \ memcpy.o memmem.o memmove.o memset.o memswap.o exit.o onexit.o \ diff --git a/com32/lib/setjmp.S b/com32/lib/setjmp.S index bea900c..5c5fbea 100644 --- a/com32/lib/setjmp.S +++ b/com32/lib/setjmp.S @@ -16,6 +16,11 @@ .text .align 4 + + .globl _setjmp + .type _setjmp, @function +_setjmp: # gcc 4.0.1 wants this as an alias? + .globl setjmp .type setjmp, @function setjmp: diff --git a/com32/modules/menu.c b/com32/modules/menu.c index 357ac9a..eee81f5 100644 --- a/com32/modules/menu.c +++ b/com32/modules/menu.c @@ -26,9 +26,8 @@ #include #include #include -#include -#include -#include +#include +#include #include #include #ifdef __COM32__ @@ -216,6 +215,42 @@ passwd_compare(const char *passwd, const char *entry) return !memcmp(sha1, pwdsha1, 20); } +static jmp_buf timeout_jump; + +static int mygetkey(clock_t timeout) +{ + clock_t t0, t; + clock_t tto, to; + int key; + + if ( !totaltimeout ) + return get_key(stdin, timeout); + + for (;;) { + tto = min(totaltimeout, INT_MAX); + to = timeout ? min(tto, timeout) : tto; + + t0 = times(NULL); + key = get_key(stdin, to); + t = times(NULL) - t0; + + if ( totaltimeout <= t ) + longjmp(timeout_jump, 1); + + totaltimeout -= t; + + if ( key != KEY_NONE ) + return key; + + if ( timeout ) { + if ( timeout <= t ) + return KEY_NONE; + + timeout -= t; + } + } +} + static int ask_passwd(const char *menu_entry) { @@ -248,7 +283,7 @@ ask_passwd(const char *menu_entry) p = user_passwd; while ( !done ) { - key = get_key(stdin, 0); + key = mygetkey(0); switch ( key ) { case KEY_ENTER: @@ -377,7 +412,7 @@ edit_cmdline(char *input, int top) redraw = 0; } - key = get_key(stdin, 0); + key = mygetkey(0); switch( key ) { case KEY_CTRL('L'): @@ -504,15 +539,28 @@ run_menu(void) { int key; int done = 0; - int entry = defentry, prev_entry = -1; + volatile int entry = defentry, prev_entry = -1; int top = 0, prev_top = -1; int clear = 1, to_clear; const char *cmdline = NULL; - clock_t key_timeout, timeout_left, this_timeout; + volatile clock_t key_timeout, timeout_left, this_timeout; - /* Convert timeout from deciseconds to clock ticks */ /* Note: for both key_timeout and timeout == 0 means no limit */ - timeout_left = key_timeout = (clock_t)(CLK_TCK*timeout+9)/10; + timeout_left = key_timeout = timeout; + + /* Handle both local and global timeout */ + if ( setjmp(timeout_jump) ) { + entry = defentry; + + if ( top < 0 || top < entry-MENU_ROWS+1 ) + top = max(0, entry-MENU_ROWS+1); + else if ( top > entry || top > max(0,nentries-MENU_ROWS) ) + top = min(entry, max(0,nentries-MENU_ROWS)); + + draw_menu(ontimeout ? -1 : entry, top, 1); + cmdline = ontimeout ? ontimeout : menu_entries[entry].cmdline; + done = 1; + } while ( !done ) { if ( entry < 0 ) @@ -544,7 +592,11 @@ run_menu(void) prev_entry = entry; prev_top = top; - if ( key_timeout && entry == defentry ) { + /* Cursor movement cancels timeout */ + if ( entry != defentry ) + key_timeout = 0; + + if ( key_timeout ) { int tol = timeout_left/CLK_TCK; int nc = snprintf(NULL, 0, " Automatic boot in %d seconds ", tol); printf("\033[%d;%dH%s Automatic boot in %s%d%s seconds ", @@ -557,8 +609,8 @@ run_menu(void) to_clear = 0; } - this_timeout = min(timeout_left, CLK_TCK); - key = get_key(stdin, this_timeout); + this_timeout = min(min(key_timeout, timeout_left), CLK_TCK); + key = mygetkey(this_timeout); if ( key != KEY_NONE ) { timeout_left = key_timeout; @@ -573,16 +625,11 @@ run_menu(void) Warning: a timeout will boot the default entry without any password! */ - timeout_left -= this_timeout; - - if ( timeout_left == 0 ) { - if ( entry != defentry ) { - entry = defentry; - timeout_left = key_timeout; - } else { - cmdline = menu_entries[defentry].cmdline; - done = 1; - } + if ( key_timeout ) { + if ( timeout_left <= this_timeout ) + longjmp(timeout_jump, 1); + + timeout_left -= this_timeout; } break; @@ -592,6 +639,7 @@ run_menu(void) case KEY_ENTER: case KEY_CTRL('J'): + key_timeout = 0; /* Cancels timeout */ if ( menu_entries[entry].passwd ) { clear = 1; done = ask_passwd(menu_entries[entry].passwd); @@ -661,6 +709,7 @@ run_menu(void) if ( allowedit ) { int ok = 1; + key_timeout = 0; /* Cancels timeout */ draw_row(entry-top+4, -1, top, 0, 0); if ( to_clear ) { @@ -691,6 +740,7 @@ run_menu(void) if ( allowedit ) { done = 1; clear = 1; + key_timeout = 0; draw_row(entry-top+4, -1, top, 0, 0); @@ -702,6 +752,7 @@ run_menu(void) if ( key > 0 && key < 0xFF ) { key &= ~0x20; /* Upper case */ if ( menu_hotkeys[key] ) { + key_timeout = 0; entry = menu_hotkeys[key] - menu_entries; /* Should we commit at this point? */ } @@ -759,7 +810,7 @@ execute(const char *cmdline) /* If this returns, something went bad; return to menu */ #else /* For testing... */ - printf("\n>>> %s\n", cmdline); + printf("\n\033[0m>>> %s\n", cmdline); exit(0); #endif } diff --git a/com32/modules/menu.h b/com32/modules/menu.h index 0d4bd00..78a553c 100644 --- a/com32/modules/menu.h +++ b/com32/modules/menu.h @@ -20,6 +20,16 @@ #ifndef MENU_H #define MENU_H +#include +#include +#include +#include +#include + +#ifndef CLK_TCK +# define CLK_TCK sysconf(_SC_CLK_TCK) +#endif + struct menu_entry { char *displayname; char *label; @@ -46,6 +56,7 @@ extern int nentries; extern int defentry; extern int allowedit; extern int timeout; +extern long long totaltimeout; extern char *menu_title; extern char *ontimeout; diff --git a/com32/modules/readconfig.c b/com32/modules/readconfig.c index 3c69979..16bb136 100644 --- a/com32/modules/readconfig.c +++ b/com32/modules/readconfig.c @@ -23,10 +23,11 @@ #include "menu.h" -int nentries = 0; -int defentry = 0; -int allowedit = 1; /* Allow edits of the command line */ -int timeout = 0; +int nentries = 0; +int defentry = 0; +int allowedit = 1; /* Allow edits of the command line */ +int timeout = 0; +long long totaltimeout = 0; char *menu_title = ""; char *ontimeout = NULL; @@ -259,7 +260,9 @@ void parse_config(const char *filename) ld.kernel = strdup(skipspace(p+6)); } } else if ( looking_at(p, "timeout") ) { - timeout = atoi(skipspace(p+7)); + timeout = (atoi(skipspace(p+7))*CLK_TCK+9)/10; + } else if ( looking_at(p, "totaltimeout") ) { + totaltimeout = (atoll(skipspace(p+13))*CLK_TCK+9)/10; } else if ( looking_at(p, "ontimeout") ) { ontimeout = strdup(skipspace(p+9)); } else if ( looking_at(p, "allowoptions") ) { diff --git a/keywords b/keywords index 1fa8c9c..bfcbc2a 100644 --- a/keywords +++ b/keywords @@ -14,6 +14,7 @@ say serial console timeout +totaltimeout allowoptions ontimeout onerror diff --git a/keywords.inc b/keywords.inc index e880154..e507e43 100644 --- a/keywords.inc +++ b/keywords.inc @@ -57,7 +57,8 @@ keywd_table: keyword say, pc_say keyword serial, pc_serial keyword console, pc_setint16, DisplayCon - keyword timeout, pc_timeout + keyword timeout, pc_timeout, KbdTimeout + keyword totaltimeout, pc_timeout, TotalTimeout keyword ontimeout, pc_ontimeout keyword onerror, pc_onerror keyword allowoptions, pc_setint16, AllowOptions diff --git a/parseconfig.inc b/parseconfig.inc index 450af29..122a230 100644 --- a/parseconfig.inc +++ b/parseconfig.inc @@ -110,16 +110,26 @@ pc_kernel: cmp byte [VKernel],0 .err: ret ; -; "timeout" command +; "timeout", "totaltimeout" command ; -pc_timeout: call getint +; N.B. 1/10 s ~ 1.D2162AABh clock ticks +; +pc_timeout: push ax + call getint + pop si jc .err - mov ax,0D215h ; There are approx 1.D215h - mul bx ; clock ticks per 1/10 s - add bx,dx - mov [KbdTimeOut],bx + mov eax,0D2162AABh + mul ebx ; clock ticks per 1/10 s + add ebx,edx + mov [si],ebx .err: ret + +; +; "totaltimeout" command +; +pc_totaltimeout: + ; ; Generic integer variable setting commands: ; "prompt", "implicit" @@ -366,11 +376,12 @@ commit_vk: section .data vk_overflow_msg db 'Out of memory parsing config file', CR, LF, 0 - align 2, db 0 + align 4, db 0 +KbdTimeout dd 0 ; Keyboard timeout (if any) +TotalTimeout dd 0 ; Total timeout (if any) AppendLen dw 0 ; Bytes in append= command OntimeoutLen dw 0 ; Bytes in ontimeout command OnerrorLen dw 0 ; Bytes in onerror command -KbdTimeOut dw 0 ; Keyboard timeout (if any) CmdLinePtr dw cmd_line_here ; Command line advancing pointer ForcePrompt dw 0 ; Force prompt NoEscape dw 0 ; No escape diff --git a/syslinux.doc b/syslinux.doc index 3da76af..37cfdd2 100644 --- a/syslinux.doc +++ b/syslinux.doc @@ -226,8 +226,20 @@ TIMEOUT timeout begun. A timeout of zero will disable the timeout completely, this is also the default. - NOTE: The maximum possible timeout value is 35996; corresponding to - just below one hour. +TOTALTIMEOUT timeout + Indicates how long to wait until booting automatically, in + units of 1/10 s. This timeout is *not* cancelled by user + input, and can thus be used to deal with serial port glitches + or "the user walked away" type situations. A timeout of zero + will disable the timeout completely, this is also the default. + + Both TIMEOUT and TOTALTIMEOUT can be used together, for + example: + + # Wait 5 seconds unless the user types something, but + # always boot after 15 minutes. + TIMEOUT 50 + TOTALTIMEOUT 9000 ONTIMEOUT kernel options... Sets the command line invoked on a timeout. Normally this is diff --git a/ui.inc b/ui.inc index c656a0c..c2c9e83 100644 --- a/ui.inc +++ b/ui.inc @@ -34,6 +34,7 @@ enter_command: mov byte [FuncFlag],0 ; not pressed mov di,command_line + ; ; get the very first character -- we can either time ; out, or receive a character press at this time. Some dorky BIOSes stuff @@ -45,40 +46,20 @@ clear_buffer: mov ah,11h ; Check for pending char mov ah,10h ; Get char int 16h jmp short clear_buffer -get_char_time: - call vgashowcursor - RESET_IDLE - mov cx,[KbdTimeOut] - and cx,cx - jz get_char ; Timeout == 0 -> no timeout - inc cx ; The first loop will happen - ; immediately as we don't - ; know the appropriate DX value -time_loop: push cx -tick_loop: push dx - call pollchar - jnz get_char_pop - mov dx,[BIOS_timer] ; Get time "of day" - pop ax - cmp dx,ax ; Has the timer advanced? - je tick_loop - pop cx - DO_IDLE - loop time_loop ; If so, decrement counter - ; Timeout!!!! - call vgahidecursor - mov si,Ontimeout ; Copy ontimeout command - mov cx,[OntimeoutLen] ; if we have one... - rep movsb -.stddefault: - jmp command_done + ; For the first character, both KbdTimeout and + ; TotalTimeout apply; after that, only TotalTimeout. + +get_char_time: + mov eax,[TotalTimeout] + mov [ThisTotalTo],eax + mov eax,[KbdTimeout] + mov [ThisKbdTo],eax -get_char_pop: pop eax ; Clear stack get_char: - call vgashowcursor - call getchar - call vgahidecursor + call getchar_timeout + and dword [ThisKbdTo],0 ; For the next time... + and al,al jz func_key @@ -422,6 +403,48 @@ on_error: ; kernel_corrupt: mov si,err_notkernel jmp abort_load + +; +; Get a key, observing ThisKbdTO and ThisTotalTO -- those are timeouts +; which can be adjusted by the caller based on the corresponding +; master variables; on return they're updated. +; +; This cheats. If we say "no timeout" we actually get a timeout of +; 7.5 years. +; +getchar_timeout: + call vgashowcursor + RESET_IDLE + +.loop: + push word [BIOS_timer] + call pollchar + jnz .got_char + pop ax + cmp ax,[BIOS_timer] ; Has the timer advanced? + je .loop + DO_IDLE + + dec dword [ThisKbdTo] + jz .timeout + dec dword [ThisTotalTo] + jnz .loop + +.timeout: + ; Timeout!!!! + pop cx ; Discard return address + call vgahidecursor + mov si,Ontimeout ; Copy ontimeout command + mov cx,[OntimeoutLen] ; if we have one... + rep movsb + jmp command_done + +.got_char: + pop cx ; Discard + call getchar + call vgahidecursor + ret + ; ; This is it! We have a name (and location on the disk)... let's load ; that sucker!! First we have to decide what kind of file this is; base @@ -515,7 +538,9 @@ kernel_good: ; Otherwise Linux kernel section .bss - alignb 2 + alignb 4 +ThisKbdTo resd 1 ; Temporary holder for KbdTimeout +ThisTotalTo resd 1 ; Temporary holder for TotalTimeout KernelExtPtr resw 1 ; During search, final null pointer CmdOptPtr resw 1 ; Pointer to first option on cmd line KbdFlags resb 1 ; Check for keyboard escapes -- 2.7.4