[syslinux] Patch sensible callback framework

Sebastian Herbszt herbszt at gmx.de
Tue Apr 27 12:22:54 PDT 2010


Ayvaz, James wrote:
> This patch adds a simple callback framework.
>
> Modified loadfile and floadfile to look for callbacks and call them if present
>
> Supports multiple callbacks
>
> Modified com32/modules/linux.c to demonstrate functionality (it's a little more complicated than it should be just to 
> demonstrate multiple callbacks).  Add progress argument to display a percentage indicator when loading.
>
> Example
>
> boot: linux progress
> Loading vmlinuz 100%
> Loading initrd.img 100%
>
>
> Let me know if you spot any problems.  Thanks.
>
>
> diff -uprN syslinux-3.86-vanilla/com32/include/syslinux/callback.h syslinux-3.86/com32/include/syslinux/callback.h
> --- syslinux-3.86-vanilla/com32/include/syslinux/callback.h 1969-12-31 18:00:00.000000000 -0600
> +++ syslinux-3.86/com32/include/syslinux/callback.h 2010-04-26 09:05:45.000000000 -0500
> @@ -0,0 +1,39 @@
> +#ifndef LIBUTIL_CALLBACK_H
> +#define LIBUTIL_CALLBACK_H
> +
> +#include <stddef.h>
> +#include <stdio.h>
> +#include <inttypes.h>
> +
> +/* supported callback types */
> +#define CB_LOADFILE    1
> +#define CB_FLOADFILE   2
> +
> +/* callback erros types */
> +#define CBE_SUCCESS             0
> +#define CBE_FAILED              1
> +#define CBE_ALREADY_REGISTERED  2
> +#define CBE_NOT_REGISTERED      3
> +
> +/* supported callback types */
> +#define CB_LOADFILE    1
> +#define CB_FLOADFILE   2

defined twice?

> +
> +
> +typedef struct callback_record {
> +  uint16_t type;
> +  void *function;
> +  struct callback_record *next;
> +} callback_record;
> +
> +typedef void (*cb_loadfile_t)(const char* file, size_t cur, size_t total);
> +typedef void (*cb_floadfile_t)(size_t cur, size_t total);
> +
> +
> +int register_callback(uint16_t type, void *callback);
> +int unregister_callback(uint16_t type, void *callback);
> +
> +callback_record* foreach_callback(callback_record *current);
> +callback_record* foreach_callback_type(callback_record *current, uint16_t type);
> +
> +#endif
> diff -uprN syslinux-3.86-vanilla/com32/lib/Makefile syslinux-3.86/com32/lib/Makefile
> --- syslinux-3.86-vanilla/com32/lib/Makefile 2010-03-31 11:24:25.000000000 -0500
> +++ syslinux-3.86/com32/lib/Makefile 2010-04-26 04:57:20.000000000 -0500
> @@ -102,6 +102,8 @@ LIBOBJS = \
>  syslinux/run_default.o syslinux/run_command.o \
>  syslinux/cleanup.o syslinux/localboot.o syslinux/runimage.o \
>  \
> + syslinux/callback.o                                             \
> + \
>  syslinux/loadfile.o syslinux/floadfile.o syslinux/zloadfile.o \
>  \
>  syslinux/load_linux.o syslinux/initramfs.o \
> diff -uprN syslinux-3.86-vanilla/com32/lib/syslinux/callback.c syslinux-3.86/com32/lib/syslinux/callback.c
> --- syslinux-3.86-vanilla/com32/lib/syslinux/callback.c 1969-12-31 18:00:00.000000000 -0600
> +++ syslinux-3.86/com32/lib/syslinux/callback.c 2010-04-26 16:39:52.000000000 -0500
> @@ -0,0 +1,125 @@
> +/* ----------------------------------------------------------------------- *
> + *
> + *   Copyright 2005-2008 H. Peter Anvin - All Rights Reserved
> + *
> + *   Permission is hereby granted, free of charge, to any person
> + *   obtaining a copy of this software and associated documentation
> + *   files (the "Software"), to deal in the Software without
> + *   restriction, including without limitation the rights to use,
> + *   copy, modify, merge, publish, distribute, sublicense, and/or
> + *   sell copies of the Software, and to permit persons to whom
> + *   the Software is furnished to do so, subject to the following
> + *   conditions:
> + *
> + *   The above copyright notice and this permission notice shall
> + *   be included in all copies or substantial portions of the Software.
> + *
> + *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
> + *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
> + *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
> + *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
> + *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
> + *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
> + *   OTHER DEALINGS IN THE SOFTWARE.
> + *
> + * ----------------------------------------------------------------------- */
> +
> +/*
> + * callback.c
> + *
> + * generic callback handlers
> + */
> +
> +#include <errno.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +#include <string.h>
> +
> +#include "syslinux/callback.h"
> +
> +callback_record *callback_head = NULL;
> +
> +int register_callback(uint16_t type, void *callback) {
> +    callback_record *curr;
> +    if (callback_head) {
> +        /* check to see if we're already registered */
> +        curr = callback_head;
> +        while (curr->next) {
> +            if (curr->type == type && curr->function == callback) {
> +                return CBE_ALREADY_REGISTERED;
> +            }
> +            curr = curr->next;
> +        }
> +    }
> +
> +    callback_record *new = malloc(sizeof(callback_record));
> +    if (!new) {
> +        return CBE_FAILED;
> +    }
> +    memset(new, 0, sizeof(callback_record));
> +
> +    new->type = type;
> +    new->function = callback;
> +    new->next = NULL;
> +
> +    if (!callback_head) {
> +        callback_head = new;
> +    }
> +    else {
> +        curr = callback_head;
> +        while (curr->next != NULL) {curr = curr->next;} /* go to end of list */
> +        curr->next = new;
> +    }
> +    return CBE_SUCCESS;
> +}
> +
> +int unregister_callback(uint16_t type, void *callback) {
> +    callback_record *curr, *prev, *next;
> +    bool found = false;
> +    if (!callback_head)
> +        return CBE_NOT_REGISTERED;
> +
> +    prev = NULL;
> +    curr = callback_head;
> +    while (curr) {
> +        if (curr->type == type && curr->function == callback) {
> +            found++;
> +            next = curr->next;
> +
> +            if (prev)
> +                prev->next = next;
> +
> +            free(curr);
> +            if (callback_head == curr)
> +                callback_head = next;
> +            curr = next;
> +        }
> +        else {
> +            prev = curr;
> +            curr = curr->next;
> +        }
> +    }
> +    if (!found)
> +        return CBE_NOT_REGISTERED;
> +    else
> +        return CBE_SUCCESS;
> +}
> +
> +callback_record* foreach_callback(callback_record *current) {
> +    if (!current) {
> +        return callback_head;
> +    }
> +
> +    return current->next;
> +}
> +
> +callback_record* foreach_callback_type(callback_record *current, uint16_t type) {
> +    while((current = foreach_callback(current))) {
> +        if (current->type == type) {
> +            return current;
> +        }
> +    }
> +    return current;
> +}
> 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-26 16:40:30.000000000 -0500
> @@ -38,6 +38,7 @@
> #include <fcntl.h>
> #include <sys/stat.h>
>
> +#include <syslinux/callback.h>
> #include <syslinux/loadfile.h>
>
> #define INCREMENTAL_CHUNK 1024*1024
> @@ -48,13 +49,13 @@ int floadfile(FILE * f, void **ptr, size
>     struct stat st;
>     void *data, *dp;
>     size_t alen, clen, rlen, xlen;
> +    callback_record *cb = NULL;
>
>     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 */
>  if (prefix_len) {
> @@ -75,6 +76,11 @@ int floadfile(FILE * f, void **ptr, size
>
>      rlen = fread((char *)data + clen, 1, alen - clen, f);
>      clen += rlen;
> +
> +     cb = NULL;
> +     while((cb = foreach_callback_type(cb, CB_FLOADFILE))) {
> +         ((cb_floadfile_t)cb->function)(clen, -1);
> +     }
>  } while (clen == alen);
>
>  *len = clen;
> @@ -93,11 +99,32 @@ int floadfile(FILE * f, void **ptr, size
>
>  memcpy(data, prefix, prefix_len);
>
> - if ((off_t) fread((char *)data + prefix_len, 1, clen - prefix_len, f)
> -     != clen - prefix_len)
> -     goto err;
> +        /* a callback is registered so read file in chunks */
> +        if (foreach_callback_type(NULL, CB_FLOADFILE)) {
> +     clen = alen = 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;
> +         cb = NULL;
> +         while((cb = foreach_callback_type(cb, CB_FLOADFILE))) {
> +             ((cb_floadfile_t)cb->function)((clen + LOADFILE_ZERO_PAD - 1) & ~(LOADFILE_ZERO_PAD - 1), xlen);
> +         }
> +     } while (clen == alen);
> +     *ptr = data;
> +        }
> +        /* read whole file at once */
> +        else {
> +            if ((off_t) fread((char *)data + prefix_len, 1, clen - prefix_len, f)
> +         != clen - prefix_len)
> +                goto err;
> +        }
>     }
> -
>     memset((char *)data + clen, 0, xlen - clen);
>     return 0;
>
> 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-26 16:40:13.000000000 -0500
> @@ -39,10 +39,21 @@
> #include <fcntl.h>
> #include <sys/stat.h>
>
> +#include <syslinux/callback.h>
> #include <syslinux/loadfile.h>
>
> #define INCREMENTAL_CHUNK 1024*1024

1 MB chunk size is pretty huge. gfxboot currently uses 64k to display the progress.

> +char *curr_filename = NULL;
> +
> +void loadfile_cb(size_t cur, size_t total) {
> +    callback_record *cb = NULL;
> +    while(cb = foreach_callback_type(cb, CB_LOADFILE)) {
> +        ((cb_loadfile_t)cb->function)(curr_filename, cur, total);
> +    }
> +}
> +
> +
> int loadfile(const char *filename, void **ptr, size_t * len)
> {
>     FILE *f;
> @@ -52,7 +63,20 @@ int loadfile(const char *filename, void
>     if (!f)
>  return -1;
>
> +    curr_filename = filename;
> +    /*
> +     * if a CB_LOADFILE callback is registered, let's register a CB_FLOADFILE callback
> +     * so that we can can add the filename
> +     */

can can?

> +    callback_record *cb = NULL;
> +    if (foreach_callback_type(cb, CB_LOADFILE)) {
> +        register_callback(CB_FLOADFILE, loadfile_cb);
> +    }
>     rv = floadfile(f, ptr, len, NULL, 0);
> +
> +    /* unregister our callback */
> +    unregister_callback(CB_FLOADFILE, loadfile_cb);
> +
>     e = errno;
>
>     fclose(f);
> diff -uprN syslinux-3.86-vanilla/com32/modules/linux.c syslinux-3.86/com32/modules/linux.c
> --- syslinux-3.86-vanilla/com32/modules/linux.c 2010-03-31 11:24:25.000000000 -0500
> +++ syslinux-3.86/com32/modules/linux.c 2010-04-26 16:52:52.000000000 -0500
> @@ -43,6 +43,7 @@
> #include <stdio.h>
> #include <string.h>
> #include <console.h>
> +#include <syslinux/callback.h>
> #include <syslinux/loadfile.h>
> #include <syslinux/linux.h>
> #include <syslinux/pxe.h>
> @@ -108,6 +109,32 @@ static char *make_cmdline(char **argv)
>     return cmdline;
> }
>
> +void linux_percent_progress_cb(const char* file, size_t cur, size_t total) {
> +    int percent = (int)(((float)cur / (float)total) * 100.0);
> +    clear_line();
> +    move_cursor_to_column(0);
> +    printf("Loading: %s %d%%", file, percent);
> +}
> +
> +void linux_dot_progress_cb(const char* file, size_t cur, size_t total) {
> +    int i = 0;
> +    int percent = (int)(((float)cur / (float)total) * 100.0);
> +    clear_line();
> +    move_cursor_to_column(0);
> +
> +    printf("Loading %s", file);
> +    while(i < percent) {
> +        printf(".");
> +        i += 5;
> +    }
> +}
> +
> +void linux_done_progress_cb(const char* file, size_t cur, size_t total) {
> +    int percent = (int)(((float)cur / (float)total) * 100.0);
> +    if (percent >= 100)
> +        printf(" OK\n");
> +}
> +
> int main(int argc, char *argv[])
> {
>     const char *kernel_name;
> @@ -118,11 +145,13 @@ int main(int argc, char *argv[])
>     size_t kernel_len;
>     bool opt_dhcpinfo = false;
>     bool opt_quiet = false;
> +    bool opt_percent = false;
>     void *dhcpdata;
>     size_t dhcplen;
>     char **argp, *arg, *p;
>
> -    openconsole(&dev_null_r, &dev_stdcon_w);
> +
> +    console_ansi_raw();
>
>     (void)argc;
>     argp = argv + 1;
> @@ -157,16 +186,23 @@ int main(int argc, char *argv[])
>     if (find_boolean(argp, "quiet"))
>  opt_quiet = true;
>
> -    if (!opt_quiet)
> - printf("Loading %s... ", kernel_name);
> +    if (find_boolean(argp, "percent"))
> + opt_percent = true;

Maybe use "-percent" and check it in the while loop above (same as -dhcpinfo) ?
Else "percent" will be passed to the linux kernel, won't it?

> +    if (!opt_quiet) {
> +        if (opt_percent) {
> +            register_callback(CB_LOADFILE, linux_percent_progress_cb);
> +        }
> +        else {
> +            register_callback(CB_LOADFILE, linux_dot_progress_cb);
> +        }
> +        register_callback(CB_LOADFILE, linux_done_progress_cb);
> +    }
>     if (loadfile(kernel_name, &kernel_data, &kernel_len)) {
>  if (opt_quiet)
>      printf("Loading %s ", kernel_name);
>  printf("failed!\n");
>  goto bail;
>     }
> -    if (!opt_quiet)
> - printf("ok\n");
>
>     cmdline = make_cmdline(argp);
>     if (!cmdline)
> @@ -183,22 +219,24 @@ int main(int argc, char *argv[])
>      if (p)
>  *p = '\0';
>
> -     if (!opt_quiet)
> - printf("Loading %s... ", arg);
>      if (initramfs_load_archive(initramfs, arg)) {
>  if (opt_quiet)
>      printf("Loading %s ", kernel_name);
>  printf("failed!\n");
>  goto bail;
>      }
> -     if (!opt_quiet)
> - printf("ok\n");
>
>      if (p)
>  *p++ = ',';
>  } while ((arg = p));
>     }
>
> +    if (!opt_quiet) {
> +        unregister_callback(CB_LOADFILE, linux_percent_progress_cb);
> +        unregister_callback(CB_LOADFILE, linux_dot_progress_cb);
> +        unregister_callback(CB_LOADFILE, linux_done_progress_cb);
> +    }
> +
>     /* Append the DHCP info */
>     if (opt_dhcpinfo &&
>  !pxe_get_cached_info(PXENV_PACKET_TYPE_DHCP_ACK, &dhcpdata, &dhcplen)) {
>

Sebastian




More information about the Syslinux mailing list