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
);
375 struct gettext_context
377 char **allowed_strings
;
378 grub_size_t nallowed_strings
;
379 grub_size_t additional_len
;
383 parse_string (const char *str
,
384 int (*hook
) (const char *var
, grub_size_t varlen
,
385 char **ptr
, struct gettext_context
*ctx
),
386 struct gettext_context
*ctx
,
393 for (ptr
= str
; ptr
&& *ptr
; )
418 ptr
= grub_strchr (optr
, '}');
421 if (hook (optr
, ptr
- optr
, &put
, ctx
))
428 while (*ptr
>= '0' && *ptr
<= '9')
430 if (hook (optr
, ptr
- optr
, &put
, ctx
))
437 while ((*ptr
>= '0' && *ptr
<= '9')
438 || (*ptr
>= 'a' && *ptr
<= 'z')
439 || (*ptr
>= 'A' && *ptr
<= 'Z')
442 if (hook (optr
, ptr
- optr
, &put
, ctx
))
447 if (hook (ptr
, 1, &put
, ctx
))
471 gettext_putvar (const char *str
, grub_size_t len
,
472 char **ptr
, struct gettext_context
*ctx
)
477 for (i
= 0; i
< ctx
->nallowed_strings
; i
++)
478 if (grub_strncmp (ctx
->allowed_strings
[i
], str
, len
) == 0
479 && ctx
->allowed_strings
[i
][len
] == 0)
483 if (i
== ctx
->nallowed_strings
)
486 /* Enough for any number. */
487 if (len
== 1 && str
[0] == '#')
489 grub_snprintf (*ptr
, 30, "%u", scope
->argv
.argc
);
490 *ptr
+= grub_strlen (*ptr
);
493 var
= grub_env_get (ctx
->allowed_strings
[i
]);
495 *ptr
= grub_stpcpy (*ptr
, var
);
500 gettext_save_allow (const char *str
, grub_size_t len
,
501 char **ptr
__attribute__ ((unused
)),
502 struct gettext_context
*ctx
)
504 ctx
->allowed_strings
[ctx
->nallowed_strings
++] = grub_strndup (str
, len
);
505 if (!ctx
->allowed_strings
[ctx
->nallowed_strings
- 1])
511 gettext_getlen (const char *str
, grub_size_t len
,
512 char **ptr
__attribute__ ((unused
)),
513 struct gettext_context
*ctx
)
518 for (i
= 0; i
< ctx
->nallowed_strings
; i
++)
519 if (grub_strncmp (ctx
->allowed_strings
[i
], str
, len
) == 0
520 && ctx
->allowed_strings
[i
][len
] == 0)
522 if (i
== ctx
->nallowed_strings
)
525 /* Enough for any number. */
526 if (len
== 1 && str
[0] == '#')
528 ctx
->additional_len
+= 30;
531 var
= grub_env_get (ctx
->allowed_strings
[i
]);
533 ctx
->additional_len
+= grub_strlen (var
);
538 gettext_append (struct grub_script_argv
*result
, const char *orig_str
)
540 const char *template;
542 struct gettext_context ctx
= {
543 .allowed_strings
= 0,
544 .nallowed_strings
= 0,
550 grub_size_t dollar_cnt
= 0;
552 for (iptr
= orig_str
; *iptr
; iptr
++)
555 ctx
.allowed_strings
= grub_malloc (sizeof (ctx
.allowed_strings
[0]) * dollar_cnt
);
557 if (parse_string (orig_str
, gettext_save_allow
, &ctx
, 0))
560 template = _(orig_str
);
562 if (parse_string (template, gettext_getlen
, &ctx
, 0))
565 res
= grub_malloc (grub_strlen (template) + ctx
.additional_len
);
569 if (parse_string (template, gettext_putvar
, &ctx
, res
))
573 escaped
= wildcard_escape (res
);
574 if (grub_script_argv_append (result
, escaped
, grub_strlen (escaped
)))
586 for (i
= 0; i
< ctx
.nallowed_strings
; i
++)
587 grub_free (ctx
.allowed_strings
[i
]);
589 grub_free (ctx
.allowed_strings
);
594 append (struct grub_script_argv
*result
,
595 const char *s
, int escape_type
)
600 if (escape_type
== 0)
601 return grub_script_argv_append (result
, s
, grub_strlen (s
));
604 p
= wildcard_escape (s
);
605 else if (escape_type
< 0)
606 p
= wildcard_unescape (s
);
611 r
= grub_script_argv_append (result
, p
, grub_strlen (p
));
616 /* Convert arguments in ARGLIST into ARGV form. */
618 grub_script_arglist_to_argv (struct grub_script_arglist
*arglist
,
619 struct grub_script_argv
*argv
)
623 struct grub_script_arg
*arg
= 0;
624 struct grub_script_argv result
= { 0, 0, 0 };
626 for (; arglist
&& arglist
->arg
; arglist
= arglist
->next
)
628 if (grub_script_argv_next (&result
))
636 case GRUB_SCRIPT_ARG_TYPE_VAR
:
637 case GRUB_SCRIPT_ARG_TYPE_DQVAR
:
639 int need_cleanup
= 0;
641 values
= grub_script_env_get (arg
->str
, arg
->type
);
642 for (i
= 0; values
&& values
[i
]; i
++)
646 if (i
!= 0 && grub_script_argv_next (&result
))
652 if (arg
->type
== GRUB_SCRIPT_ARG_TYPE_VAR
)
658 const char *s
= values
[i
];
660 len
= grub_strlen (values
[i
]);
664 p
= grub_malloc (len
* 2 + 1);
677 if (*s
== '?' || *s
== '*')
684 if (grub_script_argv_append (&result
, p
, op
- p
))
688 /* Fall through to cleanup */
693 if (append (&result
, values
[i
], 1))
695 /* Fall through to cleanup */
700 grub_free (values
[i
]);
710 case GRUB_SCRIPT_ARG_TYPE_BLOCK
:
713 if (grub_script_argv_append (&result
, "{", 1))
715 p
= wildcard_escape (arg
->str
);
718 if (grub_script_argv_append (&result
, p
,
725 if (grub_script_argv_append (&result
, "}", 1))
728 result
.script
= arg
->script
;
731 case GRUB_SCRIPT_ARG_TYPE_TEXT
:
733 grub_script_argv_append (&result
, arg
->str
,
734 grub_strlen (arg
->str
)))
738 case GRUB_SCRIPT_ARG_TYPE_GETTEXT
:
740 if (gettext_append (&result
, arg
->str
))
745 case GRUB_SCRIPT_ARG_TYPE_DQSTR
:
746 case GRUB_SCRIPT_ARG_TYPE_SQSTR
:
747 if (append (&result
, arg
->str
, 1))
755 if (! result
.args
[result
.argc
- 1])
758 /* Perform wildcard expansion. */
762 struct grub_script_argv unexpanded
= result
;
766 for (i
= 0; unexpanded
.args
[i
]; i
++)
768 char **expansions
= 0;
769 if (grub_wildcard_translator
770 && grub_wildcard_translator
->expand (unexpanded
.args
[i
],
773 grub_script_argv_free (&unexpanded
);
779 grub_script_argv_next (&result
);
780 append (&result
, unexpanded
.args
[i
], -1);
784 for (j
= 0; expansions
[j
]; j
++)
786 failed
= (failed
|| grub_script_argv_next (&result
) ||
787 append (&result
, expansions
[j
], 0));
788 grub_free (expansions
[j
]);
790 grub_free (expansions
);
794 grub_script_argv_free (&unexpanded
);
799 grub_script_argv_free (&unexpanded
);
806 grub_script_argv_free (&result
);
811 grub_script_execute_cmd (struct grub_script_cmd
*cmd
)
814 char errnobuf
[ERRNO_DIGITS_MAX
+ 1];
819 ret
= cmd
->exec (cmd
);
821 grub_snprintf (errnobuf
, sizeof (errnobuf
), "%d", ret
);
822 grub_env_set ("?", errnobuf
);
826 /* Execute a function call. */
828 grub_script_function_call (grub_script_function_t func
, int argc
, char **args
)
831 unsigned long loops
= active_loops
;
832 struct grub_script_scope
*old_scope
;
833 struct grub_script_scope new_scope
;
837 new_scope
.shifts
= 0;
838 new_scope
.argv
.argc
= argc
;
839 new_scope
.argv
.args
= args
;
844 ret
= grub_script_execute (func
->func
);
847 active_loops
= loops
;
848 replace_scope (old_scope
); /* free any scopes by setparams */
852 /* Helper for grub_script_execute_sourcecode. */
854 grub_script_execute_sourcecode_getline (char **line
,
855 int cont
__attribute__ ((unused
)),
858 const char **source
= data
;
867 p
= grub_strchr (*source
, '\n');
870 *line
= grub_strndup (*source
, p
- *source
);
872 *line
= grub_strdup (*source
);
873 *source
= p
? p
+ 1 : 0;
877 /* Execute a source script. */
879 grub_script_execute_sourcecode (const char *source
)
882 struct grub_script
*parsed_script
;
888 grub_script_execute_sourcecode_getline (&line
, 0, &source
);
889 parsed_script
= grub_script_parse
890 (line
, grub_script_execute_sourcecode_getline
, &source
);
898 ret
= grub_script_execute (parsed_script
);
899 grub_script_free (parsed_script
);
906 /* Execute a source script in new scope. */
908 grub_script_execute_new_scope (const char *source
, int argc
, char **args
)
911 struct grub_script_scope new_scope
;
912 struct grub_script_scope
*old_scope
;
914 new_scope
.argv
.argc
= argc
;
915 new_scope
.argv
.args
= args
;
921 ret
= grub_script_execute_sourcecode (source
);
927 /* Execute a single command line. */
929 grub_script_execute_cmdline (struct grub_script_cmd
*cmd
)
931 struct grub_script_cmdline
*cmdline
= (struct grub_script_cmdline
*) cmd
;
932 grub_command_t grubcmd
;
934 grub_script_function_t func
= 0;
940 struct grub_script_argv argv
= { 0, 0, 0 };
942 /* Lookup the command. */
943 if (grub_script_arglist_to_argv (cmdline
->arglist
, &argv
) || ! argv
.args
[0])
947 argc
= argv
.argc
- 1;
948 args
= argv
.args
+ 1;
949 cmdname
= argv
.args
[0];
950 if (grub_strcmp (cmdname
, "!") == 0)
952 if (argv
.argc
< 2 || ! argv
.args
[1])
954 grub_script_argv_free (&argv
);
955 return grub_error (GRUB_ERR_BAD_ARGUMENT
,
956 N_("no command is specified"));
960 argc
= argv
.argc
- 2;
961 args
= argv
.args
+ 2;
962 cmdname
= argv
.args
[1];
964 grubcmd
= grub_command_find (cmdname
);
967 grub_errno
= GRUB_ERR_NONE
;
969 /* It's not a GRUB command, try all functions. */
970 func
= grub_script_function_find (cmdname
);
973 /* As a last resort, try if it is an assignment. */
974 char *assign
= grub_strdup (cmdname
);
975 char *eq
= grub_strchr (assign
, '=');
979 /* This was set because the command was not found. */
980 grub_errno
= GRUB_ERR_NONE
;
982 /* Create two strings and set the variable. */
985 grub_script_env_set (assign
, eq
);
989 grub_snprintf (errnobuf
, sizeof (errnobuf
), "%d", grub_errno
);
990 grub_script_env_set ("?", errnobuf
);
992 grub_script_argv_free (&argv
);
999 /* Execute the GRUB command or function. */
1002 if (grub_extractor_level
&& !(grubcmd
->flags
1003 & GRUB_COMMAND_FLAG_EXTRACTOR
))
1004 ret
= grub_error (GRUB_ERR_EXTRACTOR
,
1005 "%s isn't allowed to execute in an extractor",
1007 else if ((grubcmd
->flags
& GRUB_COMMAND_FLAG_BLOCKS
) &&
1008 (grubcmd
->flags
& GRUB_COMMAND_FLAG_EXTCMD
))
1009 ret
= grub_extcmd_dispatcher (grubcmd
, argc
, args
, argv
.script
);
1011 ret
= (grubcmd
->func
) (grubcmd
, argc
, args
);
1014 ret
= grub_script_function_call (func
, argc
, args
);
1018 if (ret
== GRUB_ERR_TEST_FAILURE
)
1019 grub_errno
= ret
= GRUB_ERR_NONE
;
1020 else if (ret
== GRUB_ERR_NONE
)
1021 ret
= grub_error (GRUB_ERR_TEST_FAILURE
, N_("false"));
1024 grub_print_error ();
1025 ret
= GRUB_ERR_NONE
;
1029 /* Free arguments. */
1030 grub_script_argv_free (&argv
);
1032 if (grub_errno
== GRUB_ERR_TEST_FAILURE
)
1033 grub_errno
= GRUB_ERR_NONE
;
1035 grub_print_error ();
1037 grub_snprintf (errnobuf
, sizeof (errnobuf
), "%d", ret
);
1038 grub_env_set ("?", errnobuf
);
1043 /* Execute a block of one or more commands. */
1045 grub_script_execute_cmdlist (struct grub_script_cmd
*list
)
1048 struct grub_script_cmd
*cmd
;
1050 /* Loop over every command and execute it. */
1051 for (cmd
= list
->next
; cmd
; cmd
= cmd
->next
)
1056 ret
= grub_script_execute_cmd (cmd
);
1058 if (function_return
)
1065 /* Execute an if statement. */
1067 grub_script_execute_cmdif (struct grub_script_cmd
*cmd
)
1071 struct grub_script_cmdif
*cmdif
= (struct grub_script_cmdif
*) cmd
;
1073 /* Check if the commands results in a true or a false. The value is
1074 read from the env variable `?'. */
1075 ret
= grub_script_execute_cmd (cmdif
->exec_to_evaluate
);
1076 if (function_return
)
1079 result
= grub_env_get ("?");
1080 grub_errno
= GRUB_ERR_NONE
;
1082 /* Execute the `if' or the `else' part depending on the value of
1084 if (result
&& ! grub_strcmp (result
, "0"))
1085 return grub_script_execute_cmd (cmdif
->exec_on_true
);
1087 return grub_script_execute_cmd (cmdif
->exec_on_false
);
1090 /* Execute a for statement. */
1092 grub_script_execute_cmdfor (struct grub_script_cmd
*cmd
)
1096 struct grub_script_argv argv
= { 0, 0, 0 };
1097 struct grub_script_cmdfor
*cmdfor
= (struct grub_script_cmdfor
*) cmd
;
1099 if (grub_script_arglist_to_argv (cmdfor
->words
, &argv
))
1104 for (i
= 0; i
< argv
.argc
; i
++)
1106 if (is_continue
&& active_breaks
== 1)
1109 if (! active_breaks
)
1111 grub_script_env_set (cmdfor
->name
->str
, argv
.args
[i
]);
1112 result
= grub_script_execute_cmd (cmdfor
->list
);
1113 if (function_return
)
1122 grub_script_argv_free (&argv
);
1126 /* Execute a "while" or "until" command. */
1128 grub_script_execute_cmdwhile (struct grub_script_cmd
*cmd
)
1131 struct grub_script_cmdwhile
*cmdwhile
= (struct grub_script_cmdwhile
*) cmd
;
1135 result
= grub_script_execute_cmd (cmdwhile
->cond
);
1136 if (function_return
)
1139 if (cmdwhile
->until
? !result
: result
)
1142 result
= grub_script_execute_cmd (cmdwhile
->list
);
1143 if (function_return
)
1146 if (active_breaks
== 1 && is_continue
)
1152 } while (1); /* XXX Put a check for ^C here */
1161 /* Execute any GRUB pre-parsed command or script. */
1163 grub_script_execute (struct grub_script
*script
)
1168 return grub_script_execute_cmd (script
->cmd
);