Surprise uses error system based on libcom_err found in e2fsprogs package. Unfortunately original libcom_err implementation can't pass arguments to the errors, due to this fact it doesn't solve localization in any way and also error categorization and flagging isn't possible. We proposed extension of libcom_err which is backwards compatible - any error from libcom_err can be passed to our system (but not vice versa).
Each error has its unique 32-bit number which fits in long
C type,
in libcom_err defined as errcode_t
. Numbering space of this
value is unified with standard errno
variable. On any place with
errcode_t
declararion you can pass in values like ENOMEM
.
Please don't make any assumptions on the value of this variable - unique
error number can be quite comfortably even a negative number.
Offered error system packs each generated error event into struct
user_error
. This structure may be freed in one turn by simple
free(3)
. These error events are collected across functions and
reported to the higher levels. On the top levels these errors should be
decoded and presented to the user.
Each error has it's unique message (as in libcom_err) and the
message text itself is defined in file `foo.et
'. Sample file
follows:
# hash-ed comment lines, error definition file error_table eetb ec ET_EETB_MESSAGE_ONE, "e^>Some message with %d format string(s)" ec ET_EETB_MESSAGE_TWO, "E>Another message, emergency level, no errno reported" ec ET_EETB_BAD_ERROR_COUNT, "e>Invalid count of errors %d" end
eetb is table identification which should consist only from exactly 4 alphanumeric characters. Currently there is some tendency to use symbol starting with `S' for all Surprise-specific tables, `SF' for filesystem modules of Surprise (yes, only 2 characters left for differentiation between filesystems). Although these naming conventions are not mandatory, please note that your chosen table name has to be unique across all libraries using libcom_err linked (even dynamically) to your project. Follows the definition of messages, each should start with ET_ prefix (which is ommited in references from .c files, see below).
Each message is preceded by flags which are delimited by character `>' (greater sign). flags themselves is unsorted sequence of characters with the meaning defined as follows:
strerror(errno)
string value is reported with appending
` -
' delimiting string on the end of message.
log(EMERGENCY,...)
.
log(ALERT,...)
.
log(ERROR,...)
.
log(WARNING,...)
.
log(NOTE,...)
.
log(DEBUG,...)
.
log(VERBOSE,...)
.
For most up-to-date debug levels see definition of enum dbglevels
in /surprise/include/generic.h
.
When such error has to be presented to the user, library tries to
localize the message by calling gettext(3)
on the message text
without the preceding flags and `>' delimiter.
We assume that you have already your basic message file `foo.et' written. Now we will enhance our `Makefile' for proper error system compilation rules:
... OBJS=object1 object2 error ... object1.o: error_chket.h error.h ... include $(TOP)/Makefile.top
Note that although we chose base name `error' for our message
definition file, in fact no restrictions are given on this filename
(except maintainer-only autogen
script, beware).
Used dependencies will generate four new files for us:
`Makefile.top' will automatically clean these intermediate files
during make clean
although they look like sources. Besides this
`Makefile' magic each file of our directory using the error
system should:
#include "generic.h" #include "error_chket.h"
initialize_CODE_error_table()
where
CODE
is 4-character acronym as defined in
`error.et'. This is one of the changes from original
libcom_err where this registration would have to be done only
before user presentation - we need it before first error instantiation.
According to our sample file the call would read:
initialize_eetb_error_table();
ERR_PARAM
in its parameter list. Convention is to place it as the
very last argument, except format-string functions where it is placed
right before the format string pointer (and thus even before
consequential ...
or va_list
parameter). This ERR_PARAM
will pass the pointer to the error list of calling function and thus all
generated errors are collected in single list.
ERR_PASS
to the place of formal parameter declared as
ERR_PARAM
to pass the reference to our error context to the called
function. Please not that this ERR_PARAM
parameter may not be
evident in macros, for example provided EMALLOC()
or newobj()
macros ARE error-generating and as such require ERR_PARAM
to be
present in the function where used.
ERR_PARAM
convention, each function using error
system primitives has to have `ERR_DECL;
' right between function
declaration part and first command statement. There is currently no
performance hit when declared without any further reference by error
system.
Please note about possible macro variants and modifications:
extern
keyword gets preceded before any declarations for C header
(".h") file inclusion. You will probably use it together
with ERR_PREF
feature. Please note that by using these macros
you will also loose the structured storing of error messages as they
will get stored into one flat list.
For now we will stop the discussion and provide some hot sample code below. We will assume the error table `foo.et' as shown on the beginning of this document.
#include "generic.h" #include "foo_chket.h" char *advanced_errorer(const char *mystring,ERR_PARAM) { char *dupstring; ERR_DECL; if (!(dupstring=ESTRDUP(mystring))) /* may return NULL but error will be stored */ return(NULL); free(mystring); return(mystring); } /* returns 0 for sucess, -1 for error */ int errorer(int howmany,errcode_t which_error,ERR_PARAM) { static int inited=0; char *mystring; ERR_DECL; if (!inited) { /* Assure that we init only once */ initialize_eetb_error_table(); inited=1; } if (howmany<0) /* Report error and return immediately */ ERR_RETURN_REPORT(EETB_BAD_ERROR_COUNT,howmany); while (howmany--) ERR_REPORT_CODE(which_error); if (!advanced_errorer("my string",ERR_PASS)) ERR_RETURN(); puts("We passed advanced_errorer(), yow!"); ERR_RETURN(); }
We will finally list all the error system constructs:
ERR_PARAM+_EXTERN
Declaration for passing error list reference from higher levels, for details see above.
ERR_DECL+_EXTERN
Error helper declarations, MUST be located exactly between declaration and statement block, for details see above.
ERR_DECL_TOPLEVEL
Use on the function which doesn't report the errors to its upper callers.
You should somehow process the errors collected during the function's
run - for this processing please use ERR_DECL_DISPOSE
or
ERR_DECL_DUMP
macro variants. With `ERR_DECL_TOPLEVEL;
' you are
no longer forced to (and you are in fact forbidden to) declaring
ERR_PARAM
function parameter. All errors generated in all called and
sub-called functions will be stored and, of course, they'll get also output
to the application log. Beware - ERR_RETURN
variants are not
functional in ERR_DECL_TOPLEVEL
-ed functions, of course!
You are forced to use ERR_DECL_DISPOSE
or ERR_DECL_DUMP
before
returning from such function as otherwise memory leak from generated
errors may ocur. Of course, you need to do this cleanup only when some
error could be generated only. ERR_OCCURED()
will no longer return
correct value after the error messages were processed.
ERR_DECL_DISPOSE
Usable only in the function with ERR_DECL_TOPLEVEL
declaration.
Please read the previous ERR_DECL_TOPLEVEL
section for further
information.
This function will completely discard any collected errors. You can only take the application log as the actions souvenir.
ERR_DECL_DUMP
Usable only in the function with ERR_DECL_TOPLEVEL
declaration.
Please read the previous ERR_DECL_TOPLEVEL
section for further
information.
Any collected errors will get dumped to FILE *
output separated
by '\n'
newline character.
ERR_DECL_DUMP_OUT
Shortcut for ERR_DECL_DUMP(stdout)
.
ERR_PASS
Symbol to pass reference to our error list context to called lower-level functions, for details see above.
ERR_REPORT(code,args...)
Main error reporting function,
instantiates error as found in already initialized message catalogs by
code
substituing any args
values. All strings referenced by
`%s
' (or its length-limited variant) are duplicated during error
instantion and as such can be safely freed immediately after
ERR_REPORT
. Please note that ET_
prefix is added automatically
to the passed code
symbol value. All errors reported by this
function are automatically passed to log(severity,message)
.
log(severity,format,args...)
Report the given text message in the application log together with source file name and source line number where the event was triggered. These messages are usually not shown to casual user.
ERR_REPORT_CODE(code)
Error reporting of foreign error
code. You may report symbols from <errno.h>
(like
ENOMEM
) here or errors returned by libraries using simple original
libcom_err. Please don't use ERR_REPORT_CODE(errno)
construct, it is always better to use standard ERR_REPORT
with
error message marked by
^ errno-inclusing flag. By this message you can give much better advice during which
operation such error occured.
ERR_OCCURED()
Returns TRUE if any report was generated
during the run of this function. This includes all the errors generated
even in called sub-functions and it doesn't include any error generated
to the same error list but prior to calling our function where
ERR_OCCURED()
is used. This function cannot be used in
ERR_DECL_STDERR
-ed functions.
ERR_RETURN()
Does C-equivalent of
`return(error);
' where error
is the value representing
whether error occured during run of our function. error
is 0
for ERR_DECL_STDERR
-ed functions and for normal ERR_DECL
-ed
functions when no error occurs. error
is -1 when some error
occured, according to ERR_OCCURED()
(see previous item
description). Note that ERR_RETURN()
can be used only for functions
with convention of returning success/failure code as int
type. If
your function returns some kind of pointer etc., you have to handle the
`return(...);
' yourself from case to case.
ERR_RETURN_REPORT(code,args...)
Simple shortcut to
calling of ERR_REPORT(code,args...)
immediately followed by
ERR_RETURN()
. The same int
type return of the function
restrictions apply as in ERR_RETURN()
. See definitions of both
macros above.
ERR_NULL_RETURN_REPORT(code,args...)
Simple shortcut to calling
of ERR_RETURN_REPORT(code,args...)
immediately followed by return
NULL
.
ERR_RETURN_REPORT_CODE(code)
Simple shortcut to calling
of ERR_REPORT_CODE(code)
immediately followed by ERR_RETURN()
.
The same int
type return of the function restrictions apply as in
ERR_RETURN()
. See definitions of both macros above.
ERR_NULL_RETURN_REPORT_CODE(code)
Simple shortcut to calling of
ERR_RETURN_REPORT_CODE(code)
immediately followed by return NULL
.
ERR_PREF
You may use it to prevent very painful carrying of the
ERR_PARAM
/ ERR_PASS
through all the functions. It may get very
convenient during callbacked own code by foreign libraries.
It has additional the very-first parameter to specify string which should be preceding any generated variables/parameters. You will probably find it useful during global declarations for passing errors by system-callbacked functions.
eterr
Simple local variable of type errcode_t
prepared for you locally in each function using error system. You will
typically assign to it result values of system functions, potentially
ERR_REPORT_CODE(eterr)
-ing them when it is checked (by you) that
the error really occured.
Until now we were all errors only storing to so-called error list. Here we will introduce your chance to get hands on these valuable stored events. Three error-retrieval functions exist:
struct user_error *err_get_struct(GList **param)
This function will pop the first (oldest) error found in error list and
returns it formatted into struct user_error
. Error event is
permanently removed from the error list, returned structure can be
deallocated by simple free(3)
on the structure memory block itself.
When NULL
is returned, no more error events exist. And don't be
confused by GList **param
type, you will have to deal with error
list implementation anyway.
char *err_struct_to_string(struct user_error
*err,ERR_PARAM)
Format the given struct user_error
to user-presentable error
strings and return it malloc(3)
-ated. Any errno
stored in the
structure (not the actual one!) gets properly substituted. Given
struct user_error
is not free(3)
-d by this funtion, you have
to do it manually when you are done with its processing. This function
can generate errors, please deal with them completely separately from
the error event being parsed.
char *err_get_string(GList **param,ERR_PARAM)
Simple wrapper around previous two functions err_get_struct()
and
err_struct_to_string()
. Event is removed from the queue, processed
and intermediate struct user_error
free(3)
-d immediately.
Returned string is malloc(3)
-ated.
During error processing you shouldn't need too much to deal with ERR_PARAM
yourself. If you can't avoid it, currently this symbol is defined as
`GList ** surerr_param
'. One additional pointer is due to the fact the
list itself has to be passed across functions by reference. All newly created
errors are appended to the end of the GList *
, processed are from the
beginning (ordering is preserved).
Due to compatibility with old GCC releases we had to choose a worse
solution for checking of given variable arguments to ERR_REPORT
whether they match to the formatstring (as asserted by GCC
__attribute__((format(printf,1,2)))
). Currently the runtime
overhead is added to achieve compilation-time checks together with
checking of missing flagging specifiers. After your program gets
debugged and the final product is to be deployed, please define
NDEBUG
symbol which will remove this overhead (and thus even
parameters checking, of course). NDEBUG
commonly disables all
assert(3)
checks which are good to turn off for production release
anyway.
Unknown code boom 2
'. What is screwed up?You haven't called initialize_CODE_error_table()
, please
see
above.
ERR_PARAM
and ERR_PASS
?ERR_PARAM
is a declaration, ERR_PASS
does real value passing.
Simply in function declaration header (and in header files excluding
inlined functions generally) you have to always use ERR_PARAM
, in
function bodies when calling any function use always ERR_PASS
.
When in doubt, see their definitions in
surprise/src/include/generic.h
.
ERR_RETURN_CODE(ETAB_ERROR_1);
to return statical (no printf(3)
format marks) string.Please use simply ERR_RETURN(ETAB_ERROR_1);
as it works even when
no format marks are used (as in error `Cannot continue
optimization'
). ERR_RETURN_CODE(errcode)
is used when
errno
-type variable or symbol from <errno.h>
(like
ENOMEM
) has to be reported to the error system.