]> git.proxmox.com Git - grub2.git/blob - grub-core/script/execute.c
Support for options to appear multiple times on cmdline.
[grub2.git] / grub-core / script / execute.c
1 /* execute.c -- Execute a GRUB script. */
2 /*
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2007,2008,2009,2010 Free Software Foundation, Inc.
5 *
6 * GRUB is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * GRUB is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <grub/misc.h>
21 #include <grub/mm.h>
22 #include <grub/env.h>
23 #include <grub/script_sh.h>
24 #include <grub/command.h>
25 #include <grub/menu.h>
26 #include <grub/lib/arg.h>
27 #include <grub/normal.h>
28 #include <grub/extcmd.h>
29
30 /* Max digits for a char is 3 (0xFF is 255), similarly for an int it
31 is sizeof (int) * 3, and one extra for a possible -ve sign. */
32 #define ERRNO_DIGITS_MAX (sizeof (int) * 3 + 1)
33
34 static unsigned long is_continue;
35 static unsigned long active_loops;
36 static unsigned long active_breaks;
37 static unsigned long function_return;
38
39 #define GRUB_SCRIPT_SCOPE_MALLOCED 1
40 #define GRUB_SCRIPT_SCOPE_ARGS_MALLOCED 2
41
42 /* Scope for grub script functions. */
43 struct grub_script_scope
44 {
45 unsigned flags;
46 unsigned shifts;
47 struct grub_script_argv argv;
48 };
49 static struct grub_script_scope *scope = 0;
50
51 static void
52 replace_scope (struct grub_script_scope *new_scope)
53 {
54 if (scope)
55 {
56 scope->argv.argc += scope->shifts;
57 scope->argv.args -= scope->shifts;
58
59 if (scope->flags & GRUB_SCRIPT_SCOPE_ARGS_MALLOCED)
60 grub_script_argv_free (&scope->argv);
61
62 if (scope->flags & GRUB_SCRIPT_SCOPE_MALLOCED)
63 grub_free (scope);
64 }
65 scope = new_scope;
66 }
67
68 grub_err_t
69 grub_script_break (grub_command_t cmd, int argc, char *argv[])
70 {
71 char *p = 0;
72 unsigned long count;
73
74 if (argc == 0)
75 count = 1;
76
77 else if ((argc > 1) || (count = grub_strtoul (argv[0], &p, 10)) == 0 ||
78 (*p != '\0'))
79 return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad break");
80
81 is_continue = grub_strcmp (cmd->name, "break") ? 1 : 0;
82 active_breaks = grub_min (active_loops, count);
83 return GRUB_ERR_NONE;
84 }
85
86 grub_err_t
87 grub_script_shift (grub_command_t cmd __attribute__((unused)),
88 int argc, char *argv[])
89 {
90 char *p = 0;
91 unsigned long n = 0;
92
93 if (! scope)
94 return GRUB_ERR_NONE;
95
96 if (argc == 0)
97 n = 1;
98
99 else if (argc > 1)
100 return GRUB_ERR_BAD_ARGUMENT;
101
102 else
103 {
104 n = grub_strtoul (argv[0], &p, 10);
105 if (*p != '\0')
106 return GRUB_ERR_BAD_ARGUMENT;
107 }
108
109 if (n > scope->argv.argc)
110 return GRUB_ERR_BAD_ARGUMENT;
111
112 scope->shifts += n;
113 scope->argv.argc -= n;
114 scope->argv.args += n;
115 return GRUB_ERR_NONE;
116 }
117
118 grub_err_t
119 grub_script_setparams (grub_command_t cmd __attribute__((unused)),
120 int argc, char **args)
121 {
122 struct grub_script_scope *new_scope;
123 struct grub_script_argv argv = { 0, 0, 0 };
124
125 if (! scope)
126 return GRUB_ERR_INVALID_COMMAND;
127
128 new_scope = grub_malloc (sizeof (*new_scope));
129 if (! new_scope)
130 return grub_errno;
131
132 if (grub_script_argv_make (&argv, argc, args))
133 {
134 grub_free (new_scope);
135 return grub_errno;
136 }
137
138 new_scope->shifts = 0;
139 new_scope->argv = argv;
140 new_scope->flags = GRUB_SCRIPT_SCOPE_MALLOCED |
141 GRUB_SCRIPT_SCOPE_ARGS_MALLOCED;
142
143 replace_scope (new_scope);
144 return GRUB_ERR_NONE;
145 }
146
147 grub_err_t
148 grub_script_return (grub_command_t cmd __attribute__((unused)),
149 int argc, char *argv[])
150 {
151 char *p;
152 unsigned long n;
153
154 if (! scope || argc > 1)
155 return grub_error (GRUB_ERR_BAD_ARGUMENT, "not in function scope");
156
157 if (argc == 0)
158 {
159 function_return = 1;
160 return grub_strtoul (grub_env_get ("?"), NULL, 10);
161 }
162
163 n = grub_strtoul (argv[0], &p, 10);
164 if (*p != '\0')
165 return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad argument");
166
167 function_return = 1;
168 return n ? grub_error (GRUB_ERR_TEST_FAILURE, "false") : GRUB_ERR_NONE;
169 }
170
171 static int
172 grub_env_special (const char *name)
173 {
174 if (grub_isdigit (name[0]) ||
175 grub_strcmp (name, "#") == 0 ||
176 grub_strcmp (name, "*") == 0 ||
177 grub_strcmp (name, "@") == 0)
178 return 1;
179 return 0;
180 }
181
182 static char **
183 grub_script_env_get (const char *name, grub_script_arg_type_t type)
184 {
185 unsigned i;
186 struct grub_script_argv result = { 0, 0, 0 };
187
188 if (grub_script_argv_next (&result))
189 goto fail;
190
191 if (! grub_env_special (name))
192 {
193 char *v = grub_env_get (name);
194 if (v && v[0])
195 {
196 if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
197 {
198 if (grub_script_argv_split_append (&result, v))
199 goto fail;
200 }
201 else
202 if (grub_script_argv_append (&result, v))
203 goto fail;
204 }
205 }
206 else if (! scope)
207 {
208 if (grub_script_argv_append (&result, 0))
209 goto fail;
210 }
211 else if (grub_strcmp (name, "#") == 0)
212 {
213 char buffer[ERRNO_DIGITS_MAX + 1];
214 grub_snprintf (buffer, sizeof (buffer), "%u", scope->argv.argc);
215 if (grub_script_argv_append (&result, buffer))
216 goto fail;
217 }
218 else if (grub_strcmp (name, "*") == 0)
219 {
220 for (i = 0; i < scope->argv.argc; i++)
221 if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
222 {
223 if (i != 0 && grub_script_argv_next (&result))
224 goto fail;
225
226 if (grub_script_argv_split_append (&result, scope->argv.args[i]))
227 goto fail;
228 }
229 else
230 {
231 if (i != 0 && grub_script_argv_append (&result, " "))
232 goto fail;
233
234 if (grub_script_argv_append (&result, scope->argv.args[i]))
235 goto fail;
236 }
237 }
238 else if (grub_strcmp (name, "@") == 0)
239 {
240 for (i = 0; i < scope->argv.argc; i++)
241 {
242 if (i != 0 && grub_script_argv_next (&result))
243 goto fail;
244
245 if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
246 {
247 if (grub_script_argv_split_append (&result, scope->argv.args[i]))
248 goto fail;
249 }
250 else
251 if (grub_script_argv_append (&result, scope->argv.args[i]))
252 goto fail;
253 }
254 }
255 else
256 {
257 unsigned long num = grub_strtoul (name, 0, 10);
258 if (num == 0)
259 ; /* XXX no file name, for now. */
260
261 else if (num <= scope->argv.argc)
262 {
263 if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
264 {
265 if (grub_script_argv_split_append (&result,
266 scope->argv.args[num - 1]))
267 goto fail;
268 }
269 else
270 if (grub_script_argv_append (&result, scope->argv.args[num - 1]))
271 goto fail;
272 }
273 }
274
275 return result.args;
276
277 fail:
278
279 grub_script_argv_free (&result);
280 return 0;
281 }
282
283 static grub_err_t
284 grub_script_env_set (const char *name, const char *val)
285 {
286 if (grub_env_special (name))
287 return grub_error (GRUB_ERR_BAD_ARGUMENT, "bad variable name");
288
289 return grub_env_set (name, val);
290 }
291
292 /* Expand arguments in ARGLIST into multiple arguments. */
293 static int
294 grub_script_arglist_to_argv (struct grub_script_arglist *arglist,
295 struct grub_script_argv *argv)
296 {
297 int i;
298 char **values = 0;
299 struct grub_script_arg *arg = 0;
300 struct grub_script_argv result = { 0, 0, 0 };
301
302 for (; arglist && arglist->arg; arglist = arglist->next)
303 {
304 if (grub_script_argv_next (&result))
305 goto fail;
306
307 arg = arglist->arg;
308 while (arg)
309 {
310 switch (arg->type)
311 {
312 case GRUB_SCRIPT_ARG_TYPE_VAR:
313 case GRUB_SCRIPT_ARG_TYPE_DQVAR:
314 values = grub_script_env_get (arg->str, arg->type);
315 for (i = 0; values && values[i]; i++)
316 {
317 if (i != 0 && grub_script_argv_next (&result))
318 goto fail;
319
320 if (grub_script_argv_append (&result, values[i]))
321 goto fail;
322 }
323 grub_free (values);
324 break;
325
326 case GRUB_SCRIPT_ARG_TYPE_BLOCK:
327 if (grub_script_argv_append (&result, "{") ||
328 grub_script_argv_append (&result, arg->str) ||
329 grub_script_argv_append (&result, "}"))
330 goto fail;
331 result.script = arg->script;
332 break;
333
334 case GRUB_SCRIPT_ARG_TYPE_TEXT:
335 if (grub_strlen (arg->str) &&
336 grub_script_argv_append (&result, arg->str))
337 goto fail;
338 break;
339
340 case GRUB_SCRIPT_ARG_TYPE_DQSTR:
341 case GRUB_SCRIPT_ARG_TYPE_SQSTR:
342 if (grub_script_argv_append (&result, arg->str))
343 goto fail;
344 break;
345 }
346 arg = arg->next;
347 }
348 }
349
350 if (! result.args[result.argc - 1])
351 result.argc--;
352
353 *argv = result;
354 return 0;
355
356 fail:
357
358 grub_script_argv_free (&result);
359 return 1;
360 }
361
362 static grub_err_t
363 grub_script_execute_cmd (struct grub_script_cmd *cmd)
364 {
365 int ret;
366 char errnobuf[ERRNO_DIGITS_MAX + 1];
367
368 if (cmd == 0)
369 return 0;
370
371 ret = cmd->exec (cmd);
372
373 grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
374 grub_env_set ("?", errnobuf);
375 return ret;
376 }
377
378 /* Execute a function call. */
379 grub_err_t
380 grub_script_function_call (grub_script_function_t func, int argc, char **args)
381 {
382 grub_err_t ret = 0;
383 unsigned long loops = active_loops;
384 struct grub_script_scope *old_scope;
385 struct grub_script_scope new_scope;
386
387 active_loops = 0;
388 new_scope.flags = 0;
389 new_scope.shifts = 0;
390 new_scope.argv.argc = argc;
391 new_scope.argv.args = args;
392
393 old_scope = scope;
394 scope = &new_scope;
395
396 ret = grub_script_execute (func->func);
397
398 function_return = 0;
399 active_loops = loops;
400 replace_scope (old_scope); /* free any scopes by setparams */
401 return ret;
402 }
403
404 /* Execute a source script. */
405 grub_err_t
406 grub_script_execute_sourcecode (const char *source, int argc, char **args)
407 {
408 grub_err_t ret = 0;
409 struct grub_script *parsed_script;
410 struct grub_script_scope new_scope;
411 struct grub_script_scope *old_scope;
412
413 auto grub_err_t getline (char **line, int cont);
414 grub_err_t getline (char **line, int cont __attribute__ ((unused)))
415 {
416 const char *p;
417
418 if (! source)
419 {
420 *line = 0;
421 return 0;
422 }
423
424 p = grub_strchr (source, '\n');
425
426 if (p)
427 *line = grub_strndup (source, p - source);
428 else
429 *line = grub_strdup (source);
430 source = p ? p + 1 : 0;
431 return 0;
432 }
433
434 new_scope.argv.argc = argc;
435 new_scope.argv.args = args;
436
437 old_scope = scope;
438 scope = &new_scope;
439
440 while (source)
441 {
442 char *line;
443
444 getline (&line, 0);
445 parsed_script = grub_script_parse (line, getline);
446 if (! parsed_script)
447 {
448 ret = grub_errno;
449 break;
450 }
451
452 ret = grub_script_execute (parsed_script);
453 grub_free (line);
454 }
455
456 scope = old_scope;
457 return ret;
458 }
459
460 /* Execute a single command line. */
461 grub_err_t
462 grub_script_execute_cmdline (struct grub_script_cmd *cmd)
463 {
464 struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd;
465 grub_command_t grubcmd;
466 grub_err_t ret = 0;
467 grub_script_function_t func = 0;
468 char errnobuf[18];
469 char *cmdname;
470 struct grub_script_argv argv = { 0, 0, 0 };
471
472 /* Lookup the command. */
473 if (grub_script_arglist_to_argv (cmdline->arglist, &argv) || ! argv.args[0])
474 return grub_errno;
475
476 cmdname = argv.args[0];
477 grubcmd = grub_command_find (cmdname);
478 if (! grubcmd)
479 {
480 grub_errno = GRUB_ERR_NONE;
481
482 /* It's not a GRUB command, try all functions. */
483 func = grub_script_function_find (cmdname);
484 if (! func)
485 {
486 /* As a last resort, try if it is an assignment. */
487 char *assign = grub_strdup (cmdname);
488 char *eq = grub_strchr (assign, '=');
489
490 if (eq)
491 {
492 /* This was set because the command was not found. */
493 grub_errno = GRUB_ERR_NONE;
494
495 /* Create two strings and set the variable. */
496 *eq = '\0';
497 eq++;
498 grub_script_env_set (assign, eq);
499 }
500 grub_free (assign);
501
502 grub_snprintf (errnobuf, sizeof (errnobuf), "%d", grub_errno);
503 grub_script_env_set ("?", errnobuf);
504
505 grub_script_argv_free (&argv);
506 grub_print_error ();
507
508 return 0;
509 }
510 }
511
512 /* Execute the GRUB command or function. */
513 if (grubcmd)
514 {
515 if ((grubcmd->flags & GRUB_COMMAND_FLAG_BLOCKS) &&
516 (grubcmd->flags & GRUB_COMMAND_FLAG_EXTCMD))
517 ret = grub_extcmd_dispatcher (grubcmd, argv.argc - 1, argv.args + 1,
518 argv.script);
519 else
520 ret = (grubcmd->func) (grubcmd, argv.argc - 1, argv.args + 1);
521 }
522 else
523 ret = grub_script_function_call (func, argv.argc - 1, argv.args + 1);
524
525 /* Free arguments. */
526 grub_script_argv_free (&argv);
527
528 if (grub_errno == GRUB_ERR_TEST_FAILURE)
529 grub_errno = GRUB_ERR_NONE;
530
531 grub_print_error ();
532
533 grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
534 grub_env_set ("?", errnobuf);
535
536 return ret;
537 }
538
539 /* Execute a block of one or more commands. */
540 grub_err_t
541 grub_script_execute_cmdlist (struct grub_script_cmd *list)
542 {
543 int ret = 0;
544 struct grub_script_cmd *cmd;
545
546 /* Loop over every command and execute it. */
547 for (cmd = list->next; cmd; cmd = cmd->next)
548 {
549 if (active_breaks)
550 break;
551
552 ret = grub_script_execute_cmd (cmd);
553
554 if (function_return)
555 break;
556 }
557
558 return ret;
559 }
560
561 /* Execute an if statement. */
562 grub_err_t
563 grub_script_execute_cmdif (struct grub_script_cmd *cmd)
564 {
565 int ret;
566 char *result;
567 struct grub_script_cmdif *cmdif = (struct grub_script_cmdif *) cmd;
568
569 /* Check if the commands results in a true or a false. The value is
570 read from the env variable `?'. */
571 ret = grub_script_execute_cmd (cmdif->exec_to_evaluate);
572 if (function_return)
573 return ret;
574
575 result = grub_env_get ("?");
576 grub_errno = GRUB_ERR_NONE;
577
578 /* Execute the `if' or the `else' part depending on the value of
579 `?'. */
580 if (result && ! grub_strcmp (result, "0"))
581 return grub_script_execute_cmd (cmdif->exec_on_true);
582 else
583 return grub_script_execute_cmd (cmdif->exec_on_false);
584 }
585
586 /* Execute a for statement. */
587 grub_err_t
588 grub_script_execute_cmdfor (struct grub_script_cmd *cmd)
589 {
590 unsigned i;
591 grub_err_t result;
592 struct grub_script_argv argv = { 0, 0, 0 };
593 struct grub_script_cmdfor *cmdfor = (struct grub_script_cmdfor *) cmd;
594
595 if (grub_script_arglist_to_argv (cmdfor->words, &argv))
596 return grub_errno;
597
598 active_loops++;
599 result = 0;
600 for (i = 0; i < argv.argc; i++)
601 {
602 if (is_continue && active_breaks == 1)
603 active_breaks = 0;
604
605 if (! active_breaks)
606 {
607 grub_script_env_set (cmdfor->name->str, argv.args[i]);
608 result = grub_script_execute_cmd (cmdfor->list);
609 if (function_return)
610 break;
611 }
612 }
613
614 if (active_breaks)
615 active_breaks--;
616
617 active_loops--;
618 grub_script_argv_free (&argv);
619 return result;
620 }
621
622 /* Execute a "while" or "until" command. */
623 grub_err_t
624 grub_script_execute_cmdwhile (struct grub_script_cmd *cmd)
625 {
626 int result;
627 struct grub_script_cmdwhile *cmdwhile = (struct grub_script_cmdwhile *) cmd;
628
629 active_loops++;
630 do {
631 result = grub_script_execute_cmd (cmdwhile->cond);
632 if (function_return)
633 break;
634
635 if (cmdwhile->until ? !result : result)
636 break;
637
638 result = grub_script_execute_cmd (cmdwhile->list);
639 if (function_return)
640 break;
641
642 if (active_breaks == 1 && is_continue)
643 active_breaks = 0;
644
645 if (active_breaks)
646 break;
647
648 } while (1); /* XXX Put a check for ^C here */
649
650 if (active_breaks)
651 active_breaks--;
652
653 active_loops--;
654 return result;
655 }
656
657 /* Execute any GRUB pre-parsed command or script. */
658 grub_err_t
659 grub_script_execute (struct grub_script *script)
660 {
661 if (script == 0)
662 return 0;
663
664 return grub_script_execute_cmd (script->cmd);
665 }
666