diff options
author | Michael Brown <mcb30@ipxe.org> | 2010-11-22 04:19:24 +0000 |
---|---|---|
committer | Michael Brown <mcb30@ipxe.org> | 2010-11-22 04:27:26 +0000 |
commit | 6d68ffee396af30c14b971ea677ba6e790695926 (patch) | |
tree | 7762d472d4f0128b9266b8f621d48afb31f05017 /src/image/script.c | |
parent | 9ba988809d2bae422fe36d5e6c8563b3cd6cac55 (diff) | |
download | ipxe-6d68ffee396af30c14b971ea677ba6e790695926.tar.gz |
[script] Implement "goto" in iPXE scripts
Allow script labels to be defined using the syntax
:<labelname>
(nothing else allowed on the line, including whitespace). Labels are
ignored during script execution, but can be used as the target of the
"goto" command. For example:
#!ipxe
goto machine_${net0/ip} || goto machine_default
# Linux kernel boot
:machine_10.0.0.101
:machine_10.0.0.102
set filename http://my.boot.server/vmlinuz
goto done
# Default configuration
:machine_default
set filename pxelinux.0
goto done
# Boot selected configuration
:done
chain ${filename}
Originally-implemented-by: Shao Miller <shao.miller@yrdsb.edu.on.ca>
Originally-implemented-by: Stefan Hajnoczi <stefanha@gmail.com>
Signed-off-by: Michael Brown <mcb30@ipxe.org>
Diffstat (limited to 'src/image/script.c')
-rw-r--r-- | src/image/script.c | 214 |
1 files changed, 189 insertions, 25 deletions
diff --git a/src/image/script.c b/src/image/script.c index b65fa061f..ba098c2c1 100644 --- a/src/image/script.c +++ b/src/image/script.c @@ -27,59 +27,148 @@ FILE_LICENCE ( GPL2_OR_LATER ); #include <string.h> #include <stdlib.h> +#include <stdio.h> #include <ctype.h> #include <errno.h> +#include <getopt.h> +#include <ipxe/command.h> +#include <ipxe/parseopt.h> #include <ipxe/image.h> struct image_type script_image_type __image_type ( PROBE_NORMAL ); +/** Currently running script + * + * This is a global in order to allow goto_exec() to update the + * offset. + */ +static struct image *script; + +/** Offset within current script + * + * This is a global in order to allow goto_exec() to update the + * offset. + */ +static size_t script_offset; + /** - * Execute script + * Process script lines * - * @v image Script + * @v process_line Line processor + * @v terminate Termination check * @ret rc Return status code */ -static int script_exec ( struct image *image ) { - size_t offset = 0; +static int process_script ( int ( * process_line ) ( const char *line ), + int ( * terminate ) ( int rc ) ) { off_t eol; size_t len; int rc; - /* Temporarily de-register image, so that a "boot" command - * doesn't throw us into an execution loop. - */ - unregister_image ( image ); + script_offset = 0; - while ( offset < image->len ) { + do { /* Find length of next line, excluding any terminating '\n' */ - eol = memchr_user ( image->data, offset, '\n', - ( image->len - offset ) ); + eol = memchr_user ( script->data, script_offset, '\n', + ( script->len - script_offset ) ); if ( eol < 0 ) - eol = image->len; - len = ( eol - offset ); + eol = script->len; + len = ( eol - script_offset ); /* Copy line, terminate with NUL, and execute command */ { char cmdbuf[ len + 1 ]; - copy_from_user ( cmdbuf, image->data, offset, len ); + copy_from_user ( cmdbuf, script->data, + script_offset, len ); cmdbuf[len] = '\0'; DBG ( "$ %s\n", cmdbuf ); - if ( ( rc = system ( cmdbuf ) ) != 0 ) { - DBG ( "Command \"%s\" failed: %s\n", - cmdbuf, strerror ( rc ) ); - goto done; - } + + /* Move to next line */ + script_offset += ( len + 1 ); + + /* Process line */ + rc = process_line ( cmdbuf ); + if ( terminate ( rc ) ) + return rc; } - - /* Move to next line */ - offset += ( len + 1 ); + + } while ( script_offset < script->len ); + + return rc; +} + +/** + * Terminate script processing if line processing failed + * + * @v rc Line processing status + * @ret terminate Terminate script processing + */ +static int terminate_on_failure ( int rc ) { + return ( rc != 0 ); +} + +/** + * Terminate script processing if line processing succeeded + * + * @v rc Line processing status + * @ret terminate Terminate script processing + */ +static int terminate_on_success ( int rc ) { + return ( rc == 0 ); +} + +/** + * Execute script line + * + * @v line Line of script + * @ret rc Return status code + */ +static int script_exec_line ( const char *line ) { + int rc; + + /* Skip label lines */ + if ( line[0] == ':' ) + return 0; + + /* Execute command */ + if ( ( rc = system ( line ) ) != 0 ) { + printf ( "Aborting on \"%s\"\n", line ); + return rc; } - rc = 0; - done: - /* Re-register image and return */ + return 0; +} + +/** + * Execute script + * + * @v image Script + * @ret rc Return status code + */ +static int script_exec ( struct image *image ) { + struct image *saved_script; + size_t saved_offset; + int rc; + + /* Temporarily de-register image, so that a "boot" command + * doesn't throw us into an execution loop. + */ + unregister_image ( image ); + + /* Preserve state of any currently-running script */ + saved_script = script; + saved_offset = script_offset; + + /* Initialise state for this script */ + script = image; + + /* Process script */ + rc = process_script ( script_exec_line, terminate_on_failure ); + + /* Restore saved state, re-register image, and return */ + script_offset = saved_offset; + script = saved_script; register_image ( image ); return rc; } @@ -129,3 +218,78 @@ struct image_type script_image_type __image_type ( PROBE_NORMAL ) = { .load = script_load, .exec = script_exec, }; + +/** "goto" options */ +struct goto_options {}; + +/** "goto" option list */ +static struct option_descriptor goto_opts[] = {}; + +/** "goto" command descriptor */ +static struct command_descriptor goto_cmd = + COMMAND_DESC ( struct goto_options, goto_opts, 1, 1, + "<label>", "" ); + +/** + * Current "goto" label + * + * Valid only during goto_exec(). Consider this part of a closure. + */ +static const char *goto_label; + +/** + * Check for presence of label + * + * @v line Script line + * @ret rc Return status code + */ +static int goto_find_label ( const char *line ) { + + if ( line[0] != ':' ) + return -ENOENT; + if ( strcmp ( goto_label, &line[1] ) != 0 ) + return -ENOENT; + return 0; +} + +/** + * "goto" command + * + * @v argc Argument count + * @v argv Argument list + * @ret rc Return status code + */ +static int goto_exec ( int argc, char **argv ) { + struct goto_options opts; + size_t saved_offset; + int rc; + + /* Parse options */ + if ( ( rc = parse_options ( argc, argv, &goto_cmd, &opts ) ) != 0 ) + return rc; + + /* Sanity check */ + if ( ! script ) { + printf ( "Not in a script\n" ); + return -ENOTTY; + } + + /* Parse label */ + goto_label = argv[optind]; + + /* Find label */ + saved_offset = script_offset; + if ( ( rc = process_script ( goto_find_label, + terminate_on_success ) ) != 0 ) { + script_offset = saved_offset; + return rc; + } + + return 0; +} + +/** "goto" command */ +struct command goto_command __command = { + .name = "goto", + .exec = goto_exec, +}; |