summaryrefslogtreecommitdiffstats
path: root/src/image/script.c
diff options
context:
space:
mode:
authorMichael Brown2010-11-22 05:19:24 +0100
committerMichael Brown2010-11-22 05:27:26 +0100
commit6d68ffee396af30c14b971ea677ba6e790695926 (patch)
tree7762d472d4f0128b9266b8f621d48afb31f05017 /src/image/script.c
parent[cmdline] Add trivial logical operators to iPXE command lines (diff)
downloadipxe-6d68ffee396af30c14b971ea677ba6e790695926.tar.gz
ipxe-6d68ffee396af30c14b971ea677ba6e790695926.tar.xz
ipxe-6d68ffee396af30c14b971ea677ba6e790695926.zip
[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.c214
1 files changed, 189 insertions, 25 deletions
diff --git a/src/image/script.c b/src/image/script.c
index b65fa061..ba098c2c 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,
+};