Discussion:
Idea: elimination of the normal mode (revised version)
Bean
2008-07-07 00:29:42 UTC
Permalink
Hi,

First of all, we can still keep rescue and normal command. But instead
of depending on normal.mod, normal command depends on module arg,
which is an option parser. Also, these two type of commands are of the
same command set. In fact, module arg is implemented as a pre parser,
which goes through the list of arguments and extract the options. In
the case of rescue command, the pre parser field is null, which means
it wants to parse options itself.

Then, I think of a new structure to represent all configurable
handlers of grub. Different types of handler have different fields,
but they all share a command header:

struct grub_handler
{
.next,
.name,
.init,
.fini
};

Same type of handlers are linked together. We first define an enum to
list all types. For example:

enum {
GRUB_HANDLER_INPUT,
GRUB_HANDLER_OUTPUT,
GRUB_HANDLER_CONSOLE,
GRUB_HANDLER_MENU,
GRUB_HANDLER_SCRIPT,
GRUB_HANDLER_NUM
};

Then, we define an array to point to the head of handler linked list:
grub_handler[GRUB_HANDLER_NUM];

Head is the default selection. When we insert a new handler module, it
would automatically become the new default, although we can switch
back to old handler using a command.

Here are more details about different handlers:

input:
This is the input component of terminal:

struct grub_handler_input
{
.next,
.name,
.init,
.fini,
.checkkey,
.getkey
.flags,
};

output:
This is the output component of terminal:

struct grub_handler_output
{
.next,
.name,
.init,
.fini,
.putchar,
.getcharwidth,
.getxy,
.gotoxy,
.cls,
.setcolorstate,
.setcursor,
.flags,
};

console interface:
It represent the grub console, users type commands and execute them
line by line.

struct grub_handler_console
{
.next,
.name,
.init,
.fini,
.run
};

menu interface:
It represent the menu, users select a menu item and execute it.

struct grub_handler_menu
{
.next,
.name,
.init,
.fini,
.run
};

script engine:
It's responsible for parsing config file to get the menu list, and
execution of commands.

struct grub_handler_script
{
.next,
.name,
.init,
.fini,
.readconfig
.getmenu
.execute
};

The handlers are independent of each other. When they need something,
they called specific function of the default handler. For example, to
read a key from the console, we can use
grub_handler[GRUB_HANDLER_INPUT]->getkey. Also, to get the list of
items to be displayed on screen, the menu handler can call
grub_handler[GRUB_HANDLER_SCRIPT]->getmenu.
--
Bean
Bean
2008-07-17 03:24:23 UTC
Permalink
Post by Bean
Hi,
First of all, we can still keep rescue and normal command. But instead
of depending on normal.mod, normal command depends on module arg,
which is an option parser. Also, these two type of commands are of the
same command set. In fact, module arg is implemented as a pre parser,
which goes through the list of arguments and extract the options. In
the case of rescue command, the pre parser field is null, which means
it wants to parse options itself.
Then, I think of a new structure to represent all configurable
handlers of grub. Different types of handler have different fields,
struct grub_handler
{
.next,
.name,
.init,
.fini
};
Same type of handlers are linked together. We first define an enum to
enum {
GRUB_HANDLER_INPUT,
GRUB_HANDLER_OUTPUT,
GRUB_HANDLER_CONSOLE,
GRUB_HANDLER_MENU,
GRUB_HANDLER_SCRIPT,
GRUB_HANDLER_NUM
};
grub_handler[GRUB_HANDLER_NUM];
Head is the default selection. When we insert a new handler module, it
would automatically become the new default, although we can switch
back to old handler using a command.
struct grub_handler_input
{
.next,
.name,
.init,
.fini,
.checkkey,
.getkey
.flags,
};
struct grub_handler_output
{
.next,
.name,
.init,
.fini,
.putchar,
.getcharwidth,
.getxy,
.gotoxy,
.cls,
.setcolorstate,
.setcursor,
.flags,
};
It represent the grub console, users type commands and execute them
line by line.
struct grub_handler_console
{
.next,
.name,
.init,
.fini,
.run
};
It represent the menu, users select a menu item and execute it.
struct grub_handler_menu
{
.next,
.name,
.init,
.fini,
.run
};
It's responsible for parsing config file to get the menu list, and
execution of commands.
struct grub_handler_script
{
.next,
.name,
.init,
.fini,
.readconfig
.getmenu
.execute
};
The handlers are independent of each other. When they need something,
they called specific function of the default handler. For example, to
read a key from the console, we can use
grub_handler[GRUB_HANDLER_INPUT]->getkey. Also, to get the list of
items to be displayed on screen, the menu handler can call
grub_handler[GRUB_HANDLER_SCRIPT]->getmenu.
Any comment for this idea ?
--
Bean
Pavel Roskin
2008-07-17 18:58:29 UTC
Permalink
Post by Bean
Any comment for this idea ?
Maybe you could describe was it will give us and what the drawbacks may
be? It's hard to comment on the implementation ideas without seeing the
bigger picture.
--
Regards,
Pavel Roskin
Bean
2008-07-18 04:47:10 UTC
Permalink
Post by Pavel Roskin
Post by Bean
Any comment for this idea ?
Maybe you could describe was it will give us and what the drawbacks may
be? It's hard to comment on the implementation ideas without seeing the
bigger picture.
Hi,

In the previous thread "Idea: elimination of the normal mode", I have
listed some of the reasons. Here are the advantages of this scheme:

1. One command set

One of the annoying thing of current implementation is that it has two
command set, rescue command and normal command. Some command that can
be called in both rescue mode and normal mode have two modules, like
linux.mod and _linux.mod. What's more, normal mode command depend on
normal.mod, so if we want to include search.mod in grub-mkimage,
normal.mod will be included as well, which make the image quite big.

But if we take a look at the two command sets, one major different is
that rescue command pass argument directly to command, while normal
mode command parse it first to get options, then pass it and the rest
of argument to the command.

Therefore, normal command is essentially rescue command plus argument
parser, we can unify them into one command set. Also, normal command
would only depend on argument parser, which is a lot smaller than
normal.mod.

2. Separation of script engine and interface

Another thing about normal.mod is that it bundles script engine and
interface handler together, which makes it more difficult to extend
its function. We can split the two components:

Script engine is responsible for parsing config file, getting menu
list, and execution of commands, etc.

Interface is responsible for drawing the menu, handling user input, etc.

These two component are independent. For example, when interface
module want to know the menu list to draw on screen, it asks script
engine for it, instead of calling a fixed function.

Rescue mode and normal mode uses different script engine and interface.

Rescue mode:

The script engine is a basic line based scanner, which splits the
command line using space.

The interface is the rescue shell, which accept user input, then calls
the script engine to execute it.

Normal mode:

The script engine is a bash-like parser, it can return menu list as well.

The console interface is a little advanced than the rescue shell, it
has auto completion and history.

The menu interface is the normal menu, it calls script engine to get
the menu list, and uses it to execute commands when user select a menu
item.

Other possible interface:

simple menu:

Some terminal doesn't support arbitrary cursor position, so we could
implement a simple menu interface where the available items are
displayed line by line, with a number indicator. User can choose them
using the indicator.

graphic menu:

It's difficult to merge graphic menu with text menu, as graphic mode
provide more features. Graphic menu can use a style config file to
control the drawing of windows, while in text menu, there is really
not much option available, so we just hard-coded the drawing in the
module.

3. Highly configurable

After we seperate the above function from normal.mod, we have a highly
configurable system. We can select the component we wanted in
grub-mkimage. What's more, as there is a basic line based scanner, we
can use it to setup the initial environment, like locating root using
uuid, setup parameter for serial console, and so on. This feature
looks like the preset menu in grub legacy.

It also works well in embedded environment. For example, if we want to
use grub2 to boot directly, there is no need to include the interface
components. Also, if we have included advanced console in core.img,
there is no need for basic console.
--
Bean
Colin D Bennett
2008-07-18 14:46:45 UTC
Permalink
On Thu, 17 Jul 2008 11:24:23 +0800
Post by Bean
Post by Bean
Hi,
First of all, we can still keep rescue and normal command. But
instead of depending on normal.mod, normal command depends on
module arg, which is an option parser. Also, these two type of
commands are of the same command set. In fact, module arg is
implemented as a pre parser, which goes through the list of
arguments and extract the options. In the case of rescue command,
the pre parser field is null, which means it wants to parse options
itself.
Then, I think of a new structure to represent all configurable
handlers of grub. Different types of handler have different fields,
struct grub_handler
{
.next,
.name,
.init,
.fini
};
Same type of handlers are linked together. We first define an enum
enum {
GRUB_HANDLER_INPUT,
GRUB_HANDLER_OUTPUT,
GRUB_HANDLER_CONSOLE,
GRUB_HANDLER_MENU,
GRUB_HANDLER_SCRIPT,
GRUB_HANDLER_NUM
};
Then, we define an array to point to the head of handler linked
list: grub_handler[GRUB_HANDLER_NUM];
Head is the default selection. When we insert a new handler module,
it would automatically become the new default, although we can
switch back to old handler using a command.
struct grub_handler_input
{
.next,
.name,
.init,
.fini,
.checkkey,
.getkey
.flags,
};
struct grub_handler_output
{
.next,
.name,
.init,
.fini,
.putchar,
.getcharwidth,
.getxy,
.gotoxy,
.cls,
.setcolorstate,
.setcursor,
.flags,
};
It represent the grub console, users type commands and execute them
line by line.
struct grub_handler_console
{
.next,
.name,
.init,
.fini,
.run
};
It represent the menu, users select a menu item and execute it.
struct grub_handler_menu
{
.next,
.name,
.init,
.fini,
.run
};
It's responsible for parsing config file to get the menu list, and
execution of commands.
struct grub_handler_script
{
.next,
.name,
.init,
.fini,
.readconfig
.getmenu
.execute
};
The handlers are independent of each other. When they need
something, they called specific function of the default handler.
For example, to read a key from the console, we can use
grub_handler[GRUB_HANDLER_INPUT]->getkey. Also, to get the list of
items to be displayed on screen, the menu handler can call
grub_handler[GRUB_HANDLER_SCRIPT]->getmenu.
Any comment for this idea ?
I like the idea, especially the idea of separating the script engine
and menu loading code from the character-terminal-based menu system.

I wonder whether it would be better just to have separate variables for
the different handler types like

struct grub_handler_input grub_handler_input;
struct grub_handler_output grub_handler_output;
struct grub_handler_console grub_handler_console;

instead of an array indexed by a constant representing the type, for
the following reasons:

1. You would have to use casting to be able to call the functions in
the handler other than the common 4 (next, name, init, fini). For
instance, you couldn't do
grub_handler[GRUB_HANDLER_INPUT]->getkey()
since the type of grub_handler is struct grub_handler[].

2. If we did use casting for each call to a handler, it is more error
prone since we would not get compiler type checking and could
accidentally write GRUB_HANDLER_INPUT when we meant GRUB_HANDLER_OUTPUT
with obvious consequences.

3. It is more code to write to do
grub_handler[GRUB_HANDLER_INPUT]->getkey()
instead of
grub_handler_input->getkey()


Now, suppose we did define grub_handler_input, grub_handler_output,
etc. Then if there were a reason to iterate over all types of
handlers, or treat handlers generically for some other reason, we could
certainly also create and array of pointers to the handlers for that
purpose:

extern struct grub_handler *grub_handlers[GRUB_HANDLER_NUM];
extern const char *grub_handler_types[GRUB_HANDLER_NUM];

void print_current_handlers (void)
{
int i;
for (i = 0; i < GRUB_HANDLER_NUM; i++)
grub_printf ("handler for %s: %s\n",
grub_handler_types[i],
grub_handlers[i]);
}

We would then have to make sure the list of all handlers was kept in
sync with the separate variables. This would mean a little more code
for each handler type (a global "set current handler" function for
each) but we would probably rarely add a new handler type so it would
not be a great burden. I don't see a need for this iteration over all
handlers, anyway.

Regards,
Colin
Bean
2008-07-18 15:14:38 UTC
Permalink
Post by Colin D Bennett
On Thu, 17 Jul 2008 11:24:23 +0800
Post by Bean
Post by Bean
Hi,
First of all, we can still keep rescue and normal command. But
instead of depending on normal.mod, normal command depends on
module arg, which is an option parser. Also, these two type of
commands are of the same command set. In fact, module arg is
implemented as a pre parser, which goes through the list of
arguments and extract the options. In the case of rescue command,
the pre parser field is null, which means it wants to parse options
itself.
Then, I think of a new structure to represent all configurable
handlers of grub. Different types of handler have different fields,
struct grub_handler
{
.next,
.name,
.init,
.fini
};
Same type of handlers are linked together. We first define an enum
enum {
GRUB_HANDLER_INPUT,
GRUB_HANDLER_OUTPUT,
GRUB_HANDLER_CONSOLE,
GRUB_HANDLER_MENU,
GRUB_HANDLER_SCRIPT,
GRUB_HANDLER_NUM
};
Then, we define an array to point to the head of handler linked
list: grub_handler[GRUB_HANDLER_NUM];
Head is the default selection. When we insert a new handler module,
it would automatically become the new default, although we can
switch back to old handler using a command.
struct grub_handler_input
{
.next,
.name,
.init,
.fini,
.checkkey,
.getkey
.flags,
};
struct grub_handler_output
{
.next,
.name,
.init,
.fini,
.putchar,
.getcharwidth,
.getxy,
.gotoxy,
.cls,
.setcolorstate,
.setcursor,
.flags,
};
It represent the grub console, users type commands and execute them
line by line.
struct grub_handler_console
{
.next,
.name,
.init,
.fini,
.run
};
It represent the menu, users select a menu item and execute it.
struct grub_handler_menu
{
.next,
.name,
.init,
.fini,
.run
};
It's responsible for parsing config file to get the menu list, and
execution of commands.
struct grub_handler_script
{
.next,
.name,
.init,
.fini,
.readconfig
.getmenu
.execute
};
The handlers are independent of each other. When they need
something, they called specific function of the default handler.
For example, to read a key from the console, we can use
grub_handler[GRUB_HANDLER_INPUT]->getkey. Also, to get the list of
items to be displayed on screen, the menu handler can call
grub_handler[GRUB_HANDLER_SCRIPT]->getmenu.
Any comment for this idea ?
I like the idea, especially the idea of separating the script engine
and menu loading code from the character-terminal-based menu system.
I wonder whether it would be better just to have separate variables for
the different handler types like
struct grub_handler_input grub_handler_input;
struct grub_handler_output grub_handler_output;
struct grub_handler_console grub_handler_console;
instead of an array indexed by a constant representing the type, for
1. You would have to use casting to be able to call the functions in
the handler other than the common 4 (next, name, init, fini). For
instance, you couldn't do
grub_handler[GRUB_HANDLER_INPUT]->getkey()
since the type of grub_handler is struct grub_handler[].
2. If we did use casting for each call to a handler, it is more error
prone since we would not get compiler type checking and could
accidentally write GRUB_HANDLER_INPUT when we meant GRUB_HANDLER_OUTPUT
with obvious consequences.
3. It is more code to write to do
grub_handler[GRUB_HANDLER_INPUT]->getkey()
instead of
grub_handler_input->getkey()
Now, suppose we did define grub_handler_input, grub_handler_output,
etc. Then if there were a reason to iterate over all types of
handlers, or treat handlers generically for some other reason, we could
certainly also create and array of pointers to the handlers for that
extern struct grub_handler *grub_handlers[GRUB_HANDLER_NUM];
extern const char *grub_handler_types[GRUB_HANDLER_NUM];
void print_current_handlers (void)
{
int i;
for (i = 0; i < GRUB_HANDLER_NUM; i++)
grub_printf ("handler for %s: %s\n",
grub_handler_types[i],
grub_handlers[i]);
}
We would then have to make sure the list of all handlers was kept in
sync with the separate variables. This would mean a little more code
for each handler type (a global "set current handler" function for
each) but we would probably rarely add a new handler type so it would
not be a great burden. I don't see a need for this iteration over all
handlers, anyway.
Hi,

I like the idea. In fact, I hate using cast, it makes the code ugly.

Also, we can use generic code to handle them, define a structure like this:

{
{ "input", &grub_handler_input_head},
{ "output", &grub_handler_output_head},
{0, 0}
};

As the first few fields of handler is the same, we can use casting to
access them.
--
Bean
Marco Gerards
2008-07-20 20:02:50 UTC
Permalink
Hi,
Post by Bean
First of all, we can still keep rescue and normal command. But instead
of depending on normal.mod, normal command depends on module arg,
which is an option parser. Also, these two type of commands are of the
same command set. In fact, module arg is implemented as a pre parser,
which goes through the list of arguments and extract the options. In
the case of rescue command, the pre parser field is null, which means
it wants to parse options itself.
pre parser?
Post by Bean
Then, I think of a new structure to represent all configurable
handlers of grub. Different types of handler have different fields,
What is a handler and what are its responsibilities?
Post by Bean
struct grub_handler
{
.next,
.name,
.init,
.fini
};
Same type of handlers are linked together. We first define an enum to
enum {
GRUB_HANDLER_INPUT,
GRUB_HANDLER_OUTPUT,
GRUB_HANDLER_CONSOLE,
GRUB_HANDLER_MENU,
GRUB_HANDLER_SCRIPT,
GRUB_HANDLER_NUM
};
grub_handler[GRUB_HANDLER_NUM];
Head is the default selection. When we insert a new handler module, it
would automatically become the new default, although we can switch
back to old handler using a command.
struct grub_handler_input
{
.next,
.name,
.init,
.fini,
.checkkey,
.getkey
.flags,
};
struct grub_handler_output
{
.next,
.name,
.init,
.fini,
.putchar,
.getcharwidth,
.getxy,
.gotoxy,
.cls,
.setcolorstate,
.setcursor,
.flags,
};
Sometimes the input and output are tightly coupled. How do you want
to handle this?
Post by Bean
It represent the grub console, users type commands and execute them
line by line.
struct grub_handler_console
{
.next,
.name,
.init,
.fini,
.run
};
It represent the menu, users select a menu item and execute it.
struct grub_handler_menu
{
.next,
.name,
.init,
.fini,
.run
};
It's responsible for parsing config file to get the menu list, and
execution of commands.
struct grub_handler_script
{
.next,
.name,
.init,
.fini,
.readconfig
.getmenu
.execute
};
What I had in mind was a twofold. I wrote a parser part that actually
parses the script and code to manage the AST and to execute it. It
would be easy to extend the current design with other languages.
Although I think we should not add more than one to SVN, otherwise it
will become bloated.
Post by Bean
The handlers are independent of each other. When they need something,
they called specific function of the default handler. For example, to
read a key from the console, we can use
grub_handler[GRUB_HANDLER_INPUT]->getkey. Also, to get the list of
items to be displayed on screen, the menu handler can call
grub_handler[GRUB_HANDLER_SCRIPT]->getmenu.
How does this differ from what we have now? You can register all
kinds of objects and deregister them.

--
Marco
Bean
2008-07-20 20:23:57 UTC
Permalink
Post by Marco Gerards
Hi,
Post by Bean
First of all, we can still keep rescue and normal command. But instead
of depending on normal.mod, normal command depends on module arg,
which is an option parser. Also, these two type of commands are of the
same command set. In fact, module arg is implemented as a pre parser,
which goes through the list of arguments and extract the options. In
the case of rescue command, the pre parser field is null, which means
it wants to parse options itself.
pre parser?
Maybe not the best word, the idea is that normal command is just like
rescue command, except that it goes through an extra parser that
convert options to grub_arg_list.
Post by Marco Gerards
Post by Bean
Then, I think of a new structure to represent all configurable
handlers of grub. Different types of handler have different fields,
What is a handler and what are its responsibilities?
Actually, handler is a genetic term. For terminal, there is grub_term.
For video, there is grub_video_adapter. They're similar in operation.
For example, there can have multiple objects, but only one is active,
they all have basic field like init, fini, next, name. They can be
handled by the same function. I also extend this concept to other
objects, like script engine, console and menu interface.
Post by Marco Gerards
Post by Bean
struct grub_handler
{
.next,
.name,
.init,
.fini
};
Same type of handlers are linked together. We first define an enum to
enum {
GRUB_HANDLER_INPUT,
GRUB_HANDLER_OUTPUT,
GRUB_HANDLER_CONSOLE,
GRUB_HANDLER_MENU,
GRUB_HANDLER_SCRIPT,
GRUB_HANDLER_NUM
};
grub_handler[GRUB_HANDLER_NUM];
Head is the default selection. When we insert a new handler module, it
would automatically become the new default, although we can switch
back to old handler using a command.
struct grub_handler_input
{
.next,
.name,
.init,
.fini,
.checkkey,
.getkey
.flags,
};
struct grub_handler_output
{
.next,
.name,
.init,
.fini,
.putchar,
.getcharwidth,
.getxy,
.gotoxy,
.cls,
.setcolorstate,
.setcursor,
.flags,
};
Sometimes the input and output are tightly coupled. How do you want
to handle this?
Some module can export both input and output handler, like serial. But
some only export input (like atkeyboard), or output (gfxterm).
Post by Marco Gerards
Post by Bean
It represent the grub console, users type commands and execute them
line by line.
struct grub_handler_console
{
.next,
.name,
.init,
.fini,
.run
};
It represent the menu, users select a menu item and execute it.
struct grub_handler_menu
{
.next,
.name,
.init,
.fini,
.run
};
It's responsible for parsing config file to get the menu list, and
execution of commands.
struct grub_handler_script
{
.next,
.name,
.init,
.fini,
.readconfig
.getmenu
.execute
};
What I had in mind was a twofold. I wrote a parser part that actually
parses the script and code to manage the AST and to execute it. It
would be easy to extend the current design with other languages.
Although I think we should not add more than one to SVN, otherwise it
will become bloated.
Right, but actually, there are already two script engine existing. One
is the normal bash-like parser, another is the command line scanner in
the rescue mode.
Post by Marco Gerards
Post by Bean
The handlers are independent of each other. When they need something,
they called specific function of the default handler. For example, to
read a key from the console, we can use
grub_handler[GRUB_HANDLER_INPUT]->getkey. Also, to get the list of
items to be displayed on screen, the menu handler can call
grub_handler[GRUB_HANDLER_SCRIPT]->getmenu.
How does this differ from what we have now? You can register all
kinds of objects and deregister them.
Yes, handler is just objects, but they share common fields, so that we
can handle them together, other than writing different code for
different object.
--
Bean
Marco Gerards
2008-07-21 17:27:09 UTC
Permalink
Hi,
Post by Bean
Post by Marco Gerards
Hi,
Post by Bean
First of all, we can still keep rescue and normal command. But instead
of depending on normal.mod, normal command depends on module arg,
which is an option parser. Also, these two type of commands are of the
same command set. In fact, module arg is implemented as a pre parser,
which goes through the list of arguments and extract the options. In
the case of rescue command, the pre parser field is null, which means
it wants to parse options itself.
pre parser?
Maybe not the best word, the idea is that normal command is just like
rescue command, except that it goes through an extra parser that
convert options to grub_arg_list.
Sorry, but I still don't understand this. Do you propose:
svn move normal/arg.c kern/arg.c

?

If you propose a stripped down argument parser, how do arguments
parsers differ amongst each other?
Post by Bean
Post by Marco Gerards
Post by Bean
Then, I think of a new structure to represent all configurable
handlers of grub. Different types of handler have different fields,
What is a handler and what are its responsibilities?
Actually, handler is a genetic term. For terminal, there is grub_term.
For video, there is grub_video_adapter. They're similar in operation.
For example, there can have multiple objects, but only one is active,
they all have basic field like init, fini, next, name. They can be
handled by the same function. I also extend this concept to other
objects, like script engine, console and menu interface.
Right...
Post by Bean
Post by Marco Gerards
Post by Bean
struct grub_handler
{
.next,
.name,
.init,
.fini
};
Same type of handlers are linked together. We first define an enum to
enum {
GRUB_HANDLER_INPUT,
GRUB_HANDLER_OUTPUT,
GRUB_HANDLER_CONSOLE,
GRUB_HANDLER_MENU,
GRUB_HANDLER_SCRIPT,
GRUB_HANDLER_NUM
};
Seems ok to me.

Although what if we want additional handlers. Like image readers or
so? We do not need to centralize this. What about this:

handler.c:

grub_handler_t
grub_handler_add (const char *name)
{
...
}

grub_err_t
grub_handler_remove (const char *name)
{
...
if (module uses handler)
return grub_error (...);
...
}


/* NAME is the name of the handler, HANDLER the handler struct. */
grub_err_t
grub_handler_register (const char *name, grub_handler_t handler)
{
...
}


/* NAME is the name of the handler, HANDLER the handler struct. */
grub_err_t
grub_handler_deregister (const char *name, grub_handler_t handler)
{
...
}


/* HANDLER the handler struct. Hook the hook function. */
grub_err_t
grub_handler_register (grub_handler_t handler,
int (*hook) (grub_handler_t handler))
{
...
}


handler.h:

struct grub_handler
{
struct grub_handler *next;
const char *name;
};

#define GRUB_CREATE_HANDLER_H(name, interfaces) \
grub_err_t grub_##name##_register (struct grub_##name##_handler h);

(same for the other functions)

#define GRUB_CREATE_HANDLER_FUNCS(name, interfaces) \
grub_err_t \
grub_##name##_register (struct grub_##name##_handler h); \
{ \
grub_handler_register (name, &h->handler);
}

(same for other functions)


So the handler framework is opaque to the user. The user just works
with this like it works now. For example:

foo.h:

struct grub_foo
{
/* This is *required*. */
grub_handler_t handle;

/* Every FOO needs to foo. */
int (*foo) (int bar);
};

GRUB_CREATE_HANDLER_T ("foo", struct grub_foo);

this creates all prototypes

foo.c:

GRUB_CREATE_HANDLER ("foo", struct grub_foo);

this creates functions that are very light and are mainly there to
cast the structs back and forth so we can do type checking and get
sane warnings and maximal cleaness. Furthermore, users can now use:

grub_foo_register (...);

^^ Every foo can register itself

grub_foo_iterate (...);

^^ You can iterate over every foo

The only "weird" thing is that when you register a handler, you have
to know its name. For example disk.c would do:

{
err = grub_handler_add ("disk");
...
}


So as a summary:

grub_handler_add will be used to add new handler lists,
grub_handler_remove will remove them

grub_handler_register will register specific handlers (like disk
handlers), a macro can be used to add a wrapper that passes along the
name. grub_handler_register can fill in the mandatory member "handle"
such that later on the name is not required anymore. Requiring
strings only at register time is not very expensive.

Sorry for the crappy code, but hopefully you get the idea, otherwise I
can hack a bit or comment on your patches :-)
Post by Bean
Post by Marco Gerards
Post by Bean
grub_handler[GRUB_HANDLER_NUM];
Right. Although what I proposed above you do not need an array, but
you can register anything for free :-)
Post by Bean
Post by Marco Gerards
Post by Bean
Head is the default selection. When we insert a new handler module, it
would automatically become the new default, although we can switch
back to old handler using a command.
Are you sure this is the right way to go? This needs more feedback
from other people, I think...?
Post by Bean
Post by Marco Gerards
Post by Bean
struct grub_handler_input
{
.next,
.name,
.init,
.fini,
.checkkey,
.getkey
.flags,
};
This is what we had, right?
Post by Bean
Post by Marco Gerards
Post by Bean
struct grub_handler_output
{
.next,
.name,
.init,
.fini,
.putchar,
.getcharwidth,
.getxy,
.gotoxy,
.cls,
.setcolorstate,
.setcursor,
.flags,
};
Sometimes the input and output are tightly coupled. How do you want
to handle this?
Some module can export both input and output handler, like serial. But
some only export input (like atkeyboard), or output (gfxterm).
Okay...
Post by Bean
Post by Marco Gerards
Post by Bean
It represent the grub console, users type commands and execute them
line by line.
struct grub_handler_console
{
.next,
.name,
.init,
.fini,
.run
};
It represent the menu, users select a menu item and execute it.
struct grub_handler_menu
{
.next,
.name,
.init,
.fini,
.run
};
It's responsible for parsing config file to get the menu list, and
execution of commands.
struct grub_handler_script
{
.next,
.name,
.init,
.fini,
.readconfig
.getmenu
.execute
};
What I had in mind was a twofold. I wrote a parser part that actually
parses the script and code to manage the AST and to execute it. It
would be easy to extend the current design with other languages.
Although I think we should not add more than one to SVN, otherwise it
will become bloated.
Right, but actually, there are already two script engine existing. One
is the normal bash-like parser, another is the command line scanner in
the rescue mode.
Ah, right.
Post by Bean
Post by Marco Gerards
Post by Bean
The handlers are independent of each other. When they need something,
they called specific function of the default handler. For example, to
read a key from the console, we can use
grub_handler[GRUB_HANDLER_INPUT]->getkey. Also, to get the list of
items to be displayed on screen, the menu handler can call
grub_handler[GRUB_HANDLER_SCRIPT]->getmenu.
How does this differ from what we have now? You can register all
kinds of objects and deregister them.
Yes, handler is just objects, but they share common fields, so that we
can handle them together, other than writing different code for
different object.
Right, I agree with this.

--
Marco
Bean
2008-07-21 18:03:26 UTC
Permalink
Post by Marco Gerards
Hi,
Post by Bean
Post by Marco Gerards
Hi,
Post by Bean
First of all, we can still keep rescue and normal command. But instead
of depending on normal.mod, normal command depends on module arg,
which is an option parser. Also, these two type of commands are of the
same command set. In fact, module arg is implemented as a pre parser,
which goes through the list of arguments and extract the options. In
the case of rescue command, the pre parser field is null, which means
it wants to parse options itself.
pre parser?
Maybe not the best word, the idea is that normal command is just like
rescue command, except that it goes through an extra parser that
convert options to grub_arg_list.
svn move normal/arg.c kern/arg.c
?
If you propose a stripped down argument parser, how do arguments
parsers differ amongst each other?
Actually, no need to move normal/arg.c to kernel, it can be a
standalone module. Normal command depend on it, while rescue command
don't.

there is only one argument parser, it's job is to scan the argument
for options like -f, --file and convert them to grub_arg_list, so that
the command handler don't have to parse option itself.
Post by Marco Gerards
Seems ok to me.
Although what if we want additional handlers. Like image readers or
grub_handler_t
grub_handler_add (const char *name)
{
...
}
grub_err_t
grub_handler_remove (const char *name)
{
...
if (module uses handler)
return grub_error (...);
...
}
/* NAME is the name of the handler, HANDLER the handler struct. */
grub_err_t
grub_handler_register (const char *name, grub_handler_t handler)
{
...
}
/* NAME is the name of the handler, HANDLER the handler struct. */
grub_err_t
grub_handler_deregister (const char *name, grub_handler_t handler)
{
...
}
/* HANDLER the handler struct. Hook the hook function. */
grub_err_t
grub_handler_register (grub_handler_t handler,
int (*hook) (grub_handler_t handler))
{
...
}
struct grub_handler
{
struct grub_handler *next;
const char *name;
};
#define GRUB_CREATE_HANDLER_H(name, interfaces) \
grub_err_t grub_##name##_register (struct grub_##name##_handler h);
(same for the other functions)
#define GRUB_CREATE_HANDLER_FUNCS(name, interfaces) \
grub_err_t \
grub_##name##_register (struct grub_##name##_handler h); \
{ \
grub_handler_register (name, &h->handler);
}
(same for other functions)
So the handler framework is opaque to the user. The user just works
struct grub_foo
{
/* This is *required*. */
grub_handler_t handle;
/* Every FOO needs to foo. */
int (*foo) (int bar);
};
GRUB_CREATE_HANDLER_T ("foo", struct grub_foo);
this creates all prototypes
GRUB_CREATE_HANDLER ("foo", struct grub_foo);
this creates functions that are very light and are mainly there to
cast the structs back and forth so we can do type checking and get
grub_foo_register (...);
^^ Every foo can register itself
grub_foo_iterate (...);
^^ You can iterate over every foo
The only "weird" thing is that when you register a handler, you have
{
err = grub_handler_add ("disk");
...
}
grub_handler_add will be used to add new handler lists,
grub_handler_remove will remove them
grub_handler_register will register specific handlers (like disk
handlers), a macro can be used to add a wrapper that passes along the
name. grub_handler_register can fill in the mandatory member "handle"
such that later on the name is not required anymore. Requiring
strings only at register time is not very expensive.
Sorry for the crappy code, but hopefully you get the idea, otherwise I
can hack a bit or comment on your patches :-)
Your idea seems fine, but there is a slightly efficiency issue. For
example, when we need to call a function in the handler, we need to
acquire it using name. We need to do this in every call, as the
handler could be changed next time.

My suggestion is to use function pointer instead of name, for example,

grub_handler_register (&grub_handler_input_head, my_handler);

Then we can always use grub_handler_input_head->getkey() to read a key.

The drawback is that we need to define grub_handler_**_head as global variable.
Post by Marco Gerards
Post by Bean
Post by Marco Gerards
Post by Bean
grub_handler[GRUB_HANDLER_NUM];
Right. Although what I proposed above you do not need an array, but
you can register anything for free :-)
Post by Bean
Post by Marco Gerards
Post by Bean
Head is the default selection. When we insert a new handler module, it
would automatically become the new default, although we can switch
back to old handler using a command.
Are you sure this is the right way to go? This needs more feedback
from other people, I think...?
This is used to save handler switching command. Normally, when we push
a new handler, we want to use it right away. Although this behavior is
up for discussion ...
--
Bean
Bean
2008-07-21 18:42:01 UTC
Permalink
Post by Bean
Your idea seems fine, but there is a slightly efficiency issue. For
example, when we need to call a function in the handler, we need to
acquire it using name. We need to do this in every call, as the
handler could be changed next time.
My suggestion is to use function pointer instead of name, for example,
grub_handler_register (&grub_handler_input_head, my_handler);
Then we can always use grub_handler_input_head->getkey() to read a key.
The drawback is that we need to define grub_handler_**_head as global variable.
Hi,

Ok, I come up with a mixed solution. We can still register handler
using string, like this:

grub_handler_register ("input", my_input_handler);

We don't need grub_handler_add. Whenever it sees a new string,
grub_handler_register would create a item in the handler table.

To get the handler, we use:

grub_get_handler ("input", &local_input_handler_head);

The handler is managed as linked list, the head always points to
current selection, so we can use local_input_handler_head to call the
handler function.

To reduce unnecessary grub_get_handler calls, we export important
handler head as global variable, for example, we can use the following
in grub_main:

grub_get_handler ("input", &grub_input_handler_head);
grub_get_handler ("output", &grub_output_handler_head);
...

Other modules can use grub_input_handler_head directly to access input
handlers, but for custom handler such as "foo", they need to use
grub_get_handler to get the handler pointer.
--
Bean
Marco Gerards
2008-07-22 21:26:31 UTC
Permalink
Marco Gerards <***@xs4all.nl> writes:

[...]
Post by Marco Gerards
Although what if we want additional handlers. Like image readers or
[...]

Here is a patch to register and deal with handlers. It decentralizes
handlers and should remove some maintainance burden and boring code
duplication. Please tell me what you think of this.

Okuji, can you please comment on this, as this is important, it
changes all core functionality eventually.

--
Marco

2008-07-22 Marco Gerards <***@gnu.org>

* conf/i386-efi.rmk (grub_emu_SOURCES): Add `kern/handler.c'.
(kernel_img_SOURCES): Likewise.
(grub_setup_SOURCES): Likewise.
(kernel_mod_HEADERS): Add handler.h.

* conf/common.rmk (grub_probe_SOURCES): Add `kern/handler.c'.
(grub_fstest_SOURCES): Likewise.

* conf/i386-efi.rmk (grub_emu_SOURCES): Add `kern/handler.c'.
(kernel_mod_SOURCES): Likewise.
(kernel_mod_HEADERS): Add `handler.h'.

* conf/x86_64-efi.rmk (grub_emu_SOURCES): Add `kern/handler.c'.
(kernel_mod_SOURCES): Likewise.
(kernel_mod_HEADERS): Add `handler.h'.

* conf/powerpc-ieee1275.rmk (kernel_elf_HEADERS): Add `handler.h'.
(grub_emu_SOURCES): Add `kern/handler.c'.
(kernel_elf_SOURCES): Likewise.

* conf/i386-coreboot.rmk (kernel_elf_HEADERS): Add `handler.h'.
(grub_emu_SOURCES): Add kern/handler.c
(kernel_elf_SOURCES): Likewise.

* conf/sparc64-ieee1275.rmk (kernel_elf_HEADERS): Add `handler.h'.
(kernel_elf_SOURCES): Likewise.

* conf/i386-ieee1275.rmk (kernel_elf_HEADERS): Add `handler.h'.
(grub_emu_SOURCES): Add `kern/handler.c'.
(kernel_elf_SOURCES): Likewise.

* kern/fs.c: Register fs handler using GRUB_HANDLER_CREATE_C.
(grub_fs_list): Removed.
(grub_fs_register): Likewise.
(grub_fs_unregister): Likewise.
(grub_fs_iterate): Likewise.

* kern/handler.c: New file.

* include/grub/handler.h: Likewise.

* include/grub/fs.h: Include <grub/handler.h>. Create handler
using GRUB_HANDLER_CREATE_H_KERNEL.
(grub_fs): Removed members `name' and `next'. Use
GRUB_HANDLER_CREATE_STRUCT to recreate these members.
(grub_fs_t): Removed.

Continue reading on narkive:
Loading...