]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
Initial import of libisc/log.c and friends
authorHarlan Stenn <stenn@ntp.org>
Sun, 20 May 2007 20:32:43 +0000 (16:32 -0400)
committerHarlan Stenn <stenn@ntp.org>
Sun, 20 May 2007 20:32:43 +0000 (16:32 -0400)
bk: 4650b06b3lcX5kQV6XaX6NHkOp9VWg

ChangeLog
include/isc/dir.h [new file with mode: 0644]
include/isc/file.h [new file with mode: 0644]
include/isc/log.h [new file with mode: 0644]
include/isc/stat.h [new file with mode: 0644]
include/isc/stdio.h [new file with mode: 0644]
include/isc/time.h [new file with mode: 0644]
libisc/log.c [new file with mode: 0644]

index 62cb4e4593ab7716c6c9b54de302552cd315d3a8..69a3e885ad91e9fe6c8380dc291af025a182d437 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,4 @@
+* Initial import of libisc/log.c and friends.
 * [Bug 826] Fix redefinition of PI.
 * [Bug 825] ntp_scanner.c needs to #include <config.h> .
 * [Bug 824] New parser code has some build problems with the SIM code.
diff --git a/include/isc/dir.h b/include/isc/dir.h
new file mode 100644 (file)
index 0000000..cc85706
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2001  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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+/* $Id: dir.h,v 1.17.18.2 2005/04/29 00:17:09 marka Exp $ */
+
+/* Principal Authors: DCL */
+
+#ifndef ISC_DIR_H
+#define ISC_DIR_H 1
+
+/*! \file */
+
+#include <sys/types.h>         /* Required on some systems. */
+#include <dirent.h>
+
+#include <isc/lang.h>
+#include <isc/result.h>
+
+#define ISC_DIR_NAMEMAX 256
+#define ISC_DIR_PATHMAX 1024
+
+/*% Directory Entry */
+typedef struct isc_direntry {
+       /*!
+        * Ideally, this should be NAME_MAX, but AIX does not define it by
+        * default and dynamically allocating the space based on pathconf()
+        * complicates things undesirably, as does adding special conditionals
+        * just for AIX.  So a comfortably sized buffer is chosen instead.
+        */
+       char            name[ISC_DIR_NAMEMAX];
+       unsigned int    length;
+} isc_direntry_t;
+
+/*% Directory */
+typedef struct isc_dir {
+       unsigned int    magic;
+       /*!
+        * As with isc_direntry_t->name, making this "right" for all systems
+        * is slightly problematic because AIX does not define PATH_MAX.
+        */
+       char            dirname[ISC_DIR_PATHMAX];
+       isc_direntry_t  entry;
+       DIR *           handle;
+} isc_dir_t;
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_dir_init(isc_dir_t *dir);
+
+isc_result_t
+isc_dir_open(isc_dir_t *dir, const char *dirname);
+
+isc_result_t
+isc_dir_read(isc_dir_t *dir);
+
+isc_result_t
+isc_dir_reset(isc_dir_t *dir);
+
+void
+isc_dir_close(isc_dir_t *dir);
+
+isc_result_t
+isc_dir_chdir(const char *dirname);
+
+isc_result_t
+isc_dir_chroot(const char *dirname);
+
+isc_result_t
+isc_dir_createunique(char *templet);
+/*!<
+ * Use a templet (such as from isc_file_mktemplate()) to create a uniquely
+ * named, empty directory.  The templet string is modified in place.
+ * If result == ISC_R_SUCCESS, it is the name of the directory that was
+ * created.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISC_DIR_H */
diff --git a/include/isc/file.h b/include/isc/file.h
new file mode 100644 (file)
index 0000000..16b0075
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001  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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+/* $Id: file.h,v 1.27.18.2 2005/04/29 00:16:54 marka Exp $ */
+
+#ifndef ISC_FILE_H
+#define ISC_FILE_H 1
+
+/*! \file */
+
+#include <stdio.h>
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_file_settime(const char *file, isc_time_t *time);
+
+isc_result_t
+isc_file_getmodtime(const char *file, isc_time_t *time);
+/*!<
+ * \brief Get the time of last modication of a file.
+ *
+ * Notes:
+ *\li  The time that is set is relative to the (OS-specific) epoch, as are
+ *     all isc_time_t structures.
+ *
+ * Requires:
+ *\li  file != NULL.
+ *\li  time != NULL.
+ *
+ * Ensures:
+ *\li  If the file could not be accessed, 'time' is unchanged.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS
+ *             Success.
+ *\li  #ISC_R_NOTFOUND
+ *             No such file exists.
+ *\li  #ISC_R_INVALIDFILE
+ *             The path specified was not usable by the operating system.
+ *\li  #ISC_R_NOPERM
+ *             The file's metainformation could not be retrieved because
+ *             permission was denied to some part of the file's path.
+ *\li  #ISC_R_EIO
+ *             Hardware error interacting with the filesystem.
+ *\li  #ISC_R_UNEXPECTED
+ *             Something totally unexpected happened.
+ *
+ */
+
+isc_result_t
+isc_file_mktemplate(const char *path, char *buf, size_t buflen);
+/*!<
+ * \brief Generate a template string suitable for use with isc_file_openunique().
+ *
+ * Notes:
+ *\li  This function is intended to make creating temporary files
+ *     portable between different operating systems.
+ *
+ *\li  The path is prepended to an implementation-defined string and
+ *     placed into buf.  The string has no path characters in it,
+ *     and its maximum length is 14 characters plus a NUL.  Thus
+ *     buflen should be at least strlen(path) + 15 characters or
+ *     an error will be returned.
+ *
+ * Requires:
+ *\li  buf != NULL.
+ *
+ * Ensures:
+ *\li  If result == #ISC_R_SUCCESS:
+ *             buf contains a string suitable for use as the template argument
+ *             to isc_file_openunique().
+ *
+ *\li  If result != #ISC_R_SUCCESS:
+ *             buf is unchanged.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS  Success.
+ *\li  #ISC_R_NOSPACE  buflen indicates buf is too small for the catenation
+ *                             of the path with the internal template string.
+ */
+
+
+isc_result_t
+isc_file_openunique(char *templet, FILE **fp);
+/*!<
+ * \brief Create and open a file with a unique name based on 'templet'.
+ *
+ * Notes:
+ *\li  'template' is a reserved work in C++.  If you want to complain
+ *     about the spelling of 'templet', first look it up in the
+ *     Merriam-Webster English dictionary. (http://www.m-w.com/)
+ *
+ *\li  This function works by using the template to generate file names.
+ *     The template must be a writable string, as it is modified in place.
+ *     Trailing X characters in the file name (full file name on Unix,
+ *     basename on Win32 -- eg, tmp-XXXXXX vs XXXXXX.tmp, respectively)
+ *     are replaced with ASCII characters until a non-existent filename
+ *     is found.  If the template does not include pathname information,
+ *     the files in the working directory of the program are searched.
+ *
+ *\li  isc_file_mktemplate is a good, portable way to get a template.
+ *
+ * Requires:
+ *\li  'fp' is non-NULL and '*fp' is NULL.
+ *
+ *\li  'template' is non-NULL, and of a form suitable for use by
+ *     the system as described above.
+ *
+ * Ensures:
+ *\li  If result is #ISC_R_SUCCESS:
+ *             *fp points to an stream opening in stdio's "w+" mode.
+ *
+ *\li  If result is not #ISC_R_SUCCESS:
+ *             *fp is NULL.
+ *
+ *             No file is open.  Even if one was created (but unable
+ *             to be reopened as a stdio FILE pointer) then it has been
+ *             removed.
+ *
+ *\li  This function does *not* ensure that the template string has not been
+ *     modified, even if the operation was unsuccessful.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS
+ *             Success.
+ *\li  #ISC_R_EXISTS
+ *             No file with a unique name could be created based on the
+ *             template.
+ *\li  #ISC_R_INVALIDFILE
+ *             The path specified was not usable by the operating system.
+ *\li  #ISC_R_NOPERM
+ *             The file could not be created because permission was denied
+ *             to some part of the file's path.
+ *\li  #ISC_R_IOERROR
+ *             Hardware error interacting with the filesystem.
+ *\li  #ISC_R_UNEXPECTED
+ *             Something totally unexpected happened.
+ */
+
+isc_result_t
+isc_file_remove(const char *filename);
+/*!<
+ * \brief Remove the file named by 'filename'.
+ */
+
+isc_result_t
+isc_file_rename(const char *oldname, const char *newname);
+/*!<
+ * \brief Rename the file 'oldname' to 'newname'.
+ */
+
+isc_boolean_t
+isc_file_exists(const char *pathname);
+/*!<
+ * \brief Return #ISC_TRUE if the calling process can tell that the given file exists.
+ * Will not return true if the calling process has insufficient privileges
+ * to search the entire path.
+ */
+
+isc_boolean_t
+isc_file_isabsolute(const char *filename);
+/*!<
+ * \brief Return #ISC_TRUE if the given file name is absolute.
+ */
+
+isc_boolean_t
+isc_file_iscurrentdir(const char *filename);
+/*!<
+ * \brief Return #ISC_TRUE if the given file name is the current directory (".").
+ */
+
+isc_boolean_t
+isc_file_ischdiridempotent(const char *filename);
+/*%<
+ * Return #ISC_TRUE if calling chdir(filename) multiple times will give
+ * the same result as calling it once.
+ */
+
+const char *
+isc_file_basename(const char *filename);
+/*%<
+ * Return the final component of the path in the file name.
+ */
+
+isc_result_t
+isc_file_progname(const char *filename, char *buf, size_t buflen);
+/*!<
+ * \brief Given an operating system specific file name "filename"
+ * referring to a program, return the canonical program name. 
+ *
+ *
+ * Any directory prefix or executable file name extension (if
+ * used on the OS in case) is stripped.  On systems where program
+ * names are case insensitive, the name is canonicalized to all
+ * lower case.  The name is written to 'buf', an array of 'buflen'
+ * chars, and null terminated.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS
+ *\li  #ISC_R_NOSPACE  The name did not fit in 'buf'.
+ */
+
+isc_result_t
+isc_file_template(const char *path, const char *templet, char *buf,
+                 size_t buflen);
+/*%<
+ * Create an OS specific template using 'path' to define the directory
+ * 'templet' to describe the filename and store the result in 'buf'
+ * such that path can be renamed to buf atomically.
+ */
+
+isc_result_t
+isc_file_renameunique(const char *file, char *templet);
+/*%<
+ * Rename 'file' using 'templet' as a template for the new file name.
+ */
+
+isc_result_t
+isc_file_absolutepath(const char *filename, char *path, size_t pathlen);
+/*%<
+ * Given a file name, return the fully qualified path to the file.
+ */
+
+/*
+ * XXX We should also have a isc_file_writeeopen() function
+ * for safely open a file in a publicly writable directory
+ * (see write_open() in BIND 8's ns_config.c).
+ */
+
+isc_result_t
+isc_file_truncate(const char *filename, isc_offset_t size);
+/*%<
+ * Truncate/extend the file specified to 'size' bytes.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISC_FILE_H */
diff --git a/include/isc/log.h b/include/isc/log.h
new file mode 100644 (file)
index 0000000..c381775
--- /dev/null
@@ -0,0 +1,913 @@
+/*
+ * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2002  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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+/* $Id: log.h,v 1.47.18.3 2005/04/29 00:16:58 marka Exp $ */
+
+#ifndef ISC_LOG_H
+#define ISC_LOG_H 1
+
+/*! \file */
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <syslog.h> /* XXXDCL NT */
+
+#include <isc/formatcheck.h>
+#include <isc/lang.h>
+#include <isc/platform.h>
+#include <isc/types.h>
+
+/*@{*/
+/*!
+ * \brief Severity levels, patterned after Unix's syslog levels.
+ *
+ */
+#define ISC_LOG_DEBUG(level)   (level)
+/*!
+ * #ISC_LOG_DYNAMIC can only be used for defining channels with
+ * isc_log_createchannel(), not to specify a level in isc_log_write().
+ */
+#define ISC_LOG_DYNAMIC                  0
+#define ISC_LOG_INFO           (-1)
+#define ISC_LOG_NOTICE         (-2)
+#define ISC_LOG_WARNING        (-3)
+#define ISC_LOG_ERROR          (-4)
+#define ISC_LOG_CRITICAL       (-5)
+/*@}*/
+
+/*@{*/
+/*!
+ * \brief Destinations.
+ */
+#define ISC_LOG_TONULL         1
+#define ISC_LOG_TOSYSLOG       2
+#define ISC_LOG_TOFILE         3
+#define ISC_LOG_TOFILEDESC     4
+/*@}*/
+
+/*@{*/
+/*%
+ * Channel flags.
+ */
+#define ISC_LOG_PRINTTIME      0x0001
+#define ISC_LOG_PRINTLEVEL     0x0002
+#define ISC_LOG_PRINTCATEGORY  0x0004
+#define ISC_LOG_PRINTMODULE    0x0008
+#define ISC_LOG_PRINTTAG       0x0010
+#define ISC_LOG_PRINTALL       0x001F
+#define ISC_LOG_DEBUGONLY      0x1000
+#define ISC_LOG_OPENERR                0x8000          /* internal */
+/*@}*/
+
+/*@{*/
+/*!
+ * \brief Other options.
+ *
+ * XXXDCL INFINITE doesn't yet work.  Arguably it isn't needed, but
+ *   since I am intend to make large number of versions work efficiently,
+ *   INFINITE is going to be trivial to add to that.
+ */
+#define ISC_LOG_ROLLINFINITE   (-1)
+#define ISC_LOG_ROLLNEVER      (-2)
+/*@}*/
+
+/*!
+ * \brief Used to name the categories used by a library.  
+ *
+ * An array of isc_logcategory
+ * structures names each category, and the id value is initialized by calling
+ * isc_log_registercategories.
+ */
+struct isc_logcategory {
+       const char *name;
+       unsigned int id;
+};
+
+/*%
+ * Similar to isc_logcategory, but for all the modules a library defines.
+ */
+struct isc_logmodule {
+       const char *name;
+       unsigned int id;
+};
+
+/*%
+ * The isc_logfile structure is initialized as part of an isc_logdestination
+ * before calling isc_log_createchannel().  
+ *
+ * When defining an #ISC_LOG_TOFILE
+ * channel the name, versions and maximum_size should be set before calling
+ * isc_log_createchannel().  To define an #ISC_LOG_TOFILEDESC channel set only
+ * the stream before the call.
+ * 
+ * Setting maximum_size to zero implies no maximum.
+ */
+typedef struct isc_logfile {
+       FILE *stream;           /*%< Initialized to NULL for #ISC_LOG_TOFILE. */
+       const char *name;       /*%< NULL for #ISC_LOG_TOFILEDESC. */
+       int versions;   /* >= 0, #ISC_LOG_ROLLNEVER, #ISC_LOG_ROLLINFINITE. */
+       /*%
+        * stdio's ftell is standardized to return a long, which may well not
+        * be big enough for the largest file supportable by the operating
+        * system (though it is _probably_ big enough for the largest log
+        * anyone would want).  st_size returned by fstat should be typedef'd
+        * to a size large enough for the largest possible file on a system.
+        */
+       isc_offset_t maximum_size;
+       isc_boolean_t maximum_reached; /*%< Private. */
+} isc_logfile_t;
+
+/*%
+ * Passed to isc_log_createchannel to define the attributes of either
+ * a stdio or a syslog log.
+ */
+typedef union isc_logdestination {
+       isc_logfile_t file;
+       int facility;           /* XXXDCL NT */
+} isc_logdestination_t;
+
+/*@{*/
+/*%
+ * The built-in categories of libisc.
+ *
+ * Each library registering categories should provide library_LOGCATEGORY_name
+ * definitions with indexes into its isc_logcategory structure corresponding to
+ * the order of the names.
+ */
+LIBISC_EXTERNAL_DATA extern isc_logcategory_t isc_categories[];
+LIBISC_EXTERNAL_DATA extern isc_log_t *isc_lctx;
+LIBISC_EXTERNAL_DATA extern isc_logmodule_t isc_modules[];
+/*@}*/
+
+/*@{*/
+/*%
+ * Do not log directly to DEFAULT.  Use another category.  When in doubt,
+ * use GENERAL.
+ */
+#define ISC_LOGCATEGORY_DEFAULT        (&isc_categories[0])
+#define ISC_LOGCATEGORY_GENERAL        (&isc_categories[1])
+/*@}*/
+
+#define ISC_LOGMODULE_SOCKET (&isc_modules[0])
+#define ISC_LOGMODULE_TIME (&isc_modules[1])
+#define ISC_LOGMODULE_INTERFACE (&isc_modules[2])
+#define ISC_LOGMODULE_TIMER (&isc_modules[3])
+
+ISC_LANG_BEGINDECLS
+
+isc_result_t
+isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp);
+/*%<
+ * Establish a new logging context, with default channels.
+ *
+ * Notes:
+ *\li  isc_log_create() calls isc_logconfig_create(), so see its comment
+ *     below for more information.
+ *
+ * Requires:
+ *\li  mctx is a valid memory context.
+ *\li  lctxp is not null and *lctxp is null.
+ *\li  lcfgp is null or lcfgp is not null and *lcfgp is null.
+ *
+ * Ensures:
+ *\li  *lctxp will point to a valid logging context if all of the necessary
+ *     memory was allocated, or NULL otherwise.
+ *\li  *lcfgp will point to a valid logging configuration if all of the
+ *     necessary memory was allocated, or NULL otherwise.
+ *\li  On failure, no additional memory is allocated.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS          Success
+ *\li  #ISC_R_NOMEMORY         Resource limit: Out of memory
+ */
+
+isc_result_t
+isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp);
+/*%<
+ * Create the data structure that holds all of the configurable information
+ * about where messages are actually supposed to be sent -- the information
+ * that could changed based on some configuration file, as opposed to the
+ * the category/module specification of isc_log_[v]write[1] that is compiled
+ * into a program, or the debug_level which is dynamic state information.
+ *
+ * Notes:
+ *\li  It is necessary to specify the logging context the configuration
+ *     will be used with because the number of categories and modules
+ *     needs to be known in order to set the configuration.  However,
+ *     the configuration is not used by the logging context until the
+ *     isc_logconfig_use function is called.
+ *
+ *\li  The memory context used for operations that allocate memory for
+ *     the configuration is that of the logging context, as specified
+ *     in the isc_log_create call.
+ *
+ *\li  Four default channels are established:
+ *\verbatim
+ *             default_syslog
+ *              - log to syslog's daemon facility #ISC_LOG_INFO or higher
+ *             default_stderr
+ *              - log to stderr #ISC_LOG_INFO or higher
+ *             default_debug
+ *              - log to stderr #ISC_LOG_DEBUG dynamically
+ *             null
+ *              - log nothing
+ *\endverbatim
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ *\li  lcftp is not null and *lcfgp is null.
+ *
+ * Ensures:
+ *\li  *lcfgp will point to a valid logging context if all of the necessary
+ *     memory was allocated, or NULL otherwise.
+ *\li  On failure, no additional memory is allocated.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS          Success
+ *\li  #ISC_R_NOMEMORY         Resource limit: Out of memory
+ */
+
+isc_logconfig_t *
+isc_logconfig_get(isc_log_t *lctx);
+/*%<
+ * Returns a pointer to the configuration currently in use by the log context.
+ *
+ * Requires:
+ *\li  lctx is a valid context.
+ *
+ * Ensures:
+ *\li  The configuration pointer is non-null.
+ *
+ * Returns:
+ *\li  The configuration pointer.
+ */
+
+isc_result_t
+isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg);
+/*%<
+ * Associate a new configuration with a logging context.
+ *
+ * Notes:
+ *\li  This is thread safe.  The logging context will lock a mutex
+ *     before attempting to swap in the new configuration, and isc_log_doit
+ *     (the internal function used by all of isc_log_[v]write[1]) locks
+ *     the same lock for the duration of its use of the configuration.
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ *\li  lcfg is a valid logging configuration.
+ *\li  lctx is the same configuration given to isc_logconfig_create
+ *             when the configuration was created.
+ *
+ * Ensures:
+ *\li  Future calls to isc_log_write will use the new configuration.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS          Success
+ *\li  #ISC_R_NOMEMORY         Resource limit: Out of memory
+ */
+
+void
+isc_log_destroy(isc_log_t **lctxp);
+/*%<
+ * Deallocate the memory associated with a logging context.
+ *
+ * Requires:
+ *\li  *lctx is a valid logging context.
+ *
+ * Ensures:
+ *\li  All of the memory associated with the logging context is returned
+ *     to the free memory pool.
+ *
+ *\li  Any open files are closed.
+ *
+ *\li  The logging context is marked as invalid.
+ */
+
+void
+isc_logconfig_destroy(isc_logconfig_t **lcfgp);
+/*%<
+ * Destroy a logging configuration.
+ *
+ * Notes:
+ *\li  This function cannot be used directly with the return value of
+ *     isc_logconfig_get, because a logging context must always have
+ *     a valid configuration associated with it.
+ *
+ * Requires:
+ *\li  lcfgp is not null and *lcfgp is a valid logging configuration.
+ *\li  The logging configuration is not in use by an existing logging context.
+ *
+ * Ensures:
+ *\li  All memory allocated for the configuration is freed.
+ *
+ *\li  The configuration is marked as invalid.
+ */
+
+void
+isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]);
+/*%<
+ * Identify logging categories a library will use.
+ *
+ * Notes:
+ *\li  A category should only be registered once, but no mechanism enforces
+ *     this rule.
+ *
+ *\li  The end of the categories array is identified by a NULL name.
+ *
+ *\li  Because the name is used by #ISC_LOG_PRINTCATEGORY, it should not
+ *     be altered or destroyed after isc_log_registercategories().
+ *
+ *\li  Because each element of the categories array is used by
+ *     isc_log_categorybyname, it should not be altered or destroyed
+ *     after registration.
+ *
+ *\li  The value of the id integer in each structure is overwritten
+ *     by this function, and so id need not be initialized to any particular
+ *     value prior to the function call.
+ *
+ *\li  A subsequent call to isc_log_registercategories with the same
+ *     logging context (but new categories) will cause the last
+ *     element of the categories array from the prior call to have
+ *     its "name" member changed from NULL to point to the new
+ *     categories array, and its "id" member set to UINT_MAX.
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ *\li  categories != NULL.
+ *\li  categories[0].name != NULL.
+ *
+ * Ensures:
+ * \li There are references to each category in the logging context,
+ *     so they can be used with isc_log_usechannel() and isc_log_write().
+ */
+
+void
+isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]);
+/*%<
+ * Identify logging categories a library will use.
+ *
+ * Notes:
+ *\li  A module should only be registered once, but no mechanism enforces
+ *     this rule.
+ *
+ *\li  The end of the modules array is identified by a NULL name.
+ *
+ *\li  Because the name is used by #ISC_LOG_PRINTMODULE, it should not
+ *     be altered or destroyed after isc_log_registermodules().
+ *
+ *\li  Because each element of the modules array is used by
+ *     isc_log_modulebyname, it should not be altered or destroyed
+ *     after registration.
+ *
+ *\li  The value of the id integer in each structure is overwritten
+ *     by this function, and so id need not be initialized to any particular
+ *     value prior to the function call.
+ *
+ *\li  A subsequent call to isc_log_registermodules with the same
+ *     logging context (but new modules) will cause the last
+ *     element of the modules array from the prior call to have
+ *     its "name" member changed from NULL to point to the new
+ *     modules array, and its "id" member set to UINT_MAX.
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ *\li  modules != NULL.
+ *\li  modules[0].name != NULL;
+ *
+ * Ensures:
+ *\li  Each module has a reference in the logging context, so they can be
+ *     used with isc_log_usechannel() and isc_log_write().
+ */
+
+isc_result_t
+isc_log_createchannel(isc_logconfig_t *lcfg, const char *name,
+                     unsigned int type, int level,
+                     const isc_logdestination_t *destination,
+                     unsigned int flags);
+/*%<
+ * Specify the parameters of a logging channel.
+ *
+ * Notes:
+ *\li  The name argument is copied to memory in the logging context, so
+ *     it can be altered or destroyed after isc_log_createchannel().
+ *
+ *\li  Defining a very large number of channels will have a performance
+ *     impact on isc_log_usechannel(), since the names are searched
+ *     linearly until a match is made.  This same issue does not affect
+ *     isc_log_write, however.
+ *
+ *\li  Channel names can be redefined; this is primarily useful for programs
+ *     that want their own definition of default_syslog, default_debug
+ *     and default_stderr.
+ *
+ *\li  Any channel that is redefined will not affect logging that was
+ *     already directed to its original definition, _except_ for the
+ *     default_stderr channel.  This case is handled specially so that
+ *     the default logging category can be changed by redefining
+ *     default_stderr.  (XXXDCL Though now that I think of it, the default
+ *     logging category can be changed with only one additional function
+ *     call by defining a new channel and then calling isc_log_usechannel()
+ *     for #ISC_LOGCATEGORY_DEFAULT.)
+ *
+ *\li  Specifying #ISC_LOG_PRINTTIME or #ISC_LOG_PRINTTAG for syslog is allowed,
+ *     but probably not what you wanted to do.
+ *
+ *     #ISC_LOG_DEBUGONLY will mark the channel as usable only when the
+ *     debug level of the logging context (see isc_log_setdebuglevel)
+ *     is non-zero.
+ *
+ * Requires:
+ *\li  lcfg is a valid logging configuration.
+ *
+ *\li  name is not NULL.
+ *
+ *\li  type is #ISC_LOG_TOSYSLOG, #ISC_LOG_TOFILE, #ISC_LOG_TOFILEDESC or
+ *             #ISC_LOG_TONULL.
+ *
+ *\li  destination is not NULL unless type is #ISC_LOG_TONULL.
+ *
+ *\li  level is >= #ISC_LOG_CRITICAL (the most negative logging level).
+ *
+ *\li  flags does not include any bits aside from the ISC_LOG_PRINT* bits
+ *     or #ISC_LOG_DEBUGONLY.
+ *
+ * Ensures:
+ *\li  #ISC_R_SUCCESS
+ *             A channel with the given name is usable with
+ *             isc_log_usechannel().
+ *
+ *\li  #ISC_R_NOMEMORY or #ISC_R_UNEXPECTED
+ *             No additional memory is being used by the logging context.
+ *             Any channel that previously existed with the given name
+ *             is not redefined.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS          Success
+ *\li  #ISC_R_NOMEMORY         Resource limit: Out of memory
+ *\li  #ISC_R_UNEXPECTED       type was out of range and REQUIRE()
+ *                                     was disabled.
+ */
+
+isc_result_t
+isc_log_usechannel(isc_logconfig_t *lcfg, const char *name,
+                  const isc_logcategory_t *category,
+                  const isc_logmodule_t *module);
+/*%<
+ * Associate a named logging channel with a category and module that
+ * will use it.
+ *
+ * Notes:
+ *\li  The name is searched for linearly in the set of known channel names
+ *     until a match is found.  (Note the performance impact of a very large
+ *     number of named channels.)  When multiple channels of the same
+ *     name are defined, the most recent definition is found.
+ *
+ *\li  Specifing a very large number of channels for a category will have
+ *     a moderate impact on performance in isc_log_write(), as each
+ *     call looks up the category for the start of a linked list, which
+ *     it follows all the way to the end to find matching modules.  The
+ *     test for matching modules is  integral, though.
+ *
+ *\li  If category is NULL, then the channel is associated with the indicated
+ *     module for all known categories (including the "default" category).
+ *
+ *\li  If module is NULL, then the channel is associated with every module
+ *     that uses that category.
+ *
+ *\li  Passing both category and module as NULL would make every log message
+ *     use the indicated channel.
+ *
+ * \li Specifying a channel that is #ISC_LOG_TONULL for a category/module pair
+ *     has no effect on any other channels associated with that pair,
+ *     regardless of ordering.  Thus you cannot use it to "mask out" one
+ *     category/module pair when you have specified some other channel that
+ *     is also used by that category/module pair.
+ *
+ * Requires:
+ *\li  lcfg is a valid logging configuration.
+ *
+ *\li  category is NULL or has an id that is in the range of known ids.
+ *
+ *     module is NULL or has an id that is in the range of known ids.
+ *
+ * Ensures:
+ *\li  #ISC_R_SUCCESS
+ *             The channel will be used by the indicated category/module
+ *             arguments.
+ *
+ *\li  #ISC_R_NOMEMORY
+ *             If assignment for a specific category has been requested,
+ *             the channel has not been associated with the indicated
+ *             category/module arguments and no additional memory is
+ *             used by the logging context.
+ *             If assignment for all categories has been requested
+ *             then _some_ may have succeeded (starting with category
+ *             "default" and progressing through the order of categories
+ *             passed to isc_log_registercategories()) and additional memory
+ *             is being used by whatever assignments succeeded.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS  Success
+ *\li  #ISC_R_NOMEMORY Resource limit: Out of memory
+ */
+
+/* Attention: next four comments PRECEED code */
+/*! 
+ *   \brief
+ * Write a message to the log channels.
+ *
+ * Notes:
+ *\li  Log messages containing natural language text should be logged with
+ *     isc_log_iwrite() to allow for localization.
+ *
+ *\li  lctx can be NULL; this is allowed so that programs which use
+ *     libraries that use the ISC logging system are not required to
+ *     also use it.
+ *
+ *\li  The format argument is a printf(3) string, with additional arguments
+ *     as necessary.
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ *
+ *\li  The category and module arguments must have ids that are in the
+ *     range of known ids, as estabished by isc_log_registercategories()
+ *     and isc_log_registermodules().
+ *
+ *\li  level != #ISC_LOG_DYNAMIC.  ISC_LOG_DYNAMIC is used only to define
+ *     channels, and explicit debugging level must be identified for
+ *     isc_log_write() via ISC_LOG_DEBUG(level).
+ *
+ *\li  format != NULL.
+ *
+ * Ensures:
+ *\li  The log message is written to every channel associated with the
+ *     indicated category/module pair.
+ *
+ * Returns:
+ *\li  Nothing.  Failure to log a message is not construed as a
+ *     meaningful error.
+ */
+void
+isc_log_write(isc_log_t *lctx, isc_logcategory_t *category,
+              isc_logmodule_t *module, int level,
+             const char *format, ...)
+
+ISC_FORMAT_PRINTF(5, 6);
+
+/*%
+ * Write a message to the log channels.
+ *
+ * Notes:
+ *\li  lctx can be NULL; this is allowed so that programs which use
+ *     libraries that use the ISC logging system are not required to
+ *     also use it.
+ *
+ *\li  The format argument is a printf(3) string, with additional arguments
+ *     as necessary.
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ *
+ *\li  The category and module arguments must have ids that are in the
+ *     range of known ids, as estabished by isc_log_registercategories()
+ *     and isc_log_registermodules().
+ *
+ *\li  level != #ISC_LOG_DYNAMIC.  ISC_LOG_DYNAMIC is used only to define
+ *     channels, and explicit debugging level must be identified for
+ *     isc_log_write() via ISC_LOG_DEBUG(level).
+ *
+ *\li  format != NULL.
+ *
+ * Ensures:
+ *\li  The log message is written to every channel associated with the
+ *     indicated category/module pair.
+ *
+ * Returns:
+ *\li  Nothing.  Failure to log a message is not construed as a
+ *     meaningful error.
+ */
+void
+isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category,
+              isc_logmodule_t *module, int level,
+              const char *format, va_list args)
+
+ISC_FORMAT_PRINTF(5, 0);
+
+/*%
+ * Write a message to the log channels, pruning duplicates that occur within
+ * a configurable amount of seconds (see isc_log_[sg]etduplicateinterval).
+ * This function is otherwise identical to isc_log_write().
+ */
+void
+isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category,
+              isc_logmodule_t *module, int level, const char *format, ...)
+
+ISC_FORMAT_PRINTF(5, 6);
+
+/*%
+ * Write a message to the log channels, pruning duplicates that occur within
+ * a configurable amount of seconds (see isc_log_[sg]etduplicateinterval).
+ * This function is otherwise identical to isc_log_vwrite().
+ */
+void
+isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category,
+               isc_logmodule_t *module, int level, const char *format,
+               va_list args)
+
+ISC_FORMAT_PRINTF(5, 0);
+
+/*%
+ * These are four internationalized versions of the the isc_log_[v]write[1]
+ * functions.  
+ *
+ * The only difference is that they take arguments for a message
+ * catalog, message set, and message number, all immediately preceding the
+ * format argument.  The format argument becomes the default text, a la
+ * isc_msgcat_get.  If the message catalog is NULL, no lookup is attempted
+ * for a message -- which makes the message set and message number irrelevant,
+ * and the non-internationalized call should have probably been used instead.
+ *
+ * Yes, that means there are now *eight* interfaces to logging a message.
+ * Sheesh.   Make the madness stop!
+ */
+/*@{*/
+void
+isc_log_iwrite(isc_log_t *lctx, isc_logcategory_t *category,
+             isc_logmodule_t *module, int level,
+             isc_msgcat_t *msgcat, int msgset, int message,
+             const char *format, ...)
+ISC_FORMAT_PRINTF(8, 9);
+
+void
+isc_log_ivwrite(isc_log_t *lctx, isc_logcategory_t *category,
+               isc_logmodule_t *module, int level,
+               isc_msgcat_t *msgcat, int msgset, int message,
+               const char *format, va_list args)
+ISC_FORMAT_PRINTF(8, 0);
+
+void
+isc_log_iwrite1(isc_log_t *lctx, isc_logcategory_t *category,
+               isc_logmodule_t *module, int level,
+               isc_msgcat_t *msgcat, int msgset, int message,
+               const char *format, ...)
+ISC_FORMAT_PRINTF(8, 9);
+
+void
+isc_log_ivwrite1(isc_log_t *lctx, isc_logcategory_t *category,
+                isc_logmodule_t *module, int level,
+                isc_msgcat_t *msgcat, int msgset, int message,
+                const char *format, va_list args)
+ISC_FORMAT_PRINTF(8, 0);
+/*@}*/
+
+void
+isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level);
+/*%<
+ * Set the debugging level used for logging.
+ *
+ * Notes:
+ *\li  Setting the debugging level to 0 disables debugging log messages.
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ *
+ * Ensures:
+ *\li  The debugging level is set to the requested value.
+ */
+
+unsigned int
+isc_log_getdebuglevel(isc_log_t *lctx);
+/*%<
+ * Get the current debugging level.
+ *
+ * Notes:
+ *\li  This is provided so that a program can have a notion of
+ *     "increment debugging level" or "decrement debugging level"
+ *     without needing to keep track of what the current level is.
+ *
+ *\li  A return value of 0 indicates that debugging messages are disabled.
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ *
+ * Ensures:
+ *\li  The current logging debugging level is returned.
+ */
+
+isc_boolean_t
+isc_log_wouldlog(isc_log_t *lctx, int level);
+/*%<
+ * Determine whether logging something to 'lctx' at 'level' would
+ * actually cause something to be logged somewhere.
+ *
+ * If #ISC_FALSE is returned, it is guaranteed that nothing would
+ * be logged, allowing the caller to omit unnecessary
+ * isc_log_write() calls and possible message preformatting.
+ */
+
+void
+isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval);
+/*%<
+ * Set the interval over which duplicate log messages will be ignored
+ * by isc_log_[v]write1(), in seconds.
+ *
+ * Notes:
+ *\li  Increasing the duplicate interval from X to Y will not necessarily
+ *     filter out duplicates of messages logged in Y - X seconds since the
+ *     increase.  (Example: Message1 is logged at midnight.  Message2
+ *     is logged at 00:01:00, when the interval is only 30 seconds, causing
+ *     Message1 to be expired from the log message history.  Then the interval
+ *     is increased to 3000 (five minutes) and at 00:04:00 Message1 is logged
+ *     again.  It will appear the second time even though less than five
+ *     passed since the first occurrence.
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ */
+
+unsigned int
+isc_log_getduplicateinterval(isc_logconfig_t *lcfg);
+/*%<
+ * Get the current duplicate filtering interval.
+ *
+ * Requires:
+ *\li  lctx is a valid logging context.
+ *
+ * Returns:
+ *\li  The current duplicate filtering interval.
+ */
+
+isc_result_t
+isc_log_settag(isc_logconfig_t *lcfg, const char *tag);
+/*%<
+ * Set the program name or other identifier for #ISC_LOG_PRINTTAG.
+ *
+ * Requires:
+ *\li  lcfg is a valid logging configuration.
+ *
+ * Notes:
+ *\li  If this function has not set the tag to a non-NULL, non-empty value,
+ *     then the #ISC_LOG_PRINTTAG channel flag will not print anything.
+ *     Unlike some implementations of syslog on Unix systems, you *must* set
+ *     the tag in order to get it logged.  It is not implicitly derived from
+ *     the program name (which is pretty impossible to infer portably).
+ *
+ *\li  Setting the tag to NULL or the empty string will also cause the
+ *     #ISC_LOG_PRINTTAG channel flag to not print anything.  If tag equals the
+ *     empty string, calls to isc_log_gettag will return NULL.
+ *
+ * Returns:
+ *\li  #ISC_R_SUCCESS  Success
+ *\li  #ISC_R_NOMEMORY  Resource Limit: Out of memory
+ *
+ * XXXDCL when creating a new isc_logconfig_t, it might be nice if the tag
+ * of the currently active isc_logconfig_t was inherited.  this does not
+ * currently happen.
+ */
+
+char *
+isc_log_gettag(isc_logconfig_t *lcfg);
+/*%<
+ * Get the current identifier printed with #ISC_LOG_PRINTTAG.
+ *
+ * Requires:
+ *\li  lcfg is a valid logging configuration.
+ *
+ * Notes:
+ *\li  Since isc_log_settag() will not associate a zero-length string
+ *     with the logging configuration, attempts to do so will cause
+ *     this function to return NULL.  However, a determined programmer
+ *     will observe that (currently) a tag of length greater than zero
+ *     could be set, and then modified to be zero length.
+ *
+ * Returns:
+ *\li  A pointer to the current identifier, or NULL if none has been set.
+ */
+
+void
+isc_log_opensyslog(const char *tag, int options, int facility);
+/*%<
+ * Initialize syslog logging.
+ *
+ * Notes:
+ *\li  XXXDCL NT
+ *     This is currently equivalent to openlog(), but is not going to remain
+ *     that way.  In the meantime, the arguments are all identical to
+ *     those used by openlog(3), as follows:
+ *
+ * \code
+ *             tag: The string to use in the position of the program
+ *                     name in syslog messages.  Most (all?) syslogs
+ *                     will use basename(argv[0]) if tag is NULL.
+ *
+ *             options: LOG_CONS, LOG_PID, LOG_NDELAY ... whatever your
+ *                     syslog supports.
+ *
+ *             facility: The default syslog facility.  This is irrelevant
+ *                     since isc_log_write will ALWAYS use the channel's
+ *                     declared facility.
+ * \endcode
+ *
+ *\li  Zero effort has been made (yet) to accomodate systems with openlog()
+ *     that only takes two arguments, or to identify valid syslog
+ *     facilities or options for any given architecture.
+ *
+ *\li  It is necessary to call isc_log_opensyslog() to initialize
+ *     syslogging on machines which do not support network connections to
+ *     syslogd because they require a Unix domain socket to be used.  Since
+ *     this is a chore to determine at run-time, it is suggested that it
+ *     always be called by programs using the ISC logging system.
+ *
+ * Requires:
+ *\li  Nothing.
+ *
+ * Ensures:
+ *\li  openlog() is called to initialize the syslog system.
+ */
+
+void
+isc_log_closefilelogs(isc_log_t *lctx);
+/*%<
+ * Close all open files used by #ISC_LOG_TOFILE channels.
+ *
+ * Notes:
+ *\li  This function is provided for programs that want to use their own
+ *     log rolling mechanism rather than the one provided internally.
+ *     For example, a program that wanted to keep daily logs would define
+ *     a channel which used #ISC_LOG_ROLLNEVER, then once a day would
+ *     rename the log file and call isc_log_closefilelogs().
+ *
+ *\li  #ISC_LOG_TOFILEDESC channels are unaffected.
+ *
+ * Requires:
+ *\li  lctx is a valid context.
+ *
+ * Ensures:
+ *\li  The open files are closed and will be reopened when they are
+ *     next needed.
+ */
+
+isc_logcategory_t *
+isc_log_categorybyname(isc_log_t *lctx, const char *name);
+/*%<
+ * Find a category by its name.
+ *
+ * Notes:
+ *\li  The string name of a category is not required to be unique.
+ *
+ * Requires:
+ *\li  lctx is a valid context.
+ *\li  name is not NULL.
+ *
+ * Returns:
+ *\li  A pointer to the _first_ isc_logcategory_t structure used by "name".
+ *
+ *\li  NULL if no category exists by that name.
+ */
+
+isc_logmodule_t *
+isc_log_modulebyname(isc_log_t *lctx, const char *name);
+/*%<
+ * Find a module by its name.
+ *
+ * Notes:
+ *\li  The string name of a module is not required to be unique.
+ *
+ * Requires:
+ *\li  lctx is a valid context.
+ *\li  name is not NULL.
+ *
+ * Returns:
+ *\li  A pointer to the _first_ isc_logmodule_t structure used by "name".
+ *
+ *\li  NULL if no module exists by that name.
+ */
+
+void
+isc_log_setcontext(isc_log_t *lctx);
+/*%<
+ * Sets the context used by the libisc for logging.
+ *
+ * Requires:
+ *\li  lctx be a valid context.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISC_LOG_H */
diff --git a/include/isc/stat.h b/include/isc/stat.h
new file mode 100644 (file)
index 0000000..d1b2489
--- /dev/null
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2004  Internet Systems Consortium, Inc. ("ISC")
+ *
+ * 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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+/* $Id: stat.h,v 1.2.18.1 2004/08/19 04:42:54 marka Exp $ */
+
+#ifndef ISC_STAT_H
+#define ISC_STAT_H 1
+
+/*****
+ ***** Module Info
+ *****/
+
+/*
+ * Portable netdb.h support.
+ *
+ * This module is responsible for defining S_IS??? macros.
+ *
+ * MP:
+ *     No impact.
+ *
+ * Reliability:
+ *     No anticipated impact.
+ *
+ * Resources:
+ *     N/A.
+ *
+ * Security:
+ *     No anticipated impact.
+ *
+ */
+
+/***
+ *** Imports.
+ ***/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#endif /* ISC_STAT_H */
diff --git a/include/isc/stdio.h b/include/isc/stdio.h
new file mode 100644 (file)
index 0000000..e3bf0cd
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 2000, 2001  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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+/* $Id: stdio.h,v 1.7.18.2 2005/04/29 00:17:03 marka Exp $ */
+
+#ifndef ISC_STDIO_H
+#define ISC_STDIO_H 1
+
+/*! \file */
+
+/*% 
+ * These functions are wrappers around the corresponding stdio functions.
+ *
+ * They return a detailed error code in the form of an an isc_result_t.  ANSI C
+ * does not guarantee that stdio functions set errno, hence these functions
+ * must use platform dependent methods (e.g., the POSIX errno) to construct the
+ * error code.
+ */
+
+#include <stdio.h>
+
+#include <isc/lang.h>
+#include <isc/result.h>
+
+ISC_LANG_BEGINDECLS
+
+/*% Open */
+isc_result_t
+isc_stdio_open(const char *filename, const char *mode, FILE **fp);
+
+/*% Close */
+isc_result_t
+isc_stdio_close(FILE *f);
+
+/*% Seek */
+isc_result_t
+isc_stdio_seek(FILE *f, long offset, int whence);
+
+/*% Read */
+isc_result_t
+isc_stdio_read(void *ptr, size_t size, size_t nmemb, FILE *f,
+              size_t *nret);
+
+/*% Write */
+isc_result_t
+isc_stdio_write(const void *ptr, size_t size, size_t nmemb, FILE *f,
+               size_t *nret);
+
+/*% Flush */
+isc_result_t
+isc_stdio_flush(FILE *f);
+
+isc_result_t
+isc_stdio_sync(FILE *f);
+/*%<
+ * Invoke fsync() on the file descriptor underlying an stdio stream, or an
+ * equivalent system-dependent operation.  Note that this function has no
+ * direct counterpart in the stdio library.
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISC_STDIO_H */
diff --git a/include/isc/time.h b/include/isc/time.h
new file mode 100644 (file)
index 0000000..6579439
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2004, 2005  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1998-2001  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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+/* $Id: time.h,v 1.30.18.2 2005/04/29 00:17:10 marka Exp $ */
+
+#ifndef ISC_TIME_H
+#define ISC_TIME_H 1
+
+/*! \file */
+
+#include <isc/lang.h>
+#include <isc/types.h>
+
+/***
+ *** Intervals
+ ***/
+
+/*! 
+ *  \brief
+ * The contents of this structure are private, and MUST NOT be accessed
+ * directly by callers.
+ *
+ * The contents are exposed only to allow callers to avoid dynamic allocation.
+ */
+struct isc_interval {
+       unsigned int seconds;
+       unsigned int nanoseconds;
+};
+
+extern isc_interval_t *isc_interval_zero;
+
+ISC_LANG_BEGINDECLS
+
+void
+isc_interval_set(isc_interval_t *i,
+                unsigned int seconds, unsigned int nanoseconds);
+/*%<
+ * Set 'i' to a value representing an interval of 'seconds' seconds and
+ * 'nanoseconds' nanoseconds, suitable for use in isc_time_add() and
+ * isc_time_subtract().
+ *
+ * Requires:
+ *
+ *\li  't' is a valid pointer.
+ *\li  nanoseconds < 1000000000.
+ */
+
+isc_boolean_t
+isc_interval_iszero(const isc_interval_t *i);
+/*%<
+ * Returns ISC_TRUE iff. 'i' is the zero interval.
+ *
+ * Requires:
+ *
+ *\li  'i' is a valid pointer.
+ */
+
+/***
+ *** Absolute Times
+ ***/
+
+/*%
+ * The contents of this structure are private, and MUST NOT be accessed
+ * directly by callers.
+ *
+ * The contents are exposed only to allow callers to avoid dynamic allocation.
+ */
+
+struct isc_time {
+       unsigned int    seconds;
+       unsigned int    nanoseconds;
+};
+
+extern isc_time_t *isc_time_epoch;
+
+void
+isc_time_set(isc_time_t *t, unsigned int seconds, unsigned int nanoseconds);
+/*%<
+ * Set 't' to a particular number of seconds + nanoseconds since the epoch.
+ *
+ * Notes:
+ *\li  This call is equivalent to:
+ *\code
+ *     isc_time_settoepoch(t);
+ *     isc_interval_set(i, seconds, nanoseconds);
+ *     isc_time_add(t, i, t);
+ *\endcode
+ * Requires:
+ *\li  't' is a valid pointer.
+ *\li  nanoseconds < 1000000000.
+ */
+
+void
+isc_time_settoepoch(isc_time_t *t);
+/*%<
+ * Set 't' to the time of the epoch.
+ *
+ * Notes:
+ * \li The date of the epoch is platform-dependent.
+ *
+ * Requires:
+ *
+ *\li  't' is a valid pointer.
+ */
+
+isc_boolean_t
+isc_time_isepoch(const isc_time_t *t);
+/*%<
+ * Returns ISC_TRUE iff. 't' is the epoch ("time zero").
+ *
+ * Requires:
+ *
+ *\li  't' is a valid pointer.
+ */
+
+isc_result_t
+isc_time_now(isc_time_t *t);
+/*%<
+ * Set 't' to the current absolute time.
+ *
+ * Requires:
+ *
+ *\li  't' is a valid pointer.
+ *
+ * Returns:
+ *
+ *\li  Success
+ *\li  Unexpected error
+ *             Getting the time from the system failed.
+ *\li  Out of range
+ *             The time from the system is too large to be represented
+ *             in the current definition of isc_time_t.
+ */
+
+isc_result_t
+isc_time_nowplusinterval(isc_time_t *t, const isc_interval_t *i);
+/*%<
+ * Set *t to the current absolute time + i.
+ *
+ * Note:
+ *\li  This call is equivalent to:
+ *
+ *\code
+ *             isc_time_now(t);
+ *             isc_time_add(t, i, t);
+ *\endcode
+ *
+ * Requires:
+ *
+ *\li  't' and 'i' are valid pointers.
+ *
+ * Returns:
+ *
+ *\li  Success
+ *\li  Unexpected error
+ *             Getting the time from the system failed.
+ *\li  Out of range
+ *             The interval added to the time from the system is too large to
+ *             be represented in the current definition of isc_time_t.
+ */
+
+int
+isc_time_compare(const isc_time_t *t1, const isc_time_t *t2);
+/*%<
+ * Compare the times referenced by 't1' and 't2'
+ *
+ * Requires:
+ *
+ *\li  't1' and 't2' are valid pointers.
+ *
+ * Returns:
+ *
+ *\li  -1              t1 < t2         (comparing times, not pointers)
+ *\li  0               t1 = t2
+ *\li  1               t1 > t2
+ */
+
+isc_result_t
+isc_time_add(const isc_time_t *t, const isc_interval_t *i, isc_time_t *result);
+/*%<
+ * Add 'i' to 't', storing the result in 'result'.
+ *
+ * Requires:
+ *
+ *\li  't', 'i', and 'result' are valid pointers.
+ *
+ * Returns:
+ * \li Success
+ *\li  Out of range
+ *             The interval added to the time is too large to
+ *             be represented in the current definition of isc_time_t.
+ */
+
+isc_result_t
+isc_time_subtract(const isc_time_t *t, const isc_interval_t *i,
+                 isc_time_t *result);
+/*%<
+ * Subtract 'i' from 't', storing the result in 'result'.
+ *
+ * Requires:
+ *
+ *\li  't', 'i', and 'result' are valid pointers.
+ *
+ * Returns:
+ *\li  Success
+ *\li  Out of range
+ *             The interval is larger than the time since the epoch.
+ */
+
+isc_uint64_t
+isc_time_microdiff(const isc_time_t *t1, const isc_time_t *t2);
+/*%<
+ * Find the difference in microseconds between time t1 and time t2.
+ * t2 is the subtrahend of t1; ie, difference = t1 - t2.
+ *
+ * Requires:
+ *
+ *\li  't1' and 't2' are valid pointers.
+ *
+ * Returns:
+ *\li  The difference of t1 - t2, or 0 if t1 <= t2.
+ */
+
+isc_uint32_t
+isc_time_seconds(const isc_time_t *t);
+/*%<
+ * Return the number of seconds since the epoch stored in a time structure.
+ *
+ * Requires:
+ *
+ *\li  't' is a valid pointer.
+ */
+
+isc_result_t
+isc_time_secondsastimet(const isc_time_t *t, time_t *secondsp);
+/*%<
+ * Ensure the number of seconds in an isc_time_t is representable by a time_t.
+ *
+ * Notes:
+ *\li  The number of seconds stored in an isc_time_t might be larger
+ *     than the number of seconds a time_t is able to handle.  Since
+ *     time_t is mostly opaque according to the ANSI/ISO standard
+ *     (essentially, all you can be sure of is that it is an arithmetic type,
+ *     not even necessarily integral), it can be tricky to ensure that
+ *     the isc_time_t is in the range a time_t can handle.  Use this
+ *     function in place of isc_time_seconds() any time you need to set a
+ *     time_t from an isc_time_t.
+ *
+ * Requires:
+ *\li  't' is a valid pointer.
+ *
+ * Returns:
+ *\li  Success
+ *\li  Out of range
+ */
+
+isc_uint32_t
+isc_time_nanoseconds(const isc_time_t *t);
+/*%<
+ * Return the number of nanoseconds stored in a time structure.
+ *
+ * Notes:
+ *\li  This is the number of nanoseconds in excess of the the number
+ *     of seconds since the epoch; it will always be less than one
+ *     full second.
+ *
+ * Requires:
+ *\li  't' is a valid pointer.
+ *
+ * Ensures:
+ *\li  The returned value is less than 1*10^9.
+ */
+
+void
+isc_time_formattimestamp(const isc_time_t *t, char *buf, unsigned int len);
+/*%<
+ * Format the time 't' into the buffer 'buf' of length 'len',
+ * using a format like "30-Aug-2000 04:06:47.997" and the local time zone.
+ * If the text does not fit in the buffer, the result is indeterminate,
+ * but is always guaranteed to be null terminated.
+ *
+ *  Requires:
+ *\li      'len' > 0
+ *  \li    'buf' points to an array of at least len chars
+ *
+ */
+
+ISC_LANG_ENDDECLS
+
+#endif /* ISC_TIME_H */
diff --git a/libisc/log.c b/libisc/log.c
new file mode 100644 (file)
index 0000000..20d9627
--- /dev/null
@@ -0,0 +1,1762 @@
+/*
+ * Copyright (C) 2004-2006  Internet Systems Consortium, Inc. ("ISC")
+ * Copyright (C) 1999-2003  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 ISC DISCLAIMS ALL WARRANTIES WITH
+ * REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS.  IN NO EVENT SHALL ISC 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.
+ */
+
+/* $Id: log.c,v 1.84.18.8 2006/03/02 00:37:22 marka Exp $ */
+
+/*! \file
+ * \author  Principal Authors: DCL */
+
+#include <config.h>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <time.h>
+
+#include <sys/types.h> /* dev_t FreeBSD 2.1 */
+
+#include <isc/dir.h>
+#include <isc/file.h>
+#include <isc/log.h>
+#include <isc/magic.h>
+#include <isc/mem.h>
+#include <isc/msgs.h>
+#include <isc/mutex.h>
+#include <isc/print.h>
+#include <isc/stat.h>
+#include <isc/stdio.h>
+#include <isc/string.h>
+#include <isc/time.h>
+#include <isc/util.h>
+
+#define LCTX_MAGIC             ISC_MAGIC('L', 'c', 't', 'x')
+#define VALID_CONTEXT(lctx)    ISC_MAGIC_VALID(lctx, LCTX_MAGIC)
+
+#define LCFG_MAGIC             ISC_MAGIC('L', 'c', 'f', 'g')
+#define VALID_CONFIG(lcfg)     ISC_MAGIC_VALID(lcfg, LCFG_MAGIC)
+
+/*
+ * XXXDCL make dynamic?
+ */
+#define LOG_BUFFER_SIZE        (8 * 1024)
+
+#ifndef PATH_MAX
+#define PATH_MAX 1024  /* AIX and others don't define this. */
+#endif
+
+/*!
+ * This is the structure that holds each named channel.  A simple linked
+ * list chains all of the channels together, so an individual channel is
+ * found by doing strcmp()s with the names down the list.  Their should
+ * be no peformance penalty from this as it is expected that the number
+ * of named channels will be no more than a dozen or so, and name lookups
+ * from the head of the list are only done when isc_log_usechannel() is
+ * called, which should also be very infrequent.
+ */
+typedef struct isc_logchannel isc_logchannel_t;
+
+struct isc_logchannel {
+       char *                          name;
+       unsigned int                    type;
+       int                             level;
+       unsigned int                    flags;
+       isc_logdestination_t            destination;
+       ISC_LINK(isc_logchannel_t)      link;
+};
+
+/*!
+ * The logchannellist structure associates categories and modules with
+ * channels.  First the appropriate channellist is found based on the
+ * category, and then each structure in the linked list is checked for
+ * a matching module.  It is expected that the number of channels
+ * associated with any given category will be very short, no more than
+ * three or four in the more unusual cases.
+ */
+typedef struct isc_logchannellist isc_logchannellist_t;
+
+struct isc_logchannellist {
+       const isc_logmodule_t *         module;
+       isc_logchannel_t *              channel;
+       ISC_LINK(isc_logchannellist_t)  link;
+};
+
+/*!
+ * This structure is used to remember messages for pruning via
+ * isc_log_[v]write1().
+ */
+typedef struct isc_logmessage isc_logmessage_t;
+
+struct isc_logmessage {
+       char *                          text;
+       isc_time_t                      time;
+       ISC_LINK(isc_logmessage_t)      link;
+};
+
+/*!
+ * The isc_logconfig structure is used to store the configurable information
+ * about where messages are actually supposed to be sent -- the information
+ * that could changed based on some configuration file, as opposed to the
+ * the category/module specification of isc_log_[v]write[1] that is compiled
+ * into a program, or the debug_level which is dynamic state information.
+ */
+struct isc_logconfig {
+       unsigned int                    magic;
+       isc_log_t *                     lctx;
+       ISC_LIST(isc_logchannel_t)      channels;
+       ISC_LIST(isc_logchannellist_t) *channellists;
+       unsigned int                    channellist_count;
+       unsigned int                    duplicate_interval;
+       int                             highest_level;
+       char *                          tag;
+       isc_boolean_t                   dynamic;
+};
+
+/*!
+ * This isc_log structure provides the context for the isc_log functions.
+ * The log context locks itself in isc_log_doit, the internal backend to
+ * isc_log_write.  The locking is necessary both to provide exclusive access
+ * to the the buffer into which the message is formatted and to guard against
+ * competing threads trying to write to the same syslog resource.  (On
+ * some systems, such as BSD/OS, stdio is thread safe but syslog is not.)
+ * Unfortunately, the lock cannot guard against a _different_ logging
+ * context in the same program competing for syslog's attention.  Thus
+ * There Can Be Only One, but this is not enforced.
+ * XXXDCL enforce it?
+ *
+ * Note that the category and module information is not locked.
+ * This is because in the usual case, only one isc_log_t is ever created
+ * in a program, and the category/module registration happens only once.
+ * XXXDCL it might be wise to add more locking overall.
+ */
+struct isc_log {
+       /* Not locked. */
+       unsigned int                    magic;
+       isc_mem_t *                     mctx;
+       isc_logcategory_t *             categories;
+       unsigned int                    category_count;
+       isc_logmodule_t *               modules;
+       unsigned int                    module_count;
+       int                             debug_level;
+       isc_mutex_t                     lock;
+       /* Locked by isc_log lock. */
+       isc_logconfig_t *               logconfig;
+       char                            buffer[LOG_BUFFER_SIZE];
+       ISC_LIST(isc_logmessage_t)      messages;
+};
+
+/*!
+ * Used when ISC_LOG_PRINTLEVEL is enabled for a channel.
+ */
+static const char *log_level_strings[] = {
+       "debug",
+       "info",
+       "notice",
+       "warning",
+       "error",
+       "critical"
+};
+
+/*!
+ * Used to convert ISC_LOG_* priorities into syslog priorities.
+ * XXXDCL This will need modification for NT.
+ */
+static const int syslog_map[] = {
+       LOG_DEBUG,
+       LOG_INFO,
+       LOG_NOTICE,
+       LOG_WARNING,
+       LOG_ERR,
+       LOG_CRIT
+};
+
+/*!
+ * When adding new categories, a corresponding ISC_LOGCATEGORY_foo
+ * definition needs to be added to <isc/log.h>.
+ *
+ * The default category is provided so that the internal default can
+ * be overridden.  Since the default is always looked up as the first
+ * channellist in the log context, it must come first in isc_categories[].
+ */
+LIBISC_EXTERNAL_DATA isc_logcategory_t isc_categories[] = {
+       { "default", 0 },       /* "default" must come first. */
+       { "general", 0 },
+       { NULL, 0 }
+};
+
+/*!
+ * See above comment for categories on LIBISC_EXTERNAL_DATA, and apply it to modules.
+ */
+LIBISC_EXTERNAL_DATA isc_logmodule_t isc_modules[] = {
+       { "socket", 0 },
+       { "time", 0 },
+       { "interface", 0 },
+       { "timer", 0 },
+       { NULL, 0 }
+};
+
+/*!
+ * This essentially constant structure must be filled in at run time,
+ * because its channel member is pointed to a channel that is created
+ * dynamically with isc_log_createchannel.
+ */
+static isc_logchannellist_t default_channel;
+
+/*!
+ * libisc logs to this context.
+ */
+LIBISC_EXTERNAL_DATA isc_log_t *isc_lctx = NULL;
+
+/*!
+ * Forward declarations.
+ */
+static isc_result_t
+assignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
+             const isc_logmodule_t *module, isc_logchannel_t *channel);
+
+static isc_result_t
+sync_channellist(isc_logconfig_t *lcfg);
+
+static isc_result_t
+greatest_version(isc_logchannel_t *channel, int *greatest);
+
+static isc_result_t
+roll_log(isc_logchannel_t *channel);
+
+static void
+isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category,
+            isc_logmodule_t *module, int level, isc_boolean_t write_once,
+            isc_msgcat_t *msgcat, int msgset, int msg,
+            const char *format, va_list args)
+     ISC_FORMAT_PRINTF(9, 0);
+
+/*@{*/
+/*!
+ * Convenience macros.
+ */
+
+#define FACILITY(channel)       (channel->destination.facility)
+#define FILE_NAME(channel)      (channel->destination.file.name)
+#define FILE_STREAM(channel)    (channel->destination.file.stream)
+#define FILE_VERSIONS(channel)  (channel->destination.file.versions)
+#define FILE_MAXSIZE(channel)   (channel->destination.file.maximum_size)
+#define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached)
+
+/*@}*/
+/****
+ **** Public interfaces.
+ ****/
+
+/*
+ * Establish a new logging context, with default channels.
+ */
+isc_result_t
+isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) {
+       isc_log_t *lctx;
+       isc_logconfig_t *lcfg = NULL;
+       isc_result_t result;
+
+       REQUIRE(mctx != NULL);
+       REQUIRE(lctxp != NULL && *lctxp == NULL);
+       REQUIRE(lcfgp == NULL || *lcfgp == NULL);
+
+       lctx = isc_mem_get(mctx, sizeof(*lctx));
+       if (lctx != NULL) {
+               lctx->mctx = mctx;
+               lctx->categories = NULL;
+               lctx->category_count = 0;
+               lctx->modules = NULL;
+               lctx->module_count = 0;
+               lctx->debug_level = 0;
+
+               ISC_LIST_INIT(lctx->messages);
+
+               result = isc_mutex_init(&lctx->lock);
+               if (result != ISC_R_SUCCESS) {
+                       isc_mem_put(mctx, lctx, sizeof(*lctx));
+                       return (result);
+               }
+
+               /*
+                * Normally setting the magic number is the last step done
+                * in a creation function, but a valid log context is needed
+                * by isc_log_registercategories and isc_logconfig_create.
+                * If either fails, the lctx is destroyed and not returned
+                * to the caller.
+                */
+               lctx->magic = LCTX_MAGIC;
+
+               isc_log_registercategories(lctx, isc_categories);
+               isc_log_registermodules(lctx, isc_modules);
+               result = isc_logconfig_create(lctx, &lcfg);
+
+       } else
+               result = ISC_R_NOMEMORY;
+
+       if (result == ISC_R_SUCCESS)
+               result = sync_channellist(lcfg);
+
+       if (result == ISC_R_SUCCESS) {
+               lctx->logconfig = lcfg;
+
+               *lctxp = lctx;
+               if (lcfgp != NULL)
+                       *lcfgp = lcfg;
+
+       } else {
+               if (lcfg != NULL)
+                       isc_logconfig_destroy(&lcfg);
+               if (lctx != NULL)
+                       isc_log_destroy(&lctx);
+       }
+
+       return (result);
+}
+
+isc_result_t
+isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp) {
+       isc_logconfig_t *lcfg;
+       isc_logdestination_t destination;
+       isc_result_t result = ISC_R_SUCCESS;
+       int level = ISC_LOG_INFO;
+
+       REQUIRE(lcfgp != NULL && *lcfgp == NULL);
+       REQUIRE(VALID_CONTEXT(lctx));
+
+       lcfg = isc_mem_get(lctx->mctx, sizeof(*lcfg));
+
+       if (lcfg != NULL) {
+               lcfg->lctx = lctx;
+               lcfg->channellists = NULL;
+               lcfg->channellist_count = 0;
+               lcfg->duplicate_interval = 0;
+               lcfg->highest_level = level;
+               lcfg->tag = NULL;
+               lcfg->dynamic = ISC_FALSE;
+
+               ISC_LIST_INIT(lcfg->channels);
+
+               /*
+                * Normally the magic number is the last thing set in the
+                * structure, but isc_log_createchannel() needs a valid
+                * config.  If the channel creation fails, the lcfg is not
+                * returned to the caller.
+                */
+               lcfg->magic = LCFG_MAGIC;
+
+       } else
+               result = ISC_R_NOMEMORY;
+
+       /*
+        * Create the default channels:
+        *      default_syslog, default_stderr, default_debug and null.
+        */
+       if (result == ISC_R_SUCCESS) {
+               destination.facility = LOG_DAEMON;
+               result = isc_log_createchannel(lcfg, "default_syslog",
+                                              ISC_LOG_TOSYSLOG, level,
+                                              &destination, 0);
+       }
+
+       if (result == ISC_R_SUCCESS) {
+               destination.file.stream = stderr;
+               destination.file.name = NULL;
+               destination.file.versions = ISC_LOG_ROLLNEVER;
+               destination.file.maximum_size = 0;
+               result = isc_log_createchannel(lcfg, "default_stderr",
+                                              ISC_LOG_TOFILEDESC,
+                                              level,
+                                              &destination,
+                                              ISC_LOG_PRINTTIME);
+       }
+
+       if (result == ISC_R_SUCCESS) {
+               /*
+                * Set the default category's channel to default_stderr,
+                * which is at the head of the channels list because it was
+                * just created.
+                */
+               default_channel.channel = ISC_LIST_HEAD(lcfg->channels);
+
+               destination.file.stream = stderr;
+               destination.file.name = NULL;
+               destination.file.versions = ISC_LOG_ROLLNEVER;
+               destination.file.maximum_size = 0;
+               result = isc_log_createchannel(lcfg, "default_debug",
+                                              ISC_LOG_TOFILEDESC,
+                                              ISC_LOG_DYNAMIC,
+                                              &destination,
+                                              ISC_LOG_PRINTTIME);
+       }
+
+       if (result == ISC_R_SUCCESS)
+               result = isc_log_createchannel(lcfg, "null",
+                                              ISC_LOG_TONULL,
+                                              ISC_LOG_DYNAMIC,
+                                              NULL, 0);
+
+       if (result == ISC_R_SUCCESS)
+               *lcfgp = lcfg;
+
+       else
+               if (lcfg != NULL)
+                       isc_logconfig_destroy(&lcfg);
+
+       return (result);
+}
+
+isc_logconfig_t *
+isc_logconfig_get(isc_log_t *lctx) {
+       REQUIRE(VALID_CONTEXT(lctx));
+
+       ENSURE(lctx->logconfig != NULL);
+
+       return (lctx->logconfig);
+}
+
+isc_result_t
+isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg) {
+       isc_logconfig_t *old_cfg;
+       isc_result_t result;
+
+       REQUIRE(VALID_CONTEXT(lctx));
+       REQUIRE(VALID_CONFIG(lcfg));
+       REQUIRE(lcfg->lctx == lctx);
+
+       /*
+        * Ensure that lcfg->channellist_count == lctx->category_count.
+        * They won't be equal if isc_log_usechannel has not been called
+        * since any call to isc_log_registercategories.
+        */
+       result = sync_channellist(lcfg);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       LOCK(&lctx->lock);
+
+       old_cfg = lctx->logconfig;
+       lctx->logconfig = lcfg;
+
+       UNLOCK(&lctx->lock);
+
+       isc_logconfig_destroy(&old_cfg);
+
+       return (ISC_R_SUCCESS);
+}
+
+void
+isc_log_destroy(isc_log_t **lctxp) {
+       isc_log_t *lctx;
+       isc_logconfig_t *lcfg;
+       isc_mem_t *mctx;
+       isc_logmessage_t *message;
+
+       REQUIRE(lctxp != NULL && VALID_CONTEXT(*lctxp));
+
+       lctx = *lctxp;
+       mctx = lctx->mctx;
+
+       if (lctx->logconfig != NULL) {
+               lcfg = lctx->logconfig;
+               lctx->logconfig = NULL;
+               isc_logconfig_destroy(&lcfg);
+       }
+
+       DESTROYLOCK(&lctx->lock);
+
+       while ((message = ISC_LIST_HEAD(lctx->messages)) != NULL) {
+               ISC_LIST_UNLINK(lctx->messages, message, link);
+
+               isc_mem_put(mctx, message,
+                           sizeof(*message) + strlen(message->text) + 1);
+       }
+
+       lctx->buffer[0] = '\0';
+       lctx->debug_level = 0;
+       lctx->categories = NULL;
+       lctx->category_count = 0;
+       lctx->modules = NULL;
+       lctx->module_count = 0;
+       lctx->mctx = NULL;
+       lctx->magic = 0;
+
+       isc_mem_put(mctx, lctx, sizeof(*lctx));
+
+       *lctxp = NULL;
+}
+
+void
+isc_logconfig_destroy(isc_logconfig_t **lcfgp) {
+       isc_logconfig_t *lcfg;
+       isc_mem_t *mctx;
+       isc_logchannel_t *channel;
+       isc_logchannellist_t *item;
+       char *filename;
+       unsigned int i;
+
+       REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp));
+
+       lcfg = *lcfgp;
+
+       /*
+        * This function cannot be called with a logconfig that is in
+        * use by a log context.
+        */
+       REQUIRE(lcfg->lctx != NULL && lcfg->lctx->logconfig != lcfg);
+
+       mctx = lcfg->lctx->mctx;
+
+       while ((channel = ISC_LIST_HEAD(lcfg->channels)) != NULL) {
+               ISC_LIST_UNLINK(lcfg->channels, channel, link);
+
+               if (channel->type == ISC_LOG_TOFILE) {
+                       /*
+                        * The filename for the channel may have ultimately
+                        * started its life in user-land as a const string,
+                        * but in isc_log_createchannel it gets copied
+                        * into writable memory and is not longer truly const.
+                        */
+                       DE_CONST(FILE_NAME(channel), filename);
+                       isc_mem_free(mctx, filename);
+
+                       if (FILE_STREAM(channel) != NULL)
+                               (void)fclose(FILE_STREAM(channel));
+               }
+
+               isc_mem_free(mctx, channel->name);
+               isc_mem_put(mctx, channel, sizeof(*channel));
+       }
+
+       for (i = 0; i < lcfg->channellist_count; i++)
+               while ((item = ISC_LIST_HEAD(lcfg->channellists[i])) != NULL) {
+                       ISC_LIST_UNLINK(lcfg->channellists[i], item, link);
+                       isc_mem_put(mctx, item, sizeof(*item));
+               }
+
+       if (lcfg->channellist_count > 0)
+               isc_mem_put(mctx, lcfg->channellists,
+                           lcfg->channellist_count *
+                           sizeof(ISC_LIST(isc_logchannellist_t)));
+
+       lcfg->dynamic = ISC_FALSE;
+       if (lcfg->tag != NULL)
+               isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
+       lcfg->tag = NULL;
+       lcfg->highest_level = 0;
+       lcfg->duplicate_interval = 0;
+       lcfg->magic = 0;
+
+       isc_mem_put(mctx, lcfg, sizeof(*lcfg));
+
+       *lcfgp = NULL;
+}
+
+void
+isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]) {
+       isc_logcategory_t *catp;
+
+       REQUIRE(VALID_CONTEXT(lctx));
+       REQUIRE(categories != NULL && categories[0].name != NULL);
+
+       /*
+        * XXXDCL This somewhat sleazy situation of using the last pointer
+        * in one category array to point to the next array exists because
+        * this registration function returns void and I didn't want to have
+        * change everything that used it by making it return an isc_result_t.
+        * It would need to do that if it had to allocate memory to store
+        * pointers to each array passed in.
+        */
+       if (lctx->categories == NULL)
+               lctx->categories = categories;
+
+       else {
+               /*
+                * Adjust the last (NULL) pointer of the already registered
+                * categories to point to the incoming array.
+                */
+               for (catp = lctx->categories; catp->name != NULL; )
+                       if (catp->id == UINT_MAX)
+                               /*
+                                * The name pointer points to the next array.
+                                * Ick.
+                                */
+                               DE_CONST(catp->name, catp);
+                       else
+                               catp++;
+
+               catp->name = (void *)categories;
+               catp->id = UINT_MAX;
+       }
+
+       /*
+        * Update the id number of the category with its new global id.
+        */
+       for (catp = categories; catp->name != NULL; catp++)
+               catp->id = lctx->category_count++;
+}
+
+isc_logcategory_t *
+isc_log_categorybyname(isc_log_t *lctx, const char *name) {
+       isc_logcategory_t *catp;
+
+       REQUIRE(VALID_CONTEXT(lctx));
+       REQUIRE(name != NULL);
+
+       for (catp = lctx->categories; catp->name != NULL; )
+               if (catp->id == UINT_MAX)
+                       /*
+                        * catp is neither modified nor returned to the
+                        * caller, so removing its const qualifier is ok.
+                        */
+                       DE_CONST(catp->name, catp);
+               else {
+                       if (strcmp(catp->name, name) == 0)
+                               return (catp);
+                       catp++;
+               }
+
+       return (NULL);
+}
+
+void
+isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]) {
+       isc_logmodule_t *modp;
+
+       REQUIRE(VALID_CONTEXT(lctx));
+       REQUIRE(modules != NULL && modules[0].name != NULL);
+
+       /*
+        * XXXDCL This somewhat sleazy situation of using the last pointer
+        * in one category array to point to the next array exists because
+        * this registration function returns void and I didn't want to have
+        * change everything that used it by making it return an isc_result_t.
+        * It would need to do that if it had to allocate memory to store
+        * pointers to each array passed in.
+        */
+       if (lctx->modules == NULL)
+               lctx->modules = modules;
+
+       else {
+               /*
+                * Adjust the last (NULL) pointer of the already registered
+                * modules to point to the incoming array.
+                */
+               for (modp = lctx->modules; modp->name != NULL; )
+                       if (modp->id == UINT_MAX)
+                               /*
+                                * The name pointer points to the next array.
+                                * Ick.
+                                */
+                               DE_CONST(modp->name, modp);
+                       else
+                               modp++;
+
+               modp->name = (void *)modules;
+               modp->id = UINT_MAX;
+       }
+
+       /*
+        * Update the id number of the module with its new global id.
+        */
+       for (modp = modules; modp->name != NULL; modp++)
+               modp->id = lctx->module_count++;
+}
+
+isc_logmodule_t *
+isc_log_modulebyname(isc_log_t *lctx, const char *name) {
+       isc_logmodule_t *modp;
+
+       REQUIRE(VALID_CONTEXT(lctx));
+       REQUIRE(name != NULL);
+
+       for (modp = lctx->modules; modp->name != NULL; )
+               if (modp->id == UINT_MAX)
+                       /*
+                        * modp is neither modified nor returned to the
+                        * caller, so removing its const qualifier is ok.
+                        */
+                       DE_CONST(modp->name, modp);
+               else {
+                       if (strcmp(modp->name, name) == 0)
+                               return (modp);
+                       modp++;
+               }
+
+       return (NULL);
+}
+
+isc_result_t
+isc_log_createchannel(isc_logconfig_t *lcfg, const char *name,
+                     unsigned int type, int level,
+                     const isc_logdestination_t *destination,
+                     unsigned int flags)
+{
+       isc_logchannel_t *channel;
+       isc_mem_t *mctx;
+
+       REQUIRE(VALID_CONFIG(lcfg));
+       REQUIRE(name != NULL);
+       REQUIRE(type == ISC_LOG_TOSYSLOG   || type == ISC_LOG_TOFILE ||
+               type == ISC_LOG_TOFILEDESC || type == ISC_LOG_TONULL);
+       REQUIRE(destination != NULL || type == ISC_LOG_TONULL);
+       REQUIRE(level >= ISC_LOG_CRITICAL);
+       REQUIRE((flags &
+                (unsigned int)~(ISC_LOG_PRINTALL | ISC_LOG_DEBUGONLY)) == 0);
+
+       /* XXXDCL find duplicate names? */
+
+       mctx = lcfg->lctx->mctx;
+
+       channel = isc_mem_get(mctx, sizeof(*channel));
+       if (channel == NULL)
+               return (ISC_R_NOMEMORY);
+
+       channel->name = isc_mem_strdup(mctx, name);
+       if (channel->name == NULL) {
+               isc_mem_put(mctx, channel, sizeof(*channel));
+               return (ISC_R_NOMEMORY);
+       }
+
+       channel->type = type;
+       channel->level = level;
+       channel->flags = flags;
+       ISC_LINK_INIT(channel, link);
+
+       switch (type) {
+       case ISC_LOG_TOSYSLOG:
+               FACILITY(channel) = destination->facility;
+               break;
+
+       case ISC_LOG_TOFILE:
+               /*
+                * The file name is copied because greatest_version wants
+                * to scribble on it, so it needs to be definitely in
+                * writable memory.
+                */
+               FILE_NAME(channel) =
+                       isc_mem_strdup(mctx, destination->file.name);
+               FILE_STREAM(channel) = NULL;
+               FILE_VERSIONS(channel) = destination->file.versions;
+               FILE_MAXSIZE(channel) = destination->file.maximum_size;
+               FILE_MAXREACHED(channel) = ISC_FALSE;
+               break;
+
+       case ISC_LOG_TOFILEDESC:
+               FILE_NAME(channel) = NULL;
+               FILE_STREAM(channel) = destination->file.stream;
+               FILE_MAXSIZE(channel) = 0;
+               FILE_VERSIONS(channel) = ISC_LOG_ROLLNEVER;
+               break;
+
+       case ISC_LOG_TONULL:
+               /* Nothing. */
+               break;
+
+       default:
+               isc_mem_put(mctx, channel->name, strlen(channel->name) + 1);
+               isc_mem_put(mctx, channel, sizeof(*channel));
+               return (ISC_R_UNEXPECTED);
+       }
+
+       ISC_LIST_PREPEND(lcfg->channels, channel, link);
+
+       /*
+        * If default_stderr was redefined, make the default category
+        * point to the new default_stderr.
+        */
+       if (strcmp(name, "default_stderr") == 0)
+               default_channel.channel = channel;
+
+       return (ISC_R_SUCCESS);
+}
+
+isc_result_t
+isc_log_usechannel(isc_logconfig_t *lcfg, const char *name,
+                  const isc_logcategory_t *category,
+                  const isc_logmodule_t *module)
+{
+       isc_log_t *lctx;
+       isc_logchannel_t *channel;
+       isc_result_t result = ISC_R_SUCCESS;
+       unsigned int i;
+
+       REQUIRE(VALID_CONFIG(lcfg));
+       REQUIRE(name != NULL);
+
+       lctx = lcfg->lctx;
+
+       REQUIRE(category == NULL || category->id < lctx->category_count);
+       REQUIRE(module == NULL || module->id < lctx->module_count);
+
+       for (channel = ISC_LIST_HEAD(lcfg->channels); channel != NULL;
+            channel = ISC_LIST_NEXT(channel, link))
+               if (strcmp(name, channel->name) == 0)
+                       break;
+
+       if (channel == NULL)
+               return (ISC_R_NOTFOUND);
+
+       if (category != NULL)
+               result = assignchannel(lcfg, category->id, module, channel);
+
+       else
+               /*
+                * Assign to all categories.  Note that this includes
+                * the default channel.
+                */
+               for (i = 0; i < lctx->category_count; i++) {
+                       result = assignchannel(lcfg, i, module, channel);
+                       if (result != ISC_R_SUCCESS)
+                               break;
+               }
+
+       return (result);
+}
+
+void
+isc_log_write(isc_log_t *lctx, isc_logcategory_t *category,
+             isc_logmodule_t *module, int level, const char *format, ...)
+{
+       va_list args;
+
+       /*
+        * Contract checking is done in isc_log_doit().
+        */
+
+       va_start(args, format);
+       isc_log_doit(lctx, category, module, level, ISC_FALSE,
+                    NULL, 0, 0, format, args);
+       va_end(args);
+}
+
+void
+isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category,
+              isc_logmodule_t *module, int level,
+              const char *format, va_list args)
+{
+       /*
+        * Contract checking is done in isc_log_doit().
+        */
+       isc_log_doit(lctx, category, module, level, ISC_FALSE,
+                    NULL, 0, 0, format, args);
+}
+
+void
+isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category,
+              isc_logmodule_t *module, int level, const char *format, ...)
+{
+       va_list args;
+
+       /*
+        * Contract checking is done in isc_log_doit().
+        */
+
+       va_start(args, format);
+       isc_log_doit(lctx, category, module, level, ISC_TRUE,
+                    NULL, 0, 0, format, args);
+       va_end(args);
+}
+
+void
+isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category,
+               isc_logmodule_t *module, int level,
+               const char *format, va_list args)
+{
+       /*
+        * Contract checking is done in isc_log_doit().
+        */
+       isc_log_doit(lctx, category, module, level, ISC_TRUE,
+                    NULL, 0, 0, format, args);
+}
+
+void
+isc_log_iwrite(isc_log_t *lctx, isc_logcategory_t *category,
+              isc_logmodule_t *module, int level,
+              isc_msgcat_t *msgcat, int msgset, int msg,
+              const char *format, ...)
+{
+       va_list args;
+
+       /*
+        * Contract checking is done in isc_log_doit().
+        */
+
+       va_start(args, format);
+       isc_log_doit(lctx, category, module, level, ISC_FALSE,
+                    msgcat, msgset, msg, format, args);
+       va_end(args);
+}
+
+void
+isc_log_ivwrite(isc_log_t *lctx, isc_logcategory_t *category,
+              isc_logmodule_t *module, int level,
+              isc_msgcat_t *msgcat, int msgset, int msg,
+              const char *format, va_list args)
+{
+       /*
+        * Contract checking is done in isc_log_doit().
+        */
+       isc_log_doit(lctx, category, module, level, ISC_FALSE,
+                    msgcat, msgset, msg, format, args);
+}
+
+void
+isc_log_iwrite1(isc_log_t *lctx, isc_logcategory_t *category,
+               isc_logmodule_t *module, int level,
+               isc_msgcat_t *msgcat, int msgset, int msg,
+               const char *format, ...)
+{
+       va_list args;
+
+       /*
+        * Contract checking is done in isc_log_doit().
+        */
+
+       va_start(args, format);
+       isc_log_doit(lctx, category, module, level, ISC_TRUE,
+                    msgcat, msgset, msg, format, args);
+       va_end(args);
+}
+
+void
+isc_log_ivwrite1(isc_log_t *lctx, isc_logcategory_t *category,
+                isc_logmodule_t *module, int level,
+                isc_msgcat_t *msgcat, int msgset, int msg,
+                const char *format, va_list args)
+{
+       /*
+        * Contract checking is done in isc_log_doit().
+        */
+       isc_log_doit(lctx, category, module, level, ISC_TRUE,
+                    msgcat, msgset, msg, format, args);
+}
+
+void
+isc_log_setcontext(isc_log_t *lctx) {
+       isc_lctx = lctx;
+}
+
+void
+isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level) {
+       isc_logchannel_t *channel;
+
+       REQUIRE(VALID_CONTEXT(lctx));
+
+       LOCK(&lctx->lock);
+
+       lctx->debug_level = level;
+       /*
+        * Close ISC_LOG_DEBUGONLY channels if level is zero.
+        */
+       if (lctx->debug_level == 0)
+               for (channel = ISC_LIST_HEAD(lctx->logconfig->channels);
+                    channel != NULL;
+                    channel = ISC_LIST_NEXT(channel, link))
+                       if (channel->type == ISC_LOG_TOFILE &&
+                           (channel->flags & ISC_LOG_DEBUGONLY) != 0 &&
+                           FILE_STREAM(channel) != NULL) {
+                               (void)fclose(FILE_STREAM(channel));
+                               FILE_STREAM(channel) = NULL;
+                       }
+       UNLOCK(&lctx->lock);
+}
+
+unsigned int
+isc_log_getdebuglevel(isc_log_t *lctx) {
+       REQUIRE(VALID_CONTEXT(lctx));
+
+       return (lctx->debug_level);
+}
+
+void
+isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval) {
+       REQUIRE(VALID_CONFIG(lcfg));
+
+       lcfg->duplicate_interval = interval;
+}
+
+unsigned int
+isc_log_getduplicateinterval(isc_logconfig_t *lcfg) {
+       REQUIRE(VALID_CONTEXT(lcfg));
+
+       return (lcfg->duplicate_interval);
+}
+
+isc_result_t
+isc_log_settag(isc_logconfig_t *lcfg, const char *tag) {
+       REQUIRE(VALID_CONFIG(lcfg));
+
+       if (tag != NULL && *tag != '\0') {
+               if (lcfg->tag != NULL)
+                       isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
+               lcfg->tag = isc_mem_strdup(lcfg->lctx->mctx, tag);
+               if (lcfg->tag == NULL)
+                       return (ISC_R_NOMEMORY);
+
+       } else {
+               if (lcfg->tag != NULL)
+                       isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
+               lcfg->tag = NULL;
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+char *
+isc_log_gettag(isc_logconfig_t *lcfg) {
+       REQUIRE(VALID_CONFIG(lcfg));
+
+       return (lcfg->tag);
+}
+
+/* XXXDCL NT  -- This interface will assuredly be changing. */
+void
+isc_log_opensyslog(const char *tag, int options, int facility) {
+       (void)openlog(tag, options, facility);
+}
+
+void
+isc_log_closefilelogs(isc_log_t *lctx) {
+       isc_logchannel_t *channel;
+
+       REQUIRE(VALID_CONTEXT(lctx));
+
+       LOCK(&lctx->lock);
+       for (channel = ISC_LIST_HEAD(lctx->logconfig->channels);
+            channel != NULL;
+            channel = ISC_LIST_NEXT(channel, link))
+
+               if (channel->type == ISC_LOG_TOFILE &&
+                   FILE_STREAM(channel) != NULL) {
+                       (void)fclose(FILE_STREAM(channel));
+                       FILE_STREAM(channel) = NULL;
+               }
+       UNLOCK(&lctx->lock);
+}
+
+/****
+ **** Internal functions
+ ****/
+
+static isc_result_t
+assignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
+             const isc_logmodule_t *module, isc_logchannel_t *channel)
+{
+       isc_logchannellist_t *new_item;
+       isc_log_t *lctx;
+       isc_result_t result;
+
+       REQUIRE(VALID_CONFIG(lcfg));
+
+       lctx = lcfg->lctx;
+
+       REQUIRE(category_id < lctx->category_count);
+       REQUIRE(module == NULL || module->id < lctx->module_count);
+       REQUIRE(channel != NULL);
+
+       /*
+        * Ensure lcfg->channellist_count == lctx->category_count.
+        */
+       result = sync_channellist(lcfg);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       new_item = isc_mem_get(lctx->mctx, sizeof(*new_item));
+       if (new_item == NULL)
+               return (ISC_R_NOMEMORY);
+
+       new_item->channel = channel;
+       new_item->module = module;
+       ISC_LIST_INITANDPREPEND(lcfg->channellists[category_id],
+                              new_item, link);
+
+       /*
+        * Remember the highest logging level set by any channel in the
+        * logging config, so isc_log_doit() can quickly return if the
+        * message is too high to be logged by any channel.
+        */
+       if (channel->type != ISC_LOG_TONULL) {
+               if (lcfg->highest_level < channel->level)
+                       lcfg->highest_level = channel->level;
+               if (channel->level == ISC_LOG_DYNAMIC)
+                       lcfg->dynamic = ISC_TRUE;
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+/*
+ * This would ideally be part of isc_log_registercategories(), except then
+ * that function would have to return isc_result_t instead of void.
+ */
+static isc_result_t
+sync_channellist(isc_logconfig_t *lcfg) {
+       unsigned int bytes;
+       isc_log_t *lctx;
+       void *lists;
+
+       REQUIRE(VALID_CONFIG(lcfg));
+
+       lctx = lcfg->lctx;
+
+       REQUIRE(lctx->category_count != 0);
+
+       if (lctx->category_count == lcfg->channellist_count)
+               return (ISC_R_SUCCESS);
+
+       bytes = lctx->category_count * sizeof(ISC_LIST(isc_logchannellist_t));
+
+       lists = isc_mem_get(lctx->mctx, bytes);
+
+       if (lists == NULL)
+               return (ISC_R_NOMEMORY);
+
+       memset(lists, 0, bytes);
+
+       if (lcfg->channellist_count != 0) {
+               bytes = lcfg->channellist_count *
+                       sizeof(ISC_LIST(isc_logchannellist_t));
+               memcpy(lists, lcfg->channellists, bytes);
+               isc_mem_put(lctx->mctx, lcfg->channellists, bytes);
+       }
+
+       lcfg->channellists = lists;
+       lcfg->channellist_count = lctx->category_count;
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+greatest_version(isc_logchannel_t *channel, int *greatestp) {
+       /* XXXDCL HIGHLY NT */
+       char *basename, *digit_end;
+       const char *dirname;
+       int version, greatest = -1;
+       unsigned int basenamelen;
+       isc_dir_t dir;
+       isc_result_t result;
+       char sep = '/';
+#ifdef _WIN32
+       char *basename2;
+#endif
+
+       REQUIRE(channel->type == ISC_LOG_TOFILE);
+
+       /*
+        * It is safe to DE_CONST the file.name because it was copied
+        * with isc_mem_strdup in isc_log_createchannel.
+        */
+       basename = strrchr(FILE_NAME(channel), sep);
+#ifdef _WIN32
+       basename2 = strrchr(FILE_NAME(channel), '\\');
+       if ((basename != NULL && basename2 != NULL && basename2 > basename) ||
+           (basename == NULL && basename2 != NULL)) {
+               basename = basename2;
+               sep = '\\';
+       }
+#endif
+       if (basename != NULL) {
+               *basename++ = '\0';
+               dirname = FILE_NAME(channel);
+       } else {
+               DE_CONST(FILE_NAME(channel), basename);
+               dirname = ".";
+       }
+       basenamelen = strlen(basename);
+
+       isc_dir_init(&dir);
+       result = isc_dir_open(&dir, dirname);
+
+       /*
+        * Replace the file separator if it was taken out.
+        */
+       if (basename != FILE_NAME(channel))
+               *(basename - 1) = sep;
+
+       /*
+        * Return if the directory open failed.
+        */
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
+               if (dir.entry.length > basenamelen &&
+                   strncmp(dir.entry.name, basename, basenamelen) == 0 &&
+                   dir.entry.name[basenamelen] == '.') {
+
+                       version = strtol(&dir.entry.name[basenamelen + 1],
+                                        &digit_end, 10);
+                       if (*digit_end == '\0' && version > greatest)
+                               greatest = version;
+               }
+       }
+       isc_dir_close(&dir);
+
+       *greatestp = ++greatest;
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+roll_log(isc_logchannel_t *channel) {
+       int i, n, greatest;
+       char current[PATH_MAX + 1];
+       char new[PATH_MAX + 1];
+       const char *path;
+       isc_result_t result;
+
+       /*
+        * Do nothing (not even excess version trimming) if ISC_LOG_ROLLNEVER
+        * is specified.  Apparently complete external control over the log
+        * files is desired.
+        */
+       if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER)
+               return (ISC_R_SUCCESS);
+
+       path = FILE_NAME(channel);
+
+       /*
+        * Set greatest_version to the greatest existing version
+        * (not the maximum requested version).  This is 1 based even
+        * though the file names are 0 based, so an oldest log of log.1
+        * is a greatest_version of 2.
+        */
+       result = greatest_version(channel, &greatest);
+       if (result != ISC_R_SUCCESS)
+               return (result);
+
+       /*
+        * Now greatest should be set to the highest version number desired.
+        * Since the highest number is one less than FILE_VERSIONS(channel)
+        * when not doing infinite log rolling, greatest will need to be
+        * decremented when it is equal to -- or greater than --
+        * FILE_VERSIONS(channel).  When greatest is less than
+        * FILE_VERSIONS(channel), it is already suitable for use as
+        * the maximum version number.
+        */
+
+       if (FILE_VERSIONS(channel) == ISC_LOG_ROLLINFINITE ||
+           FILE_VERSIONS(channel) > greatest)
+               ;               /* Do nothing. */
+       else
+               /*
+                * When greatest is >= FILE_VERSIONS(channel), it needs to
+                * be reduced until it is FILE_VERSIONS(channel) - 1.
+                * Remove any excess logs on the way to that value.
+                */
+               while (--greatest >= FILE_VERSIONS(channel)) {
+                       n = snprintf(current, sizeof(current), "%s.%d",
+                                    path, greatest);
+                       if (n >= (int)sizeof(current) || n < 0)
+                               result = ISC_R_NOSPACE;
+                       else
+                               result = isc_file_remove(current);
+                       if (result != ISC_R_SUCCESS &&
+                           result != ISC_R_FILENOTFOUND)
+                               syslog(LOG_ERR,
+                                      "unable to remove log file '%s.%d': %s",
+                                      path, greatest,
+                                      isc_result_totext(result));
+               }
+
+       for (i = greatest; i > 0; i--) {
+               result = ISC_R_SUCCESS;
+               n = snprintf(current, sizeof(current), "%s.%d", path, i - 1);
+               if (n >= (int)sizeof(current) || n < 0)
+                       result = ISC_R_NOSPACE;
+               if (result == ISC_R_SUCCESS) {
+                       n = snprintf(new, sizeof(new), "%s.%d", path, i);
+                       if (n >= (int)sizeof(new) || n < 0)
+                               result = ISC_R_NOSPACE;
+               }
+               if (result == ISC_R_SUCCESS)
+                       result = isc_file_rename(current, new);
+               if (result != ISC_R_SUCCESS &&
+                   result != ISC_R_FILENOTFOUND)
+                       syslog(LOG_ERR,
+                              "unable to rename log file '%s.%d' to "
+                              "'%s.%d': %s", path, i - 1, path, i,
+                              isc_result_totext(result));
+       }
+
+       if (FILE_VERSIONS(channel) != 0) {
+               n = snprintf(new, sizeof(new), "%s.0", path);
+               if (n >= (int)sizeof(new) || n < 0)
+                       result = ISC_R_NOSPACE;
+               else
+                       result = isc_file_rename(path, new);
+               if (result != ISC_R_SUCCESS &&
+                   result != ISC_R_FILENOTFOUND)
+                       syslog(LOG_ERR,
+                              "unable to rename log file '%s' to '%s.0': %s",
+                              path, path, isc_result_totext(result));
+       } else {
+               result = isc_file_remove(path);
+               if (result != ISC_R_SUCCESS &&
+                   result != ISC_R_FILENOTFOUND)
+                       syslog(LOG_ERR, "unable to remove log file '%s': %s",
+                              path, isc_result_totext(result));
+       }
+
+       return (ISC_R_SUCCESS);
+}
+
+static isc_result_t
+isc_log_open(isc_logchannel_t *channel) {
+       struct stat statbuf;
+       isc_boolean_t regular_file;
+       isc_boolean_t roll = ISC_FALSE;
+       isc_result_t result = ISC_R_SUCCESS;
+       const char *path;
+
+       REQUIRE(channel->type == ISC_LOG_TOFILE);
+       REQUIRE(FILE_STREAM(channel) == NULL);
+
+       path = FILE_NAME(channel);
+
+       REQUIRE(path != NULL && *path != '\0');
+
+       /*
+        * Determine type of file; only regular files will be
+        * version renamed, and only if the base file exists
+        * and either has no size limit or has reached its size limit.
+        */
+       if (stat(path, &statbuf) == 0) {
+               regular_file = S_ISREG(statbuf.st_mode) ? ISC_TRUE : ISC_FALSE;
+               /* XXXDCL if not regular_file complain? */
+               if ((FILE_MAXSIZE(channel) == 0 &&
+                    FILE_VERSIONS(channel) != ISC_LOG_ROLLNEVER) ||
+                   (FILE_MAXSIZE(channel) > 0 &&
+                    statbuf.st_size >= FILE_MAXSIZE(channel)))
+                       roll = regular_file;
+       } else if (errno == ENOENT)
+               regular_file = ISC_TRUE;
+       else
+               result = ISC_R_INVALIDFILE;
+
+       /*
+        * Version control.
+        */
+       if (result == ISC_R_SUCCESS && roll) {
+               if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER)
+                       return (ISC_R_MAXSIZE);
+               result = roll_log(channel);
+               if (result != ISC_R_SUCCESS) {
+                       if ((channel->flags & ISC_LOG_OPENERR) == 0) {
+                               syslog(LOG_ERR,
+                                      "isc_log_open: roll_log '%s' "
+                                      "failed: %s",
+                                      FILE_NAME(channel),
+                                      isc_result_totext(result));
+                               channel->flags |= ISC_LOG_OPENERR;
+                       }
+                       return (result);
+               }
+       }
+
+       result = isc_stdio_open(path, "a", &FILE_STREAM(channel));
+
+       return (result);
+}
+
+isc_boolean_t
+isc_log_wouldlog(isc_log_t *lctx, int level) {
+       /*
+        * Try to avoid locking the mutex for messages which can't
+        * possibly be logged to any channels -- primarily debugging
+        * messages that the debug level is not high enough to print.
+        *
+        * If the level is (mathematically) less than or equal to the
+        * highest_level, or if there is a dynamic channel and the level is
+        * less than or equal to the debug level, the main loop must be
+        * entered to see if the message should really be output.
+        *
+        * NOTE: this is UNLOCKED access to the logconfig.  However,
+        * the worst thing that can happen is that a bad decision is made
+        * about returning without logging, and that's not a big concern,
+        * because that's a risk anyway if the logconfig is being
+        * dynamically changed.
+        */
+
+       if (lctx == NULL || lctx->logconfig == NULL)
+               return (ISC_FALSE);
+
+       return (ISC_TF(level <= lctx->logconfig->highest_level ||
+                      (lctx->logconfig->dynamic &&
+                       level <= lctx->debug_level)));
+}
+
+static void
+isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category,
+            isc_logmodule_t *module, int level, isc_boolean_t write_once,
+            isc_msgcat_t *msgcat, int msgset, int msg,
+            const char *format, va_list args)
+{
+       int syslog_level;
+       char time_string[64];
+       char level_string[24];
+       const char *iformat;
+       struct stat statbuf;
+       isc_boolean_t matched = ISC_FALSE;
+       isc_boolean_t printtime, printtag;
+       isc_boolean_t printcategory, printmodule, printlevel;
+       isc_logconfig_t *lcfg;
+       isc_logchannel_t *channel;
+       isc_logchannellist_t *category_channels;
+       isc_result_t result;
+
+       REQUIRE(lctx == NULL || VALID_CONTEXT(lctx));
+       REQUIRE(category != NULL);
+       REQUIRE(module != NULL);
+       REQUIRE(level != ISC_LOG_DYNAMIC);
+       REQUIRE(format != NULL);
+
+       /*
+        * Programs can use libraries that use this logging code without
+        * wanting to do any logging, thus the log context is allowed to
+        * be non-existent.
+        */
+       if (lctx == NULL)
+               return;
+
+       REQUIRE(category->id < lctx->category_count);
+       REQUIRE(module->id < lctx->module_count);
+
+       if (! isc_log_wouldlog(lctx, level))
+               return;
+
+       if (msgcat != NULL)
+               iformat = isc_msgcat_get(msgcat, msgset, msg, format);
+       else
+               iformat = format;
+
+       time_string[0]  = '\0';
+       level_string[0] = '\0';
+
+       LOCK(&lctx->lock);
+
+       lctx->buffer[0] = '\0';
+       
+       lcfg = lctx->logconfig;
+
+       category_channels = ISC_LIST_HEAD(lcfg->channellists[category->id]);
+
+       /*
+        * XXXDCL add duplicate filtering? (To not write multiple times to
+        * the same source via various channels).
+        */
+       do {
+               /*
+                * If the channel list end was reached and a match was made,
+                * everything is finished.
+                */
+               if (category_channels == NULL && matched)
+                       break;
+
+               if (category_channels == NULL && ! matched &&
+                   category_channels != ISC_LIST_HEAD(lcfg->channellists[0]))
+                       /*
+                        * No category/module pair was explicitly configured.
+                        * Try the category named "default".
+                        */
+                       category_channels =
+                               ISC_LIST_HEAD(lcfg->channellists[0]);
+
+               if (category_channels == NULL && ! matched)
+                       /*
+                        * No matching module was explicitly configured
+                        * for the category named "default".  Use the internal
+                        * default channel.
+                        */
+                       category_channels = &default_channel;
+
+               if (category_channels->module != NULL &&
+                   category_channels->module != module) {
+                       category_channels = ISC_LIST_NEXT(category_channels,
+                                                         link);
+                       continue;
+               }
+
+               matched = ISC_TRUE;
+
+               channel = category_channels->channel;
+               category_channels = ISC_LIST_NEXT(category_channels, link);
+
+               if (((channel->flags & ISC_LOG_DEBUGONLY) != 0) &&
+                   lctx->debug_level == 0)
+                       continue;
+
+               if (channel->level == ISC_LOG_DYNAMIC) {
+                       if (lctx->debug_level < level)
+                               continue;
+               } else if (channel->level < level)
+                       continue;
+
+               if ((channel->flags & ISC_LOG_PRINTTIME) != 0 &&
+                   time_string[0] == '\0') {
+                       isc_time_t isctime;
+                       
+                       TIME_NOW(&isctime);
+                       isc_time_formattimestamp(&isctime, time_string,
+                                                sizeof(time_string));
+               }
+
+               if ((channel->flags & ISC_LOG_PRINTLEVEL) != 0 &&
+                   level_string[0] == '\0') {
+                       if (level < ISC_LOG_CRITICAL)
+                               snprintf(level_string, sizeof(level_string),
+                                        isc_msgcat_get(isc_msgcat,
+                                                       ISC_MSGSET_LOG,
+                                                       ISC_MSG_LEVEL,
+                                                       "level %d: "),
+                                        level);
+                       else if (level > ISC_LOG_DYNAMIC)
+                               snprintf(level_string, sizeof(level_string),
+                                        "%s %d: ", log_level_strings[0],
+                                        level);
+                       else
+                               snprintf(level_string, sizeof(level_string),
+                                        "%s: ", log_level_strings[-level]);
+               }
+
+               /*
+                * Only format the message once.
+                */
+               if (lctx->buffer[0] == '\0') {
+                       (void)vsnprintf(lctx->buffer, sizeof(lctx->buffer),
+                                       iformat, args);
+
+                       /*
+                        * Check for duplicates.
+                        */
+                       if (write_once) {
+                               isc_logmessage_t *message, *new;
+                               isc_time_t oldest;
+                               isc_interval_t interval;
+
+                               isc_interval_set(&interval,
+                                                lcfg->duplicate_interval, 0);
+
+                               /*
+                                * 'oldest' is the age of the oldest messages
+                                * which fall within the duplicate_interval
+                                * range.
+                                */
+                               TIME_NOW(&oldest);
+                               if (isc_time_subtract(&oldest, &interval, &oldest)
+                                   != ISC_R_SUCCESS)
+                                       /*
+                                        * Can't effectively do the checking
+                                        * without having a valid time.
+                                        */
+                                       message = NULL;
+                               else
+                                       message =ISC_LIST_HEAD(lctx->messages);
+
+                               while (message != NULL) {
+                                       if (isc_time_compare(&message->time,
+                                                            &oldest) < 0) {
+                                               /*
+                                                * This message is older
+                                                * than the duplicate_interval,
+                                                * so it should be dropped from
+                                                * the history.
+                                                *
+                                                * Setting the interval to be
+                                                * to be longer will obviously
+                                                * not cause the expired
+                                                * message to spring back into
+                                                * existence.
+                                                */
+                                               new = ISC_LIST_NEXT(message,
+                                                                   link);
+
+                                               ISC_LIST_UNLINK(lctx->messages,
+                                                               message, link);
+
+                                               isc_mem_put(lctx->mctx,
+                                                       message,
+                                                       sizeof(*message) + 1 +
+                                                       strlen(message->text));
+
+                                               message = new;
+                                               continue;
+                                       }
+
+                                       /*
+                                        * This message is in the duplicate
+                                        * filtering interval ...
+                                        */
+                                       if (strcmp(lctx->buffer, message->text)
+                                           == 0) {
+                                               /*
+                                                * ... and it is a duplicate.
+                                                * Unlock the mutex and
+                                                * get the hell out of Dodge.
+                                                */
+                                               UNLOCK(&lctx->lock);
+                                               return;
+                                       }
+
+                                       message = ISC_LIST_NEXT(message, link);
+                               }
+
+                               /*
+                                * It wasn't in the duplicate interval,
+                                * so add it to the message list.
+                                */
+                               new = isc_mem_get(lctx->mctx,
+                                                 sizeof(isc_logmessage_t) +
+                                                 strlen(lctx->buffer) + 1);
+                               if (new != NULL) {
+                                       /*
+                                        * Put the text immediately after
+                                        * the struct.  The strcpy is safe.
+                                        */
+                                       new->text = (char *)(new + 1);
+                                       strcpy(new->text, lctx->buffer);
+
+                                       TIME_NOW(&new->time);
+
+                                       ISC_LIST_APPEND(lctx->messages,
+                                                       new, link);
+                               }
+                       }
+               }
+
+               printtime     = ISC_TF((channel->flags & ISC_LOG_PRINTTIME)
+                                      != 0);
+               printtag      = ISC_TF((channel->flags & ISC_LOG_PRINTTAG)
+                                      != 0 && lcfg->tag != NULL);
+               printcategory = ISC_TF((channel->flags & ISC_LOG_PRINTCATEGORY)
+                                      != 0);
+               printmodule   = ISC_TF((channel->flags & ISC_LOG_PRINTMODULE)
+                                      != 0);
+               printlevel    = ISC_TF((channel->flags & ISC_LOG_PRINTLEVEL)
+                                      != 0);
+
+               switch (channel->type) {
+               case ISC_LOG_TOFILE:
+                       if (FILE_MAXREACHED(channel)) {
+                               /*
+                                * If the file can be rolled, OR
+                                * If the file no longer exists, OR
+                                * If the file is less than the maximum size,
+                                *    (such as if it had been renamed and
+                                *     a new one touched, or it was truncated
+                                *     in place)
+                                * ... then close it to trigger reopening.
+                                */
+                               if (FILE_VERSIONS(channel) !=
+                                   ISC_LOG_ROLLNEVER ||
+                                   (stat(FILE_NAME(channel), &statbuf) != 0 &&
+                                    errno == ENOENT) ||
+                                   statbuf.st_size < FILE_MAXSIZE(channel)) {
+                                       (void)fclose(FILE_STREAM(channel));
+                                       FILE_STREAM(channel) = NULL;
+                                       FILE_MAXREACHED(channel) = ISC_FALSE;
+                               } else
+                                       /*
+                                        * Eh, skip it.
+                                        */
+                                       break;
+                       }
+
+                       if (FILE_STREAM(channel) == NULL) {
+                               result = isc_log_open(channel);
+                               if (result != ISC_R_SUCCESS &&
+                                   result != ISC_R_MAXSIZE &&
+                                   (channel->flags & ISC_LOG_OPENERR) == 0) {
+                                       syslog(LOG_ERR,
+                                              "isc_log_open '%s' failed: %s",
+                                              FILE_NAME(channel),
+                                              isc_result_totext(result));
+                                       channel->flags |= ISC_LOG_OPENERR;
+                               }
+                               if (result != ISC_R_SUCCESS)
+                                       break;
+                               channel->flags &= ~ISC_LOG_OPENERR;
+                       }
+                       /* FALLTHROUGH */
+
+               case ISC_LOG_TOFILEDESC:
+                       fprintf(FILE_STREAM(channel), "%s%s%s%s%s%s%s%s%s%s\n",
+                               printtime     ? time_string     : "",
+                               printtime     ? " "             : "",
+                               printtag      ? lcfg->tag       : "",
+                               printtag      ? ": "            : "",
+                               printcategory ? category->name  : "",
+                               printcategory ? ": "            : "",
+                               printmodule   ? (module != NULL ? module->name
+                                                               : "no_module")
+                                                               : "",
+                               printmodule   ? ": "            : "",
+                               printlevel    ? level_string    : "",
+                               lctx->buffer);
+
+                       fflush(FILE_STREAM(channel));
+
+                       /*
+                        * If the file now exceeds its maximum size
+                        * threshold, note it so that it will not be logged
+                        * to any more.
+                        */
+                       if (FILE_MAXSIZE(channel) > 0) {
+                               INSIST(channel->type == ISC_LOG_TOFILE);
+
+                               /* XXXDCL NT fstat/fileno */
+                               /* XXXDCL complain if fstat fails? */
+                               if (fstat(fileno(FILE_STREAM(channel)),
+                                         &statbuf) >= 0 &&
+                                   statbuf.st_size > FILE_MAXSIZE(channel))
+                                       FILE_MAXREACHED(channel) = ISC_TRUE;
+                       }
+
+                       break;
+
+               case ISC_LOG_TOSYSLOG:
+                       if (level > 0)
+                               syslog_level = LOG_DEBUG;
+                       else if (level < ISC_LOG_CRITICAL)
+                               syslog_level = LOG_CRIT;
+                       else
+                               syslog_level = syslog_map[-level];
+
+                       (void)syslog(FACILITY(channel) | syslog_level,
+                              "%s%s%s%s%s%s%s%s%s%s",
+                              printtime     ? time_string      : "",
+                              printtime     ? " "              : "",
+                              printtag      ? lcfg->tag        : "",
+                              printtag      ? ": "             : "",
+                              printcategory ? category->name   : "",
+                              printcategory ? ": "             : "",
+                              printmodule   ? (module != NULL  ? module->name
+                                                               : "no_module")
+                                                               : "",
+                              printmodule   ? ": "             : "",
+                              printlevel    ? level_string     : "",
+                              lctx->buffer);
+                       break;
+
+               case ISC_LOG_TONULL:
+                       break;
+
+               }
+
+       } while (1);
+
+       UNLOCK(&lctx->lock);
+}