]> git.ipfire.org Git - thirdparty/bind9.git/commitdiff
revise hooks.h comments
authorEvan Hunt <each@isc.org>
Wed, 5 Dec 2018 04:06:03 +0000 (20:06 -0800)
committerEvan Hunt <each@isc.org>
Thu, 6 Dec 2018 18:36:50 +0000 (10:36 -0800)
lib/ns/include/ns/hooks.h

index 1a7dfe26bb057fa0220af6fd9f6bcda40413745c..0570d225a6068c2fe05bbb16ccf1f7da0f3bc50b 100644 (file)
 #include <ns/client.h>
 #include <ns/query.h>
 /*
- * Hooks provide a way of running a callback function once a certain place in
- * code is reached.  Current use is limited to libns unit tests and thus:
- *
- *   - hook-related types and macros are not placed in libns header files,
- *   - hook-related code is compiled away unless --with-atf is used,
- *   - hook-related macro names are prefixed with "NS_".
- *
- * However, the implementation is pretty generic and could be repurposed for
- * general use, e.g. as part of libisc, after some further customization.
- *
- * Hooks are created by inserting a macro into any function returning
- * isc_result_t (NS_PROCESS_HOOK()) or void (NS_PROCESS_HOOK_VOID()).  As both
- * of these macros contain a return statement which is inlined into the
- * function into which the hook is inserted, a hook callback is able to cause
- * that function to return at hook insertion point.  For functions returning
- * isc_result_t, if a hook callback intends to cause a return at hook insertion
- * point, it also has to set the value to be returned by the function.
- *
- * Hook callbacks are functions which:
- *
- *   - return a boolean value; if true is returned by the callback, the
- *     function into which the hook is inserted will return at hook insertion
- *     point; if false is returned by the callback, execution of the
- *     function into which the hook is inserted continues normally,
+ * "Hooks" are a mechanism to call a defined function or set of functions once
+ * a certain place in code is reached.  Hook actions can inspect and alter the
+ * state of an ongoing process, allowing processing to continue afterward or
+ * triggering an early return.
+ *
+ * Currently hooks are used in two ways: in plugins, which use them to
+ * add functionality to query processing, and in the unit tests for libns,
+ * where they are used to inspect state before and after certain functions have
+ * run.
+ *
+ * Both of these uses are limited to libns, so hooks are currently defined in
+ * the ns/hooks.h header file, and hook-related macro and function names are
+ * prefixed with `NS_` and `ns_`.  However, the design is fairly generic and
+ * could be repurposed for general use, e.g. as part of libisc, after some
+ * further customization.
+ *
+ * Hooks are created by defining a hook point identifier in the ns_hookpoint_t
+ * enum below, and placing a special call at a corresponding location in the
+ * code which invokes the action(s) for that hook; there are two such special
+ * calls currently implemented, namely the CALL_HOOK() and CALL_HOOK_NORETURN()
+ * macros in query.c.  The former macro contains a "goto cleanup" statement
+ * which is inlined into the function into which the hook has been inserted;
+ * this enables the hook action to cause the calling function to return from
+ * the hook insertion point.  For functions returning isc_result_t, if a hook
+ * action intends to cause a return at hook insertion point, it also has to set
+ * the value to be returned by the calling function.
+ *
+ * A hook table is an array (indexed by the value of the hook point identifier)
+ * in which each cell contains a linked list of structures, each of which
+ * contains a function pointer to a hook action and a pointer to data which is
+ * to be passed to the action function when it is called.
+ *
+ * Each view has its own separate hook table, populated by loading plugin
+ * modules specified in the "plugin" statements in named.conf.  There is also a
+ * special, global hook table (ns__hook_table) that is only used by libns unit
+ * tests and whose existence can be safely ignored by plugin modules.
+ *
+ * Hook actions are functions which:
+ *
+ *   - return a boolean value: if true is returned by the hook action, the
+ *     function into which the hook is inserted will return and no further hook
+ *     actions at the same hook point will be invoked; if false is returned by
+ *     the hook action and there are further hook actions set up at the same
+ *     hook point, they will be processed; if false is returned and there are
+ *     no further hook actions set up at the same hook point, execution of the
+ *     function into which the hook has been inserted will be resumed,
  *
  *   - accept three pointers as arguments:
+ *       - a pointer specified by the special call at the hook insertion point,
+ *       - a pointer specified upon inserting the action into the hook table,
+ *       - a pointer to an isc_result_t value which will be returned by the
+ *         function into which the hook is inserted if the action returns true.
  *
- *       - a pointer specified by the hook itself,
- *       - a pointer specified upon inserting the callback into the hook table,
- *       - a pointer to isc_result_t which will be returned by the function
- *         into which the hook is inserted if the callback returns true.
- *
- * Hook tables are arrays which consist of a number of tuples (one tuple per
- * hook identifier), each of which determines the callback to be invoked when a
- * given hook is processed and the data to be passed to that callback.  In an
- * attempt to keep things as simple as possible, current implementation uses
- * hook tables which are statically-sized arrays only allowing a single
- * callback to be invoked for each hook identifier.
- *
- * In order for a hook callback to be called for a given hook, a pointer to
- * that callback (along with an optional pointer to callback-specific data) has
- * to be inserted into the relevant hook table entry for that hook.  Replacing
- * whole hook tables is also possible.
+ * In order for a hook action to be called for a given hook, a pointer to that
+ * action function (along with an optional pointer to action-specific data) has
+ * to be inserted into the relevant hook table entry for that hook using an
+ * ns_hook_add() call.  If multiple actions are set up at a single hook point,
+ * they are processed in FIFO order.
  *
- * Consider the following sample code:
+ * As an example, consider the following hypothetical function in query.c:
  *
  * ----------------------------------------------------------------------------
- * ns_hook_t *foo_hook_table = NULL;
+ * static isc_result_t
+ * query_foo(query_ctx_t *qctx) {
+ *     isc_result_t result;
  *
- * isc_result_t
- * foo_bar(void) {
- *     int val = 42;
+ *     CALL_HOOK(NS_QUERY_FOO_BEGIN, qctx);
  *
- *     ...
+ *     ns_client_log(qctx->client, NS_LOGCATEGORY_CLIENT, NS_LOGMODULE_QUERY,
+ *                   ISC_LOG_DEBUG(99), "Lorem ipsum dolor sit amet...");
  *
- *     NS_PROCESS_HOOK(foo_hook_table, FOO_EXTRACT_VAL, &val);
+ *     result = ISC_R_COMPLETE;
  *
- *     ...
- *
- *     printf("This message may not be printed due to use of hooks.");
- *
- *     return (ISC_R_SUCCESS);
+ *  cleanup:
+ *     return (result);
  * }
+ * ----------------------------------------------------------------------------
  *
- * bool
- * cause_failure(void *hook_data, void *callback_data, isc_result_t *resultp) {
- *     int *valp = (int *)hook_data;
- *     bool *calledp = (bool *)callback_data;
+ * and the following hook action:
  *
- *     ...
+ * ----------------------------------------------------------------------------
+ * static bool
+ * cause_failure(void *hook_data, void *action_data, isc_result_t *resultp) {
+ *     UNUSED(hook_data);
+ *     UNUSED(action_data);
  *
  *     *resultp = ISC_R_FAILURE;
  *
  *     return (true);
  * }
+ * ----------------------------------------------------------------------------
  *
- * bool
- * examine_val(void *hook_data, void *callback_data, isc_result_t *resultp) {
- *     int *valp = (int *)hook_data;
- *     int *valcopyp = (int *)callback_data;
- *
- *     UNUSED(resultp);
+ * If this hook action was installed in the hook table using:
  *
- *     ...
+ * ----------------------------------------------------------------------------
+ * const ns_hook_t foo_fail = {
+ *     .action = cause_failure,
+ * };
  *
- *     return (false);
- * }
+ * ns_hook_add(..., NS_QUERY_FOO_BEGIN, &foo_fail);
+ * ----------------------------------------------------------------------------
  *
- * void
- * test_foo_bar(void) {
- *     bool called = false;
- *     int valcopy;
+ * then query_foo() would return ISC_R_FAILURE every time it is called due to
+ * the cause_failure() hook action returning true and setting '*resultp' to
+ * ISC_R_FAILURE.  query_foo() would also never log the "Lorem ipsum dolor sit
+ * amet..." message.
  *
- *     ns_hook_t my_hooks[FOO_HOOKS_COUNT] = {
- *         [FOO_EXTRACT_VAL] = {
- *             .callback = cause_failure,
- *             .callback_data = &called,
- *         },
- *     };
+ * Consider a different hook action:
  *
- *     foo_hook_table = my_hooks;
- *     foo_bar();
+ * ----------------------------------------------------------------------------
+ * static bool
+ * log_qtype(void *hook_data, void *action_data, isc_result_t *resultp) {
+ *     query_ctx_t *qctx = (query_ctx_t *)hook_data;
+ *     FILE *stream = (FILE *)action_data;
  *
- *     {
- *         const ns_hook_t examine_hook = {
- *             .callback = examine_val,
- *             .callback_data = &valcopy,
- *         };
+ *     UNUSED(resultp);
  *
- *         my_hooks[FOO_EXTRACT_VAL] = examine_hook;
- *     }
- *     foo_bar();
+ *     fprintf(stream, "QTYPE=%u\n", qctx->qtype);
  *
+ *     return (false);
  * }
  * ----------------------------------------------------------------------------
  *
- * When test_foo_bar() is called, "foo_hook_table" is set to "my_hooks".  Then
- * foo_bar() gets invoked.  Once execution reaches the insertion point for hook
- * FOO_EXTRACT_VAL, cause_failure() will be called with &val as "hook_data" and
- * &called as "callback_data".  It can do whatever it pleases with these two
- * values.  Eventually, cause_failure() sets *resultp to ISC_R_FAILURE and
- * returns true, which causes foo_bar() to return ISC_R_FAILURE and never
- * execute the printf() call below hook insertion point.
- *
- * Execution then returns to test_foo_bar().  Unlike before the first call to
- * foo_bar(), this time only a single hook ("examine_hook") is defined instead
- * of a complete hook table.  This hook is then subsequently inserted at index
- * FOO_EXTRACT_VAL into the "my_hook" hook table.  This causes the hook
- * previously set at that index (the one calling cause_failure()) to be
- * replaced with "examine_hook".  Thus, when the second call to foo_bar() is
- * subsequently made, examine_val() will be called with &val as "hook_data" and
- * &valcopy as "callback_data".  Contrary to cause_failure(), extract_val()
- * returns false, which means it does not access "resultp" and does not
- * cause foo_bar() to return at hook insertion point.  Thus, printf() will be
- * called this time and foo_bar() will return ISC_R_SUCCESS.
+ * If this hook action was installed in the hook table instead of
+ * cause_failure(), using:
+ *
+ * ----------------------------------------------------------------------------
+ * const ns_hook_t foo_log_qtype = {
+ *     .action = log_qtype,
+ *     .action_data = stderr,
+ * };
+ *
+ * ns_hook_add(..., NS_QUERY_FOO_BEGIN, &foo_log_qtype);
+ * ----------------------------------------------------------------------------
+ *
+ * then the QTYPE stored in the query context passed to query_foo() would be
+ * logged to stderr upon each call to that function; 'qctx' would be passed to
+ * the hook action in 'hook_data' since it is specified in the CALL_HOOK() call
+ * inside query_foo() while stderr would be passed to the hook action in
+ * 'action_data' since it is specified in the ns_hook_t structure passed to
+ * ns_hook_add().  As the hook action returns false, query_foo() would also be
+ * logging the "Lorem ipsum dolor sit amet..." message before returning
+ * ISC_R_COMPLETE.
  */
 
 /*!
@@ -342,7 +350,7 @@ ns_hook_add(ns_hooktable_t *hooktable, isc_mem_t *mctx,
            ns_hookpoint_t hookpoint, const ns_hook_t *hook);
 /*%<
  * Allocate (using memory context 'mctx') a copy of the 'hook' structure
- * describing a hook callback and append it to the list of hooks at 'hookpoint'
+ * describing a hook action and append it to the list of hooks at 'hookpoint'
  * in 'hooktable'.
  *
  * Requires: