New "totaltimeout" option; change the menu behaviour on timeout.
authorhpa <hpa>
Tue, 30 Aug 2005 22:36:49 +0000 (22:36 +0000)
committerhpa <hpa>
Tue, 30 Aug 2005 22:36:49 +0000 (22:36 +0000)
13 files changed:
NEWS
com32/include/klibc/archsetjmp.h [new file with mode: 0644]
com32/include/setjmp.h [new file with mode: 0644]
com32/lib/Makefile
com32/lib/setjmp.S
com32/modules/menu.c
com32/modules/menu.h
com32/modules/readconfig.c
keywords
keywords.inc
parseconfig.inc
syslinux.doc
ui.inc

diff --git a/NEWS b/NEWS
index ccec82a..b175768 100644 (file)
--- 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 (file)
index 0000000..db04314
--- /dev/null
@@ -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 (file)
index 0000000..b504eb6
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * setjmp.h
+ */
+
+#ifndef _SETJMP_H
+#define _SETJMP_H
+
+#include <klibc/extern.h>
+#include <klibc/compiler.h>
+#include <stddef.h>
+#include <signal.h>
+
+#include <klibc/archsetjmp.h>
+
+__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 */
index af64950..3aa0a62 100644 (file)
@@ -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  \
index bea900c..5c5fbea 100644 (file)
 
        .text
        .align 4
+
+       .globl _setjmp
+       .type _setjmp, @function
+_setjmp:                               # gcc 4.0.1 wants this as an alias?
+               
        .globl setjmp
        .type setjmp, @function
 setjmp:
index 357ac9a..eee81f5 100644 (file)
@@ -26,9 +26,8 @@
 #include <consoles.h>
 #include <getkey.h>
 #include <minmax.h>
-#include <time.h>
-#include <sys/times.h>
-#include <unistd.h>
+#include <setjmp.h>
+#include <limits.h>
 #include <sha1.h>
 #include <base64.h>
 #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
 }
index 0d4bd00..78a553c 100644 (file)
 #ifndef MENU_H
 #define MENU_H
 
+#include <time.h>
+#include <sys/time.h>
+#include <sys/times.h>
+#include <inttypes.h>
+#include <unistd.h>
+
+#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;
index 3c69979..16bb136 100644 (file)
 
 #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") ) {
index 1fa8c9c..bfcbc2a 100644 (file)
--- a/keywords
+++ b/keywords
@@ -14,6 +14,7 @@ say
 serial
 console
 timeout
+totaltimeout
 allowoptions
 ontimeout
 onerror
index e880154..e507e43 100644 (file)
@@ -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
index 450af29..122a230 100644 (file)
@@ -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
index 3da76af..37cfdd2 100644 (file)
@@ -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 (file)
--- a/ui.inc
+++ b/ui.inc
@@ -34,6 +34,7 @@ enter_command:
 
                mov byte [FuncFlag],0           ; <Ctrl-F> 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