|
|
1.1 root 1: /*
2: * Copyright (C) 2007 Michael Brown <[email protected]>.
3: *
4: * This program is free software; you can redistribute it and/or
5: * modify it under the terms of the GNU General Public License as
6: * published by the Free Software Foundation; either version 2 of the
7: * License, or any later version.
8: *
9: * This program is distributed in the hope that it will be useful, but
10: * WITHOUT ANY WARRANTY; without even the implied warranty of
11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12: * General Public License for more details.
13: *
14: * You should have received a copy of the GNU General Public License
15: * along with this program; if not, write to the Free Software
16: * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17: */
18:
19: FILE_LICENCE ( GPL2_OR_LATER );
20:
21: /**
22: * @file
23: *
24: * iPXE scripts
25: *
26: */
27:
28: #include <string.h>
29: #include <stdlib.h>
30: #include <stdio.h>
31: #include <ctype.h>
32: #include <errno.h>
33: #include <getopt.h>
34: #include <ipxe/command.h>
35: #include <ipxe/parseopt.h>
36: #include <ipxe/image.h>
37: #include <ipxe/shell.h>
38: #include <usr/prompt.h>
39: #include <ipxe/script.h>
40:
41: /** Offset within current script
42: *
43: * This is a global in order to allow goto_exec() to update the
44: * offset.
45: */
46: static size_t script_offset;
47:
48: /**
49: * Process script lines
50: *
51: * @v image Script
52: * @v process_line Line processor
53: * @v terminate Termination check
54: * @ret rc Return status code
55: */
56: static int process_script ( struct image *image,
57: int ( * process_line ) ( const char *line ),
58: int ( * terminate ) ( int rc ) ) {
59: off_t eol;
60: size_t len;
61: int rc;
62:
63: script_offset = 0;
64:
65: do {
66:
67: /* Find length of next line, excluding any terminating '\n' */
68: eol = memchr_user ( image->data, script_offset, '\n',
69: ( image->len - script_offset ) );
70: if ( eol < 0 )
71: eol = image->len;
72: len = ( eol - script_offset );
73:
74: /* Copy line, terminate with NUL, and execute command */
75: {
76: char cmdbuf[ len + 1 ];
77:
78: copy_from_user ( cmdbuf, image->data,
79: script_offset, len );
80: cmdbuf[len] = '\0';
81: DBG ( "$ %s\n", cmdbuf );
82:
83: /* Move to next line */
84: script_offset += ( len + 1 );
85:
86: /* Process line */
87: rc = process_line ( cmdbuf );
88: if ( terminate ( rc ) )
89: return rc;
90: }
91:
92: } while ( script_offset < image->len );
93:
94: return rc;
95: }
96:
97: /**
98: * Terminate script processing on shell exit or command failure
99: *
100: * @v rc Line processing status
101: * @ret terminate Terminate script processing
102: */
103: static int terminate_on_exit_or_failure ( int rc ) {
104:
105: return ( shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) ||
106: ( rc != 0 ) );
107: }
108:
109: /**
110: * Execute script line
111: *
112: * @v line Line of script
113: * @ret rc Return status code
114: */
115: static int script_exec_line ( const char *line ) {
116: int rc;
117:
118: /* Skip label lines */
119: if ( line[0] == ':' )
120: return 0;
121:
122: /* Execute command */
123: if ( ( rc = system ( line ) ) != 0 )
124: return rc;
125:
126: return 0;
127: }
128:
129: /**
130: * Execute script
131: *
132: * @v image Script
133: * @ret rc Return status code
134: */
135: static int script_exec ( struct image *image ) {
136: size_t saved_offset;
137: int rc;
138:
139: /* Temporarily de-register image, so that a "boot" command
140: * doesn't throw us into an execution loop.
141: */
142: unregister_image ( image );
143:
144: /* Preserve state of any currently-running script */
145: saved_offset = script_offset;
146:
147: /* Process script */
148: rc = process_script ( image, script_exec_line,
149: terminate_on_exit_or_failure );
150:
151: /* Restore saved state */
152: script_offset = saved_offset;
153:
154: /* Re-register image (unless we have been replaced) */
155: if ( ! image->replacement )
156: register_image ( image );
157:
158: return rc;
159: }
160:
161: /**
162: * Probe script image
163: *
164: * @v image Script
165: * @ret rc Return status code
166: */
167: static int script_probe ( struct image *image ) {
168: static const char ipxe_magic[] = "#!ipxe";
169: static const char gpxe_magic[] = "#!gpxe";
170: linker_assert ( sizeof ( ipxe_magic ) == sizeof ( gpxe_magic ),
171: magic_size_mismatch );
172: char test[ sizeof ( ipxe_magic ) - 1 /* NUL */
173: + 1 /* terminating space */];
174:
175: /* Sanity check */
176: if ( image->len < sizeof ( test ) ) {
177: DBG ( "Too short to be a script\n" );
178: return -ENOEXEC;
179: }
180:
181: /* Check for magic signature */
182: copy_from_user ( test, image->data, 0, sizeof ( test ) );
183: if ( ! ( ( ( memcmp ( test, ipxe_magic, sizeof ( test ) - 1 ) == 0 ) ||
184: ( memcmp ( test, gpxe_magic, sizeof ( test ) - 1 ) == 0 )) &&
185: isspace ( test[ sizeof ( test ) - 1 ] ) ) ) {
186: DBG ( "Invalid magic signature\n" );
187: return -ENOEXEC;
188: }
189:
190: return 0;
191: }
192:
193: /** Script image type */
194: struct image_type script_image_type __image_type ( PROBE_NORMAL ) = {
195: .name = "script",
196: .probe = script_probe,
197: .exec = script_exec,
198: };
199:
200: /** "goto" options */
201: struct goto_options {};
202:
203: /** "goto" option list */
204: static struct option_descriptor goto_opts[] = {};
205:
206: /** "goto" command descriptor */
207: static struct command_descriptor goto_cmd =
208: COMMAND_DESC ( struct goto_options, goto_opts, 1, 1, "<label>" );
209:
210: /**
211: * Current "goto" label
212: *
213: * Valid only during goto_exec(). Consider this part of a closure.
214: */
215: static const char *goto_label;
216:
217: /**
218: * Check for presence of label
219: *
220: * @v line Script line
221: * @ret rc Return status code
222: */
223: static int goto_find_label ( const char *line ) {
224:
225: if ( line[0] != ':' )
226: return -ENOENT;
227: if ( strcmp ( goto_label, &line[1] ) != 0 )
228: return -ENOENT;
229: return 0;
230: }
231:
232: /**
233: * Terminate script processing when label is found
234: *
235: * @v rc Line processing status
236: * @ret terminate Terminate script processing
237: */
238: static int terminate_on_label_found ( int rc ) {
239: return ( rc == 0 );
240: }
241:
242: /**
243: * "goto" command
244: *
245: * @v argc Argument count
246: * @v argv Argument list
247: * @ret rc Return status code
248: */
249: static int goto_exec ( int argc, char **argv ) {
250: struct goto_options opts;
251: size_t saved_offset;
252: int rc;
253:
254: /* Parse options */
255: if ( ( rc = parse_options ( argc, argv, &goto_cmd, &opts ) ) != 0 )
256: return rc;
257:
258: /* Sanity check */
259: if ( ! current_image ) {
260: rc = -ENOTTY;
261: printf ( "Not in a script: %s\n", strerror ( rc ) );
262: return rc;
263: }
264:
265: /* Parse label */
266: goto_label = argv[optind];
267:
268: /* Find label */
269: saved_offset = script_offset;
270: if ( ( rc = process_script ( current_image, goto_find_label,
271: terminate_on_label_found ) ) != 0 ) {
272: script_offset = saved_offset;
273: return rc;
274: }
275:
276: /* Terminate processing of current command */
277: shell_stop ( SHELL_STOP_COMMAND );
278:
279: return 0;
280: }
281:
282: /** "goto" command */
283: struct command goto_command __command = {
284: .name = "goto",
285: .exec = goto_exec,
286: };
287:
288: /** "prompt" options */
289: struct prompt_options {
290: /** Key to wait for */
291: unsigned int key;
292: /** Timeout */
293: unsigned int timeout;
294: };
295:
296: /** "prompt" option list */
297: static struct option_descriptor prompt_opts[] = {
298: OPTION_DESC ( "key", 'k', required_argument,
299: struct prompt_options, key, parse_integer ),
300: OPTION_DESC ( "timeout", 't', required_argument,
301: struct prompt_options, timeout, parse_integer ),
302: };
303:
304: /** "prompt" command descriptor */
305: static struct command_descriptor prompt_cmd =
306: COMMAND_DESC ( struct prompt_options, prompt_opts, 0, MAX_ARGUMENTS,
307: "[--key <key>] [--timeout <timeout>] [<text>]" );
308:
309: /**
310: * "prompt" command
311: *
312: * @v argc Argument count
313: * @v argv Argument list
314: * @ret rc Return status code
315: */
316: static int prompt_exec ( int argc, char **argv ) {
317: struct prompt_options opts;
318: char *text;
319: int rc;
320:
321: /* Parse options */
322: if ( ( rc = parse_options ( argc, argv, &prompt_cmd, &opts ) ) != 0 )
323: goto err_parse;
324:
325: /* Parse prompt text */
326: text = concat_args ( &argv[optind] );
327: if ( ! text ) {
328: rc = -ENOMEM;
329: goto err_concat;
330: }
331:
332: /* Display prompt and wait for key */
333: if ( ( rc = prompt ( text, opts.timeout, opts.key ) ) != 0 )
334: goto err_prompt;
335:
336: /* Free prompt text */
337: free ( text );
338:
339: return 0;
340:
341: err_prompt:
342: free ( text );
343: err_concat:
344: err_parse:
345: return rc;
346: }
347:
348: /** "prompt" command */
349: struct command prompt_command __command = {
350: .name = "prompt",
351: .exec = prompt_exec,
352: };
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.