~pmdj/ubuntu/trusty/qemu/2.9+applesmc+fadtv3

« back to all changes in this revision

Viewing changes to roms/ipxe/src/image/script.c

  • Committer: Phil Dennis-Jordan
  • Date: 2017-07-21 08:03:43 UTC
  • mfrom: (1.1.1)
  • Revision ID: phil@philjordan.eu-20170721080343-2yr2vdj7713czahv
New upstream release 2.9.0.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Copyright (C) 2007 Michael Brown <mbrown@fensystems.co.uk>.
 
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., 51 Franklin Street, Fifth Floor, Boston, MA
 
17
 * 02110-1301, USA.
 
18
 *
 
19
 * You can also choose to distribute this program under the terms of
 
20
 * the Unmodified Binary Distribution Licence (as given in the file
 
21
 * COPYING.UBDL), provided that you have satisfied its requirements.
 
22
 */
 
23
 
 
24
FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
 
25
 
 
26
/**
 
27
 * @file
 
28
 *
 
29
 * iPXE scripts
 
30
 *
 
31
 */
 
32
 
 
33
#include <string.h>
 
34
#include <stdlib.h>
 
35
#include <stdio.h>
 
36
#include <ctype.h>
 
37
#include <errno.h>
 
38
#include <getopt.h>
 
39
#include <ipxe/command.h>
 
40
#include <ipxe/parseopt.h>
 
41
#include <ipxe/image.h>
 
42
#include <ipxe/shell.h>
 
43
#include <usr/prompt.h>
 
44
#include <ipxe/script.h>
 
45
 
 
46
/** Offset within current script
 
47
 *
 
48
 * This is a global in order to allow goto_exec() to update the
 
49
 * offset.
 
50
 */
 
51
static size_t script_offset;
 
52
 
 
53
/**
 
54
 * Process script lines
 
55
 *
 
56
 * @v image             Script
 
57
 * @v process_line      Line processor
 
58
 * @v terminate         Termination check
 
59
 * @ret rc              Return status code
 
60
 */
 
61
static int process_script ( struct image *image,
 
62
                            int ( * process_line ) ( struct image *image,
 
63
                                                     size_t offset,
 
64
                                                     const char *label,
 
65
                                                     const char *command ),
 
66
                            int ( * terminate ) ( int rc ) ) {
 
67
        size_t len = 0;
 
68
        char *line = NULL;
 
69
        size_t line_offset;
 
70
        char *label;
 
71
        char *command;
 
72
        off_t eol;
 
73
        size_t frag_len;
 
74
        char *tmp;
 
75
        int rc;
 
76
 
 
77
        /* Initialise script and line offsets */
 
78
        script_offset = 0;
 
79
        line_offset = 0;
 
80
 
 
81
        do {
 
82
 
 
83
                /* Find length of next line, excluding any terminating '\n' */
 
84
                eol = memchr_user ( image->data, script_offset, '\n',
 
85
                                    ( image->len - script_offset ) );
 
86
                if ( eol < 0 )
 
87
                        eol = image->len;
 
88
                frag_len = ( eol - script_offset );
 
89
 
 
90
                /* Allocate buffer for line */
 
91
                tmp = realloc ( line, ( len + frag_len + 1 /* NUL */ ) );
 
92
                if ( ! tmp ) {
 
93
                        rc = -ENOMEM;
 
94
                        goto err_alloc;
 
95
                }
 
96
                line = tmp;
 
97
 
 
98
                /* Copy line */
 
99
                copy_from_user ( ( line + len ), image->data, script_offset,
 
100
                                 frag_len );
 
101
                len += frag_len;
 
102
 
 
103
                /* Move to next line in script */
 
104
                script_offset += ( frag_len + 1 );
 
105
 
 
106
                /* Strip trailing CR, if present */
 
107
                if ( len && ( line[ len - 1 ] == '\r' ) )
 
108
                        len--;
 
109
 
 
110
                /* Handle backslash continuations */
 
111
                if ( len && ( line[ len - 1 ] == '\\' ) ) {
 
112
                        len--;
 
113
                        rc = -EINVAL;
 
114
                        continue;
 
115
                }
 
116
 
 
117
                /* Terminate line */
 
118
                line[len] = '\0';
 
119
 
 
120
                /* Split line into (optional) label and command */
 
121
                command = line;
 
122
                while ( isspace ( *command ) )
 
123
                        command++;
 
124
                if ( *command == ':' ) {
 
125
                        label = ++command;
 
126
                        while ( *command && ! isspace ( *command ) )
 
127
                                command++;
 
128
                        if ( *command )
 
129
                                *(command++) = '\0';
 
130
                } else {
 
131
                        label = NULL;
 
132
                }
 
133
 
 
134
                /* Process line */
 
135
                rc = process_line ( image, line_offset, label, command );
 
136
                if ( terminate ( rc ) )
 
137
                        goto err_process;
 
138
 
 
139
                /* Free line */
 
140
                free ( line );
 
141
                line = NULL;
 
142
                len = 0;
 
143
 
 
144
                /* Update line offset */
 
145
                line_offset = script_offset;
 
146
 
 
147
        } while ( script_offset < image->len );
 
148
 
 
149
 err_process:
 
150
 err_alloc:
 
151
        free ( line );
 
152
        return rc;
 
153
}
 
154
 
 
155
/**
 
156
 * Terminate script processing on shell exit or command failure
 
157
 *
 
158
 * @v rc                Line processing status
 
159
 * @ret terminate       Terminate script processing
 
160
 */
 
161
static int terminate_on_exit_or_failure ( int rc ) {
 
162
 
 
163
        return ( shell_stopped ( SHELL_STOP_COMMAND_SEQUENCE ) ||
 
164
                 ( rc != 0 ) );
 
165
}
 
166
 
 
167
/**
 
168
 * Execute script line
 
169
 *
 
170
 * @v image             Script
 
171
 * @v offset            Offset within script
 
172
 * @v label             Label, or NULL
 
173
 * @v command           Command
 
174
 * @ret rc              Return status code
 
175
 */
 
176
static int script_exec_line ( struct image *image, size_t offset,
 
177
                              const char *label __unused,
 
178
                              const char *command ) {
 
179
        int rc;
 
180
 
 
181
        DBGC ( image, "[%04zx] $ %s\n", offset, command );
 
182
 
 
183
        /* Execute command */
 
184
        if ( ( rc = system ( command ) ) != 0 )
 
185
                return rc;
 
186
 
 
187
        return 0;
 
188
}
 
189
 
 
190
/**
 
191
 * Execute script
 
192
 *
 
193
 * @v image             Script
 
194
 * @ret rc              Return status code
 
195
 */
 
196
static int script_exec ( struct image *image ) {
 
197
        size_t saved_offset;
 
198
        int rc;
 
199
 
 
200
        /* Temporarily de-register image, so that a "boot" command
 
201
         * doesn't throw us into an execution loop.
 
202
         */
 
203
        unregister_image ( image );
 
204
 
 
205
        /* Preserve state of any currently-running script */
 
206
        saved_offset = script_offset;
 
207
 
 
208
        /* Process script */
 
209
        rc = process_script ( image, script_exec_line,
 
210
                              terminate_on_exit_or_failure );
 
211
 
 
212
        /* Restore saved state */
 
213
        script_offset = saved_offset;
 
214
 
 
215
        /* Re-register image (unless we have been replaced) */
 
216
        if ( ! image->replacement )
 
217
                register_image ( image );
 
218
 
 
219
        return rc;
 
220
}
 
221
 
 
222
/**
 
223
 * Probe script image
 
224
 *
 
225
 * @v image             Script
 
226
 * @ret rc              Return status code
 
227
 */
 
228
static int script_probe ( struct image *image ) {
 
229
        static const char ipxe_magic[] = "#!ipxe";
 
230
        static const char gpxe_magic[] = "#!gpxe";
 
231
        linker_assert ( sizeof ( ipxe_magic ) == sizeof ( gpxe_magic ),
 
232
                        magic_size_mismatch );
 
233
        char test[ sizeof ( ipxe_magic ) - 1 /* NUL */
 
234
                   + 1 /* terminating space */];
 
235
 
 
236
        /* Sanity check */
 
237
        if ( image->len < sizeof ( test ) ) {
 
238
                DBGC ( image, "Too short to be a script\n" );
 
239
                return -ENOEXEC;
 
240
        }
 
241
 
 
242
        /* Check for magic signature */
 
243
        copy_from_user ( test, image->data, 0, sizeof ( test ) );
 
244
        if ( ! ( ( ( memcmp ( test, ipxe_magic, sizeof ( test ) - 1 ) == 0 ) ||
 
245
                   ( memcmp ( test, gpxe_magic, sizeof ( test ) - 1 ) == 0 )) &&
 
246
                 isspace ( test[ sizeof ( test ) - 1 ] ) ) ) {
 
247
                DBGC ( image, "Invalid magic signature\n" );
 
248
                return -ENOEXEC;
 
249
        }
 
250
 
 
251
        return 0;
 
252
}
 
253
 
 
254
/** Script image type */
 
255
struct image_type script_image_type __image_type ( PROBE_NORMAL ) = {
 
256
        .name = "script",
 
257
        .probe = script_probe,
 
258
        .exec = script_exec,
 
259
};
 
260
 
 
261
/** "goto" options */
 
262
struct goto_options {};
 
263
 
 
264
/** "goto" option list */
 
265
static struct option_descriptor goto_opts[] = {};
 
266
 
 
267
/** "goto" command descriptor */
 
268
static struct command_descriptor goto_cmd =
 
269
        COMMAND_DESC ( struct goto_options, goto_opts, 1, 1, "<label>" );
 
270
 
 
271
/**
 
272
 * Current "goto" label
 
273
 *
 
274
 * Valid only during goto_exec().  Consider this part of a closure.
 
275
 */
 
276
static const char *goto_label;
 
277
 
 
278
/**
 
279
 * Check for presence of label
 
280
 *
 
281
 * @v image             Script
 
282
 * @v offset            Offset within script
 
283
 * @v label             Label
 
284
 * @v command           Command
 
285
 * @ret rc              Return status code
 
286
 */
 
287
static int goto_find_label ( struct image *image, size_t offset,
 
288
                             const char *label, const char *command __unused ) {
 
289
 
 
290
        /* Check label exists */
 
291
        if ( ! label )
 
292
                return -ENOENT;
 
293
 
 
294
        /* Check label matches */
 
295
        if ( strcmp ( goto_label, label ) != 0 )
 
296
                return -ENOENT;
 
297
 
 
298
        /* Update script offset */
 
299
        script_offset = offset;
 
300
        DBGC ( image, "[%04zx] Gone to :%s\n", offset, label );
 
301
 
 
302
        return 0;
 
303
}
 
304
 
 
305
/**
 
306
 * Terminate script processing when label is found
 
307
 *
 
308
 * @v rc                Line processing status
 
309
 * @ret terminate       Terminate script processing
 
310
 */
 
311
static int terminate_on_label_found ( int rc ) {
 
312
        return ( rc == 0 );
 
313
}
 
314
 
 
315
/**
 
316
 * "goto" command
 
317
 *
 
318
 * @v argc              Argument count
 
319
 * @v argv              Argument list
 
320
 * @ret rc              Return status code
 
321
 */
 
322
static int goto_exec ( int argc, char **argv ) {
 
323
        struct goto_options opts;
 
324
        size_t saved_offset;
 
325
        int rc;
 
326
 
 
327
        /* Parse options */
 
328
        if ( ( rc = parse_options ( argc, argv, &goto_cmd, &opts ) ) != 0 )
 
329
                return rc;
 
330
 
 
331
        /* Sanity check */
 
332
        if ( ! current_image ) {
 
333
                rc = -ENOTTY;
 
334
                printf ( "Not in a script: %s\n", strerror ( rc ) );
 
335
                return rc;
 
336
        }
 
337
 
 
338
        /* Parse label */
 
339
        goto_label = argv[optind];
 
340
 
 
341
        /* Find label */
 
342
        saved_offset = script_offset;
 
343
        if ( ( rc = process_script ( current_image, goto_find_label,
 
344
                                     terminate_on_label_found ) ) != 0 ) {
 
345
                script_offset = saved_offset;
 
346
                DBGC ( current_image, "[%04zx] No such label :%s\n",
 
347
                       script_offset, goto_label );
 
348
                return rc;
 
349
        }
 
350
 
 
351
        /* Terminate processing of current command */
 
352
        shell_stop ( SHELL_STOP_COMMAND );
 
353
 
 
354
        return 0;
 
355
}
 
356
 
 
357
/** "goto" command */
 
358
struct command goto_command __command = {
 
359
        .name = "goto",
 
360
        .exec = goto_exec,
 
361
};
 
362
 
 
363
/** "prompt" options */
 
364
struct prompt_options {
 
365
        /** Key to wait for */
 
366
        unsigned int key;
 
367
        /** Timeout */
 
368
        unsigned long timeout;
 
369
};
 
370
 
 
371
/** "prompt" option list */
 
372
static struct option_descriptor prompt_opts[] = {
 
373
        OPTION_DESC ( "key", 'k', required_argument,
 
374
                      struct prompt_options, key, parse_key ),
 
375
        OPTION_DESC ( "timeout", 't', required_argument,
 
376
                      struct prompt_options, timeout, parse_timeout ),
 
377
};
 
378
 
 
379
/** "prompt" command descriptor */
 
380
static struct command_descriptor prompt_cmd =
 
381
        COMMAND_DESC ( struct prompt_options, prompt_opts, 0, MAX_ARGUMENTS,
 
382
                       "[<text>]" );
 
383
 
 
384
/**
 
385
 * "prompt" command
 
386
 *
 
387
 * @v argc              Argument count
 
388
 * @v argv              Argument list
 
389
 * @ret rc              Return status code
 
390
 */
 
391
static int prompt_exec ( int argc, char **argv ) {
 
392
        struct prompt_options opts;
 
393
        char *text;
 
394
        int rc;
 
395
 
 
396
        /* Parse options */
 
397
        if ( ( rc = parse_options ( argc, argv, &prompt_cmd, &opts ) ) != 0 )
 
398
                goto err_parse;
 
399
 
 
400
        /* Parse prompt text */
 
401
        text = concat_args ( &argv[optind] );
 
402
        if ( ! text ) {
 
403
                rc = -ENOMEM;
 
404
                goto err_concat;
 
405
        }
 
406
 
 
407
        /* Display prompt and wait for key */
 
408
        if ( ( rc = prompt ( text, opts.timeout, opts.key ) ) != 0 )
 
409
                goto err_prompt;
 
410
 
 
411
        /* Free prompt text */
 
412
        free ( text );
 
413
 
 
414
        return 0;
 
415
 
 
416
 err_prompt:
 
417
        free ( text );
 
418
 err_concat:
 
419
 err_parse:
 
420
        return rc;
 
421
}
 
422
 
 
423
/** "prompt" command */
 
424
struct command prompt_command __command = {
 
425
        .name = "prompt",
 
426
        .exec = prompt_exec,
 
427
};