mirror of
https://gitlab.isc.org/isc-projects/bind9
synced 2025-09-01 23:25:38 +00:00
add
This commit is contained in:
31
doc/dev/DBC
Normal file
31
doc/dev/DBC
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
Design By Contract
|
||||||
|
|
||||||
|
|
||||||
|
BIND 9 uses the "Design by Contract" idea for most function calls.
|
||||||
|
|
||||||
|
A quick summary of the idea is that a function and its caller make a
|
||||||
|
contract. If the caller meets certain preconditions, then the
|
||||||
|
function promises to either fulfill its contract (i.e. guarantee a set
|
||||||
|
of postconditions), or to clearly fail.
|
||||||
|
|
||||||
|
"Clearly fail" means that if the function cannot succeed, then it will
|
||||||
|
not silently fail and return a value which the caller might interpret
|
||||||
|
as success.
|
||||||
|
|
||||||
|
If a caller doesn't meet the preconditions, then "further execution is
|
||||||
|
undefined". The function can crash, compute a garbage result, fail silently,
|
||||||
|
etc. Allowing the function to define preconditions greatly simplifies many
|
||||||
|
APIs, because the API need not have a way of saying "hey caller, the values
|
||||||
|
you passed in are garbage".
|
||||||
|
|
||||||
|
Typically, preconditions are specified in the functions .h file, and encoded
|
||||||
|
in its body with REQUIRE statements. The REQUIRE statements cause the program
|
||||||
|
to dump core if they are not true, and can be used to identify callers that
|
||||||
|
are not meeting their preconditions.
|
||||||
|
|
||||||
|
Postconditions can be encoded with ENSURE statements. Within the body of
|
||||||
|
a function, INSIST is used to assert that a particular expression must be
|
||||||
|
true. Assertions must not have side effects that the function relies upon,
|
||||||
|
because assertion checking can be turned off.
|
||||||
|
|
362
doc/dev/coding.html
Normal file
362
doc/dev/coding.html
Normal file
@@ -0,0 +1,362 @@
|
|||||||
|
<H2>C Language</H2>
|
||||||
|
|
||||||
|
An ANSI standard C compiler and library are assumed. Feel free to use any
|
||||||
|
ANSI C feature.<P>
|
||||||
|
|
||||||
|
<H2>Warnings</H2>
|
||||||
|
Given a reasonable set of things to warn about (e.g. -W -Wall for gcc), the
|
||||||
|
goal is to compile with no warnings.
|
||||||
|
|
||||||
|
<H2>C Source Code</H2>
|
||||||
|
|
||||||
|
<H3>Copyright</H3>
|
||||||
|
|
||||||
|
All source files should have a copyright. The copyright year(s)
|
||||||
|
should be kept current. The files and the copyright year(s) should be
|
||||||
|
listed in util/copyrights.<P>
|
||||||
|
|
||||||
|
<H3>Line Formatting</H3>
|
||||||
|
<H4>Indentation</H4>
|
||||||
|
Use tabs. Spaces are only allowed when needed to line up a continued
|
||||||
|
expression. In the following example, spaces used for indentation are
|
||||||
|
indicated with "_":
|
||||||
|
<PRE><CODE>
|
||||||
|
printf("this is going to be %s very long %s statement\n",
|
||||||
|
_______"a", "printf");
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H4>Line Length</H4> Lines should not be longer than 80 characters,
|
||||||
|
even if it requires violating the indentation rules to do so.
|
||||||
|
|
||||||
|
<H3>Comments</H3>
|
||||||
|
Comments should be used anytime they improve the readability of the code.<P>
|
||||||
|
|
||||||
|
Comments may be single-line or multiline. A single-line comment should be
|
||||||
|
at the end of the line of there is other text on the line, and should start
|
||||||
|
in the same column as other nearby end-of-line comments. The comment
|
||||||
|
should be at the same indentation level as the text it is referring to.
|
||||||
|
Multiline comments should start with "/*" on a line by itself. Subsequent
|
||||||
|
lines should have " *" lined-up with the "*" above. The end of the comment
|
||||||
|
should be " */" on a line by itself, again with the "*" lined-up with the
|
||||||
|
one above. Comments should start with a capital letter and end with a
|
||||||
|
period.<P>
|
||||||
|
Good:<P>
|
||||||
|
<PRE><CODE>
|
||||||
|
/*
|
||||||
|
* Private variables.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static int a /* Description of 'a'. */
|
||||||
|
static int b /* Description of 'b'. */
|
||||||
|
static char * c /* Description of 'c'. */
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
The following lint and lint-like comments should be used where appropriate:
|
||||||
|
<PRE><CODE>
|
||||||
|
/* ARGSUSED */
|
||||||
|
/* FALLTHROUGH */
|
||||||
|
/* NOTREACHED */
|
||||||
|
/* VARARGS */
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H3>.h files</H3>
|
||||||
|
.h files should not rely on other files having been included. .h
|
||||||
|
files should prevent multiple inclusion. The OS is assumed to prevent
|
||||||
|
multiple inclusion of its .h files.<P>
|
||||||
|
.h files that define modules should have a structure like the following:<P>
|
||||||
|
<PRE><CODE>
|
||||||
|
/*
|
||||||
|
* Copyright (C) 1998 Internet Software Consortium.
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
|
||||||
|
* ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
|
||||||
|
* OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
|
||||||
|
* CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
|
||||||
|
* PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
|
||||||
|
* ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef ISC_WHATEVER_H
|
||||||
|
#define ISC_WHATEVER_H 1
|
||||||
|
|
||||||
|
/*****
|
||||||
|
***** Module Info
|
||||||
|
*****/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* <Module name here>
|
||||||
|
*
|
||||||
|
* <One line description here>
|
||||||
|
*
|
||||||
|
* <Extended description and notes here>
|
||||||
|
*
|
||||||
|
* MP:
|
||||||
|
* <Information about multiprocessing considerations here, e.g. locking
|
||||||
|
* requirements>
|
||||||
|
*
|
||||||
|
* Reliability:
|
||||||
|
* <Any reliability concerns should be mentioned here>
|
||||||
|
*
|
||||||
|
* Resources:
|
||||||
|
* <A rough guide to how resources are used by this module>
|
||||||
|
*
|
||||||
|
* Security:
|
||||||
|
* <Any security issues are discussed here>
|
||||||
|
*
|
||||||
|
* Standards:
|
||||||
|
* <Any standards relevant to the module are listed here>
|
||||||
|
*/
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** Imports
|
||||||
|
***/
|
||||||
|
|
||||||
|
/* <#includes here> */
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** Types
|
||||||
|
***/
|
||||||
|
|
||||||
|
/* <Type definitions here> */
|
||||||
|
|
||||||
|
/***
|
||||||
|
*** Functions
|
||||||
|
***/
|
||||||
|
|
||||||
|
#endif /* ISC_WHATEVER_H */
|
||||||
|
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H3>C Source</H3>
|
||||||
|
<H4>Including Interfaces (.h files)</H4>
|
||||||
|
The first file to be included must be config.h.
|
||||||
|
Try to include only necessary files, not everything under the
|
||||||
|
sun.<P>
|
||||||
|
Operating-system-specific files should not be included by most modules.<P>
|
||||||
|
Include UNIX "sys" .h files before ordinary C includes.<P>
|
||||||
|
|
||||||
|
<H4>Statements</H4>
|
||||||
|
There should be at most one statement per line.<P>
|
||||||
|
Bad:<P>
|
||||||
|
<PRE><CODE>
|
||||||
|
if (i > 0) {
|
||||||
|
printf("yes\n"); i = 0; j = 0;
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
<H4>Functions<H4>
|
||||||
|
The use of ANSI C function prototypes is required.<P>
|
||||||
|
The return type of the function should be listed on a line by itself when
|
||||||
|
specifying the implementation of the function. The opening curly brace should
|
||||||
|
occur on the same line as the argument list, unless the argument list is
|
||||||
|
more than one line long.<P>
|
||||||
|
Good:<P>
|
||||||
|
static inline void
|
||||||
|
f(int i) {
|
||||||
|
/* whatever */
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
g(int i, /* other args here */
|
||||||
|
int last_argument)
|
||||||
|
{
|
||||||
|
return (i * i);
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H4>Curly Braces</H4> Curly Braces do not get their own indentation.
|
||||||
|
An opening brace does not start a new line. The statements enclosed
|
||||||
|
by the braces should not be on the same line as the opening or closing
|
||||||
|
brace. A closing brace should be the only thing on the line, unless
|
||||||
|
it's part of an else clause.<P>
|
||||||
|
Good:<P>
|
||||||
|
<PRE><CODE>
|
||||||
|
static void
|
||||||
|
f(int i) {
|
||||||
|
if (i > 0) {
|
||||||
|
printf("yes\n");
|
||||||
|
i = 0;
|
||||||
|
} else
|
||||||
|
printf("no\n");
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
Bad:<P>
|
||||||
|
<PRE><CODE>
|
||||||
|
void f(int i)
|
||||||
|
{
|
||||||
|
if(i<0){i=0;printf("was negative\n");}
|
||||||
|
if (i > 0)
|
||||||
|
{
|
||||||
|
printf("yes\n");
|
||||||
|
i = 0;
|
||||||
|
}}
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H4>Spaces</H4>
|
||||||
|
<UL>
|
||||||
|
<LI>Do put a space between operators like '+', '==', etc.
|
||||||
|
<LI>Do put a space after ','.
|
||||||
|
<LI>Do put a space after ';' in a 'for' statement.
|
||||||
|
<LI>Do put a space after 'return', and also parenthesize the return value.
|
||||||
|
</UL>
|
||||||
|
<UL>
|
||||||
|
<LI>Do not put a space between a variable or function name and '(' or '['.
|
||||||
|
<LI>Do not put a space immediately after a '(' or immediately before a ')',
|
||||||
|
unless it improves readability. The same goes for '[' and ']'.
|
||||||
|
<LI>Do not put a space before '++' or '--' when used in
|
||||||
|
post-increment/decrement mode, or after them when used in
|
||||||
|
pre-increment/decrement mode.
|
||||||
|
<LI>Do not put a space before ';' when terminating a statement or in a 'for'
|
||||||
|
statement.
|
||||||
|
<LI>Do not put a space after '*' when used to dereference a pointer, or on
|
||||||
|
either side of '->'.
|
||||||
|
<LI>Do not put a space after '~'.
|
||||||
|
<LI>The '|' operator may either have a space on both sides or it may have no
|
||||||
|
spaces.
|
||||||
|
</UL>
|
||||||
|
|
||||||
|
<H4>Return Values</H4>
|
||||||
|
If a function returns a value, it should be cast to (void) if you don't
|
||||||
|
care what the value is. (Exception for <CODE>printf()</CODE>?)<P>
|
||||||
|
|
||||||
|
All error conditions must be handled.<P>
|
||||||
|
|
||||||
|
Mixing of error status and valid results within a single type should be
|
||||||
|
avoided.<P>
|
||||||
|
Good:
|
||||||
|
<PRE><CODE>
|
||||||
|
os_descriptor_t s;
|
||||||
|
os_result_t result;
|
||||||
|
|
||||||
|
result = os_socket_create(AF_INET, SOCK_STREAM, 0, &s);
|
||||||
|
if (result != OS_R_SUCCESS) {
|
||||||
|
/* Do something about the error. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
Not so good:
|
||||||
|
<PRE><CODE>
|
||||||
|
int s;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Obviously using interfaces like socket() (below) is allowed
|
||||||
|
* since otherwise you couldn't call operating system routines; the
|
||||||
|
* point is not to write more interfaces like them.
|
||||||
|
*/
|
||||||
|
s = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (s < 0) {
|
||||||
|
/* Do something about the error using errno. */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H4>Integral Types</H4>
|
||||||
|
Careful thought should be given to whether an integral type should be
|
||||||
|
signed or unsigned, and to whether a specific size is required. "int"
|
||||||
|
should be used for generic variables (e.g. iteration counters, array
|
||||||
|
subscripts). Other than for generic variables, if a negative value isn't
|
||||||
|
meaningful, the variable should be unsigned. Assignments and
|
||||||
|
comparisons between signed and unsigned integers should be avoided;
|
||||||
|
suppressing the warnings with casts is not desireable.<P>
|
||||||
|
|
||||||
|
<H4>Casting</H4>
|
||||||
|
Casting should be avoided when possible.<P>
|
||||||
|
|
||||||
|
<H4>Clear Success or Failure</H4>
|
||||||
|
A function should report success or failure, and do so accurately. It
|
||||||
|
should never fail silently. Use of Design by Contract can help here.<P>
|
||||||
|
|
||||||
|
<H4>Testing Bits</H4>
|
||||||
|
Bit testing should be as follows:<P>
|
||||||
|
Good:
|
||||||
|
<PRE><CODE>
|
||||||
|
/* Test if flag set. */
|
||||||
|
if ((flags & FOO) != 0) {
|
||||||
|
|
||||||
|
}
|
||||||
|
/* Test if flag clear. */
|
||||||
|
if ((flags & BAR) == 0) {
|
||||||
|
|
||||||
|
}
|
||||||
|
/* Test if both flags set. */
|
||||||
|
if ((flags & (FOO|BAR)) == (FOO|BAR)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
Bad:
|
||||||
|
<PRE><CODE>
|
||||||
|
/* Test if flag set. */
|
||||||
|
if (flags & FOO) {
|
||||||
|
|
||||||
|
}
|
||||||
|
/* Test if flag clear. */
|
||||||
|
if (! (flags & BAR)) {
|
||||||
|
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H4>Pointers</H4>
|
||||||
|
<H5>Null Pointer</H5>
|
||||||
|
The null pointer value should be referred to with "NULL", not with "0".
|
||||||
|
Testing to see whether a pointer is NULL should be explicit.<P>
|
||||||
|
Good:
|
||||||
|
<PRE><CODE>
|
||||||
|
char *c = NULL;
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
if (c == NULL) {
|
||||||
|
/* Do something. */
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H5>Invalidating Pointers</H5>
|
||||||
|
When the data a pointer points to has been freed, or is otherwise no longer
|
||||||
|
valid, the pointer should be set to NULL unless the pointer is part of a
|
||||||
|
structure which is itself going to be freed immediately.<P>
|
||||||
|
Good:
|
||||||
|
<PRE><CODE>
|
||||||
|
char *text;
|
||||||
|
|
||||||
|
/* text is initalized here. */
|
||||||
|
|
||||||
|
free(text);
|
||||||
|
text = NULL;
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H4>Testing for Zero or Non-zero</H4>
|
||||||
|
Explicit testing against zero is required for numeric, non-boolean variables.
|
||||||
|
<P>
|
||||||
|
Good:
|
||||||
|
<PRE><CODE>
|
||||||
|
int i = 10;
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
if (i != 0) {
|
||||||
|
/* Do something. */
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
Bad:
|
||||||
|
<PRE><CODE>
|
||||||
|
int i = 10;
|
||||||
|
|
||||||
|
/* ... */
|
||||||
|
|
||||||
|
if (i) {
|
||||||
|
/* Do something. */
|
||||||
|
}
|
||||||
|
</CODE></PRE>
|
||||||
|
|
||||||
|
<H3>Initialization</H3>
|
||||||
|
When an object is allocated from the heap, all fields in the object must be
|
||||||
|
initialized.<P>
|
||||||
|
|
||||||
|
<H3>Dead Code Pruning</H3>
|
||||||
|
Source which becomes obsolete should be removed, not just disabled with
|
||||||
|
#if 0 ... #endif.<P>
|
60
doc/dev/magic_numbers
Normal file
60
doc/dev/magic_numbers
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
|
||||||
|
Magic Numbers
|
||||||
|
|
||||||
|
A number of data structures in the ISC and DNS libraries have an unsigned int
|
||||||
|
magic number as the first field. The purpose of the magic number is
|
||||||
|
principally to validate that a pointer a subroutine has gotten really points
|
||||||
|
to the type it claims to be. This helps detect problems caused by resources
|
||||||
|
being freed prematurely, that have been corrupted, or that have not been
|
||||||
|
properly initalized. It can also be handy in debugging.
|
||||||
|
|
||||||
|
Magic numbers should always be the first field. They never require locking
|
||||||
|
to access. As to the actual value to be used, something mnemonic is good:
|
||||||
|
|
||||||
|
#define TASK_MAGIC 0x5441534BU /* TASK. */
|
||||||
|
#define VALID_TASK(t) ((t) != NULL && \
|
||||||
|
(t)->magic == TASK_MAGIC)
|
||||||
|
|
||||||
|
#define TASK_MANAGER_MAGIC 0x54534B4DU /* TSKM. */
|
||||||
|
#define VALID_MANAGER(m) ((m) != NULL && \
|
||||||
|
(m)->magic ==
|
||||||
|
TASK_MANAGER_MAGIC)
|
||||||
|
|
||||||
|
Unless the memory cost is critical, most objects should have a magic number.
|
||||||
|
|
||||||
|
The magic number should be the last field set in a creation routine, so that
|
||||||
|
an object will never be stamped with a magic number unless it is valid.
|
||||||
|
|
||||||
|
The magic number should be set to zero immediately before the object is
|
||||||
|
freed.
|
||||||
|
|
||||||
|
Magic values are generally private to the implementation of the type. I.e.
|
||||||
|
they are defined in the .c file, not the .h file.
|
||||||
|
|
||||||
|
Validation of magic numbers is done by routines that manipulate the type,
|
||||||
|
not by users of the type. Indeed, user validation is usually not possible
|
||||||
|
because the magic number is not public.
|
||||||
|
|
||||||
|
Magic number checking may become a build option in a future release. E.g.
|
||||||
|
|
||||||
|
struct foo {
|
||||||
|
ISC_MAGIC_DECLARATION
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
foo_create() {
|
||||||
|
/* ... */
|
||||||
|
ISC_MAGIC_SET(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
foo_destroy() {
|
||||||
|
/* ... */
|
||||||
|
ISC_MAGIC_CLEAR(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
#define FOO_MAGIC 0x00010203U
|
||||||
|
#define VALID_FOO(f) ISC_MAGIC_VALIDATE(f, FOO_MAGIC)
|
||||||
|
|
||||||
|
foo_dosomething(foo *f) {
|
||||||
|
REQUIRE(VALID_FOO(f));
|
||||||
|
}
|
57
doc/dev/results
Normal file
57
doc/dev/results
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
|
||||||
|
Result Codes
|
||||||
|
|
||||||
|
The use of global variables or a GetLastError() function to return results
|
||||||
|
doesn't work well in a multithreaded application. The global variable has
|
||||||
|
obvious problems, as does a global GetLastError(). A per-object GetLastError()
|
||||||
|
seems more promising, e.g.
|
||||||
|
|
||||||
|
sometype_t s;
|
||||||
|
|
||||||
|
sometype_dosomething(s, buffer);
|
||||||
|
if (sometype_error(s)) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
|
||||||
|
If 's' is shared however this approach doesn't work unless the locking is
|
||||||
|
done by the caller, e.g.
|
||||||
|
|
||||||
|
sometype_lock();
|
||||||
|
sometype_dosomething(s, buffer);
|
||||||
|
if (sometype_error(s)) {
|
||||||
|
/* ... */
|
||||||
|
}
|
||||||
|
sometype_unlock();
|
||||||
|
|
||||||
|
Those ISC and DNS libraries which have locks almost universally put the
|
||||||
|
locking inside of the called routines, since it's more convenient for
|
||||||
|
the calling programmer, makes for a cleaner API, and puts the burden
|
||||||
|
of locking on the library programmer, who should know best what the
|
||||||
|
locking needs of the routine are.
|
||||||
|
|
||||||
|
Because of this locking style the ISC and DNS libraries typically provide
|
||||||
|
result information as the return value of the function. E.g.
|
||||||
|
|
||||||
|
isc_result_t result;
|
||||||
|
|
||||||
|
result = isc_task_send(task, &event);
|
||||||
|
|
||||||
|
Note that an explicit result type is used, instead of mixing the error result
|
||||||
|
type with the normal result type. E.g. the C library routine getc() can
|
||||||
|
return a character or EOF, but the BIND 9 style keeps the types of the
|
||||||
|
function's return values separate.
|
||||||
|
|
||||||
|
char c;
|
||||||
|
|
||||||
|
result = isc_io_getc(stream, &c);
|
||||||
|
if (result == ISC_R_SUCCESS) {
|
||||||
|
/* Do something with 'c'. */
|
||||||
|
} else if (result == ISC_R_EOF) {
|
||||||
|
/* EOF. */
|
||||||
|
} else {
|
||||||
|
/* Some other error. */
|
||||||
|
}
|
||||||
|
|
||||||
|
Functions which cannot fail (assuming the caller has provided valid
|
||||||
|
arguments) need not return a result type. For example, dns_name_issubdomain()
|
||||||
|
returns an isc_boolean_t, because it cannot fail.
|
44
doc/dev/unexpected
Normal file
44
doc/dev/unexpected
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
|
||||||
|
Unexpected Errors
|
||||||
|
|
||||||
|
For portability, the ISC and DNS libraries define their own result codes
|
||||||
|
instead of using the operating system's. E.g. the ISC library uses
|
||||||
|
ISC_R_NOMEMORY instead of the UNIX-specific ENOMEM.
|
||||||
|
|
||||||
|
The ISC and DNS libraries have a common way of looking at errors and
|
||||||
|
other non-success results. An "expected" result is something that can
|
||||||
|
happen in the ordinary course of using a function, that is not very
|
||||||
|
improbable, and that the caller might care to know. For example, a
|
||||||
|
function which opens a file must have a way to say "file not found"
|
||||||
|
and "permission denied".
|
||||||
|
|
||||||
|
Other kinds of errors are "unexpected". For example, an I/O error
|
||||||
|
might occur. When an unexpected error occurs, we want to be able to
|
||||||
|
log the information, but we don't want to translate every
|
||||||
|
operating-system-specific error code into and ISC_R_ or DNS_R_ code
|
||||||
|
because the are too many of them, and they aren't meaningful to
|
||||||
|
clients anyway (they're unexpected errors). If we were using a
|
||||||
|
language where we could throw an exception, we'd do that. Since we're
|
||||||
|
not, we call UNEXPECTED_ERROR(). E.g.
|
||||||
|
|
||||||
|
#include <isc/error.h>
|
||||||
|
|
||||||
|
void foo() {
|
||||||
|
if (some_unix_thang() < 0) {
|
||||||
|
UNEXPECTED_ERROR(__FILE__, __LINE__,
|
||||||
|
"some_unix_thang() failed: %s",
|
||||||
|
strerror(errno));
|
||||||
|
return (ISC_R_UNEXPECTED);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
The UNEXPECTED error routine may be specified by the calling application. It
|
||||||
|
will log the error somehow (e.g. via. syslog, or printing to stderr).
|
||||||
|
|
||||||
|
This method is a compromise. It makes useful error information available,
|
||||||
|
but avoids the complexity of a more sophisticated multi-library "error table"
|
||||||
|
scheme.
|
||||||
|
|
||||||
|
In the (rare) situation where a library routine encounters a fatal error and
|
||||||
|
has no way of reporting the error to the application, the library may call
|
||||||
|
FATAL_ERROR(). This will log the problem and then terminate the application.
|
Reference in New Issue
Block a user