Ayvaz, James
2010-Apr-23 15:41 UTC
[syslinux] Path simple menu integrated progress indicator
This patch modifies the simple menu com32 program to include integrated load progress. It also adds new options to the simple menu configuration MENU LOADMSG -- allows the user to specify the text to display when loading MENU LOADMSGROW -- controls where the text is written Example configuration: MENU LOADMSG Booting MENU LOADMSGROW 25 It works with my setup, please let me know if you find any bugs or spot any problems. Thank you. diff -uprN syslinux-3.86-vanilla//com32/include/syslinux/loadfile.h syslinux-3.86/com32/include/syslinux/loadfile.h --- syslinux-3.86-vanilla//com32/include/syslinux/loadfile.h 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/include/syslinux/loadfile.h 2010-04-15 10:14:24.000000000 -0500 @@ -9,7 +9,9 @@ #define LOADFILE_ZERO_PAD 64 int loadfile(const char *, void **, size_t *); +int loadfile_w_callback(const char *filename, void **ptr, size_t *len, void (*callback)(const char* file, size_t cur, size_t total)); int zloadfile(const char *, void **, size_t *); int floadfile(FILE *, void **, size_t *, const void *, size_t); - +int floadfile_w_callback(FILE *f, void **ptr, size_t *len, const void *prefix, + size_t prefix_len, const char *filename, void (*callback)(const char* file, size_t cur, size_t total)); #endif diff -uprN syslinux-3.86-vanilla//com32/include/syslinux/vesacon.h syslinux-3.86/com32/include/syslinux/vesacon.h --- syslinux-3.86-vanilla//com32/include/syslinux/vesacon.h 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/include/syslinux/vesacon.h 2010-04-15 08:21:00.000000000 -0500 @@ -32,6 +32,7 @@ int vesacon_default_background(void); void vesacon_set_resolution(int, int); +void vesacon_get_resolution(int *, int *); int vesacon_load_background(const char *); int vesacon_set_background(unsigned int); void vesacon_cursor_enable(bool); diff -uprN syslinux-3.86-vanilla//com32/lib/sys/vesacon_write.c syslinux-3.86/com32/lib/sys/vesacon_write.c --- syslinux-3.86-vanilla//com32/lib/sys/vesacon_write.c 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/lib/sys/vesacon_write.c 2010-04-15 08:20:46.000000000 -0500 @@ -85,6 +85,15 @@ void vesacon_set_resolution(int x, int y vesacon_resolution.y = y; } +/* Get current resolution */ +void vesacon_get_resolution(int *x, int *y) +{ + x = vesacon_resolution.x; + y = vesacon_resolution.y; +} + + + /* Common setup */ int __vesacon_open(struct file_info *fp) { diff -uprN syslinux-3.86-vanilla//com32/lib/syslinux/floadfile.c syslinux-3.86/com32/lib/syslinux/floadfile.c --- syslinux-3.86-vanilla//com32/lib/syslinux/floadfile.c 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/lib/syslinux/floadfile.c 2010-04-15 10:15:10.000000000 -0500 @@ -106,3 +106,64 @@ err: free(data); return -1; } + +int floadfile_w_callback(FILE *f, void **ptr, size_t *len, const void *prefix, + size_t prefix_len, const char *filename, void (*callback)(const char *name, size_t cur, size_t total)) +{ + struct stat st; + void *data, *dp; + size_t alen, clen, rlen, xlen, flen; + + clen = alen = 0; + data = NULL; + + if ( fstat(fileno(f), &st) ) + goto err; + + + if (!S_ISREG(st.st_mode)) { + /* Not a regular file, we can't assume we know the file size */ + flen = -1; + } + else { + flen = st.st_size + prefix_len - ftell(f); + } + + if (prefix_len) { + clen = alen = prefix_len; + data = malloc(prefix_len); + if (!data) + goto err; + + memcpy(data, prefix, prefix_len); + } + + do { + alen += INCREMENTAL_CHUNK; + dp = realloc(data, alen); + if (!dp) + goto err; + data = dp; + + rlen = fread((char *)data+clen, 1, alen-clen, f); + clen += rlen; + callback(filename, clen, flen); + } while (clen == alen); + + *len = clen; + xlen = (clen + LOADFILE_ZERO_PAD-1) & ~(LOADFILE_ZERO_PAD-1); + dp = realloc(data, xlen); + if (dp) + data = dp; + *ptr = data; + + memset((char *)data + clen, 0, xlen-clen); + return 0; + + err: + if (data) + free(data); + return -1; +} + + diff -uprN syslinux-3.86-vanilla//com32/lib/syslinux/loadfile.c syslinux-3.86/com32/lib/syslinux/loadfile.c --- syslinux-3.86-vanilla//com32/lib/syslinux/loadfile.c 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/lib/syslinux/loadfile.c 2010-04-15 10:12:32.000000000 -0500 @@ -61,3 +61,24 @@ int loadfile(const char *filename, void errno = e; return rv; } + +int loadfile_w_callback(const char *filename, void **ptr, size_t *len, void (*callback)(const char* name, size_t cur, size_t total)) +{ + FILE *f; + int rv, e; + + f = fopen(filename, "r"); + if ( !f ) + return -1; + + rv = floadfile_w_callback(f, ptr, len, NULL, 0, filename, callback); + e = errno; + + fclose(f); + + if (rv) + errno = e; + return rv; +} + + diff -uprN syslinux-3.86-vanilla//com32/menu/execute.c syslinux-3.86/com32/menu/execute.c --- syslinux-3.86-vanilla//com32/menu/execute.c 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/menu/execute.c 2010-04-15 10:10:29.000000000 -0500 @@ -10,20 +10,123 @@ * * ----------------------------------------------------------------------- */ +#include <stdio.h> #include <stdlib.h> #include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <syslinux/linux.h> + #include <com32.h> #include "menu.h" +bool opt_quiet = false; + +static const char *refdup_word(char **p) +{ + char *sp = *p; + char *ep = sp; + + while (*ep && !my_isspace(*ep)) + ep++; + + *p = ep; + return refstrndup(sp, ep - sp); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +char *skip_spaces(char *s) +{ + while(*s && (*s == ' ' || *s == '\t')) s++; + + return s; +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +char *skip_nonspaces(char *s) +{ + while(*s && *s != ' ' && *s != '\t') s++; + + return s; +} + +/* Search for a boolean argument; return its position, or 0 if not present */ +static int find_boolean(char **argv, const char *argument) +{ + char **arg; + + for (arg = argv; *arg; arg++) { + if (!strcmp(*arg, argument)) + return (arg - argv) + 1; + } + + return 0; +} + +/* + * Find the last instance of a particular command line argument + * (which should include the final =; do not use for boolean arguments) + * Note: the resulting string is typically not null-terminated. + */ +const char *find_argument(const char *cmdline, const char *argument) +{ + const char *found = NULL; + const char *p = cmdline; + bool was_space = true; + size_t la = strlen(argument); + + while (*p) { + if (my_isspace(*p)) { + was_space = true; + } else if (was_space) { + if (!memcmp(p, argument, la)) + found = p + la; + was_space = false; + } + p++; + } + + return found; +} + +void console_prepare(void) +{ + fputs("\033[0m\033[25l", stdout); +} + +void console_cleanup(void) +{ + /* For the serial console, be nice and clean up */ + fputs("\033[0m", stdout); +} + +void +clear_screen(void) +{ + fputs("\033e\033%@\033)0\033(B\1#0\033[?25l\033[2J", stdout); +} + +int initramfs_load_archive_w_callback(struct initramfs *ihead, const char *filename, void (*callback)(const char* name, size_t cur, size_t total)) +{ + void *data; + size_t len; + + if (loadfile_w_callback(filename, &data, &len, callback)) + return -1; + + return initramfs_add_data(ihead, data, len, len, 4); +} + + void execute(const char *cmdline, enum kernel_type type) { com32sys_t ireg; - const char *p, *const *pp; + char *p, *const *pp; char *q = __com32.cs_bounce; const char *kernel, *args; - memset(&ireg, 0, sizeof ireg); - kernel = q; p = cmdline; while (*p && !my_isspace(*p)) { @@ -48,22 +151,60 @@ void execute(const char *cmdline, enum k } if (type == KT_LOCALBOOT) { - ireg.eax.w[0] = 0x0014; /* Local boot */ - ireg.edx.w[0] = strtoul(kernel, NULL, 0); + syslinux_local_boot(strtoul(kernel,NULL,0)); } else { - if (type < KT_KERNEL) - type = KT_KERNEL; - ireg.eax.w[0] = 0x0016; /* Run kernel image */ - ireg.esi.w[0] = OFFS(kernel); - ireg.ds = SEG(kernel); - ireg.ebx.w[0] = OFFS(args); - ireg.es = SEG(args); - ireg.edx.l = type - KT_KERNEL; - /* ireg.ecx.l = 0; *//* We do ipappend "manually" */ + size_t kernel_size = 0, initrd_size = 0; + char* kernel_name; + void* kernel_data, *initrd_buf; + char *s, *s0, *t, *initrd_arg; + struct initramfs *initramfs = NULL; + + char *arg; + opt_quiet = false; + + kernel_name = refdup_word(&cmdline); + loadfile(kernel_name, &kernel_data, &kernel_size); + if(!kernel_data) { + fprintf(stdout, "%s: read error\n", kernel_name); + return; /* Error! */ + } + + char *cmdline2 = malloc(strlen(cmdline) + strlen(kernel_name) + 12); + memset(cmdline2, 0, (strlen(cmdline) + strlen(kernel_name) + 12)); + strcat(cmdline2, kernel_name); + strcat(cmdline2, " "); + strcat(cmdline2, cmdline); + + /* Initialize the initramfs chain */ + initramfs = initramfs_init(); + if (!initramfs) + return; + + + if ((arg = find_argument(cmdline2, "initrd="))) { + do { + char* initrdarg = refdup_word(&arg); + p = strchr(initrdarg, ','); + if (p) + *p = '\0'; + + if (initramfs_load_archive_w_callback(initramfs, initrdarg, draw_progress)) { + printf("failed!\n"); + return 1; + } + + if (p) + *p++ = ','; + } while ((arg = p)); + } + + console_cleanup(); + console_prepare(); + + syslinux_boot_linux(kernel_data, kernel_size, initramfs, cmdline2); } - __intcall(0x22, &ireg, NULL); /* If this returns, something went bad; return to menu */ } diff -uprN syslinux-3.86-vanilla//com32/menu/menu.c syslinux-3.86/com32/menu/menu.c --- syslinux-3.86-vanilla//com32/menu/menu.c 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/menu/menu.c 2010-04-15 10:07:34.000000000 -0500 @@ -42,3 +42,25 @@ void start_console(void) { console_ansi_raw(); } + +void draw_progress(const char *file, size_t cur, size_t total) +{ + static bool once = true; + float percent; + percent = ((float)cur / (float)total) * 100.0; + + if (opt_quiet) + return; + + if (once) { + printf("Loading %s", file); + once = false; + } + + printf("."); + + if (percent>=100) + printf("ok\n"); +} + + diff -uprN syslinux-3.86-vanilla//com32/menu/menu.h syslinux-3.86/com32/menu/menu.h --- syslinux-3.86-vanilla//com32/menu/menu.h 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/menu/menu.h 2010-04-15 10:08:01.000000000 -0500 @@ -97,6 +97,7 @@ enum parameter_number { P_PASSWD_MARGIN, P_MENU_ROWS, P_TABMSG_ROW, + P_LOADMSG_ROW, P_CMDLINE_ROW, P_END_ROW, P_PASSWD_ROW, @@ -115,6 +116,7 @@ enum message_number { MSG_TITLE, MSG_AUTOBOOT, MSG_TAB, + MSG_LOAD, MSG_NOTAB, MSG_PASSPROMPT, @@ -188,6 +190,7 @@ int draw_background(const char *filename void set_resolution(int x, int y); void start_console(void); void local_cursor_enable(bool); +void draw_progress(const char *file, size_t cur, size_t total); static inline int my_isspace(char c) { @@ -227,4 +230,27 @@ void execute(const char *cmdline, enum k /* drain.c */ void drain_keyboard(void); +extern bool opt_quiet; + +/* The symbol "cm" always refers to the current menu across this file... */ +extern struct menu *cm; + +/* These macros assume "cm" is a pointer to the current menu */ +#define WIDTH (cm->mparm[P_WIDTH]) +#define MARGIN (cm->mparm[P_MARGIN]) +#define PASSWD_MARGIN (cm->mparm[P_PASSWD_MARGIN]) +#define MENU_ROWS (cm->mparm[P_MENU_ROWS]) +#define TABMSG_ROW (cm->mparm[P_TABMSG_ROW]+VSHIFT) +#define LOADMSG_ROW (cm->mparm[P_LOADMSG_ROW]+VSHIFT) +#define CMDLINE_ROW (cm->mparm[P_CMDLINE_ROW]+VSHIFT) +#define END_ROW (cm->mparm[P_END_ROW]) +#define PASSWD_ROW (cm->mparm[P_PASSWD_ROW]+VSHIFT) +#define TIMEOUT_ROW (cm->mparm[P_TIMEOUT_ROW]+VSHIFT) +#define HELPMSG_ROW (cm->mparm[P_HELPMSG_ROW]+VSHIFT) +#define HELPMSGEND_ROW (cm->mparm[P_HELPMSGEND_ROW]) +#define HSHIFT (cm->mparm[P_HSHIFT]) +#define VSHIFT (cm->mparm[P_VSHIFT]) +#define HIDDEN_ROW (cm->mparm[P_HIDDEN_ROW]) + + #endif /* MENU_H */ diff -uprN syslinux-3.86-vanilla//com32/menu/menumain.c syslinux-3.86/com32/menu/menumain.c --- syslinux-3.86-vanilla//com32/menu/menumain.c 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/menu/menumain.c 2010-04-15 13:08:03.000000000 -0500 @@ -29,11 +29,13 @@ #include <limits.h> #include <com32.h> #include <syslinux/adv.h> +#include <console.h> #include "menu.h" /* The symbol "cm" always refers to the current menu across this file... */ -static struct menu *cm; +//static struct menu *cm; +struct menu *cm; const struct menu_parameter mparm[NPARAMS] = { [P_WIDTH] = {"width", 0}, @@ -41,6 +43,7 @@ const struct menu_parameter mparm[NPARAM [P_PASSWD_MARGIN] = {"passwordmargin", 3}, [P_MENU_ROWS] = {"rows", 12}, [P_TABMSG_ROW] = {"tabmsgrow", 18}, + [P_LOADMSG_ROW] = {"loadmsgrow", 18}, [P_CMDLINE_ROW] = {"cmdlinerow", 18}, [P_END_ROW] = {"endrow", -1}, [P_PASSWD_ROW] = {"passwordrow", 11}, @@ -52,22 +55,6 @@ const struct menu_parameter mparm[NPARAM [P_HIDDEN_ROW] = {"hiddenrow", -2}, }; -/* These macros assume "cm" is a pointer to the current menu */ -#define WIDTH (cm->mparm[P_WIDTH]) -#define MARGIN (cm->mparm[P_MARGIN]) -#define PASSWD_MARGIN (cm->mparm[P_PASSWD_MARGIN]) -#define MENU_ROWS (cm->mparm[P_MENU_ROWS]) -#define TABMSG_ROW (cm->mparm[P_TABMSG_ROW]+VSHIFT) -#define CMDLINE_ROW (cm->mparm[P_CMDLINE_ROW]+VSHIFT) -#define END_ROW (cm->mparm[P_END_ROW]) -#define PASSWD_ROW (cm->mparm[P_PASSWD_ROW]+VSHIFT) -#define TIMEOUT_ROW (cm->mparm[P_TIMEOUT_ROW]+VSHIFT) -#define HELPMSG_ROW (cm->mparm[P_HELPMSG_ROW]+VSHIFT) -#define HELPMSGEND_ROW (cm->mparm[P_HELPMSGEND_ROW]) -#define HSHIFT (cm->mparm[P_HSHIFT]) -#define VSHIFT (cm->mparm[P_VSHIFT]) -#define HIDDEN_ROW (cm->mparm[P_HIDDEN_ROW]) - static char *pad_line(const char *text, int align, int width) { static char buffer[MAX_CMDLINE_LEN]; diff -uprN syslinux-3.86-vanilla//com32/menu/readconfig.c syslinux-3.86/com32/menu/readconfig.c --- syslinux-3.86-vanilla//com32/menu/readconfig.c 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/menu/readconfig.c 2010-04-15 09:36:23.000000000 -0500 @@ -49,6 +49,7 @@ static struct menu_entry **all_entries_e static const struct messages messages[MSG_COUNT] = { [MSG_AUTOBOOT] = {"autoboot", "Automatic boot in # second{,s}..."}, [MSG_TAB] = {"tabmsg", "Press [Tab] to edit options"}, + [MSG_LOAD] = {"loadmsg", "Loading"}, [MSG_NOTAB] = {"notabmsg", ""}, [MSG_PASSPROMPT] = {"passprompt", "Password required"}, }; diff -uprN syslinux-3.86-vanilla//com32/menu/vesamenu.c syslinux-3.86/com32/menu/vesamenu.c --- syslinux-3.86-vanilla//com32/menu/vesamenu.c 2010-03-31 11:24:25.000000000 -0500 +++ syslinux-3.86/com32/menu/vesamenu.c 2010-04-15 13:05:52.000000000 -0500 @@ -22,6 +22,7 @@ #include <stdio.h> #include <console.h> +#include <syslinux/video.h> #include <syslinux/vesacon.h> #include "menu.h" @@ -50,3 +51,17 @@ void start_console(void) { openconsole(&dev_rawcon_r, &dev_vesaserial_w); } + +void draw_progress(const char *file, size_t cur, size_t total) +{ + int i; + float percent, bars, fill; + percent = ((float)cur / (float)total) * 100.0; + + const char* loadmsg = cm->messages[MSG_LOAD]; + int loadmsg_len = strlen(loadmsg); + + printf("\033[%d;%dH\2#14%s %d%%", + LOADMSG_ROW, 1 + HSHIFT + ((WIDTH - loadmsg_len) >> 1), loadmsg, (int)percent); +} +
H. Peter Anvin
2010-Apr-23 18:43 UTC
[syslinux] Path simple menu integrated progress indicator
On 04/23/2010 08:41 AM, Ayvaz, James wrote:> This patch modifies the simple menu com32 program to include integrated load progress. It also adds new options to the simple menu configuration > > MENU LOADMSG -- allows the user to specify the text to display when loading > MENU LOADMSGROW -- controls where the text is written > > Example configuration: > MENU LOADMSG Booting > MENU LOADMSGROW 25 > > > It works with my setup, please let me know if you find any bugs or spot any problems. Thank you. >Overall good work, but there are some technical details of this that I really don't like. a) It is totally broken for anything which isn't a Linux kernel. Say, for example, that someone is trying to boot Xen via mboot.c32. This patch will make it try to load mboot.c32 as if it had been a Linux kernel! b) It duplicates a lot of code already in linux.c32, but doesn't put it in a library. Instead it open-codes it in execute.c. c) I'm not sure that it makes sense to have a different function for loadfile with callback. In fact, a set of sensible callbacks is also a requirement for the future of gfxboot, and I'm thinking more that it might make more sense to have an interface that looks more like: register_callback(CB_LOADFILE, mycallback); /* ... possible multiple intervening layers ... */ loadfile(); register_callback(CB_LOADFILE, NULL); ... or something like that. One of the questions, I guess, is if there is ever a need for *multiple* callbacks. I suspect it would make life a little simpler if there isn't. -hpa