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>
29 #include <grub/i18n.h>
31 /* Max digits for a char is 3 (0xFF is 255), similarly for an int it
32 is sizeof (int) * 3, and one extra for a possible -ve sign. */
33 #define ERRNO_DIGITS_MAX (sizeof (int) * 3 + 1)
35 static unsigned long is_continue
;
36 static unsigned long active_loops
;
37 static unsigned long active_breaks
;
38 static unsigned long function_return
;
40 #define GRUB_SCRIPT_SCOPE_MALLOCED 1
41 #define GRUB_SCRIPT_SCOPE_ARGS_MALLOCED 2
43 /* Scope for grub script functions. */
44 struct grub_script_scope
48 struct grub_script_argv argv
;
50 static struct grub_script_scope
*scope
= 0;
52 /* Wildcard translator for GRUB script. */
53 struct grub_script_wildcard_translator
*grub_wildcard_translator
;
56 wildcard_escape (const char *s
)
63 len
= grub_strlen (s
);
64 p
= grub_malloc (len
* 2 + 1);
71 if (ch
== '*' || ch
== '\\' || ch
== '?')
80 wildcard_unescape (const char *s
)
87 len
= grub_strlen (s
);
88 p
= grub_malloc (len
+ 1);
105 replace_scope (struct grub_script_scope
*new_scope
)
109 scope
->argv
.argc
+= scope
->shifts
;
110 scope
->argv
.args
-= scope
->shifts
;
112 if (scope
->flags
& GRUB_SCRIPT_SCOPE_ARGS_MALLOCED
)
113 grub_script_argv_free (&scope
->argv
);
115 if (scope
->flags
& GRUB_SCRIPT_SCOPE_MALLOCED
)
122 grub_script_break (grub_command_t cmd
, int argc
, char *argv
[])
130 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("one argument expected"));
133 count
= grub_strtoul (argv
[0], &p
, 10);
137 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("unrecognized number"));
139 /* TRANSLATORS: 0 is a quantifier. "break" (similar to bash)
140 can be used e.g. to break 3 loops at once.
141 But asking it to break 0 loops makes no sense. */
142 return grub_error (GRUB_ERR_BAD_ARGUMENT
, N_("can't break 0 loops"));
145 is_continue
= grub_strcmp (cmd
->name
, "break") ? 1 : 0;
146 active_breaks
= count
;
147 if (active_breaks
> active_loops
)
148 active_breaks
= active_loops
;
149 return GRUB_ERR_NONE
;
153 grub_script_shift (grub_command_t cmd
__attribute__((unused
)),
154 int argc
, char *argv
[])
160 return GRUB_ERR_NONE
;
166 return GRUB_ERR_BAD_ARGUMENT
;
170 n
= grub_strtoul (argv
[0], &p
, 10);
172 return GRUB_ERR_BAD_ARGUMENT
;
175 if (n
> scope
->argv
.argc
)
176 return GRUB_ERR_BAD_ARGUMENT
;
179 scope
->argv
.argc
-= n
;
180 scope
->argv
.args
+= n
;
181 return GRUB_ERR_NONE
;
185 grub_script_setparams (grub_command_t cmd
__attribute__((unused
)),
186 int argc
, char **args
)
188 struct grub_script_scope
*new_scope
;
189 struct grub_script_argv argv
= { 0, 0, 0 };
192 return GRUB_ERR_INVALID_COMMAND
;
194 new_scope
= grub_malloc (sizeof (*new_scope
));
198 if (grub_script_argv_make (&argv
, argc
, args
))
200 grub_free (new_scope
);
204 new_scope
->shifts
= 0;
205 new_scope
->argv
= argv
;
206 new_scope
->flags
= GRUB_SCRIPT_SCOPE_MALLOCED
|
207 GRUB_SCRIPT_SCOPE_ARGS_MALLOCED
;
209 replace_scope (new_scope
);
210 return GRUB_ERR_NONE
;
214 grub_script_return (grub_command_t cmd
__attribute__((unused
)),
215 int argc
, char *argv
[])
220 if (! scope
|| argc
> 1)
221 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
222 /* TRANSLATORS: It's about not being
223 inside a function. "return" can be used only
224 in a function and this error occurs if it's used
226 N_("not in function body"));
232 t
= grub_env_get ("?");
234 return GRUB_ERR_NONE
;
235 return grub_strtoul (t
, NULL
, 10);
238 n
= grub_strtoul (argv
[0], &p
, 10);
242 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
243 N_("unrecognized number"));
246 return n
? grub_error (n
, N_("false")) : GRUB_ERR_NONE
;
250 grub_env_special (const char *name
)
252 if (grub_isdigit (name
[0]) ||
253 grub_strcmp (name
, "#") == 0 ||
254 grub_strcmp (name
, "*") == 0 ||
255 grub_strcmp (name
, "@") == 0)
261 grub_script_env_get (const char *name
, grub_script_arg_type_t type
)
264 struct grub_script_argv result
= { 0, 0, 0 };
266 if (grub_script_argv_next (&result
))
269 if (! grub_env_special (name
))
271 const char *v
= grub_env_get (name
);
274 if (type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
276 if (grub_script_argv_split_append (&result
, v
))
280 if (grub_script_argv_append (&result
, v
, grub_strlen (v
)))
286 if (grub_script_argv_append (&result
, 0, 0))
289 else if (grub_strcmp (name
, "#") == 0)
291 char buffer
[ERRNO_DIGITS_MAX
+ 1];
292 grub_snprintf (buffer
, sizeof (buffer
), "%u", scope
->argv
.argc
);
293 if (grub_script_argv_append (&result
, buffer
, grub_strlen (buffer
)))
296 else if (grub_strcmp (name
, "*") == 0)
298 for (i
= 0; i
< scope
->argv
.argc
; i
++)
299 if (type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
301 if (i
!= 0 && grub_script_argv_next (&result
))
304 if (grub_script_argv_split_append (&result
, scope
->argv
.args
[i
]))
309 if (i
!= 0 && grub_script_argv_append (&result
, " ", 1))
312 if (grub_script_argv_append (&result
, scope
->argv
.args
[i
],
313 grub_strlen (scope
->argv
.args
[i
])))
317 else if (grub_strcmp (name
, "@") == 0)
319 for (i
= 0; i
< scope
->argv
.argc
; i
++)
321 if (i
!= 0 && grub_script_argv_next (&result
))
324 if (type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
326 if (grub_script_argv_split_append (&result
, scope
->argv
.args
[i
]))
330 if (grub_script_argv_append (&result
, scope
->argv
.args
[i
],
331 grub_strlen (scope
->argv
.args
[i
])))
337 unsigned long num
= grub_strtoul (name
, 0, 10);
339 ; /* XXX no file name, for now. */
341 else if (num
<= scope
->argv
.argc
)
343 if (type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
345 if (grub_script_argv_split_append (&result
,
346 scope
->argv
.args
[num
- 1]))
350 if (grub_script_argv_append (&result
, scope
->argv
.args
[num
- 1],
351 grub_strlen (scope
->argv
.args
[num
- 1])
361 grub_script_argv_free (&result
);
366 grub_script_env_set (const char *name
, const char *val
)
368 if (grub_env_special (name
))
369 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
370 N_("invalid variable name `%s'"), name
);
372 return grub_env_set (name
, val
);
376 parse_string (const char *str
,
377 int (*hook
) (const char *var
, grub_size_t varlen
),
384 for (ptr
= str
; ptr
&& *ptr
; )
409 ptr
= grub_strchr (optr
, '}');
412 if (hook (optr
, ptr
- optr
))
419 while (*ptr
>= '0' && *ptr
<= '9')
421 if (hook (optr
, ptr
- optr
))
428 while ((*ptr
>= '0' && *ptr
<= '9')
429 || (*ptr
>= 'a' && *ptr
<= 'z')
430 || (*ptr
>= 'A' && *ptr
<= 'Z')
433 if (hook (optr
, ptr
- optr
))
460 gettext_append (struct grub_script_argv
*result
, const char *orig_str
)
462 const char *template;
464 char **allowed_strings
;
465 grub_size_t nallowed_strings
= 0;
466 grub_size_t additional_len
= 1;
470 auto int save_allow (const char *str
, grub_size_t len
);
471 int save_allow (const char *str
, grub_size_t len
)
473 allowed_strings
[nallowed_strings
++] = grub_strndup (str
, len
);
474 if (!allowed_strings
[nallowed_strings
- 1])
479 auto int getlen (const char *str
, grub_size_t len
);
480 int getlen (const char *str
, grub_size_t len
)
485 for (i
= 0; i
< nallowed_strings
; i
++)
486 if (grub_strncmp (allowed_strings
[i
], str
, len
) == 0
487 && allowed_strings
[i
][len
] == 0)
489 if (i
== nallowed_strings
)
492 /* Enough for any number. */
493 if (len
== 1 && str
[0] == '#')
495 additional_len
+= 30;
498 var
= grub_env_get (allowed_strings
[i
]);
500 additional_len
+= grub_strlen (var
);
504 auto int putvar (const char *str
, grub_size_t len
);
505 int putvar (const char *str
, grub_size_t len
)
510 for (i
= 0; i
< nallowed_strings
; i
++)
511 if (grub_strncmp (allowed_strings
[i
], str
, len
) == 0
512 && allowed_strings
[i
][len
] == 0)
516 if (i
== nallowed_strings
)
519 /* Enough for any number. */
520 if (len
== 1 && str
[0] == '#')
522 grub_snprintf (ptr
, 30, "%u", scope
->argv
.argc
);
523 ptr
+= grub_strlen (ptr
);
526 var
= grub_env_get (allowed_strings
[i
]);
528 ptr
= grub_stpcpy (ptr
, var
);
532 grub_size_t dollar_cnt
= 0;
534 for (iptr
= orig_str
; *iptr
; iptr
++)
537 allowed_strings
= grub_malloc (sizeof (allowed_strings
[0]) * dollar_cnt
);
539 if (parse_string (orig_str
, save_allow
, 0))
542 template = _(orig_str
);
544 if (parse_string (template, getlen
, 0))
547 res
= grub_malloc (grub_strlen (template) + additional_len
);
552 if (parse_string (template, putvar
, &ptr
))
557 escaped
= wildcard_escape (res
);
558 if (grub_script_argv_append (result
, escaped
, grub_strlen (escaped
)))
570 for (i
= 0; i
< nallowed_strings
; i
++)
571 grub_free (allowed_strings
[i
]);
573 grub_free (allowed_strings
);
577 /* Convert arguments in ARGLIST into ARGV form. */
579 grub_script_arglist_to_argv (struct grub_script_arglist
*arglist
,
580 struct grub_script_argv
*argv
)
584 struct grub_script_arg
*arg
= 0;
585 struct grub_script_argv result
= { 0, 0, 0 };
587 auto int append (const char *s
, int escape_type
);
588 int append (const char *s
, int escape_type
)
593 if (escape_type
== 0)
594 return grub_script_argv_append (&result
, s
, grub_strlen (s
));
597 p
= wildcard_escape (s
);
598 else if (escape_type
< 0)
599 p
= wildcard_unescape (s
);
604 r
= grub_script_argv_append (&result
, p
, grub_strlen (p
));
609 for (; arglist
&& arglist
->arg
; arglist
= arglist
->next
)
611 if (grub_script_argv_next (&result
))
619 case GRUB_SCRIPT_ARG_TYPE_VAR
:
620 case GRUB_SCRIPT_ARG_TYPE_DQVAR
:
621 values
= grub_script_env_get (arg
->str
, arg
->type
);
622 for (i
= 0; values
&& values
[i
]; i
++)
624 if (i
!= 0 && grub_script_argv_next (&result
))
627 if (arg
->type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
629 if (grub_script_argv_append (&result
, values
[i
],
630 grub_strlen (values
[i
])))
635 if (append (values
[i
], 1))
639 grub_free (values
[i
]);
644 case GRUB_SCRIPT_ARG_TYPE_BLOCK
:
647 if (grub_script_argv_append (&result
, "{", 1))
649 p
= wildcard_escape (arg
->str
);
652 if (grub_script_argv_append (&result
, p
,
659 if (grub_script_argv_append (&result
, "}", 1))
662 result
.script
= arg
->script
;
665 case GRUB_SCRIPT_ARG_TYPE_TEXT
:
667 grub_script_argv_append (&result
, arg
->str
,
668 grub_strlen (arg
->str
)))
672 case GRUB_SCRIPT_ARG_TYPE_GETTEXT
:
674 if (gettext_append (&result
, arg
->str
))
679 case GRUB_SCRIPT_ARG_TYPE_DQSTR
:
680 case GRUB_SCRIPT_ARG_TYPE_SQSTR
:
681 if (append (arg
->str
, 1))
689 if (! result
.args
[result
.argc
- 1])
692 /* Perform wildcard expansion. */
696 struct grub_script_argv unexpanded
= result
;
700 for (i
= 0; unexpanded
.args
[i
]; i
++)
702 char **expansions
= 0;
703 if (grub_wildcard_translator
704 && grub_wildcard_translator
->expand (unexpanded
.args
[i
],
707 grub_script_argv_free (&unexpanded
);
713 grub_script_argv_next (&result
);
714 append (unexpanded
.args
[i
], -1);
718 for (j
= 0; expansions
[j
]; j
++)
720 failed
= (failed
|| grub_script_argv_next (&result
) ||
721 append (expansions
[j
], 0));
722 grub_free (expansions
[j
]);
724 grub_free (expansions
);
728 grub_script_argv_free (&unexpanded
);
733 grub_script_argv_free (&unexpanded
);
740 grub_script_argv_free (&result
);
745 grub_script_execute_cmd (struct grub_script_cmd
*cmd
)
748 char errnobuf
[ERRNO_DIGITS_MAX
+ 1];
753 ret
= cmd
->exec (cmd
);
755 grub_snprintf (errnobuf
, sizeof (errnobuf
), "%d", ret
);
756 grub_env_set ("?", errnobuf
);
760 /* Execute a function call. */
762 grub_script_function_call (grub_script_function_t func
, int argc
, char **args
)
765 unsigned long loops
= active_loops
;
766 struct grub_script_scope
*old_scope
;
767 struct grub_script_scope new_scope
;
771 new_scope
.shifts
= 0;
772 new_scope
.argv
.argc
= argc
;
773 new_scope
.argv
.args
= args
;
778 ret
= grub_script_execute (func
->func
);
781 active_loops
= loops
;
782 replace_scope (old_scope
); /* free any scopes by setparams */
786 /* Execute a source script. */
788 grub_script_execute_sourcecode (const char *source
, int argc
, char **args
)
791 struct grub_script
*parsed_script
;
792 struct grub_script_scope new_scope
;
793 struct grub_script_scope
*old_scope
;
795 auto grub_err_t
getline (char **line
, int cont
);
796 grub_err_t
getline (char **line
, int cont
__attribute__ ((unused
)))
806 p
= grub_strchr (source
, '\n');
809 *line
= grub_strndup (source
, p
- source
);
811 *line
= grub_strdup (source
);
812 source
= p
? p
+ 1 : 0;
816 new_scope
.argv
.argc
= argc
;
817 new_scope
.argv
.args
= args
;
828 parsed_script
= grub_script_parse (line
, getline
);
835 ret
= grub_script_execute (parsed_script
);
843 /* Execute a single command line. */
845 grub_script_execute_cmdline (struct grub_script_cmd
*cmd
)
847 struct grub_script_cmdline
*cmdline
= (struct grub_script_cmdline
*) cmd
;
848 grub_command_t grubcmd
;
850 grub_script_function_t func
= 0;
856 struct grub_script_argv argv
= { 0, 0, 0 };
858 /* Lookup the command. */
859 if (grub_script_arglist_to_argv (cmdline
->arglist
, &argv
) || ! argv
.args
[0])
863 argc
= argv
.argc
- 1;
864 args
= argv
.args
+ 1;
865 cmdname
= argv
.args
[0];
866 if (grub_strcmp (cmdname
, "!") == 0)
868 if (argv
.argc
< 2 || ! argv
.args
[1])
870 grub_script_argv_free (&argv
);
871 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
872 N_("no command is specified"));
876 argc
= argv
.argc
- 2;
877 args
= argv
.args
+ 2;
878 cmdname
= argv
.args
[1];
880 grubcmd
= grub_command_find (cmdname
);
883 grub_errno
= GRUB_ERR_NONE
;
885 /* It's not a GRUB command, try all functions. */
886 func
= grub_script_function_find (cmdname
);
889 /* As a last resort, try if it is an assignment. */
890 char *assign
= grub_strdup (cmdname
);
891 char *eq
= grub_strchr (assign
, '=');
895 /* This was set because the command was not found. */
896 grub_errno
= GRUB_ERR_NONE
;
898 /* Create two strings and set the variable. */
901 grub_script_env_set (assign
, eq
);
905 grub_snprintf (errnobuf
, sizeof (errnobuf
), "%d", grub_errno
);
906 grub_script_env_set ("?", errnobuf
);
908 grub_script_argv_free (&argv
);
915 /* Execute the GRUB command or function. */
918 if (grub_extractor_level
&& !(grubcmd
->flags
919 & GRUB_COMMAND_FLAG_EXTRACTOR
))
920 ret
= grub_error (GRUB_ERR_EXTRACTOR
,
921 "%s isn't allowed to execute in an extractor",
923 else if ((grubcmd
->flags
& GRUB_COMMAND_FLAG_BLOCKS
) &&
924 (grubcmd
->flags
& GRUB_COMMAND_FLAG_EXTCMD
))
925 ret
= grub_extcmd_dispatcher (grubcmd
, argc
, args
, argv
.script
);
927 ret
= (grubcmd
->func
) (grubcmd
, argc
, args
);
930 ret
= grub_script_function_call (func
, argc
, args
);
934 if (ret
== GRUB_ERR_TEST_FAILURE
)
935 grub_errno
= ret
= GRUB_ERR_NONE
;
936 else if (ret
== GRUB_ERR_NONE
)
937 ret
= grub_error (GRUB_ERR_TEST_FAILURE
, N_("false"));
945 /* Free arguments. */
946 grub_script_argv_free (&argv
);
948 if (grub_errno
== GRUB_ERR_TEST_FAILURE
)
949 grub_errno
= GRUB_ERR_NONE
;
953 grub_snprintf (errnobuf
, sizeof (errnobuf
), "%d", ret
);
954 grub_env_set ("?", errnobuf
);
959 /* Execute a block of one or more commands. */
961 grub_script_execute_cmdlist (struct grub_script_cmd
*list
)
964 struct grub_script_cmd
*cmd
;
966 /* Loop over every command and execute it. */
967 for (cmd
= list
->next
; cmd
; cmd
= cmd
->next
)
972 ret
= grub_script_execute_cmd (cmd
);
981 /* Execute an if statement. */
983 grub_script_execute_cmdif (struct grub_script_cmd
*cmd
)
987 struct grub_script_cmdif
*cmdif
= (struct grub_script_cmdif
*) cmd
;
989 /* Check if the commands results in a true or a false. The value is
990 read from the env variable `?'. */
991 ret
= grub_script_execute_cmd (cmdif
->exec_to_evaluate
);
995 result
= grub_env_get ("?");
996 grub_errno
= GRUB_ERR_NONE
;
998 /* Execute the `if' or the `else' part depending on the value of
1000 if (result
&& ! grub_strcmp (result
, "0"))
1001 return grub_script_execute_cmd (cmdif
->exec_on_true
);
1003 return grub_script_execute_cmd (cmdif
->exec_on_false
);
1006 /* Execute a for statement. */
1008 grub_script_execute_cmdfor (struct grub_script_cmd
*cmd
)
1012 struct grub_script_argv argv
= { 0, 0, 0 };
1013 struct grub_script_cmdfor
*cmdfor
= (struct grub_script_cmdfor
*) cmd
;
1015 if (grub_script_arglist_to_argv (cmdfor
->words
, &argv
))
1020 for (i
= 0; i
< argv
.argc
; i
++)
1022 if (is_continue
&& active_breaks
== 1)
1025 if (! active_breaks
)
1027 grub_script_env_set (cmdfor
->name
->str
, argv
.args
[i
]);
1028 result
= grub_script_execute_cmd (cmdfor
->list
);
1029 if (function_return
)
1038 grub_script_argv_free (&argv
);
1042 /* Execute a "while" or "until" command. */
1044 grub_script_execute_cmdwhile (struct grub_script_cmd
*cmd
)
1047 struct grub_script_cmdwhile
*cmdwhile
= (struct grub_script_cmdwhile
*) cmd
;
1051 result
= grub_script_execute_cmd (cmdwhile
->cond
);
1052 if (function_return
)
1055 if (cmdwhile
->until
? !result
: result
)
1058 result
= grub_script_execute_cmd (cmdwhile
->list
);
1059 if (function_return
)
1062 if (active_breaks
== 1 && is_continue
)
1068 } while (1); /* XXX Put a check for ^C here */
1077 /* Execute any GRUB pre-parsed command or script. */
1079 grub_script_execute (struct grub_script
*script
)
1084 return grub_script_execute_cmd (script
->cmd
);