12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193 |
- /* execute.c -- Execute a GRUB script. */
- /*
- * GRUB -- GRand Unified Bootloader
- * Copyright (C) 2005,2007,2008,2009,2010 Free Software Foundation, Inc.
- *
- * GRUB is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * GRUB is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with GRUB. If not, see <http://www.gnu.org/licenses/>.
- */
- #include <grub/misc.h>
- #include <grub/mm.h>
- #include <grub/env.h>
- #include <grub/script_sh.h>
- #include <grub/command.h>
- #include <grub/menu.h>
- #include <grub/lib/arg.h>
- #include <grub/normal.h>
- #include <grub/extcmd.h>
- #include <grub/i18n.h>
- #include <grub/verify.h>
- /* Max digits for a char is 3 (0xFF is 255), similarly for an int it
- is sizeof (int) * 3, and one extra for a possible -ve sign. */
- #define ERRNO_DIGITS_MAX (sizeof (int) * 3 + 1)
- static unsigned long is_continue;
- static unsigned long active_loops;
- static unsigned long active_breaks;
- static unsigned long function_return;
- #define GRUB_SCRIPT_SCOPE_MALLOCED 1
- #define GRUB_SCRIPT_SCOPE_ARGS_MALLOCED 2
- /* Scope for grub script functions. */
- struct grub_script_scope
- {
- unsigned flags;
- unsigned shifts;
- struct grub_script_argv argv;
- };
- static struct grub_script_scope *scope = 0;
- /* Wildcard translator for GRUB script. */
- struct grub_script_wildcard_translator *grub_wildcard_translator;
- static char*
- wildcard_escape (const char *s)
- {
- int i;
- int len;
- char ch;
- char *p;
- len = grub_strlen (s);
- p = grub_malloc (len * 2 + 1);
- if (! p)
- return NULL;
- i = 0;
- while ((ch = *s++))
- {
- if (ch == '*' || ch == '\\' || ch == '?')
- p[i++] = '\\';
- p[i++] = ch;
- }
- p[i] = '\0';
- return p;
- }
- static char*
- wildcard_unescape (const char *s)
- {
- int i;
- int len;
- char ch;
- char *p;
- len = grub_strlen (s);
- p = grub_malloc (len + 1);
- if (! p)
- return NULL;
- i = 0;
- while ((ch = *s++))
- {
- if (ch == '\\')
- p[i++] = *s++;
- else
- p[i++] = ch;
- }
- p[i] = '\0';
- return p;
- }
- static void
- replace_scope (struct grub_script_scope *new_scope)
- {
- if (scope)
- {
- scope->argv.argc += scope->shifts;
- scope->argv.args -= scope->shifts;
- if (scope->flags & GRUB_SCRIPT_SCOPE_ARGS_MALLOCED)
- grub_script_argv_free (&scope->argv);
- if (scope->flags & GRUB_SCRIPT_SCOPE_MALLOCED)
- grub_free (scope);
- }
- scope = new_scope;
- }
- grub_err_t
- grub_script_break (grub_command_t cmd, int argc, char *argv[])
- {
- const char *p = NULL;
- unsigned long count;
- if (argc == 0)
- count = 1;
- else if (argc > 1)
- return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("one argument expected"));
- else
- {
- count = grub_strtoul (argv[0], &p, 10);
- if (grub_errno)
- return grub_errno;
- if (*p != '\0')
- return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("unrecognized number"));
- if (count == 0)
- /* TRANSLATORS: 0 is a quantifier. "break" (similar to bash)
- can be used e.g. to break 3 loops at once.
- But asking it to break 0 loops makes no sense. */
- return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("can't break 0 loops"));
- }
- is_continue = grub_strcmp (cmd->name, "break") ? 1 : 0;
- active_breaks = count;
- if (active_breaks > active_loops)
- active_breaks = active_loops;
- return GRUB_ERR_NONE;
- }
- grub_err_t
- grub_script_shift (grub_command_t cmd __attribute__((unused)),
- int argc, char *argv[])
- {
- const char *p = NULL;
- unsigned long n = 0;
- if (! scope)
- return GRUB_ERR_NONE;
- if (argc == 0)
- n = 1;
- else if (argc > 1)
- return GRUB_ERR_BAD_ARGUMENT;
- else
- {
- n = grub_strtoul (argv[0], &p, 10);
- if (*p != '\0')
- return GRUB_ERR_BAD_ARGUMENT;
- }
- if (n > scope->argv.argc)
- return GRUB_ERR_BAD_ARGUMENT;
- scope->shifts += n;
- scope->argv.argc -= n;
- scope->argv.args += n;
- return GRUB_ERR_NONE;
- }
- grub_err_t
- grub_script_setparams (grub_command_t cmd __attribute__((unused)),
- int argc, char **args)
- {
- struct grub_script_scope *new_scope;
- struct grub_script_argv argv = { 0, 0, 0 };
- if (! scope)
- return GRUB_ERR_INVALID_COMMAND;
- new_scope = grub_malloc (sizeof (*new_scope));
- if (! new_scope)
- return grub_errno;
- if (grub_script_argv_make (&argv, argc, args))
- {
- grub_free (new_scope);
- return grub_errno;
- }
- new_scope->shifts = 0;
- new_scope->argv = argv;
- new_scope->flags = GRUB_SCRIPT_SCOPE_MALLOCED |
- GRUB_SCRIPT_SCOPE_ARGS_MALLOCED;
- replace_scope (new_scope);
- return GRUB_ERR_NONE;
- }
- grub_err_t
- grub_script_return (grub_command_t cmd __attribute__((unused)),
- int argc, char *argv[])
- {
- const char *p = NULL;
- unsigned long n;
- if (! scope || argc > 1)
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
- /* TRANSLATORS: It's about not being
- inside a function. "return" can be used only
- in a function and this error occurs if it's used
- anywhere else. */
- N_("not in function body"));
- if (argc == 0)
- {
- const char *t;
- function_return = 1;
- t = grub_env_get ("?");
- if (!t)
- return GRUB_ERR_NONE;
- return grub_strtoul (t, NULL, 10);
- }
- n = grub_strtoul (argv[0], &p, 10);
- if (grub_errno)
- return grub_errno;
- if (*p != '\0')
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
- N_("unrecognized number"));
- function_return = 1;
- return n ? grub_error (n, N_("false")) : GRUB_ERR_NONE;
- }
- static int
- grub_env_special (const char *name)
- {
- if (grub_isdigit (name[0]) ||
- grub_strcmp (name, "#") == 0 ||
- grub_strcmp (name, "*") == 0 ||
- grub_strcmp (name, "@") == 0)
- return 1;
- return 0;
- }
- static char **
- grub_script_env_get (const char *name, grub_script_arg_type_t type)
- {
- unsigned i;
- struct grub_script_argv result = { 0, 0, 0 };
- if (grub_script_argv_next (&result))
- goto fail;
- if (! grub_env_special (name))
- {
- const char *v = grub_env_get (name);
- if (v && v[0])
- {
- if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
- {
- if (grub_script_argv_split_append (&result, v))
- goto fail;
- }
- else
- if (grub_script_argv_append (&result, v, grub_strlen (v)))
- goto fail;
- }
- }
- else if (! scope)
- {
- if (grub_script_argv_append (&result, 0, 0))
- goto fail;
- }
- else if (grub_strcmp (name, "#") == 0)
- {
- char buffer[ERRNO_DIGITS_MAX + 1];
- grub_snprintf (buffer, sizeof (buffer), "%u", scope->argv.argc);
- if (grub_script_argv_append (&result, buffer, grub_strlen (buffer)))
- goto fail;
- }
- else if (grub_strcmp (name, "*") == 0)
- {
- for (i = 0; i < scope->argv.argc; i++)
- if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
- {
- if (i != 0 && grub_script_argv_next (&result))
- goto fail;
- if (grub_script_argv_split_append (&result, scope->argv.args[i]))
- goto fail;
- }
- else
- {
- if (i != 0 && grub_script_argv_append (&result, " ", 1))
- goto fail;
- if (grub_script_argv_append (&result, scope->argv.args[i],
- grub_strlen (scope->argv.args[i])))
- goto fail;
- }
- }
- else if (grub_strcmp (name, "@") == 0)
- {
- for (i = 0; i < scope->argv.argc; i++)
- {
- if (i != 0 && grub_script_argv_next (&result))
- goto fail;
- if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
- {
- if (grub_script_argv_split_append (&result, scope->argv.args[i]))
- goto fail;
- }
- else
- if (grub_script_argv_append (&result, scope->argv.args[i],
- grub_strlen (scope->argv.args[i])))
- goto fail;
- }
- }
- else
- {
- unsigned long num = grub_strtoul (name, 0, 10);
- if (num == 0)
- ; /* XXX no file name, for now. */
- else if (num <= scope->argv.argc)
- {
- if (type == GRUB_SCRIPT_ARG_TYPE_VAR)
- {
- if (grub_script_argv_split_append (&result,
- scope->argv.args[num - 1]))
- goto fail;
- }
- else
- if (grub_script_argv_append (&result, scope->argv.args[num - 1],
- grub_strlen (scope->argv.args[num - 1])
- ))
- goto fail;
- }
- }
- return result.args;
- fail:
- grub_script_argv_free (&result);
- return 0;
- }
- static grub_err_t
- grub_script_env_set (const char *name, const char *val)
- {
- if (grub_env_special (name))
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
- N_("invalid variable name `%s'"), name);
- return grub_env_set (name, val);
- }
- struct gettext_context
- {
- char **allowed_strings;
- grub_size_t nallowed_strings;
- grub_size_t additional_len;
- };
- static int
- parse_string (const char *str,
- int (*hook) (const char *var, grub_size_t varlen,
- char **ptr, struct gettext_context *ctx),
- struct gettext_context *ctx,
- char *put)
- {
- const char *ptr;
- int escaped = 0;
- const char *optr;
- for (ptr = str; ptr && *ptr; )
- switch (*ptr)
- {
- case '\\':
- escaped = !escaped;
- if (!escaped && put)
- *(put++) = '\\';
- ptr++;
- break;
- case '$':
- if (escaped)
- {
- escaped = 0;
- if (put)
- *(put++) = *ptr;
- ptr++;
- break;
- }
- ptr++;
- switch (*ptr)
- {
- case '{':
- {
- optr = ptr + 1;
- ptr = grub_strchr (optr, '}');
- if (!ptr)
- break;
- if (hook (optr, ptr - optr, &put, ctx))
- return 1;
- ptr++;
- break;
- }
- case '0' ... '9':
- optr = ptr;
- while (*ptr >= '0' && *ptr <= '9')
- ptr++;
- if (hook (optr, ptr - optr, &put, ctx))
- return 1;
- break;
- case 'a' ... 'z':
- case 'A' ... 'Z':
- case '_':
- optr = ptr;
- while ((*ptr >= '0' && *ptr <= '9')
- || (*ptr >= 'a' && *ptr <= 'z')
- || (*ptr >= 'A' && *ptr <= 'Z')
- || *ptr == '_')
- ptr++;
- if (hook (optr, ptr - optr, &put, ctx))
- return 1;
- break;
- case '?':
- case '#':
- if (hook (ptr, 1, &put, ctx))
- return 1;
- ptr++;
- break;
- default:
- if (put)
- *(put++) = '$';
- }
- break;
- default:
- if (escaped && put)
- *(put++) = '\\';
- escaped = 0;
- if (put)
- *(put++) = *ptr;
- ptr++;
- break;
- }
- if (put)
- *(put++) = 0;
- return 0;
- }
- static int
- gettext_putvar (const char *str, grub_size_t len,
- char **ptr, struct gettext_context *ctx)
- {
- const char *var;
- grub_size_t i;
- for (i = 0; i < ctx->nallowed_strings; i++)
- if (grub_strncmp (ctx->allowed_strings[i], str, len) == 0
- && ctx->allowed_strings[i][len] == 0)
- {
- break;
- }
- if (i == ctx->nallowed_strings)
- return 0;
- /* Enough for any number. */
- if (len == 1 && str[0] == '#' && scope != NULL)
- {
- grub_snprintf (*ptr, 30, "%u", scope->argv.argc);
- *ptr += grub_strlen (*ptr);
- return 0;
- }
- var = grub_env_get (ctx->allowed_strings[i]);
- if (var)
- *ptr = grub_stpcpy (*ptr, var);
- return 0;
- }
- static int
- gettext_save_allow (const char *str, grub_size_t len,
- char **ptr __attribute__ ((unused)),
- struct gettext_context *ctx)
- {
- ctx->allowed_strings[ctx->nallowed_strings++] = grub_strndup (str, len);
- if (!ctx->allowed_strings[ctx->nallowed_strings - 1])
- return 1;
- return 0;
- }
- static int
- gettext_getlen (const char *str, grub_size_t len,
- char **ptr __attribute__ ((unused)),
- struct gettext_context *ctx)
- {
- const char *var;
- grub_size_t i;
- for (i = 0; i < ctx->nallowed_strings; i++)
- if (grub_strncmp (ctx->allowed_strings[i], str, len) == 0
- && ctx->allowed_strings[i][len] == 0)
- break;
- if (i == ctx->nallowed_strings)
- return 0;
- /* Enough for any number. */
- if (len == 1 && str[0] == '#')
- {
- ctx->additional_len += 30;
- return 0;
- }
- var = grub_env_get (ctx->allowed_strings[i]);
- if (var)
- ctx->additional_len += grub_strlen (var);
- return 0;
- }
- static int
- gettext_append (struct grub_script_argv *result, const char *orig_str)
- {
- const char *template;
- char *res = 0;
- struct gettext_context ctx = {
- .allowed_strings = 0,
- .nallowed_strings = 0,
- .additional_len = 1
- };
- int rval = 1;
- const char *iptr;
- grub_size_t dollar_cnt = 0;
- for (iptr = orig_str; *iptr; iptr++)
- if (*iptr == '$')
- dollar_cnt++;
- ctx.allowed_strings = grub_calloc (dollar_cnt, sizeof (ctx.allowed_strings[0]));
- if (parse_string (orig_str, gettext_save_allow, &ctx, 0))
- goto fail;
- template = _(orig_str);
- if (parse_string (template, gettext_getlen, &ctx, 0))
- goto fail;
- res = grub_malloc (grub_strlen (template) + ctx.additional_len);
- if (!res)
- goto fail;
- if (parse_string (template, gettext_putvar, &ctx, res))
- goto fail;
- char *escaped = 0;
- escaped = wildcard_escape (res);
- if (grub_script_argv_append (result, escaped, grub_strlen (escaped)))
- {
- grub_free (escaped);
- goto fail;
- }
- grub_free (escaped);
- rval = 0;
- fail:
- grub_free (res);
- {
- grub_size_t i;
- for (i = 0; i < ctx.nallowed_strings; i++)
- grub_free (ctx.allowed_strings[i]);
- }
- grub_free (ctx.allowed_strings);
- return rval;
- }
- static int
- append (struct grub_script_argv *result,
- const char *s, int escape_type)
- {
- int r;
- char *p = 0;
- if (escape_type == 0)
- return grub_script_argv_append (result, s, grub_strlen (s));
- if (escape_type > 0)
- p = wildcard_escape (s);
- else if (escape_type < 0)
- p = wildcard_unescape (s);
- if (! p)
- return 1;
- r = grub_script_argv_append (result, p, grub_strlen (p));
- grub_free (p);
- return r;
- }
- /* Convert arguments in ARGLIST into ARGV form. */
- static int
- grub_script_arglist_to_argv (struct grub_script_arglist *arglist,
- struct grub_script_argv *argv)
- {
- int i;
- char **values = 0;
- struct grub_script_arg *arg = 0;
- struct grub_script_argv result = { 0, 0, 0 };
- if (arglist == NULL)
- return 1;
- for (; arglist && arglist->arg; arglist = arglist->next)
- {
- if (grub_script_argv_next (&result))
- goto fail;
- arg = arglist->arg;
- while (arg)
- {
- switch (arg->type)
- {
- case GRUB_SCRIPT_ARG_TYPE_VAR:
- case GRUB_SCRIPT_ARG_TYPE_DQVAR:
- {
- int need_cleanup = 0;
- values = grub_script_env_get (arg->str, arg->type);
- for (i = 0; values && values[i]; i++)
- {
- if (!need_cleanup)
- {
- if (i != 0 && grub_script_argv_next (&result))
- {
- need_cleanup = 1;
- goto cleanup;
- }
- if (arg->type == GRUB_SCRIPT_ARG_TYPE_VAR)
- {
- int len;
- char ch;
- char *p;
- char *op;
- const char *s = values[i];
- len = grub_strlen (values[i]);
- /* \? -> \\\? */
- /* \* -> \\\* */
- /* \ -> \\ */
- p = grub_malloc (len * 2 + 1);
- if (! p)
- {
- need_cleanup = 1;
- goto cleanup;
- }
- op = p;
- while ((ch = *s++))
- {
- if (ch == '\\')
- {
- *op++ = '\\';
- if (*s == '?' || *s == '*')
- *op++ = '\\';
- }
- *op++ = ch;
- }
- *op = '\0';
- need_cleanup = grub_script_argv_append (&result, p, op - p);
- grub_free (p);
- /* Fall through to cleanup */
- }
- else
- {
- need_cleanup = append (&result, values[i], 1);
- /* Fall through to cleanup */
- }
- }
- cleanup:
- grub_free (values[i]);
- }
- grub_free (values);
- if (need_cleanup)
- goto fail;
- break;
- }
- case GRUB_SCRIPT_ARG_TYPE_BLOCK:
- {
- char *p;
- if (grub_script_argv_append (&result, "{", 1))
- goto fail;
- p = wildcard_escape (arg->str);
- if (!p)
- goto fail;
- if (grub_script_argv_append (&result, p,
- grub_strlen (p)))
- {
- grub_free (p);
- goto fail;
- }
- grub_free (p);
- if (grub_script_argv_append (&result, "}", 1))
- goto fail;
- }
- result.script = arg->script;
- break;
- case GRUB_SCRIPT_ARG_TYPE_TEXT:
- if (arg->str[0] &&
- grub_script_argv_append (&result, arg->str,
- grub_strlen (arg->str)))
- goto fail;
- break;
- case GRUB_SCRIPT_ARG_TYPE_GETTEXT:
- {
- if (gettext_append (&result, arg->str))
- goto fail;
- }
- break;
- case GRUB_SCRIPT_ARG_TYPE_DQSTR:
- case GRUB_SCRIPT_ARG_TYPE_SQSTR:
- if (append (&result, arg->str, 1))
- goto fail;
- break;
- }
- arg = arg->next;
- }
- }
- if (! result.args[result.argc - 1])
- result.argc--;
- /* Perform wildcard expansion. */
- int j;
- int failed = 0;
- struct grub_script_argv unexpanded = result;
- result.argc = 0;
- result.args = 0;
- for (i = 0; unexpanded.args[i]; i++)
- {
- char **expansions = 0;
- if (grub_wildcard_translator
- && grub_wildcard_translator->expand (unexpanded.args[i],
- &expansions))
- {
- grub_script_argv_free (&unexpanded);
- goto fail;
- }
- if (! expansions)
- {
- grub_script_argv_next (&result);
- append (&result, unexpanded.args[i], -1);
- }
- else
- {
- for (j = 0; expansions[j]; j++)
- {
- failed = (failed || grub_script_argv_next (&result) ||
- append (&result, expansions[j], 0));
- grub_free (expansions[j]);
- }
- grub_free (expansions);
- if (failed)
- {
- grub_script_argv_free (&unexpanded);
- goto fail;
- }
- }
- }
- grub_script_argv_free (&unexpanded);
- *argv = result;
- return 0;
- fail:
- grub_script_argv_free (&result);
- return 1;
- }
- static grub_err_t
- grub_script_execute_cmd (struct grub_script_cmd *cmd)
- {
- int ret;
- char errnobuf[ERRNO_DIGITS_MAX + 1];
- if (cmd == 0)
- return 0;
- ret = cmd->exec (cmd);
- grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
- grub_env_set ("?", errnobuf);
- return ret;
- }
- /* Execute a function call. */
- grub_err_t
- grub_script_function_call (grub_script_function_t func, int argc, char **args)
- {
- grub_err_t ret = 0;
- unsigned long loops = active_loops;
- struct grub_script_scope *old_scope;
- struct grub_script_scope new_scope;
- active_loops = 0;
- new_scope.flags = 0;
- new_scope.shifts = 0;
- new_scope.argv.argc = argc;
- new_scope.argv.args = args;
- old_scope = scope;
- scope = &new_scope;
- func->executing++;
- ret = grub_script_execute (func->func);
- func->executing--;
- function_return = 0;
- active_loops = loops;
- replace_scope (old_scope); /* free any scopes by setparams */
- return ret;
- }
- /* Helper for grub_script_execute_sourcecode. */
- static grub_err_t
- grub_script_execute_sourcecode_getline (char **line,
- int cont __attribute__ ((unused)),
- void *data)
- {
- const char **source = data;
- const char *p;
- if (! *source)
- {
- *line = 0;
- return 0;
- }
- p = grub_strchr (*source, '\n');
- if (p)
- *line = grub_strndup (*source, p - *source);
- else
- *line = grub_strdup (*source);
- *source = p ? p + 1 : 0;
- return 0;
- }
- /* Execute a source script. */
- grub_err_t
- grub_script_execute_sourcecode (const char *source)
- {
- grub_err_t ret = 0;
- struct grub_script *parsed_script;
- while (source)
- {
- char *line;
- grub_script_execute_sourcecode_getline (&line, 0, &source);
- parsed_script = grub_script_parse
- (line, grub_script_execute_sourcecode_getline, &source);
- if (! parsed_script)
- {
- ret = grub_errno;
- grub_free (line);
- break;
- }
- ret = grub_script_execute (parsed_script);
- grub_script_free (parsed_script);
- grub_free (line);
- }
- return ret;
- }
- /* Execute a source script in new scope. */
- grub_err_t
- grub_script_execute_new_scope (const char *source, int argc, char **args)
- {
- grub_err_t ret = 0;
- struct grub_script_scope new_scope;
- struct grub_script_scope *old_scope;
- new_scope.argv.argc = argc;
- new_scope.argv.args = args;
- new_scope.flags = 0;
- old_scope = scope;
- scope = &new_scope;
- ret = grub_script_execute_sourcecode (source);
- scope = old_scope;
- return ret;
- }
- /* Execute a single command line. */
- grub_err_t
- grub_script_execute_cmdline (struct grub_script_cmd *cmd)
- {
- struct grub_script_cmdline *cmdline = (struct grub_script_cmdline *) cmd;
- grub_command_t grubcmd;
- grub_err_t ret = 0;
- grub_script_function_t func = 0;
- char errnobuf[18];
- char *cmdname, *cmdstring;
- int argc, offset = 0, cmdlen = 0;
- unsigned int i;
- char **args;
- int invert;
- struct grub_script_argv argv = { 0, 0, 0 };
- /* Lookup the command. */
- if (grub_script_arglist_to_argv (cmdline->arglist, &argv) || ! argv.args || ! argv.args[0])
- return grub_errno;
- for (i = 0; i < argv.argc; i++)
- {
- cmdlen += grub_strlen (argv.args[i]) + 1;
- }
- cmdstring = grub_malloc (cmdlen);
- if (!cmdstring)
- {
- return grub_error (GRUB_ERR_OUT_OF_MEMORY,
- N_("cannot allocate command buffer"));
- }
- for (i = 0; i < argv.argc; i++)
- {
- offset += grub_snprintf (cmdstring + offset, cmdlen - offset, "%s ",
- argv.args[i]);
- }
- cmdstring[cmdlen - 1] = '\0';
- grub_verify_string (cmdstring, GRUB_VERIFY_COMMAND);
- grub_free (cmdstring);
- invert = 0;
- argc = argv.argc - 1;
- args = argv.args + 1;
- cmdname = argv.args[0];
- if (grub_strcmp (cmdname, "!") == 0)
- {
- if (argv.argc < 2 || ! argv.args[1])
- {
- grub_script_argv_free (&argv);
- return grub_error (GRUB_ERR_BAD_ARGUMENT,
- N_("no command is specified"));
- }
- invert = 1;
- argc = argv.argc - 2;
- args = argv.args + 2;
- cmdname = argv.args[1];
- }
- grubcmd = grub_command_find (cmdname);
- if (! grubcmd)
- {
- grub_errno = GRUB_ERR_NONE;
- /* It's not a GRUB command, try all functions. */
- func = grub_script_function_find (cmdname);
- if (! func)
- {
- /* As a last resort, try if it is an assignment. */
- char *assign = grub_strdup (cmdname);
- char *eq = grub_strchr (assign, '=');
- if (eq)
- {
- /* This was set because the command was not found. */
- grub_errno = GRUB_ERR_NONE;
- /* Create two strings and set the variable. */
- *eq = '\0';
- eq++;
- grub_script_env_set (assign, eq);
- }
- grub_free (assign);
- grub_snprintf (errnobuf, sizeof (errnobuf), "%d", grub_errno);
- grub_script_env_set ("?", errnobuf);
- grub_script_argv_free (&argv);
- grub_print_error ();
- return 0;
- }
- }
- /* Execute the GRUB command or function. */
- if (grubcmd)
- {
- if (grub_extractor_level && !(grubcmd->flags
- & GRUB_COMMAND_FLAG_EXTRACTOR))
- ret = grub_error (GRUB_ERR_EXTRACTOR,
- "%s isn't allowed to execute in an extractor",
- cmdname);
- else if ((grubcmd->flags & GRUB_COMMAND_FLAG_BLOCKS) &&
- (grubcmd->flags & GRUB_COMMAND_FLAG_EXTCMD))
- ret = grub_extcmd_dispatcher (grubcmd, argc, args, argv.script);
- else
- ret = (grubcmd->func) (grubcmd, argc, args);
- }
- else
- ret = grub_script_function_call (func, argc, args);
- if (invert)
- {
- if (ret == GRUB_ERR_TEST_FAILURE)
- grub_errno = ret = GRUB_ERR_NONE;
- else if (ret == GRUB_ERR_NONE)
- ret = grub_error (GRUB_ERR_TEST_FAILURE, N_("false"));
- else
- {
- grub_print_error ();
- ret = GRUB_ERR_NONE;
- }
- }
- /* Free arguments. */
- grub_script_argv_free (&argv);
- if (grub_errno == GRUB_ERR_TEST_FAILURE)
- grub_errno = GRUB_ERR_NONE;
- grub_print_error ();
- grub_snprintf (errnobuf, sizeof (errnobuf), "%d", ret);
- grub_env_set ("?", errnobuf);
- return ret;
- }
- /* Execute a block of one or more commands. */
- grub_err_t
- grub_script_execute_cmdlist (struct grub_script_cmd *list)
- {
- int ret = 0;
- struct grub_script_cmd *cmd;
- /* Loop over every command and execute it. */
- for (cmd = list->next; cmd; cmd = cmd->next)
- {
- if (active_breaks)
- break;
- ret = grub_script_execute_cmd (cmd);
- if (function_return)
- break;
- }
- return ret;
- }
- /* Execute an if statement. */
- grub_err_t
- grub_script_execute_cmdif (struct grub_script_cmd *cmd)
- {
- int ret;
- const char *result;
- struct grub_script_cmdif *cmdif = (struct grub_script_cmdif *) cmd;
- /* Check if the commands results in a true or a false. The value is
- read from the env variable `?'. */
- ret = grub_script_execute_cmd (cmdif->exec_to_evaluate);
- if (function_return)
- return ret;
- result = grub_env_get ("?");
- grub_errno = GRUB_ERR_NONE;
- /* Execute the `if' or the `else' part depending on the value of
- `?'. */
- if (result && ! grub_strcmp (result, "0"))
- return grub_script_execute_cmd (cmdif->exec_on_true);
- else
- return grub_script_execute_cmd (cmdif->exec_on_false);
- }
- /* Execute a for statement. */
- grub_err_t
- grub_script_execute_cmdfor (struct grub_script_cmd *cmd)
- {
- unsigned i;
- grub_err_t result;
- struct grub_script_argv argv = { 0, 0, 0 };
- struct grub_script_cmdfor *cmdfor = (struct grub_script_cmdfor *) cmd;
- if (grub_script_arglist_to_argv (cmdfor->words, &argv))
- return grub_errno;
- active_loops++;
- result = 0;
- for (i = 0; i < argv.argc; i++)
- {
- if (is_continue && active_breaks == 1)
- active_breaks = 0;
- if (! active_breaks)
- {
- grub_script_env_set (cmdfor->name->str, argv.args[i]);
- result = grub_script_execute_cmd (cmdfor->list);
- if (function_return)
- break;
- }
- }
- if (active_breaks)
- active_breaks--;
- active_loops--;
- grub_script_argv_free (&argv);
- return result;
- }
- /* Execute a "while" or "until" command. */
- grub_err_t
- grub_script_execute_cmdwhile (struct grub_script_cmd *cmd)
- {
- int result;
- struct grub_script_cmdwhile *cmdwhile = (struct grub_script_cmdwhile *) cmd;
- active_loops++;
- do {
- result = grub_script_execute_cmd (cmdwhile->cond);
- if (function_return)
- break;
- if (cmdwhile->until ? !result : result)
- break;
- result = grub_script_execute_cmd (cmdwhile->list);
- if (function_return)
- break;
- if (active_breaks == 1 && is_continue)
- active_breaks = 0;
- if (active_breaks)
- break;
- } while (1); /* XXX Put a check for ^C here */
- if (active_breaks)
- active_breaks--;
- active_loops--;
- return result;
- }
- /* Execute any GRUB pre-parsed command or script. */
- grub_err_t
- grub_script_execute (struct grub_script *script)
- {
- if (script == 0)
- return 0;
- return grub_script_execute_cmd (script->cmd);
- }
|