grub-dev: Error Handling
7 Error Handling
****************
Error handling in GRUB 2 is based on exception handling model. As C
language doesn't directly support exceptions, exception handling
behavior is emulated in software.
When exception is raised, function must return to calling function.
If calling function does not provide handling of the exception it must
return back to its calling function and so on, until exception is
handled. If exception is not handled before prompt is displayed, error
message will be shown to user.
Exception information is stored on 'grub_errno' global variable. If
'grub_errno' variable contains value 'GRUB_ERR_NONE', there is no active
exception and application can continue normal processing. When
'grub_errno' has other value, it is required that application code
either handles this error or returns instantly to caller. If function
is with return type 'grub_err_t' is about to return 'GRUB_ERR_NONE', it
should not set 'grub_errno' to that value. Only set 'grub_errno' in
cases where there is error situation.
Simple exception forwarder.
grub_err_t
forwarding_example (void)
{
/* Call function that might cause exception. */
foobar ();
/* No special exception handler, just forward possible exceptions. */
if (grub_errno != GRUB_ERR_NONE)
{
return grub_errno;
}
/* All is OK, do more processing. */
/* Return OK signal, to caller. */
return GRUB_ERR_NONE;
}
Error reporting has two components, the actual error code (of type
'grub_err_t') and textual message that will be displayed to user. List
of valid error codes is listed in header file 'include/grub/err.h'.
Textual error message can contain any textual data. At time of writing,
error message can contain up to 256 characters (including terminating
NUL). To ease error reporting there is a helper function 'grub_error'
that allows easier formatting of error messages and should be used
instead of writing directly to global variables.
Example of error reporting.
grub_err_t
failing_example ()
{
return grub_error (GRUB_ERR_FILE_NOT_FOUND,
"Failed to read %s, tried %d times.",
"test.txt",
10);
}
If there is a special reason that error code does not need to be
taken account, 'grub_errno' can be zeroed back to 'GRUB_ERR_NONE'. In
cases like this all previous error codes should have been handled
correctly. This makes sure that there are no unhandled exceptions.
Example of zeroing 'grub_errno'.
grub_err_t
probe_example ()
{
/* Try to probe device type 1. */
probe_for_device ();
if (grub_errno == GRUB_ERR_NONE)
{
/* Device type 1 was found on system. */
register_device ();
return GRUB_ERR_NONE;
}
/* Zero out error code. */
grub_errno = GRUB_ERR_NONE;
/* No device type 1 found, try to probe device type 2. */
probe_for_device2 ();
if (grub_errno == GRUB_ERR_NONE)
{
/* Device type 2 was found on system. */
register_device2 ();
return GRUB_ERR_NONE;
}
/* Zero out error code. */
grub_errno = GRUB_ERR_NONE;
/* Return custom error message. */
return grub_error (GRUB_ERR_UNKNOWN_DEVICE, "No device type 1 or 2 found.");
}
Some times there is a need to continue processing even if there is a
error state in application. In situations like this, there is a needed
to save old error state and then call other functions that might fail.
To aid in this, there is a error stack implemented. Error state can be
pushed to error stack by calling function 'grub_error_push ()'. When
processing has been completed, 'grub_error_pop ()' can be used to pop
error state from stack. Error stack contains predefined amount of error
stack items. Error stack is protected for overflow and marks these
situations so overflow error does not get unseen. If there is no space
available to store error message, it is simply discarded and overflow
will be marked as happened. When overflow happens, it most likely will
corrupt error stack consistency as for pushed error there is no matching
pop, but overflow message will be shown to inform user about the
situation. Overflow message will be shown at time when prompt is about
to be drawn.
Example usage of error stack.
/* Save possible old error message. */
grub_error_push ();
/* Do your stuff here. */
call_possibly_failing_function ();
if (grub_errno != GRUB_ERR_NONE)
{
/* Inform rest of the code that there is error (grub_errno
is set). There is no pop here as we want both error states
to be displayed. */
return;
}
/* Restore old error state by popping previous item from stack. */
grub_error_pop ();