5822ff87a2
Automatic loading of commands is supported. * normal/main.c (read_command_list): New function. (grub_normal_execute): Call read_command_list. * normal/command.c (grub_register_command): Return zero or CMD. Allocate CMD->NAME from the heap. Initialize CMD->MODULE_NAME to zero. Find the same name as well. If the same command is found and it is a dummy command, overwrite members. If it is not a dummy command, return zero. (grub_unregister_command): Free Q->NAME and Q->MODULE_NAME. (grub_command_find): If a dummy command is found, load a module and retry to find a command only once. * normal/cmdline.c (grub_tab_complete): Call grub_command_find to make sure that each command is loaded. * include/grub/normal.h (GRUB_COMMAND_FLAG_NOT_LOADED): New macro. (struct grub_command): Remove const from the member `name'. Add a new member `module_name'. (grub_register_command): Return grub_command_t. * commands/help.c (grub_cmd_help): Call grub_command_find to make sure that each command is loaded. * genmk.rb (PModule::rule): Specify a module name without the suffix ".mod" to gencmdlist.sh.
401 lines
9 KiB
C
401 lines
9 KiB
C
/*
|
|
* GRUB -- GRand Unified Bootloader
|
|
* Copyright (C) 2003, 2005 Free Software Foundation, Inc.
|
|
*
|
|
* This program 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 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program 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 this program; if not, write to the Free Software
|
|
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
#include <grub/normal.h>
|
|
#include <grub/misc.h>
|
|
#include <grub/mm.h>
|
|
#include <grub/err.h>
|
|
#include <grub/term.h>
|
|
#include <grub/env.h>
|
|
#include <grub/dl.h>
|
|
|
|
static grub_command_t grub_command_list;
|
|
|
|
grub_command_t
|
|
grub_register_command (const char *name,
|
|
grub_err_t (*func) (struct grub_arg_list *state,
|
|
int argc, char **args),
|
|
unsigned flags,
|
|
const char *summary,
|
|
const char *description,
|
|
const struct grub_arg_option *options)
|
|
{
|
|
grub_command_t cmd, *p;
|
|
|
|
cmd = (grub_command_t) grub_malloc (sizeof (*cmd));
|
|
if (! cmd)
|
|
return 0;
|
|
|
|
cmd->name = grub_strdup (name);
|
|
if (! cmd->name)
|
|
{
|
|
grub_free (cmd);
|
|
return 0;
|
|
}
|
|
|
|
cmd->func = func;
|
|
cmd->flags = flags;
|
|
cmd->summary = summary;
|
|
cmd->description = description;
|
|
cmd->options = options;
|
|
cmd->module_name = 0;
|
|
|
|
/* Keep the list sorted for simplicity. */
|
|
p = &grub_command_list;
|
|
while (*p)
|
|
{
|
|
if (grub_strcmp ((*p)->name, name) >= 0)
|
|
break;
|
|
|
|
p = &((*p)->next);
|
|
}
|
|
|
|
if (*p && grub_strcmp ((*p)->name, name) == 0)
|
|
{
|
|
grub_command_t q;
|
|
|
|
q = *p;
|
|
if (q->flags & GRUB_COMMAND_FLAG_NOT_LOADED)
|
|
{
|
|
q->func = cmd->func;
|
|
q->flags = cmd->flags;
|
|
q->summary = cmd->summary;
|
|
q->description = cmd->description;
|
|
q->options = cmd->options;
|
|
grub_free (cmd->name);
|
|
grub_free (cmd->module_name);
|
|
grub_free (cmd);
|
|
cmd = q;
|
|
}
|
|
else
|
|
{
|
|
grub_free (cmd->name);
|
|
grub_free (cmd);
|
|
cmd = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cmd->next = *p;
|
|
*p = cmd;
|
|
}
|
|
|
|
return cmd;
|
|
}
|
|
|
|
void
|
|
grub_unregister_command (const char *name)
|
|
{
|
|
grub_command_t *p, q;
|
|
|
|
for (p = &grub_command_list, q = *p; q; p = &(q->next), q = q->next)
|
|
if (grub_strcmp (name, q->name) == 0)
|
|
{
|
|
*p = q->next;
|
|
grub_free (q->name);
|
|
grub_free (q->module_name);
|
|
grub_free (q);
|
|
break;
|
|
}
|
|
}
|
|
|
|
grub_command_t
|
|
grub_command_find (char *cmdline)
|
|
{
|
|
char *first_space;
|
|
grub_command_t cmd;
|
|
int count = 0;
|
|
|
|
first_space = grub_strchr (cmdline, ' ');
|
|
if (first_space)
|
|
*first_space = '\0';
|
|
|
|
again:
|
|
|
|
for (cmd = grub_command_list; cmd; cmd = cmd->next)
|
|
if (grub_strcmp (cmdline, cmd->name) == 0)
|
|
break;
|
|
|
|
if (! cmd)
|
|
grub_error (GRUB_ERR_UNKNOWN_COMMAND, "unknown command `%s'", cmdline);
|
|
else if (cmd->flags & GRUB_COMMAND_FLAG_NOT_LOADED)
|
|
{
|
|
/* Automatically load the command. */
|
|
if (count == 0)
|
|
{
|
|
grub_dl_t mod;
|
|
char *module_name;
|
|
|
|
module_name = grub_strdup (cmd->module_name);
|
|
if (module_name)
|
|
{
|
|
mod = grub_dl_load (module_name);
|
|
if (mod)
|
|
{
|
|
grub_dl_ref (mod);
|
|
count++;
|
|
goto again;
|
|
}
|
|
|
|
grub_free (module_name);
|
|
}
|
|
}
|
|
|
|
/* This module seems broken. */
|
|
grub_unregister_command (cmdline);
|
|
grub_error (GRUB_ERR_UNKNOWN_COMMAND, "unknown command `%s'", cmdline);
|
|
cmd = 0;
|
|
}
|
|
|
|
if (first_space)
|
|
*first_space = ' ';
|
|
|
|
return cmd;
|
|
}
|
|
|
|
int
|
|
grub_iterate_commands (int (*iterate) (grub_command_t))
|
|
{
|
|
grub_command_t cmd;
|
|
for (cmd = grub_command_list; cmd; cmd = cmd->next)
|
|
iterate (cmd);
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
grub_command_execute (char *cmdline)
|
|
{
|
|
auto grub_err_t cmdline_get (char **s);
|
|
grub_err_t cmdline_get (char **s)
|
|
{
|
|
*s = grub_malloc (GRUB_MAX_CMDLINE);
|
|
*s[0] = '\0';
|
|
return grub_cmdline_get (">", *s, GRUB_MAX_CMDLINE, 0, 1);
|
|
}
|
|
|
|
grub_command_t cmd;
|
|
grub_err_t ret = 0;
|
|
char *pager;
|
|
int num;
|
|
char **args;
|
|
struct grub_arg_list *state;
|
|
struct grub_arg_option *parser;
|
|
int maxargs = 0;
|
|
char **arglist;
|
|
int numargs;
|
|
|
|
if (grub_split_cmdline (cmdline, cmdline_get, &num, &args))
|
|
return 0;
|
|
|
|
/* In case of an assignment set the environment accordingly instead
|
|
of calling a function. */
|
|
if (num == 0 && grub_strchr (args[0], '='))
|
|
{
|
|
char *val = grub_strchr (args[0], '=');
|
|
val[0] = 0;
|
|
grub_env_set (args[0], val + 1);
|
|
val[0] = '=';
|
|
return 0;
|
|
}
|
|
|
|
cmd = grub_command_find (args[0]);
|
|
if (! cmd)
|
|
return -1;
|
|
|
|
/* Enable the pager if the environment pager is set to 1. */
|
|
pager = grub_env_get ("pager");
|
|
if (pager && (! grub_strcmp (pager, "1")))
|
|
grub_set_more (1);
|
|
|
|
parser = (struct grub_arg_option *) cmd->options;
|
|
while (parser && (parser++)->doc)
|
|
maxargs++;
|
|
|
|
state = grub_malloc (sizeof (struct grub_arg_list) * maxargs);
|
|
grub_memset (state, 0, sizeof (struct grub_arg_list) * maxargs);
|
|
if (! (cmd->flags & GRUB_COMMAND_FLAG_NO_ARG_PARSE))
|
|
{
|
|
if (grub_arg_parse (cmd, num, &args[1], state, &arglist, &numargs))
|
|
ret = (cmd->func) (state, numargs, arglist);
|
|
}
|
|
else
|
|
ret = (cmd->func) (state, num, &args[1]);
|
|
|
|
grub_free (state);
|
|
|
|
if (pager && (! grub_strcmp (pager, "1")))
|
|
grub_set_more (0);
|
|
|
|
grub_free (args);
|
|
return ret;
|
|
}
|
|
|
|
static grub_err_t
|
|
rescue_command (struct grub_arg_list *state __attribute__ ((unused)),
|
|
int argc __attribute__ ((unused)),
|
|
char **args __attribute__ ((unused)))
|
|
{
|
|
grub_longjmp (grub_exit_env, 0);
|
|
|
|
/* Never reach here. */
|
|
return 0;
|
|
}
|
|
|
|
|
|
static grub_err_t
|
|
set_command (struct grub_arg_list *state __attribute__ ((unused)),
|
|
int argc, char **args)
|
|
{
|
|
char *var;
|
|
char *val;
|
|
|
|
auto int print_env (struct grub_env_var *env);
|
|
int print_env (struct grub_env_var *env)
|
|
{
|
|
grub_printf ("%s=%s\n", env->name, env->value);
|
|
return 0;
|
|
}
|
|
|
|
if (! argc)
|
|
{
|
|
grub_env_iterate (print_env);
|
|
return 0;
|
|
}
|
|
|
|
var = args[0];
|
|
val = grub_strchr (var, '=');
|
|
if (! val)
|
|
{
|
|
grub_error (GRUB_ERR_BAD_ARGUMENT, "not an assignment");
|
|
return grub_errno;
|
|
}
|
|
|
|
val[0] = 0;
|
|
grub_env_set (var, val + 1);
|
|
val[0] = '=';
|
|
return 0;
|
|
}
|
|
|
|
static grub_err_t
|
|
unset_command (struct grub_arg_list *state __attribute__ ((unused)),
|
|
int argc, char **args)
|
|
{
|
|
if (argc < 1)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT,
|
|
"no environment variable specified");
|
|
|
|
grub_env_unset (args[0]);
|
|
return 0;
|
|
}
|
|
|
|
static grub_err_t
|
|
insmod_command (struct grub_arg_list *state __attribute__ ((unused)),
|
|
int argc, char **args)
|
|
{
|
|
char *p;
|
|
grub_dl_t mod;
|
|
|
|
if (argc == 0)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
|
|
|
|
p = grub_strchr (args[0], '/');
|
|
if (! p)
|
|
mod = grub_dl_load (args[0]);
|
|
else
|
|
mod = grub_dl_load_file (args[0]);
|
|
|
|
if (mod)
|
|
grub_dl_ref (mod);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static grub_err_t
|
|
rmmod_command (struct grub_arg_list *state __attribute__ ((unused)),
|
|
int argc, char **args)
|
|
{
|
|
grub_dl_t mod;
|
|
|
|
if (argc == 0)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no module specified");
|
|
|
|
mod = grub_dl_get (args[0]);
|
|
if (! mod)
|
|
return grub_error (GRUB_ERR_BAD_ARGUMENT, "no such module");
|
|
|
|
if (! grub_dl_unref (mod))
|
|
grub_dl_unload (mod);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static grub_err_t
|
|
lsmod_command (struct grub_arg_list *state __attribute__ ((unused)),
|
|
int argc __attribute__ ((unused)),
|
|
char **args __attribute__ ((unused)))
|
|
{
|
|
auto int print_module (grub_dl_t mod);
|
|
|
|
int print_module (grub_dl_t mod)
|
|
{
|
|
grub_dl_dep_t dep;
|
|
|
|
grub_printf ("%s\t%d\t\t", mod->name, mod->ref_count);
|
|
for (dep = mod->dep; dep; dep = dep->next)
|
|
{
|
|
if (dep != mod->dep)
|
|
grub_putchar (',');
|
|
|
|
grub_printf ("%s", dep->mod->name);
|
|
}
|
|
grub_putchar ('\n');
|
|
grub_refresh ();
|
|
|
|
return 0;
|
|
}
|
|
|
|
grub_printf ("Name\tRef Count\tDependencies\n");
|
|
grub_dl_iterate (print_module);
|
|
return 0;
|
|
}
|
|
|
|
void
|
|
grub_command_init (void)
|
|
{
|
|
/* This is a special command, because this never be called actually. */
|
|
grub_register_command ("title", 0, GRUB_COMMAND_FLAG_TITLE, 0, 0, 0);
|
|
|
|
grub_register_command ("rescue", rescue_command, GRUB_COMMAND_FLAG_BOTH,
|
|
"rescue", "Enter into the rescue mode.", 0);
|
|
|
|
grub_register_command ("set", set_command, GRUB_COMMAND_FLAG_BOTH,
|
|
"set [ENVVAR=VALUE]", "Set an environment variable.", 0);
|
|
|
|
grub_register_command ("unset", unset_command, GRUB_COMMAND_FLAG_BOTH,
|
|
"unset ENVVAR", "Remove an environment variable.", 0);
|
|
|
|
grub_register_command ("insmod", insmod_command, GRUB_COMMAND_FLAG_BOTH,
|
|
"insmod MODULE|FILE", "Insert a module.", 0);
|
|
|
|
grub_register_command ("rmmod", rmmod_command, GRUB_COMMAND_FLAG_BOTH,
|
|
"rmmod MODULE", "Remove a module.", 0);
|
|
|
|
grub_register_command ("lsmod", lsmod_command, GRUB_COMMAND_FLAG_BOTH,
|
|
"lsmod", "Show loaded modules.", 0);
|
|
}
|