grub-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

automagic module loading - normal commands


From: Tomas Ebenlendr
Subject: automagic module loading - normal commands
Date: Wed, 22 Sep 2004 23:04:50 +0200
User-agent: Mutt/1.5.6i

I want to do better my autocmd patch. Now it reads file autocmd.lst in
root directory. (this can be changed via variable 'autocmd'). I have
following questions:
    - Do we want to have special file like autocmd.lst? Or
      to 'probe' all modules: read a module, and read from some special
      elf section supported commands? Or both? (Or other way to decide
      which module to load?)
    - Do we want to have automatic module loading hardcoded? Or should
      there be a generic interface, so some other module will drive
      automatic module loading?
    - If there will be autocmd.lst:
        - is autocmd.lst good name?
        - Shoud it be generated from *.rmk files?
          Or from *.c (by preprocessing and greping, such as symbols
          are from *.h)
          Or else way?

I can implement any solution. Just feel free and say your opinion about
this. (And other things that you like/dislike on the patch.)

Here is current version: (warning patch may be not aplicable, see date of
files, to know version of grub).

diff -rupN -x CVS grub2_x/autocmd.lst grub2_debug/autocmd.lst
--- grub2_x/autocmd.lst 1970-01-01 01:00:00.000000000 +0100
+++ grub2_debug/autocmd.lst     2004-06-29 01:52:26.000000000 +0200
@@ -0,0 +1,2 @@
+boot boot
+chainloader chain
diff -rupN -x CVS grub2_x/include/grub/normal.h 
grub2_debug/include/grub/normal.h
--- grub2_x/include/grub/normal.h       2004-06-05 00:20:17.000000000 +0200
+++ grub2_debug/include/grub/normal.h   2004-06-29 03:07:59.000000000 +0200
@@ -1,7 +1,7 @@
 /* normal.h - prototypes for the normal mode */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2002,2003  Free Software Foundation, Inc.
+ *  Copyright (C) 2002,2003,2004  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
@@ -136,6 +136,7 @@ grub_err_t grub_set_history (int newsize
 int grub_iterate_commands (int (*iterate) (grub_command_t));
 int grub_command_execute (char *cmdline);
 void grub_command_init (void);
+void grub_command_done (void);
 void grub_normal_init_page (void);
 int grub_arg_parse (grub_command_t parser, int argc, char **argv,
                    struct grub_arg_list *usr, char ***args, int *argnum);
diff -rupN -x CVS grub2_x/normal/command.c grub2_debug/normal/command.c
--- grub2_x/normal/command.c    2004-06-05 00:20:18.000000000 +0200
+++ grub2_debug/normal/command.c        2004-07-01 16:44:07.000000000 +0200
@@ -1,6 +1,6 @@
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2003  Free Software Foundation, Inc.
+ *  Copyright (C) 2003,2004  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
@@ -24,9 +24,21 @@
 #include <grub/term.h>
 #include <grub/env.h>
 #include <grub/dl.h>
+#include <grub/file.h>
 
 static grub_command_t grub_command_list;
 
+typedef struct
+{
+  char * cmdname;
+  char * modulename;
+} grub_autocmd_list_t;
+
+static grub_autocmd_list_t * grub_autocmd_list = 0;
+/* Value less than zero disables warnings at start.  */
+static int grub_autocmd_count = -1;
+static char * grub_autocmd_buffer = 0;
+
 void
 grub_register_command (const char *name,
                       grub_err_t (*func) (struct grub_arg_list *state,
@@ -82,6 +94,8 @@ grub_command_find (char *cmdline)
 {
   char *first_space;
   grub_command_t cmd;
+  grub_dl_t mod;
+  int i;
 
   first_space = grub_strchr (cmdline, ' ');
   if (first_space)
@@ -91,8 +105,40 @@ grub_command_find (char *cmdline)
     if (grub_strcmp (cmdline, cmd->name) == 0)
       break;
 
+  /* Automatic module loading.  */
   if (! cmd)
-    grub_error (GRUB_ERR_UNKNOWN_COMMAND, "unknown command `%s'", cmdline);
+    {
+      for (i = 0; i < grub_autocmd_count; i++)
+       {
+         if (grub_strcmp (cmdline, grub_autocmd_list[i].cmdname) == 0)
+           {
+             mod = grub_dl_load (grub_autocmd_list[i].modulename);
+
+             if (mod)
+               {
+                 grub_dl_ref (mod);
+                 for (cmd = grub_command_list; cmd; cmd = cmd->next)
+                   if (grub_strcmp (cmdline, cmd->name) == 0)
+                     break;
+
+                 /* Unload module if command was not there.  */
+                 if (!cmd)
+                   {
+                     if (! grub_dl_unref (mod))
+                       grub_dl_unload (mod);
+
+                     grub_error(GRUB_ERR_UNKNOWN_COMMAND,
+                                "command `%s' not found in module `%s'",
+                                cmdline, grub_autocmd_list[i].modulename);
+                   }
+               }
+
+             break;
+           }
+       }
+      if (i >= grub_autocmd_count)
+       grub_error (GRUB_ERR_UNKNOWN_COMMAND, "unknown command `%s'", cmdline);
+    }
   
   if (first_space)
     *first_space = ' ';
@@ -171,6 +217,185 @@ grub_command_execute (char *cmdline)
   return ret;
 }
 
+
+/* Reads autocmd.lst and sets up list of commands provided by modules.  */
+static grub_err_t
+grub_autocmd_read (const char * name, int silent)
+{
+  grub_file_t file = 0;
+  grub_ssize_t size;
+  int state, i;
+  char *cmdname = 0, *modulename = 0, *pos, *end;
+
+  if ((name != 0) && (name[0] != 0))
+    {
+      file = grub_file_open (name);
+      if (! file)
+       {
+         if (! silent)
+           grub_error (grub_errno, "couldn't open file %s", name);
+
+         /* Not goto failed. Preserve old setting here.  */
+         return grub_errno;
+       }
+    }
+
+  grub_free (grub_autocmd_buffer);
+  grub_autocmd_buffer = 0;
+  grub_free (grub_autocmd_list);
+  grub_autocmd_list = 0;
+  grub_autocmd_count = 0;
+
+  /* Disable autoloading.  */
+  if ((name == 0) || (name[0] == 0))
+    return 0;
+
+  size = grub_file_size (file);
+  grub_autocmd_buffer = grub_malloc(size + 1);
+  if (! grub_autocmd_buffer)
+    goto failed;
+  if (grub_file_read (file, grub_autocmd_buffer, size) != (int) size)
+    goto failed;
+
+  /* Don't allow unfinished line.  */
+  grub_autocmd_buffer[size] = '\n';
+  end = grub_autocmd_buffer + size + 1;
+
+  /* Count number of entries.  */
+  /* Following state machine accepts lines with at least 2 words,
+   * the second word must not contain `/'.  */
+  state = 0;
+  /* State machine for text parsing:
+   * state == 0   begining of line,
+   * state == 1   first word,
+   * state == 2   spaces after first word,
+   * state == 3   rest of the line.
+   * state == 5   error. \0 or / in module name.  */
+  for (pos = grub_autocmd_buffer; pos < end; pos++)
+    switch (*pos)
+      {
+       case '\0':
+         /* Bad character in text file.  */
+         state = 5;
+         break;
+
+       case '\n':
+
+         /* At least two words.  */
+         if (state == 3)
+           grub_autocmd_count++;
+
+         state = 0;
+         break;
+
+       case ' ':
+         if (state == 1)
+           state = 2;
+         break;
+
+       case '/':
+         /* Bad module name.  */
+         if (state == 3)
+           state = 5;
+
+       default:
+         /* Move to state 1 or 3.  */
+         state |= 1;
+      }
+
+  grub_autocmd_list = grub_malloc(grub_autocmd_count
+                                 * sizeof(grub_autocmd_list_t));
+  if (! grub_autocmd_list) goto failed;
+  
+  /* Fill in the list.  */
+  /* The same state machine. It sets pointers to names of commands and
+   * modules. A name is a word separated by ` '.  */
+  i=0; state = 0;
+  for (pos = grub_autocmd_buffer; pos < end; pos++)
+    switch (*pos)
+      {
+       case '\n':
+
+         /* At least two words.  */
+         if (state == 3)
+           {
+             grub_autocmd_list[i].cmdname = cmdname;
+             grub_autocmd_list[i].modulename = modulename;
+             i++;
+             *pos = 0;
+           }
+
+         state = 0;
+         break;
+
+       case ' ':
+         /* Terminate the word.  */
+         *pos = 0;
+         if (state == 1)
+           state = 2;
+         break;
+
+       case '/':
+       case '\0':
+         /* Bad module name.  */
+         if (state == 3)
+           state = 5;
+
+       default:
+         switch (state)
+           {
+             case 0:
+               cmdname = pos;
+               state = 1;
+               break;
+             case 2:
+               modulename = pos;
+               state = 3;
+               break;
+           }
+      }
+
+  grub_file_close(file);
+  return 0;
+
+failed:
+  grub_file_close(file);
+  grub_free (grub_autocmd_buffer);
+  grub_autocmd_buffer = 0;
+  grub_free (grub_autocmd_list);
+  grub_autocmd_list = 0;
+  grub_autocmd_count = 0;
+
+  if (! silent) grub_error(grub_errno, "not enough memory");
+  return grub_errno;
+}
+
+/* `autocmd' variable write hook */
+static grub_err_t
+grub_autocmd_set (struct grub_env_var * env)
+{
+  char * p;
+  char * prefix = grub_env_get("prefix");
+
+  p = grub_strchr (env->value, '/');
+  if (! p)
+    {
+      p = grub_malloc (grub_strlen (prefix) + 1
+                      + grub_strlen (env->value) + 1);
+      if (p)
+       {
+         grub_sprintf (p, "%s/%s", prefix, env->value);
+         grub_autocmd_read (p, grub_autocmd_count < 0);
+         grub_free (p);
+       }
+      return grub_errno;
+    }
+  else
+    return grub_autocmd_read(env->value, grub_autocmd_count < 0);
+}
+
+/* Basic normal mode commands.  */
+
 static grub_err_t
 rescue_command (struct grub_arg_list *state __attribute__ ((unused)),
                int argc __attribute__ ((unused)),
@@ -303,6 +528,8 @@ lsmod_command (struct grub_arg_list *sta
 void
 grub_command_init (void)
 {
+  char * fname;
+
   /* This is a special command, because this never be called actually.  */
   grub_register_command ("title", 0, GRUB_COMMAND_FLAG_TITLE, 0, 0, 0);
 
@@ -323,4 +550,24 @@ grub_command_init (void)
 
   grub_register_command ("lsmod", lsmod_command, GRUB_COMMAND_FLAG_BOTH,
                         "lsmod", "Show loaded modules.", 0);
+
+  grub_register_variable_hook("autocmd", 0, grub_autocmd_set);
+
+  fname = grub_env_get("autocmd");
+  /* Provide default value for `autocmd'.  */
+  if ((fname == 0) || (fname[0] == 0))
+    {
+      grub_env_set ("autocmd", "autocmd.lst");
+      if (grub_autocmd_count <= 0)
+       grub_env_set ("autocmd", "");
+    }
+  else
+    grub_env_set("autocmd",fname);
+}
+
+void
+grub_command_done (void)
+{
+  grub_register_variable_hook("autocmd", 0, 0);
+  grub_autocmd_read(0, 0);
 }
diff -rupN -x CVS grub2_x/normal/main.c grub2_debug/normal/main.c
--- grub2_x/normal/main.c       2004-06-05 00:20:18.000000000 +0200
+++ grub2_debug/normal/main.c   2004-07-01 16:45:38.000000000 +0200
@@ -1,7 +1,7 @@
 /* main.c - the normal mode main routine */
 /*
  *  GRUB  --  GRand Unified Bootloader
- *  Copyright (C) 2000,2001,2002,2003  Free Software Foundation, Inc.
+ *  Copyright (C) 2000,2001,2002,2003,2004  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
@@ -368,12 +368,14 @@ GRUB_MOD_INIT
   grub_rescue_register_command ("normal", grub_rescue_cmd_normal,
                                "enter normal mode");
 
-  /* This registers some built-in commands.  */
+  /* This registers some built-in commands and sets up autoloading
+   * of commands.  */
   grub_command_init ();
 }
 
 GRUB_MOD_FINI
 {
+  grub_command_done ();
   grub_set_history (0);
   grub_rescue_unregister_command ("normal");
 }
-- 
                                 Tomas 'ebi' Ebenlendr
                                 http://get.to/ebik
                                 PF 2004.72663172308





reply via email to

[Prev in Thread] Current Thread [Next in Thread]