소스 검색

[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>
tags/v1.20.1
Michael Brown 14 년 전
부모
커밋
6d68ffee39
1개의 변경된 파일189개의 추가작업 그리고 25개의 파일을 삭제
  1. 189
    25
      src/image/script.c

+ 189
- 25
src/image/script.c 파일 보기

@@ -27,59 +27,148 @@ FILE_LICENCE ( GPL2_OR_LATER );
27 27
 
28 28
 #include <string.h>
29 29
 #include <stdlib.h>
30
+#include <stdio.h>
30 31
 #include <ctype.h>
31 32
 #include <errno.h>
33
+#include <getopt.h>
34
+#include <ipxe/command.h>
35
+#include <ipxe/parseopt.h>
32 36
 #include <ipxe/image.h>
33 37
 
34 38
 struct image_type script_image_type __image_type ( PROBE_NORMAL );
35 39
 
40
+/** Currently running script
41
+ *
42
+ * This is a global in order to allow goto_exec() to update the
43
+ * offset.
44
+ */
45
+static struct image *script;
46
+
47
+/** Offset within current script
48
+ *
49
+ * This is a global in order to allow goto_exec() to update the
50
+ * offset.
51
+ */
52
+static size_t script_offset;
53
+
36 54
 /**
37
- * Execute script
55
+ * Process script lines
38 56
  *
39
- * @v image		Script
57
+ * @v process_line	Line processor
58
+ * @v terminate		Termination check
40 59
  * @ret rc		Return status code
41 60
  */
42
-static int script_exec ( struct image *image ) {
43
-	size_t offset = 0;
61
+static int process_script ( int ( * process_line ) ( const char *line ),
62
+			    int ( * terminate ) ( int rc ) ) {
44 63
 	off_t eol;
45 64
 	size_t len;
46 65
 	int rc;
47 66
 
48
-	/* Temporarily de-register image, so that a "boot" command
49
-	 * doesn't throw us into an execution loop.
50
-	 */
51
-	unregister_image ( image );
67
+	script_offset = 0;
52 68
 
53
-	while ( offset < image->len ) {
69
+	do {
54 70
 	
55 71
 		/* Find length of next line, excluding any terminating '\n' */
56
-		eol = memchr_user ( image->data, offset, '\n',
57
-				    ( image->len - offset ) );
72
+		eol = memchr_user ( script->data, script_offset, '\n',
73
+				    ( script->len - script_offset ) );
58 74
 		if ( eol < 0 )
59
-			eol = image->len;
60
-		len = ( eol - offset );
75
+			eol = script->len;
76
+		len = ( eol - script_offset );
61 77
 
62 78
 		/* Copy line, terminate with NUL, and execute command */
63 79
 		{
64 80
 			char cmdbuf[ len + 1 ];
65 81
 
66
-			copy_from_user ( cmdbuf, image->data, offset, len );
82
+			copy_from_user ( cmdbuf, script->data,
83
+					 script_offset, len );
67 84
 			cmdbuf[len] = '\0';
68 85
 			DBG ( "$ %s\n", cmdbuf );
69
-			if ( ( rc = system ( cmdbuf ) ) != 0 ) {
70
-				DBG ( "Command \"%s\" failed: %s\n",
71
-				      cmdbuf, strerror ( rc ) );
72
-				goto done;
73
-			}
86
+
87
+			/* Move to next line */
88
+			script_offset += ( len + 1 );
89
+
90
+			/* Process line */
91
+			rc = process_line ( cmdbuf );
92
+			if ( terminate ( rc ) )
93
+				return rc;
74 94
 		}
75
-		
76
-		/* Move to next line */
77
-		offset += ( len + 1 );
95
+
96
+	} while ( script_offset < script->len );
97
+
98
+	return rc;
99
+}
100
+
101
+/**
102
+ * Terminate script processing if line processing failed
103
+ *
104
+ * @v rc		Line processing status
105
+ * @ret terminate	Terminate script processing
106
+ */
107
+static int terminate_on_failure ( int rc ) {
108
+	return ( rc != 0 );
109
+}
110
+
111
+/**
112
+ * Terminate script processing if line processing succeeded
113
+ *
114
+ * @v rc		Line processing status
115
+ * @ret terminate	Terminate script processing
116
+ */
117
+static int terminate_on_success ( int rc ) {
118
+	return ( rc == 0 );
119
+}
120
+
121
+/**
122
+ * Execute script line
123
+ *
124
+ * @v line		Line of script
125
+ * @ret rc		Return status code
126
+ */
127
+static int script_exec_line ( const char *line ) {
128
+	int rc;
129
+
130
+	/* Skip label lines */
131
+	if ( line[0] == ':' )
132
+		return 0;
133
+
134
+	/* Execute command */
135
+	if ( ( rc = system ( line ) ) != 0 ) {
136
+		printf ( "Aborting on \"%s\"\n", line );
137
+		return rc;
78 138
 	}
79 139
 
80
-	rc = 0;
81
- done:
82
-	/* Re-register image and return */
140
+	return 0;
141
+}
142
+
143
+/**
144
+ * Execute script
145
+ *
146
+ * @v image		Script
147
+ * @ret rc		Return status code
148
+ */
149
+static int script_exec ( struct image *image ) {
150
+	struct image *saved_script;
151
+	size_t saved_offset;
152
+	int rc;
153
+
154
+	/* Temporarily de-register image, so that a "boot" command
155
+	 * doesn't throw us into an execution loop.
156
+	 */
157
+	unregister_image ( image );
158
+
159
+	/* Preserve state of any currently-running script */
160
+	saved_script = script;
161
+	saved_offset = script_offset;
162
+
163
+	/* Initialise state for this script */
164
+	script = image;
165
+
166
+	/* Process script */
167
+	rc = process_script ( script_exec_line, terminate_on_failure );
168
+
169
+	/* Restore saved state, re-register image, and return */
170
+	script_offset = saved_offset;
171
+	script = saved_script;
83 172
 	register_image ( image );
84 173
 	return rc;
85 174
 }
@@ -129,3 +218,78 @@ struct image_type script_image_type __image_type ( PROBE_NORMAL ) = {
129 218
 	.load = script_load,
130 219
 	.exec = script_exec,
131 220
 };
221
+
222
+/** "goto" options */
223
+struct goto_options {};
224
+
225
+/** "goto" option list */
226
+static struct option_descriptor goto_opts[] = {};
227
+
228
+/** "goto" command descriptor */
229
+static struct command_descriptor goto_cmd =
230
+	COMMAND_DESC ( struct goto_options, goto_opts, 1, 1,
231
+		       "<label>", "" );
232
+
233
+/**
234
+ * Current "goto" label
235
+ *
236
+ * Valid only during goto_exec().  Consider this part of a closure.
237
+ */
238
+static const char *goto_label;
239
+
240
+/**
241
+ * Check for presence of label
242
+ *
243
+ * @v line		Script line
244
+ * @ret rc		Return status code
245
+ */
246
+static int goto_find_label ( const char *line ) {
247
+
248
+	if ( line[0] != ':' )
249
+		return -ENOENT;
250
+	if ( strcmp ( goto_label, &line[1] ) != 0 )
251
+		return -ENOENT;
252
+	return 0;
253
+}
254
+
255
+/**
256
+ * "goto" command
257
+ *
258
+ * @v argc		Argument count
259
+ * @v argv		Argument list
260
+ * @ret rc		Return status code
261
+ */
262
+static int goto_exec ( int argc, char **argv ) {
263
+	struct goto_options opts;
264
+	size_t saved_offset;
265
+	int rc;
266
+
267
+	/* Parse options */
268
+	if ( ( rc = parse_options ( argc, argv, &goto_cmd, &opts ) ) != 0 )
269
+		return rc;
270
+
271
+	/* Sanity check */
272
+	if ( ! script ) {
273
+		printf ( "Not in a script\n" );
274
+		return -ENOTTY;
275
+	}
276
+
277
+	/* Parse label */
278
+	goto_label = argv[optind];
279
+
280
+	/* Find label */
281
+	saved_offset = script_offset;
282
+	if ( ( rc = process_script ( goto_find_label,
283
+				     terminate_on_success ) ) != 0 ) {
284
+		script_offset = saved_offset;
285
+		return rc;
286
+	}
287
+
288
+	return 0;
289
+}
290
+
291
+/** "goto" command */
292
+struct command goto_command __command = {
293
+	.name = "goto",
294
+	.exec = goto_exec,
295
+};

Loading…
취소
저장