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 ();