grub-devel
[Top][All Lists]
Advanced

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

history, autoloading was: Re: normal mode chainloader


From: Tomas Ebenlendr
Subject: history, autoloading was: Re: normal mode chainloader
Date: Thu, 1 Jul 2004 18:03:43 +0200
User-agent: Mutt/1.5.6i

> 
> > ad modules:
> > In fact the dependencies are written somewhere in elf format and
> > depmod.lst is only for user to see what depends on what.
> 
> So that doesn't mean _chain gets autoloaded?
> 

It gets auoloaded. I only noted that you can delete depmod.lst, because
the information is stored somewhere in modules. I don't understand elf,
but it seems be the same way as in unix.

> > autoloading:
> > I wrote simple cmd-autoloading patch, but it triggers some mm-bug which I 
> > hunt
> > now. Moreover my network card in my laptop (where I hack for grub) is 
> > broken,
> > so I will post the patch when I return.
> 
> Which bugs are those?  I had some mm problems with the PPC port as
> well.
> 
> If it is incorrect usage of malloc and free, you could use valgrind.
> 

I don't know where the bug was, but it isn't trigerred anymore with
following history optimisation.

I also add patch that autoloads modules if they contain requested
commands. (It contains example autoload.lst).
I thinked of automatic generating of autoload.lst, but it is not so
easy. I'm not decided if I want to grep for register_command or do it in
some other way.

-------- HISTORY OPTIMISATION PATCH --------
Changelog:
        History is now replaced 'lazy'. It was replaced at each char
        before, which was not good, because history replacement is free
        on old content and strdup of new.

diff -rupN -x CVS grub2_x/normal/cmdline.c grub2_work_x/normal/cmdline.c
--- grub2_x/normal/cmdline.c    2004-06-28 19:09:55.000000000 +0200
+++ grub2_work_x/normal/cmdline.c       2004-07-01 16:32:06.000000000 +0200
@@ -640,6 +640,7 @@ grub_cmdline_get (const char *prompt, ch
 
                lpos = 0;
 
+               grub_history_replace (histpos, buf);
                if (histpos > 0)
                  histpos--;
 
@@ -655,6 +656,7 @@ grub_cmdline_get (const char *prompt, ch
 
                lpos = 0;
 
+               grub_history_replace (histpos, buf);
                if (histpos < hist_used - 1)
                  histpos++;
 
@@ -724,8 +726,9 @@ grub_cmdline_get (const char *prompt, ch
          break;
        }
 
-      grub_history_replace (histpos, buf);
+      //grub_history_replace (histpos, buf);
     }
+  grub_history_replace (histpos, buf);
 
   grub_putchar ('\n');
   grub_refresh ();
------- END OF PATCH ------

-------- COMMAND AUTOLOAD PATCH ---------
Changelog:
        Added command autoloading support to variable `autocmd'.
        Whenever this variable is set to some file, this file is
        parsed to build list of commands and their modules. When unkonwn
        command is used and is contained in this list, it's module is
        loaded and command is run then.

        * autocmd.lst: New file. It should be in grub's root directory.
        This file is only an example.
        * include/grub/normal.h (grub_command_done): New prototype.
        Destrutor for normal/command.c.
        * normal/command.c (grub_autocmd_list_t): New data type.
        (grub_command_find): Added the autoloading code.
        (grub_autocmd_read): New function. Parses autocmd.lst.
        (grub_autocmd_set): New function. Write hook for variable
        `autocmd'.
        (grub_command_init): Added initialisation of variable `autocmd'.
        (grub_command_done): New function. Destructor for autoloading.
        Frees the list of commands and modules and unsets the write hook
        for `autocmd'.
        

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");
 }
------- END OF PATCH ------
-- 
                                 Tomas 'ebi' Ebenlendr
                                 http://get.to/ebik
                                 PF 2004.49926055581





reply via email to

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