|
|
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.