1 /* execute.c -- Execute a GRUB script. */
3 * GRUB -- GRand Unified Bootloader
4 * Copyright (C) 2005,2007,2008,2009,2010 Free Software Foundation, Inc.
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.
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.
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/>.
20 #include <grub/misc.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>
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)
34 static unsigned long is_continue
;
35 static unsigned long active_loops
;
36 static unsigned long active_breaks
;
37 static unsigned long function_return
;
39 #define GRUB_SCRIPT_SCOPE_MALLOCED 1
40 #define GRUB_SCRIPT_SCOPE_ARGS_MALLOCED 2
42 /* Scope for grub script functions. */
43 struct grub_script_scope
47 struct grub_script_argv argv
;
49 static struct grub_script_scope
*scope
= 0;
52 replace_scope (struct grub_script_scope
*new_scope
)
56 scope
->argv
.argc
+= scope
->shifts
;
57 scope
->argv
.args
-= scope
->shifts
;
59 if (scope
->flags
& GRUB_SCRIPT_SCOPE_ARGS_MALLOCED
)
60 grub_script_argv_free (&scope
->argv
);
62 if (scope
->flags
& GRUB_SCRIPT_SCOPE_MALLOCED
)
69 grub_script_break (grub_command_t cmd
, int argc
, char *argv
[])
77 else if ((argc
> 1) || (count
= grub_strtoul (argv
[0], &p
, 10)) == 0 ||
79 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "bad break");
81 is_continue
= grub_strcmp (cmd
->name
, "break") ? 1 : 0;
82 active_breaks
= grub_min (active_loops
, count
);
87 grub_script_shift (grub_command_t cmd
__attribute__((unused
)),
88 int argc
, char *argv
[])
100 return GRUB_ERR_BAD_ARGUMENT
;
104 n
= grub_strtoul (argv
[0], &p
, 10);
106 return GRUB_ERR_BAD_ARGUMENT
;
109 if (n
> scope
->argv
.argc
)
110 return GRUB_ERR_BAD_ARGUMENT
;
113 scope
->argv
.argc
-= n
;
114 scope
->argv
.args
+= n
;
115 return GRUB_ERR_NONE
;
119 grub_script_setparams (grub_command_t cmd
__attribute__((unused
)),
120 int argc
, char **args
)
122 struct grub_script_scope
*new_scope
;
123 struct grub_script_argv argv
= { 0, 0, 0 };
126 return GRUB_ERR_INVALID_COMMAND
;
128 new_scope
= grub_malloc (sizeof (*new_scope
));
132 if (grub_script_argv_make (&argv
, argc
, args
))
134 grub_free (new_scope
);
138 new_scope
->shifts
= 0;
139 new_scope
->argv
= argv
;
140 new_scope
->flags
= GRUB_SCRIPT_SCOPE_MALLOCED
|
141 GRUB_SCRIPT_SCOPE_ARGS_MALLOCED
;
143 replace_scope (new_scope
);
144 return GRUB_ERR_NONE
;
148 grub_script_return (grub_command_t cmd
__attribute__((unused
)),
149 int argc
, char *argv
[])
154 if (! scope
|| argc
> 1)
155 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "not in function scope");
160 return grub_strtoul (grub_env_get ("?"), NULL
, 10);
163 n
= grub_strtoul (argv
[0], &p
, 10);
165 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "bad argument");
168 return n
? grub_error (GRUB_ERR_TEST_FAILURE
, "false") : GRUB_ERR_NONE
;
172 grub_env_special (const char *name
)
174 if (grub_isdigit (name
[0]) ||
175 grub_strcmp (name
, "#") == 0 ||
176 grub_strcmp (name
, "*") == 0 ||
177 grub_strcmp (name
, "@") == 0)
183 grub_script_env_get (const char *name
, grub_script_arg_type_t type
)
186 struct grub_script_argv result
= { 0, 0, 0 };
188 if (grub_script_argv_next (&result
))
191 if (! grub_env_special (name
))
193 char *v
= grub_env_get (name
);
196 if (type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
198 if (grub_script_argv_split_append (&result
, v
))
202 if (grub_script_argv_append (&result
, v
))
208 if (grub_script_argv_append (&result
, 0))
211 else if (grub_strcmp (name
, "#") == 0)
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
))
218 else if (grub_strcmp (name
, "*") == 0)
220 for (i
= 0; i
< scope
->argv
.argc
; i
++)
221 if (type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
223 if (i
!= 0 && grub_script_argv_next (&result
))
226 if (grub_script_argv_split_append (&result
, scope
->argv
.args
[i
]))
231 if (i
!= 0 && grub_script_argv_append (&result
, " "))
234 if (grub_script_argv_append (&result
, scope
->argv
.args
[i
]))
238 else if (grub_strcmp (name
, "@") == 0)
240 for (i
= 0; i
< scope
->argv
.argc
; i
++)
242 if (i
!= 0 && grub_script_argv_next (&result
))
245 if (type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
247 if (grub_script_argv_split_append (&result
, scope
->argv
.args
[i
]))
251 if (grub_script_argv_append (&result
, scope
->argv
.args
[i
]))
257 unsigned long num
= grub_strtoul (name
, 0, 10);
259 ; /* XXX no file name, for now. */
261 else if (num
<= scope
->argv
.argc
)
263 if (type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
265 if (grub_script_argv_split_append (&result
,
266 scope
->argv
.args
[num
- 1]))
270 if (grub_script_argv_append (&result
, scope
->argv
.args
[num
- 1]))
279 grub_script_argv_free (&result
);
284 grub_script_env_set (const char *name
, const char *val
)
286 if (grub_env_special (name
))
287 return grub_error (GRUB_ERR_BAD_ARGUMENT
, "bad variable name");
289 return grub_env_set (name
, val
);
292 /* Expand arguments in ARGLIST into multiple arguments. */
294 grub_script_arglist_to_argv (struct grub_script_arglist
*arglist
,
295 struct grub_script_argv
*argv
)
299 struct grub_script_arg
*arg
= 0;
300 struct grub_script_argv result
= { 0, 0, 0 };
302 for (; arglist
&& arglist
->arg
; arglist
= arglist
->next
)
304 if (grub_script_argv_next (&result
))
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
++)
317 if (i
!= 0 && grub_script_argv_next (&result
))
320 if (grub_script_argv_append (&result
, values
[i
]))
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
, "}"))
331 result
.script
= arg
->script
;
334 case GRUB_SCRIPT_ARG_TYPE_TEXT
:
335 if (grub_strlen (arg
->str
) &&
336 grub_script_argv_append (&result
, arg
->str
))
340 case GRUB_SCRIPT_ARG_TYPE_DQSTR
:
341 case GRUB_SCRIPT_ARG_TYPE_SQSTR
:
342 if (grub_script_argv_append (&result
, arg
->str
))
350 if (! result
.args
[result
.argc
- 1])
358 grub_script_argv_free (&result
);
363 grub_script_execute_cmd (struct grub_script_cmd
*cmd
)
366 char errnobuf
[ERRNO_DIGITS_MAX
+ 1];
371 ret
= cmd
->exec (cmd
);
373 grub_snprintf (errnobuf
, sizeof (errnobuf
), "%d", ret
);
374 grub_env_set ("?", errnobuf
);
378 /* Execute a function call. */
380 grub_script_function_call (grub_script_function_t func
, int argc
, char **args
)
383 unsigned long loops
= active_loops
;
384 struct grub_script_scope
*old_scope
;
385 struct grub_script_scope new_scope
;
389 new_scope
.shifts
= 0;
390 new_scope
.argv
.argc
= argc
;
391 new_scope
.argv
.args
= args
;
396 ret
= grub_script_execute (func
->func
);
399 active_loops
= loops
;
400 replace_scope (old_scope
); /* free any scopes by setparams */
404 /* Execute a source script. */
406 grub_script_execute_sourcecode (const char *source
, int argc
, char **args
)
409 struct grub_script
*parsed_script
;
410 struct grub_script_scope new_scope
;
411 struct grub_script_scope
*old_scope
;
413 auto grub_err_t
getline (char **line
, int cont
);
414 grub_err_t
getline (char **line
, int cont
__attribute__ ((unused
)))
424 p
= grub_strchr (source
, '\n');
427 *line
= grub_strndup (source
, p
- source
);
429 *line
= grub_strdup (source
);
430 source
= p
? p
+ 1 : 0;
434 new_scope
.argv
.argc
= argc
;
435 new_scope
.argv
.args
= args
;
445 parsed_script
= grub_script_parse (line
, getline
);
452 ret
= grub_script_execute (parsed_script
);
460 /* Execute a single command line. */
462 grub_script_execute_cmdline (struct grub_script_cmd
*cmd
)
464 struct grub_script_cmdline
*cmdline
= (struct grub_script_cmdline
*) cmd
;
465 grub_command_t grubcmd
;
467 grub_script_function_t func
= 0;
470 struct grub_script_argv argv
= { 0, 0, 0 };
472 /* Lookup the command. */
473 if (grub_script_arglist_to_argv (cmdline
->arglist
, &argv
) || ! argv
.args
[0])
476 cmdname
= argv
.args
[0];
477 grubcmd
= grub_command_find (cmdname
);
480 grub_errno
= GRUB_ERR_NONE
;
482 /* It's not a GRUB command, try all functions. */
483 func
= grub_script_function_find (cmdname
);
486 /* As a last resort, try if it is an assignment. */
487 char *assign
= grub_strdup (cmdname
);
488 char *eq
= grub_strchr (assign
, '=');
492 /* This was set because the command was not found. */
493 grub_errno
= GRUB_ERR_NONE
;
495 /* Create two strings and set the variable. */
498 grub_script_env_set (assign
, eq
);
502 grub_snprintf (errnobuf
, sizeof (errnobuf
), "%d", grub_errno
);
503 grub_script_env_set ("?", errnobuf
);
505 grub_script_argv_free (&argv
);
512 /* Execute the GRUB command or function. */
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,
520 ret
= (grubcmd
->func
) (grubcmd
, argv
.argc
- 1, argv
.args
+ 1);
523 ret
= grub_script_function_call (func
, argv
.argc
- 1, argv
.args
+ 1);
525 /* Free arguments. */
526 grub_script_argv_free (&argv
);
528 if (grub_errno
== GRUB_ERR_TEST_FAILURE
)
529 grub_errno
= GRUB_ERR_NONE
;
533 grub_snprintf (errnobuf
, sizeof (errnobuf
), "%d", ret
);
534 grub_env_set ("?", errnobuf
);
539 /* Execute a block of one or more commands. */
541 grub_script_execute_cmdlist (struct grub_script_cmd
*list
)
544 struct grub_script_cmd
*cmd
;
546 /* Loop over every command and execute it. */
547 for (cmd
= list
->next
; cmd
; cmd
= cmd
->next
)
552 ret
= grub_script_execute_cmd (cmd
);
561 /* Execute an if statement. */
563 grub_script_execute_cmdif (struct grub_script_cmd
*cmd
)
567 struct grub_script_cmdif
*cmdif
= (struct grub_script_cmdif
*) cmd
;
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
);
575 result
= grub_env_get ("?");
576 grub_errno
= GRUB_ERR_NONE
;
578 /* Execute the `if' or the `else' part depending on the value of
580 if (result
&& ! grub_strcmp (result
, "0"))
581 return grub_script_execute_cmd (cmdif
->exec_on_true
);
583 return grub_script_execute_cmd (cmdif
->exec_on_false
);
586 /* Execute a for statement. */
588 grub_script_execute_cmdfor (struct grub_script_cmd
*cmd
)
592 struct grub_script_argv argv
= { 0, 0, 0 };
593 struct grub_script_cmdfor
*cmdfor
= (struct grub_script_cmdfor
*) cmd
;
595 if (grub_script_arglist_to_argv (cmdfor
->words
, &argv
))
600 for (i
= 0; i
< argv
.argc
; i
++)
602 if (is_continue
&& active_breaks
== 1)
607 grub_script_env_set (cmdfor
->name
->str
, argv
.args
[i
]);
608 result
= grub_script_execute_cmd (cmdfor
->list
);
618 grub_script_argv_free (&argv
);
622 /* Execute a "while" or "until" command. */
624 grub_script_execute_cmdwhile (struct grub_script_cmd
*cmd
)
627 struct grub_script_cmdwhile
*cmdwhile
= (struct grub_script_cmdwhile
*) cmd
;
631 result
= grub_script_execute_cmd (cmdwhile
->cond
);
635 if (cmdwhile
->until
? !result
: result
)
638 result
= grub_script_execute_cmd (cmdwhile
->list
);
642 if (active_breaks
== 1 && is_continue
)
648 } while (1); /* XXX Put a check for ^C here */
657 /* Execute any GRUB pre-parsed command or script. */
659 grub_script_execute (struct grub_script
*script
)
664 return grub_script_execute_cmd (script
->cmd
);