"Fossies" - the Fresh Open Source Software Archive

Member "bind-9.17.5/lib/isc/log.c" (4 Sep 2020, 48380 Bytes) of package /linux/misc/dns/bind9/9.17.5/bind-9.17.5.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "log.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 9.17.3_vs_9.17.4.

    1 /*
    2  * Copyright (C) Internet Systems Consortium, Inc. ("ISC")
    3  *
    4  * This Source Code Form is subject to the terms of the Mozilla Public
    5  * License, v. 2.0. If a copy of the MPL was not distributed with this
    6  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
    7  *
    8  * See the COPYRIGHT file distributed with this work for additional
    9  * information regarding copyright ownership.
   10  */
   11 
   12 /*! \file */
   13 
   14 #include <errno.h>
   15 #include <inttypes.h>
   16 #include <limits.h>
   17 #include <stdbool.h>
   18 #include <stdlib.h>
   19 #include <sys/types.h> /* dev_t FreeBSD 2.1 */
   20 #include <time.h>
   21 
   22 #include <isc/atomic.h>
   23 #include <isc/dir.h>
   24 #include <isc/file.h>
   25 #include <isc/log.h>
   26 #include <isc/magic.h>
   27 #include <isc/mem.h>
   28 #include <isc/platform.h>
   29 #include <isc/print.h>
   30 #include <isc/rwlock.h>
   31 #include <isc/stat.h>
   32 #include <isc/stdio.h>
   33 #include <isc/string.h>
   34 #include <isc/thread.h>
   35 #include <isc/time.h>
   36 #include <isc/util.h>
   37 
   38 #define LCTX_MAGIC      ISC_MAGIC('L', 'c', 't', 'x')
   39 #define VALID_CONTEXT(lctx) ISC_MAGIC_VALID(lctx, LCTX_MAGIC)
   40 
   41 #define LCFG_MAGIC     ISC_MAGIC('L', 'c', 'f', 'g')
   42 #define VALID_CONFIG(lcfg) ISC_MAGIC_VALID(lcfg, LCFG_MAGIC)
   43 
   44 #define RDLOCK(lp)   RWLOCK(lp, isc_rwlocktype_read);
   45 #define WRLOCK(lp)   RWLOCK(lp, isc_rwlocktype_write);
   46 #define RDUNLOCK(lp) RWUNLOCK(lp, isc_rwlocktype_read);
   47 #define WRUNLOCK(lp) RWUNLOCK(lp, isc_rwlocktype_write);
   48 
   49 static thread_local bool forcelog = false;
   50 
   51 /*
   52  * XXXDCL make dynamic?
   53  */
   54 #define LOG_BUFFER_SIZE (8 * 1024)
   55 
   56 /*!
   57  * This is the structure that holds each named channel.  A simple linked
   58  * list chains all of the channels together, so an individual channel is
   59  * found by doing strcmp()s with the names down the list.  Their should
   60  * be no performance penalty from this as it is expected that the number
   61  * of named channels will be no more than a dozen or so, and name lookups
   62  * from the head of the list are only done when isc_log_usechannel() is
   63  * called, which should also be very infrequent.
   64  */
   65 typedef struct isc_logchannel isc_logchannel_t;
   66 
   67 struct isc_logchannel {
   68     char *name;
   69     unsigned int type;
   70     int level;
   71     unsigned int flags;
   72     isc_logdestination_t destination;
   73     ISC_LINK(isc_logchannel_t) link;
   74 };
   75 
   76 /*!
   77  * The logchannellist structure associates categories and modules with
   78  * channels.  First the appropriate channellist is found based on the
   79  * category, and then each structure in the linked list is checked for
   80  * a matching module.  It is expected that the number of channels
   81  * associated with any given category will be very short, no more than
   82  * three or four in the more unusual cases.
   83  */
   84 typedef struct isc_logchannellist isc_logchannellist_t;
   85 
   86 struct isc_logchannellist {
   87     const isc_logmodule_t *module;
   88     isc_logchannel_t *channel;
   89     ISC_LINK(isc_logchannellist_t) link;
   90 };
   91 
   92 /*!
   93  * This structure is used to remember messages for pruning via
   94  * isc_log_[v]write1().
   95  */
   96 typedef struct isc_logmessage isc_logmessage_t;
   97 
   98 struct isc_logmessage {
   99     char *text;
  100     isc_time_t time;
  101     ISC_LINK(isc_logmessage_t) link;
  102 };
  103 
  104 /*!
  105  * The isc_logconfig structure is used to store the configurable information
  106  * about where messages are actually supposed to be sent -- the information
  107  * that could changed based on some configuration file, as opposed to the
  108  * the category/module specification of isc_log_[v]write[1] that is compiled
  109  * into a program, or the debug_level which is dynamic state information.
  110  */
  111 struct isc_logconfig {
  112     unsigned int magic;
  113     isc_log_t *lctx;
  114     ISC_LIST(isc_logchannel_t) channels;
  115     ISC_LIST(isc_logchannellist_t) * channellists;
  116     unsigned int channellist_count;
  117     unsigned int duplicate_interval;
  118     int_fast32_t highest_level;
  119     char *tag;
  120     bool dynamic;
  121 };
  122 
  123 /*!
  124  * This isc_log structure provides the context for the isc_log functions.
  125  * The log context locks itself in isc_log_doit, the internal backend to
  126  * isc_log_write.  The locking is necessary both to provide exclusive access
  127  * to the buffer into which the message is formatted and to guard against
  128  * competing threads trying to write to the same syslog resource.  (On
  129  * some systems, such as BSD/OS, stdio is thread safe but syslog is not.)
  130  * Unfortunately, the lock cannot guard against a _different_ logging
  131  * context in the same program competing for syslog's attention.  Thus
  132  * There Can Be Only One, but this is not enforced.
  133  * XXXDCL enforce it?
  134  *
  135  * Note that the category and module information is not locked.
  136  * This is because in the usual case, only one isc_log_t is ever created
  137  * in a program, and the category/module registration happens only once.
  138  * XXXDCL it might be wise to add more locking overall.
  139  */
  140 struct isc_log {
  141     /* Not locked. */
  142     unsigned int magic;
  143     isc_mem_t *mctx;
  144     isc_logcategory_t *categories;
  145     unsigned int category_count;
  146     isc_logmodule_t *modules;
  147     unsigned int module_count;
  148     atomic_int_fast32_t debug_level;
  149     isc_rwlock_t lcfg_rwl;
  150     /* Locked by isc_log lcfg_rwl */
  151     isc_logconfig_t *logconfig;
  152     isc_mutex_t lock;
  153     /* Locked by isc_log lock. */
  154     char buffer[LOG_BUFFER_SIZE];
  155     ISC_LIST(isc_logmessage_t) messages;
  156     atomic_bool dynamic;
  157     atomic_int_fast32_t highest_level;
  158 };
  159 
  160 /*!
  161  * Used when ISC_LOG_PRINTLEVEL is enabled for a channel.
  162  */
  163 static const char *log_level_strings[] = { "debug",   "info",  "notice",
  164                        "warning", "error", "critical" };
  165 
  166 /*!
  167  * Used to convert ISC_LOG_* priorities into syslog priorities.
  168  * XXXDCL This will need modification for NT.
  169  */
  170 static const int syslog_map[] = { LOG_DEBUG,   LOG_INFO, LOG_NOTICE,
  171                   LOG_WARNING, LOG_ERR,  LOG_CRIT };
  172 
  173 /*!
  174  * When adding new categories, a corresponding ISC_LOGCATEGORY_foo
  175  * definition needs to be added to <isc/log.h>.
  176  *
  177  * The default category is provided so that the internal default can
  178  * be overridden.  Since the default is always looked up as the first
  179  * channellist in the log context, it must come first in isc_categories[].
  180  */
  181 LIBISC_EXTERNAL_DATA isc_logcategory_t isc_categories[] = { { "default",
  182                                   0 }, /* "default
  183                                       must come
  184                                       first. */
  185                                 { "general", 0 },
  186                                 { NULL, 0 } };
  187 
  188 /*!
  189  * See above comment for categories on LIBISC_EXTERNAL_DATA, and apply it to
  190  * modules.
  191  */
  192 LIBISC_EXTERNAL_DATA isc_logmodule_t isc_modules[] = {
  193     { "socket", 0 }, { "time", 0 },   { "interface", 0 }, { "timer", 0 },
  194     { "file", 0 },   { "netmgr", 0 }, { "other", 0 },     { NULL, 0 }
  195 };
  196 
  197 /*!
  198  * This essentially constant structure must be filled in at run time,
  199  * because its channel member is pointed to a channel that is created
  200  * dynamically with isc_log_createchannel.
  201  */
  202 static isc_logchannellist_t default_channel;
  203 
  204 /*!
  205  * libisc logs to this context.
  206  */
  207 LIBISC_EXTERNAL_DATA isc_log_t *isc_lctx = NULL;
  208 
  209 /*!
  210  * Forward declarations.
  211  */
  212 static void
  213 assignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
  214           const isc_logmodule_t *module, isc_logchannel_t *channel);
  215 
  216 static void
  217 sync_channellist(isc_logconfig_t *lcfg);
  218 
  219 static void
  220 sync_highest_level(isc_log_t *lctx, isc_logconfig_t *lcfg);
  221 
  222 static isc_result_t
  223 greatest_version(isc_logfile_t *file, int versions, int *greatest);
  224 
  225 static void
  226 isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category,
  227          isc_logmodule_t *module, int level, bool write_once,
  228          const char *format, va_list args) ISC_FORMAT_PRINTF(6, 0);
  229 
  230 /*@{*/
  231 /*!
  232  * Convenience macros.
  233  */
  234 
  235 #define FACILITY(channel)    (channel->destination.facility)
  236 #define FILE_NAME(channel)   (channel->destination.file.name)
  237 #define FILE_STREAM(channel)     (channel->destination.file.stream)
  238 #define FILE_VERSIONS(channel)   (channel->destination.file.versions)
  239 #define FILE_SUFFIX(channel)     (channel->destination.file.suffix)
  240 #define FILE_MAXSIZE(channel)    (channel->destination.file.maximum_size)
  241 #define FILE_MAXREACHED(channel) (channel->destination.file.maximum_reached)
  242 
  243 /*@}*/
  244 /****
  245 **** Public interfaces.
  246 ****/
  247 
  248 /*
  249  * Establish a new logging context, with default channels.
  250  */
  251 void
  252 isc_log_create(isc_mem_t *mctx, isc_log_t **lctxp, isc_logconfig_t **lcfgp) {
  253     isc_log_t *lctx;
  254     isc_logconfig_t *lcfg = NULL;
  255 
  256     REQUIRE(mctx != NULL);
  257     REQUIRE(lctxp != NULL && *lctxp == NULL);
  258     REQUIRE(lcfgp == NULL || *lcfgp == NULL);
  259 
  260     lctx = isc_mem_get(mctx, sizeof(*lctx));
  261     lctx->mctx = NULL;
  262     isc_mem_attach(mctx, &lctx->mctx);
  263     lctx->categories = NULL;
  264     lctx->category_count = 0;
  265     lctx->modules = NULL;
  266     lctx->module_count = 0;
  267     atomic_init(&lctx->debug_level, 0);
  268 
  269     ISC_LIST_INIT(lctx->messages);
  270 
  271     isc_mutex_init(&lctx->lock);
  272     isc_rwlock_init(&lctx->lcfg_rwl, 0, 0);
  273 
  274     /*
  275      * Normally setting the magic number is the last step done
  276      * in a creation function, but a valid log context is needed
  277      * by isc_log_registercategories and isc_logconfig_create.
  278      * If either fails, the lctx is destroyed and not returned
  279      * to the caller.
  280      */
  281     lctx->magic = LCTX_MAGIC;
  282 
  283     isc_log_registercategories(lctx, isc_categories);
  284     isc_log_registermodules(lctx, isc_modules);
  285     isc_logconfig_create(lctx, &lcfg);
  286 
  287     sync_channellist(lcfg);
  288 
  289     lctx->logconfig = lcfg;
  290 
  291     atomic_init(&lctx->highest_level, lcfg->highest_level);
  292     atomic_init(&lctx->dynamic, lcfg->dynamic);
  293 
  294     *lctxp = lctx;
  295     if (lcfgp != NULL) {
  296         *lcfgp = lcfg;
  297     }
  298 }
  299 
  300 void
  301 isc_logconfig_create(isc_log_t *lctx, isc_logconfig_t **lcfgp) {
  302     isc_logconfig_t *lcfg;
  303     isc_logdestination_t destination;
  304     int level = ISC_LOG_INFO;
  305 
  306     REQUIRE(lcfgp != NULL && *lcfgp == NULL);
  307     REQUIRE(VALID_CONTEXT(lctx));
  308 
  309     lcfg = isc_mem_get(lctx->mctx, sizeof(*lcfg));
  310 
  311     lcfg->lctx = lctx;
  312     lcfg->channellists = NULL;
  313     lcfg->channellist_count = 0;
  314     lcfg->duplicate_interval = 0;
  315     lcfg->highest_level = level;
  316     lcfg->tag = NULL;
  317     lcfg->dynamic = false;
  318     ISC_LIST_INIT(lcfg->channels);
  319     lcfg->magic = LCFG_MAGIC;
  320 
  321     /*
  322      * Create the default channels:
  323      *      default_syslog, default_stderr, default_debug and null.
  324      */
  325     destination.facility = LOG_DAEMON;
  326     isc_log_createchannel(lcfg, "default_syslog", ISC_LOG_TOSYSLOG, level,
  327                   &destination, 0);
  328 
  329     destination.file.stream = stderr;
  330     destination.file.name = NULL;
  331     destination.file.versions = ISC_LOG_ROLLNEVER;
  332     destination.file.suffix = isc_log_rollsuffix_increment;
  333     destination.file.maximum_size = 0;
  334     isc_log_createchannel(lcfg, "default_stderr", ISC_LOG_TOFILEDESC, level,
  335                   &destination, ISC_LOG_PRINTTIME);
  336 
  337     /*
  338      * Set the default category's channel to default_stderr,
  339      * which is at the head of the channels list because it was
  340      * just created.
  341      */
  342     default_channel.channel = ISC_LIST_HEAD(lcfg->channels);
  343 
  344     destination.file.stream = stderr;
  345     destination.file.name = NULL;
  346     destination.file.versions = ISC_LOG_ROLLNEVER;
  347     destination.file.suffix = isc_log_rollsuffix_increment;
  348     destination.file.maximum_size = 0;
  349     isc_log_createchannel(lcfg, "default_debug", ISC_LOG_TOFILEDESC,
  350                   ISC_LOG_DYNAMIC, &destination, ISC_LOG_PRINTTIME);
  351 
  352     isc_log_createchannel(lcfg, "null", ISC_LOG_TONULL, ISC_LOG_DYNAMIC,
  353                   NULL, 0);
  354 
  355     *lcfgp = lcfg;
  356 }
  357 
  358 void
  359 isc_logconfig_use(isc_log_t *lctx, isc_logconfig_t *lcfg) {
  360     isc_logconfig_t *old_cfg;
  361 
  362     REQUIRE(VALID_CONTEXT(lctx));
  363     REQUIRE(VALID_CONFIG(lcfg));
  364     REQUIRE(lcfg->lctx == lctx);
  365 
  366     /*
  367      * Ensure that lcfg->channellist_count == lctx->category_count.
  368      * They won't be equal if isc_log_usechannel has not been called
  369      * since any call to isc_log_registercategories.
  370      */
  371     sync_channellist(lcfg);
  372 
  373     WRLOCK(&lctx->lcfg_rwl);
  374     old_cfg = lctx->logconfig;
  375     lctx->logconfig = lcfg;
  376     sync_highest_level(lctx, lcfg);
  377     WRUNLOCK(&lctx->lcfg_rwl);
  378 
  379     isc_logconfig_destroy(&old_cfg);
  380 }
  381 
  382 void
  383 isc_log_destroy(isc_log_t **lctxp) {
  384     isc_log_t *lctx;
  385     isc_logconfig_t *lcfg;
  386     isc_mem_t *mctx;
  387     isc_logmessage_t *message;
  388 
  389     REQUIRE(lctxp != NULL && VALID_CONTEXT(*lctxp));
  390 
  391     lctx = *lctxp;
  392     *lctxp = NULL;
  393     mctx = lctx->mctx;
  394 
  395     /* Stop the logging as a first thing */
  396     atomic_store_release(&lctx->debug_level, 0);
  397     atomic_store_release(&lctx->highest_level, 0);
  398     atomic_store_release(&lctx->dynamic, false);
  399 
  400     WRLOCK(&lctx->lcfg_rwl);
  401     lcfg = lctx->logconfig;
  402     lctx->logconfig = NULL;
  403     WRUNLOCK(&lctx->lcfg_rwl);
  404 
  405     if (lcfg != NULL) {
  406         isc_logconfig_destroy(&lcfg);
  407     }
  408 
  409     isc_rwlock_destroy(&lctx->lcfg_rwl);
  410     isc_mutex_destroy(&lctx->lock);
  411 
  412     while ((message = ISC_LIST_HEAD(lctx->messages)) != NULL) {
  413         ISC_LIST_UNLINK(lctx->messages, message, link);
  414 
  415         isc_mem_put(mctx, message,
  416                 sizeof(*message) + strlen(message->text) + 1);
  417     }
  418 
  419     lctx->buffer[0] = '\0';
  420     lctx->categories = NULL;
  421     lctx->category_count = 0;
  422     lctx->modules = NULL;
  423     lctx->module_count = 0;
  424     lctx->mctx = NULL;
  425     lctx->magic = 0;
  426 
  427     isc_mem_putanddetach(&mctx, lctx, sizeof(*lctx));
  428 }
  429 
  430 void
  431 isc_logconfig_destroy(isc_logconfig_t **lcfgp) {
  432     isc_logconfig_t *lcfg;
  433     isc_mem_t *mctx;
  434     isc_logchannel_t *channel;
  435     char *filename;
  436     unsigned int i;
  437 
  438     REQUIRE(lcfgp != NULL && VALID_CONFIG(*lcfgp));
  439 
  440     lcfg = *lcfgp;
  441     *lcfgp = NULL;
  442 
  443     /*
  444      * This function cannot be called with a logconfig that is in
  445      * use by a log context.
  446      */
  447     REQUIRE(lcfg->lctx != NULL);
  448 
  449     RDLOCK(&lcfg->lctx->lcfg_rwl);
  450     REQUIRE(lcfg->lctx->logconfig != lcfg);
  451     RDUNLOCK(&lcfg->lctx->lcfg_rwl);
  452 
  453     mctx = lcfg->lctx->mctx;
  454 
  455     while ((channel = ISC_LIST_HEAD(lcfg->channels)) != NULL) {
  456         ISC_LIST_UNLINK(lcfg->channels, channel, link);
  457 
  458         if (channel->type == ISC_LOG_TOFILE) {
  459             /*
  460              * The filename for the channel may have ultimately
  461              * started its life in user-land as a const string,
  462              * but in isc_log_createchannel it gets copied
  463              * into writable memory and is not longer truly const.
  464              */
  465             DE_CONST(FILE_NAME(channel), filename);
  466             isc_mem_free(mctx, filename);
  467 
  468             if (FILE_STREAM(channel) != NULL) {
  469                 (void)fclose(FILE_STREAM(channel));
  470             }
  471         }
  472 
  473         isc_mem_free(mctx, channel->name);
  474         isc_mem_put(mctx, channel, sizeof(*channel));
  475     }
  476 
  477     for (i = 0; i < lcfg->channellist_count; i++) {
  478         isc_logchannellist_t *item;
  479         while ((item = ISC_LIST_HEAD(lcfg->channellists[i])) != NULL) {
  480             ISC_LIST_UNLINK(lcfg->channellists[i], item, link);
  481             isc_mem_put(mctx, item, sizeof(*item));
  482         }
  483     }
  484 
  485     if (lcfg->channellist_count > 0) {
  486         isc_mem_put(mctx, lcfg->channellists,
  487                 lcfg->channellist_count *
  488                     sizeof(ISC_LIST(isc_logchannellist_t)));
  489     }
  490 
  491     lcfg->dynamic = false;
  492     if (lcfg->tag != NULL) {
  493         isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
  494     }
  495     lcfg->tag = NULL;
  496     lcfg->highest_level = 0;
  497     lcfg->duplicate_interval = 0;
  498     lcfg->magic = 0;
  499 
  500     isc_mem_put(mctx, lcfg, sizeof(*lcfg));
  501 }
  502 
  503 void
  504 isc_log_registercategories(isc_log_t *lctx, isc_logcategory_t categories[]) {
  505     isc_logcategory_t *catp;
  506 
  507     REQUIRE(VALID_CONTEXT(lctx));
  508     REQUIRE(categories != NULL && categories[0].name != NULL);
  509 
  510     /*
  511      * XXXDCL This somewhat sleazy situation of using the last pointer
  512      * in one category array to point to the next array exists because
  513      * this registration function returns void and I didn't want to have
  514      * change everything that used it by making it return an isc_result_t.
  515      * It would need to do that if it had to allocate memory to store
  516      * pointers to each array passed in.
  517      */
  518     if (lctx->categories == NULL) {
  519         lctx->categories = categories;
  520     } else {
  521         /*
  522          * Adjust the last (NULL) pointer of the already registered
  523          * categories to point to the incoming array.
  524          */
  525         for (catp = lctx->categories; catp->name != NULL;) {
  526             if (catp->id == UINT_MAX) {
  527                 /*
  528                  * The name pointer points to the next array.
  529                  * Ick.
  530                  */
  531                 DE_CONST(catp->name, catp);
  532             } else {
  533                 catp++;
  534             }
  535         }
  536 
  537         catp->name = (void *)categories;
  538         catp->id = UINT_MAX;
  539     }
  540 
  541     /*
  542      * Update the id number of the category with its new global id.
  543      */
  544     for (catp = categories; catp->name != NULL; catp++) {
  545         catp->id = lctx->category_count++;
  546     }
  547 }
  548 
  549 isc_logcategory_t *
  550 isc_log_categorybyname(isc_log_t *lctx, const char *name) {
  551     isc_logcategory_t *catp;
  552 
  553     REQUIRE(VALID_CONTEXT(lctx));
  554     REQUIRE(name != NULL);
  555 
  556     for (catp = lctx->categories; catp->name != NULL;) {
  557         if (catp->id == UINT_MAX) {
  558             /*
  559              * catp is neither modified nor returned to the
  560              * caller, so removing its const qualifier is ok.
  561              */
  562             DE_CONST(catp->name, catp);
  563         } else {
  564             if (strcmp(catp->name, name) == 0) {
  565                 return (catp);
  566             }
  567             catp++;
  568         }
  569     }
  570 
  571     return (NULL);
  572 }
  573 
  574 void
  575 isc_log_registermodules(isc_log_t *lctx, isc_logmodule_t modules[]) {
  576     isc_logmodule_t *modp;
  577 
  578     REQUIRE(VALID_CONTEXT(lctx));
  579     REQUIRE(modules != NULL && modules[0].name != NULL);
  580 
  581     /*
  582      * XXXDCL This somewhat sleazy situation of using the last pointer
  583      * in one category array to point to the next array exists because
  584      * this registration function returns void and I didn't want to have
  585      * change everything that used it by making it return an isc_result_t.
  586      * It would need to do that if it had to allocate memory to store
  587      * pointers to each array passed in.
  588      */
  589     if (lctx->modules == NULL) {
  590         lctx->modules = modules;
  591     } else {
  592         /*
  593          * Adjust the last (NULL) pointer of the already registered
  594          * modules to point to the incoming array.
  595          */
  596         for (modp = lctx->modules; modp->name != NULL;) {
  597             if (modp->id == UINT_MAX) {
  598                 /*
  599                  * The name pointer points to the next array.
  600                  * Ick.
  601                  */
  602                 DE_CONST(modp->name, modp);
  603             } else {
  604                 modp++;
  605             }
  606         }
  607 
  608         modp->name = (void *)modules;
  609         modp->id = UINT_MAX;
  610     }
  611 
  612     /*
  613      * Update the id number of the module with its new global id.
  614      */
  615     for (modp = modules; modp->name != NULL; modp++) {
  616         modp->id = lctx->module_count++;
  617     }
  618 }
  619 
  620 isc_logmodule_t *
  621 isc_log_modulebyname(isc_log_t *lctx, const char *name) {
  622     isc_logmodule_t *modp;
  623 
  624     REQUIRE(VALID_CONTEXT(lctx));
  625     REQUIRE(name != NULL);
  626 
  627     for (modp = lctx->modules; modp->name != NULL;) {
  628         if (modp->id == UINT_MAX) {
  629             /*
  630              * modp is neither modified nor returned to the
  631              * caller, so removing its const qualifier is ok.
  632              */
  633             DE_CONST(modp->name, modp);
  634         } else {
  635             if (strcmp(modp->name, name) == 0) {
  636                 return (modp);
  637             }
  638             modp++;
  639         }
  640     }
  641 
  642     return (NULL);
  643 }
  644 
  645 void
  646 isc_log_createchannel(isc_logconfig_t *lcfg, const char *name,
  647               unsigned int type, int level,
  648               const isc_logdestination_t *destination,
  649               unsigned int flags) {
  650     isc_logchannel_t *channel;
  651     isc_mem_t *mctx;
  652     unsigned int permitted = ISC_LOG_PRINTALL | ISC_LOG_DEBUGONLY |
  653                  ISC_LOG_BUFFERED | ISC_LOG_ISO8601 |
  654                  ISC_LOG_UTC;
  655 
  656     REQUIRE(VALID_CONFIG(lcfg));
  657     REQUIRE(name != NULL);
  658     REQUIRE(type == ISC_LOG_TOSYSLOG || type == ISC_LOG_TOFILE ||
  659         type == ISC_LOG_TOFILEDESC || type == ISC_LOG_TONULL);
  660     REQUIRE(destination != NULL || type == ISC_LOG_TONULL);
  661     REQUIRE(level >= ISC_LOG_CRITICAL);
  662     REQUIRE((flags & ~permitted) == 0);
  663 
  664     /* XXXDCL find duplicate names? */
  665 
  666     mctx = lcfg->lctx->mctx;
  667 
  668     channel = isc_mem_get(mctx, sizeof(*channel));
  669 
  670     channel->name = isc_mem_strdup(mctx, name);
  671 
  672     channel->type = type;
  673     channel->level = level;
  674     channel->flags = flags;
  675     ISC_LINK_INIT(channel, link);
  676 
  677     switch (type) {
  678     case ISC_LOG_TOSYSLOG:
  679         FACILITY(channel) = destination->facility;
  680         break;
  681 
  682     case ISC_LOG_TOFILE:
  683         /*
  684          * The file name is copied because greatest_version wants
  685          * to scribble on it, so it needs to be definitely in
  686          * writable memory.
  687          */
  688         FILE_NAME(channel) = isc_mem_strdup(mctx,
  689                             destination->file.name);
  690         FILE_STREAM(channel) = NULL;
  691         FILE_VERSIONS(channel) = destination->file.versions;
  692         FILE_SUFFIX(channel) = destination->file.suffix;
  693         FILE_MAXSIZE(channel) = destination->file.maximum_size;
  694         FILE_MAXREACHED(channel) = false;
  695         break;
  696 
  697     case ISC_LOG_TOFILEDESC:
  698         FILE_NAME(channel) = NULL;
  699         FILE_STREAM(channel) = destination->file.stream;
  700         FILE_MAXSIZE(channel) = 0;
  701         FILE_VERSIONS(channel) = ISC_LOG_ROLLNEVER;
  702         FILE_SUFFIX(channel) = isc_log_rollsuffix_increment;
  703         break;
  704 
  705     case ISC_LOG_TONULL:
  706         /* Nothing. */
  707         break;
  708 
  709     default:
  710         INSIST(0);
  711         ISC_UNREACHABLE();
  712     }
  713 
  714     ISC_LIST_PREPEND(lcfg->channels, channel, link);
  715 
  716     /*
  717      * If default_stderr was redefined, make the default category
  718      * point to the new default_stderr.
  719      */
  720     if (strcmp(name, "default_stderr") == 0) {
  721         default_channel.channel = channel;
  722     }
  723 }
  724 
  725 isc_result_t
  726 isc_log_usechannel(isc_logconfig_t *lcfg, const char *name,
  727            const isc_logcategory_t *category,
  728            const isc_logmodule_t *module) {
  729     isc_log_t *lctx;
  730     isc_logchannel_t *channel;
  731 
  732     REQUIRE(VALID_CONFIG(lcfg));
  733     REQUIRE(name != NULL);
  734 
  735     lctx = lcfg->lctx;
  736 
  737     REQUIRE(category == NULL || category->id < lctx->category_count);
  738     REQUIRE(module == NULL || module->id < lctx->module_count);
  739 
  740     for (channel = ISC_LIST_HEAD(lcfg->channels); channel != NULL;
  741          channel = ISC_LIST_NEXT(channel, link))
  742     {
  743         if (strcmp(name, channel->name) == 0) {
  744             break;
  745         }
  746     }
  747 
  748     if (channel == NULL) {
  749         return (ISC_R_NOTFOUND);
  750     }
  751 
  752     if (category != NULL) {
  753         assignchannel(lcfg, category->id, module, channel);
  754     } else {
  755         /*
  756          * Assign to all categories.  Note that this includes
  757          * the default channel.
  758          */
  759         for (size_t i = 0; i < lctx->category_count; i++) {
  760             assignchannel(lcfg, i, module, channel);
  761         }
  762     }
  763 
  764     /*
  765      * Update the highest logging level, if the current lcfg is in use.
  766      */
  767     if (lcfg->lctx->logconfig == lcfg) {
  768         sync_highest_level(lctx, lcfg);
  769     }
  770 
  771     return (ISC_R_SUCCESS);
  772 }
  773 
  774 void
  775 isc_log_write(isc_log_t *lctx, isc_logcategory_t *category,
  776           isc_logmodule_t *module, int level, const char *format, ...) {
  777     va_list args;
  778 
  779     /*
  780      * Contract checking is done in isc_log_doit().
  781      */
  782 
  783     va_start(args, format);
  784     isc_log_doit(lctx, category, module, level, false, format, args);
  785     va_end(args);
  786 }
  787 
  788 void
  789 isc_log_vwrite(isc_log_t *lctx, isc_logcategory_t *category,
  790            isc_logmodule_t *module, int level, const char *format,
  791            va_list args) {
  792     /*
  793      * Contract checking is done in isc_log_doit().
  794      */
  795     isc_log_doit(lctx, category, module, level, false, format, args);
  796 }
  797 
  798 void
  799 isc_log_write1(isc_log_t *lctx, isc_logcategory_t *category,
  800            isc_logmodule_t *module, int level, const char *format, ...) {
  801     va_list args;
  802 
  803     /*
  804      * Contract checking is done in isc_log_doit().
  805      */
  806 
  807     va_start(args, format);
  808     isc_log_doit(lctx, category, module, level, true, format, args);
  809     va_end(args);
  810 }
  811 
  812 void
  813 isc_log_vwrite1(isc_log_t *lctx, isc_logcategory_t *category,
  814         isc_logmodule_t *module, int level, const char *format,
  815         va_list args) {
  816     /*
  817      * Contract checking is done in isc_log_doit().
  818      */
  819     isc_log_doit(lctx, category, module, level, true, format, args);
  820 }
  821 
  822 void
  823 isc_log_setcontext(isc_log_t *lctx) {
  824     isc_lctx = lctx;
  825 }
  826 
  827 void
  828 isc_log_setdebuglevel(isc_log_t *lctx, unsigned int level) {
  829     REQUIRE(VALID_CONTEXT(lctx));
  830 
  831     atomic_store_release(&lctx->debug_level, level);
  832     /*
  833      * Close ISC_LOG_DEBUGONLY channels if level is zero.
  834      */
  835     if (level == 0) {
  836         RDLOCK(&lctx->lcfg_rwl);
  837         isc_logconfig_t *lcfg = lctx->logconfig;
  838         if (lcfg != NULL) {
  839             LOCK(&lctx->lock);
  840             for (isc_logchannel_t *channel =
  841                      ISC_LIST_HEAD(lcfg->channels);
  842                  channel != NULL;
  843                  channel = ISC_LIST_NEXT(channel, link))
  844             {
  845                 if (channel->type == ISC_LOG_TOFILE &&
  846                     (channel->flags & ISC_LOG_DEBUGONLY) != 0 &&
  847                     FILE_STREAM(channel) != NULL)
  848                 {
  849                     (void)fclose(FILE_STREAM(channel));
  850                     FILE_STREAM(channel) = NULL;
  851                 }
  852             }
  853             UNLOCK(&lctx->lock);
  854         }
  855         RDUNLOCK(&lctx->lcfg_rwl);
  856     }
  857 }
  858 
  859 unsigned int
  860 isc_log_getdebuglevel(isc_log_t *lctx) {
  861     REQUIRE(VALID_CONTEXT(lctx));
  862 
  863     return (atomic_load_acquire(&lctx->debug_level));
  864 }
  865 
  866 void
  867 isc_log_setduplicateinterval(isc_logconfig_t *lcfg, unsigned int interval) {
  868     REQUIRE(VALID_CONFIG(lcfg));
  869 
  870     lcfg->duplicate_interval = interval;
  871 }
  872 
  873 unsigned int
  874 isc_log_getduplicateinterval(isc_logconfig_t *lcfg) {
  875     REQUIRE(VALID_CONTEXT(lcfg));
  876 
  877     return (lcfg->duplicate_interval);
  878 }
  879 
  880 void
  881 isc_log_settag(isc_logconfig_t *lcfg, const char *tag) {
  882     REQUIRE(VALID_CONFIG(lcfg));
  883 
  884     if (tag != NULL && *tag != '\0') {
  885         if (lcfg->tag != NULL) {
  886             isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
  887         }
  888         lcfg->tag = isc_mem_strdup(lcfg->lctx->mctx, tag);
  889     } else {
  890         if (lcfg->tag != NULL) {
  891             isc_mem_free(lcfg->lctx->mctx, lcfg->tag);
  892         }
  893         lcfg->tag = NULL;
  894     }
  895 }
  896 
  897 char *
  898 isc_log_gettag(isc_logconfig_t *lcfg) {
  899     REQUIRE(VALID_CONFIG(lcfg));
  900 
  901     return (lcfg->tag);
  902 }
  903 
  904 /* XXXDCL NT  -- This interface will assuredly be changing. */
  905 void
  906 isc_log_opensyslog(const char *tag, int options, int facility) {
  907     (void)openlog(tag, options, facility);
  908 }
  909 
  910 void
  911 isc_log_closefilelogs(isc_log_t *lctx) {
  912     REQUIRE(VALID_CONTEXT(lctx));
  913 
  914     RDLOCK(&lctx->lcfg_rwl);
  915     isc_logconfig_t *lcfg = lctx->logconfig;
  916     if (lcfg != NULL) {
  917         LOCK(&lctx->lock);
  918         for (isc_logchannel_t *channel = ISC_LIST_HEAD(lcfg->channels);
  919              channel != NULL; channel = ISC_LIST_NEXT(channel, link))
  920         {
  921             if (channel->type == ISC_LOG_TOFILE &&
  922                 FILE_STREAM(channel) != NULL) {
  923                 (void)fclose(FILE_STREAM(channel));
  924                 FILE_STREAM(channel) = NULL;
  925             }
  926         }
  927         UNLOCK(&lctx->lock);
  928     }
  929     RDUNLOCK(&lctx->lcfg_rwl);
  930 }
  931 
  932 /****
  933 **** Internal functions
  934 ****/
  935 
  936 static void
  937 assignchannel(isc_logconfig_t *lcfg, unsigned int category_id,
  938           const isc_logmodule_t *module, isc_logchannel_t *channel) {
  939     isc_logchannellist_t *new_item;
  940     isc_log_t *lctx;
  941 
  942     REQUIRE(VALID_CONFIG(lcfg));
  943 
  944     lctx = lcfg->lctx;
  945 
  946     REQUIRE(category_id < lctx->category_count);
  947     REQUIRE(module == NULL || module->id < lctx->module_count);
  948     REQUIRE(channel != NULL);
  949 
  950     /*
  951      * Ensure lcfg->channellist_count == lctx->category_count.
  952      */
  953     sync_channellist(lcfg);
  954 
  955     new_item = isc_mem_get(lctx->mctx, sizeof(*new_item));
  956 
  957     new_item->channel = channel;
  958     new_item->module = module;
  959     ISC_LIST_INITANDPREPEND(lcfg->channellists[category_id], new_item,
  960                 link);
  961 
  962     /*
  963      * Remember the highest logging level set by any channel in the
  964      * logging config, so isc_log_doit() can quickly return if the
  965      * message is too high to be logged by any channel.
  966      */
  967     if (channel->type != ISC_LOG_TONULL) {
  968         if (lcfg->highest_level < channel->level) {
  969             lcfg->highest_level = channel->level;
  970         }
  971         if (channel->level == ISC_LOG_DYNAMIC) {
  972             lcfg->dynamic = true;
  973         }
  974     }
  975 }
  976 
  977 /*
  978  * This would ideally be part of isc_log_registercategories(), except then
  979  * that function would have to return isc_result_t instead of void.
  980  */
  981 static void
  982 sync_channellist(isc_logconfig_t *lcfg) {
  983     unsigned int bytes;
  984     isc_log_t *lctx;
  985     void *lists;
  986 
  987     REQUIRE(VALID_CONFIG(lcfg));
  988 
  989     lctx = lcfg->lctx;
  990 
  991     REQUIRE(lctx->category_count != 0);
  992 
  993     if (lctx->category_count == lcfg->channellist_count) {
  994         return;
  995     }
  996 
  997     bytes = lctx->category_count * sizeof(ISC_LIST(isc_logchannellist_t));
  998 
  999     lists = isc_mem_get(lctx->mctx, bytes);
 1000 
 1001     memset(lists, 0, bytes);
 1002 
 1003     if (lcfg->channellist_count != 0) {
 1004         bytes = lcfg->channellist_count *
 1005             sizeof(ISC_LIST(isc_logchannellist_t));
 1006         memmove(lists, lcfg->channellists, bytes);
 1007         isc_mem_put(lctx->mctx, lcfg->channellists, bytes);
 1008     }
 1009 
 1010     lcfg->channellists = lists;
 1011     lcfg->channellist_count = lctx->category_count;
 1012 }
 1013 
 1014 static void
 1015 sync_highest_level(isc_log_t *lctx, isc_logconfig_t *lcfg) {
 1016     atomic_store(&lctx->highest_level, lcfg->highest_level);
 1017     atomic_store(&lctx->dynamic, lcfg->dynamic);
 1018 }
 1019 
 1020 static isc_result_t
 1021 greatest_version(isc_logfile_t *file, int versions, int *greatestp) {
 1022     char *bname, *digit_end;
 1023     const char *dirname;
 1024     int version, greatest = -1;
 1025     size_t bnamelen;
 1026     isc_dir_t dir;
 1027     isc_result_t result;
 1028     char sep = '/';
 1029 #ifdef _WIN32
 1030     char *bname2;
 1031 #endif /* ifdef _WIN32 */
 1032 
 1033     /*
 1034      * It is safe to DE_CONST the file.name because it was copied
 1035      * with isc_mem_strdup().
 1036      */
 1037     bname = strrchr(file->name, sep);
 1038 #ifdef _WIN32
 1039     bname2 = strrchr(file->name, '\\');
 1040     if ((bname != NULL && bname2 != NULL && bname2 > bname) ||
 1041         (bname == NULL && bname2 != NULL))
 1042     {
 1043         bname = bname2;
 1044         sep = '\\';
 1045     }
 1046 #endif /* ifdef _WIN32 */
 1047     if (bname != NULL) {
 1048         *bname++ = '\0';
 1049         dirname = file->name;
 1050     } else {
 1051         DE_CONST(file->name, bname);
 1052         dirname = ".";
 1053     }
 1054     bnamelen = strlen(bname);
 1055 
 1056     isc_dir_init(&dir);
 1057     result = isc_dir_open(&dir, dirname);
 1058 
 1059     /*
 1060      * Replace the file separator if it was taken out.
 1061      */
 1062     if (bname != file->name) {
 1063         *(bname - 1) = sep;
 1064     }
 1065 
 1066     /*
 1067      * Return if the directory open failed.
 1068      */
 1069     if (result != ISC_R_SUCCESS) {
 1070         return (result);
 1071     }
 1072 
 1073     while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
 1074         if (dir.entry.length > bnamelen &&
 1075             strncmp(dir.entry.name, bname, bnamelen) == 0 &&
 1076             dir.entry.name[bnamelen] == '.')
 1077         {
 1078             version = strtol(&dir.entry.name[bnamelen + 1],
 1079                      &digit_end, 10);
 1080             /*
 1081              * Remove any backup files that exceed versions.
 1082              */
 1083             if (*digit_end == '\0' && version >= versions) {
 1084                 result = isc_file_remove(dir.entry.name);
 1085                 if (result != ISC_R_SUCCESS &&
 1086                     result != ISC_R_FILENOTFOUND) {
 1087                     syslog(LOG_ERR,
 1088                            "unable to remove "
 1089                            "log file '%s': %s",
 1090                            dir.entry.name,
 1091                            isc_result_totext(result));
 1092                 }
 1093             } else if (*digit_end == '\0' && version > greatest) {
 1094                 greatest = version;
 1095             }
 1096         }
 1097     }
 1098     isc_dir_close(&dir);
 1099 
 1100     *greatestp = greatest;
 1101 
 1102     return (ISC_R_SUCCESS);
 1103 }
 1104 
 1105 static void
 1106 insert_sort(int64_t to_keep[], int64_t versions, int version) {
 1107     int i = 0;
 1108     while (i < versions && version < to_keep[i]) {
 1109         i++;
 1110     }
 1111     if (i == versions) {
 1112         return;
 1113     }
 1114     if (i < versions - 1) {
 1115         memmove(&to_keep[i + 1], &to_keep[i],
 1116             sizeof(to_keep[0]) * (versions - i - 1));
 1117     }
 1118     to_keep[i] = version;
 1119 }
 1120 
 1121 static int64_t
 1122 last_to_keep(int64_t versions, isc_dir_t *dirp, char *bname, size_t bnamelen) {
 1123     if (versions <= 0) {
 1124         return INT64_MAX;
 1125     }
 1126 
 1127     int64_t to_keep[ISC_LOG_MAX_VERSIONS] = { 0 };
 1128     int64_t version = 0;
 1129     if (versions > ISC_LOG_MAX_VERSIONS) {
 1130         versions = ISC_LOG_MAX_VERSIONS;
 1131     }
 1132     /*
 1133      * First we fill 'to_keep' structure using insertion sort
 1134      */
 1135     memset(to_keep, 0, sizeof(to_keep));
 1136     while (isc_dir_read(dirp) == ISC_R_SUCCESS) {
 1137         if (dirp->entry.length <= bnamelen ||
 1138             strncmp(dirp->entry.name, bname, bnamelen) != 0 ||
 1139             dirp->entry.name[bnamelen] != '.')
 1140         {
 1141             continue;
 1142         }
 1143 
 1144         char *digit_end;
 1145         char *ename = &dirp->entry.name[bnamelen + 1];
 1146         version = strtoull(ename, &digit_end, 10);
 1147         if (*digit_end == '\0') {
 1148             insert_sort(to_keep, versions, version);
 1149         }
 1150     }
 1151 
 1152     isc_dir_reset(dirp);
 1153 
 1154     /*
 1155      * to_keep[versions - 1] is the last one we want to keep
 1156      */
 1157     return (to_keep[versions - 1]);
 1158 }
 1159 
 1160 static isc_result_t
 1161 remove_old_tsversions(isc_logfile_t *file, int versions) {
 1162     isc_result_t result;
 1163     char *bname, *digit_end;
 1164     const char *dirname;
 1165     int64_t version, last = INT64_MAX;
 1166     size_t bnamelen;
 1167     isc_dir_t dir;
 1168     char sep = '/';
 1169 #ifdef _WIN32
 1170     char *bname2;
 1171 #endif /* ifdef _WIN32 */
 1172     /*
 1173      * It is safe to DE_CONST the file.name because it was copied
 1174      * with isc_mem_strdup().
 1175      */
 1176     bname = strrchr(file->name, sep);
 1177 #ifdef _WIN32
 1178     bname2 = strrchr(file->name, '\\');
 1179     if ((bname != NULL && bname2 != NULL && bname2 > bname) ||
 1180         (bname == NULL && bname2 != NULL))
 1181     {
 1182         bname = bname2;
 1183         sep = '\\';
 1184     }
 1185 #endif /* ifdef _WIN32 */
 1186     if (bname != NULL) {
 1187         *bname++ = '\0';
 1188         dirname = file->name;
 1189     } else {
 1190         DE_CONST(file->name, bname);
 1191         dirname = ".";
 1192     }
 1193     bnamelen = strlen(bname);
 1194 
 1195     isc_dir_init(&dir);
 1196     result = isc_dir_open(&dir, dirname);
 1197 
 1198     /*
 1199      * Replace the file separator if it was taken out.
 1200      */
 1201     if (bname != file->name) {
 1202         *(bname - 1) = sep;
 1203     }
 1204 
 1205     /*
 1206      * Return if the directory open failed.
 1207      */
 1208     if (result != ISC_R_SUCCESS) {
 1209         return (result);
 1210     }
 1211 
 1212     last = last_to_keep(versions, &dir, bname, bnamelen);
 1213 
 1214     /*
 1215      * Then we remove all files that we don't want to_keep
 1216      */
 1217     while (isc_dir_read(&dir) == ISC_R_SUCCESS) {
 1218         if (dir.entry.length > bnamelen &&
 1219             strncmp(dir.entry.name, bname, bnamelen) == 0 &&
 1220             dir.entry.name[bnamelen] == '.')
 1221         {
 1222             char *ename = &dir.entry.name[bnamelen + 1];
 1223             version = strtoull(ename, &digit_end, 10);
 1224             /*
 1225              * Remove any backup files that exceed versions.
 1226              */
 1227             if (*digit_end == '\0' && version < last) {
 1228                 result = isc_file_remove(dir.entry.name);
 1229                 if (result != ISC_R_SUCCESS &&
 1230                     result != ISC_R_FILENOTFOUND) {
 1231                     syslog(LOG_ERR,
 1232                            "unable to remove "
 1233                            "log file '%s': %s",
 1234                            dir.entry.name,
 1235                            isc_result_totext(result));
 1236                 }
 1237             }
 1238         }
 1239     }
 1240 
 1241     isc_dir_close(&dir);
 1242 
 1243     return (ISC_R_SUCCESS);
 1244 }
 1245 
 1246 static isc_result_t
 1247 roll_increment(isc_logfile_t *file) {
 1248     int i, n, greatest;
 1249     char current[PATH_MAX + 1];
 1250     char newpath[PATH_MAX + 1];
 1251     const char *path;
 1252     isc_result_t result = ISC_R_SUCCESS;
 1253 
 1254     REQUIRE(file != NULL);
 1255     REQUIRE(file->versions != 0);
 1256 
 1257     path = file->name;
 1258 
 1259     if (file->versions == ISC_LOG_ROLLINFINITE) {
 1260         /*
 1261          * Find the first missing entry in the log file sequence.
 1262          */
 1263         for (greatest = 0; greatest < INT_MAX; greatest++) {
 1264             n = snprintf(current, sizeof(current), "%s.%u", path,
 1265                      (unsigned)greatest);
 1266             if (n >= (int)sizeof(current) || n < 0 ||
 1267                 !isc_file_exists(current)) {
 1268                 break;
 1269             }
 1270         }
 1271     } else {
 1272         /*
 1273          * Get the largest existing version and remove any
 1274          * version greater than the permitted version.
 1275          */
 1276         result = greatest_version(file, file->versions, &greatest);
 1277         if (result != ISC_R_SUCCESS) {
 1278             return (result);
 1279         }
 1280 
 1281         /*
 1282          * Increment if greatest is not the actual maximum value.
 1283          */
 1284         if (greatest < file->versions - 1) {
 1285             greatest++;
 1286         }
 1287     }
 1288 
 1289     for (i = greatest; i > 0; i--) {
 1290         result = ISC_R_SUCCESS;
 1291         n = snprintf(current, sizeof(current), "%s.%u", path,
 1292                  (unsigned)(i - 1));
 1293         if (n >= (int)sizeof(current) || n < 0) {
 1294             result = ISC_R_NOSPACE;
 1295         }
 1296         if (result == ISC_R_SUCCESS) {
 1297             n = snprintf(newpath, sizeof(newpath), "%s.%u", path,
 1298                      (unsigned)i);
 1299             if (n >= (int)sizeof(newpath) || n < 0) {
 1300                 result = ISC_R_NOSPACE;
 1301             }
 1302         }
 1303         if (result == ISC_R_SUCCESS) {
 1304             result = isc_file_rename(current, newpath);
 1305         }
 1306         if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
 1307             syslog(LOG_ERR,
 1308                    "unable to rename log file '%s.%u' to "
 1309                    "'%s.%u': %s",
 1310                    path, i - 1, path, i, isc_result_totext(result));
 1311         }
 1312     }
 1313 
 1314     n = snprintf(newpath, sizeof(newpath), "%s.0", path);
 1315     if (n >= (int)sizeof(newpath) || n < 0) {
 1316         result = ISC_R_NOSPACE;
 1317     } else {
 1318         result = isc_file_rename(path, newpath);
 1319     }
 1320     if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
 1321         syslog(LOG_ERR, "unable to rename log file '%s' to '%s.0': %s",
 1322                path, path, isc_result_totext(result));
 1323     }
 1324 
 1325     return (ISC_R_SUCCESS);
 1326 }
 1327 
 1328 static isc_result_t
 1329 roll_timestamp(isc_logfile_t *file) {
 1330     int n;
 1331     char newts[PATH_MAX + 1];
 1332     char newpath[PATH_MAX + 1];
 1333     const char *path;
 1334     isc_time_t now;
 1335     isc_result_t result = ISC_R_SUCCESS;
 1336 
 1337     REQUIRE(file != NULL);
 1338     REQUIRE(file->versions != 0);
 1339 
 1340     path = file->name;
 1341 
 1342     /*
 1343      * First find all the logfiles and remove the oldest ones
 1344      * Save one fewer than file->versions because we'll be renaming
 1345      * the existing file to a timestamped version after this.
 1346      */
 1347     if (file->versions != ISC_LOG_ROLLINFINITE) {
 1348         remove_old_tsversions(file, file->versions - 1);
 1349     }
 1350 
 1351     /* Then just rename the current logfile */
 1352     isc_time_now(&now);
 1353     isc_time_formatshorttimestamp(&now, newts, PATH_MAX + 1);
 1354     n = snprintf(newpath, sizeof(newpath), "%s.%s", path, newts);
 1355     if (n >= (int)sizeof(newpath) || n < 0) {
 1356         result = ISC_R_NOSPACE;
 1357     } else {
 1358         result = isc_file_rename(path, newpath);
 1359     }
 1360     if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
 1361         syslog(LOG_ERR, "unable to rename log file '%s' to '%s.0': %s",
 1362                path, path, isc_result_totext(result));
 1363     }
 1364 
 1365     return (ISC_R_SUCCESS);
 1366 }
 1367 
 1368 isc_result_t
 1369 isc_logfile_roll(isc_logfile_t *file) {
 1370     isc_result_t result;
 1371 
 1372     REQUIRE(file != NULL);
 1373 
 1374     /*
 1375      * Do nothing (not even excess version trimming) if ISC_LOG_ROLLNEVER
 1376      * is specified.  Apparently complete external control over the log
 1377      * files is desired.
 1378      */
 1379     if (file->versions == ISC_LOG_ROLLNEVER) {
 1380         return (ISC_R_SUCCESS);
 1381     } else if (file->versions == 0) {
 1382         result = isc_file_remove(file->name);
 1383         if (result != ISC_R_SUCCESS && result != ISC_R_FILENOTFOUND) {
 1384             syslog(LOG_ERR, "unable to remove log file '%s': %s",
 1385                    file->name, isc_result_totext(result));
 1386         }
 1387         return (ISC_R_SUCCESS);
 1388     }
 1389 
 1390     switch (file->suffix) {
 1391     case isc_log_rollsuffix_increment:
 1392         return (roll_increment(file));
 1393     case isc_log_rollsuffix_timestamp:
 1394         return (roll_timestamp(file));
 1395     default:
 1396         return (ISC_R_UNEXPECTED);
 1397     }
 1398 }
 1399 
 1400 static isc_result_t
 1401 isc_log_open(isc_logchannel_t *channel) {
 1402     struct stat statbuf;
 1403     bool regular_file;
 1404     bool roll = false;
 1405     isc_result_t result = ISC_R_SUCCESS;
 1406     const char *path;
 1407 
 1408     REQUIRE(channel->type == ISC_LOG_TOFILE);
 1409     REQUIRE(FILE_STREAM(channel) == NULL);
 1410 
 1411     path = FILE_NAME(channel);
 1412 
 1413     REQUIRE(path != NULL && *path != '\0');
 1414 
 1415     /*
 1416      * Determine type of file; only regular files will be
 1417      * version renamed, and only if the base file exists
 1418      * and either has no size limit or has reached its size limit.
 1419      */
 1420     if (stat(path, &statbuf) == 0) {
 1421         regular_file = S_ISREG(statbuf.st_mode) ? true : false;
 1422         /* XXXDCL if not regular_file complain? */
 1423         if ((FILE_MAXSIZE(channel) == 0 &&
 1424              FILE_VERSIONS(channel) != ISC_LOG_ROLLNEVER) ||
 1425             (FILE_MAXSIZE(channel) > 0 &&
 1426              statbuf.st_size >= FILE_MAXSIZE(channel)))
 1427         {
 1428             roll = regular_file;
 1429         }
 1430     } else if (errno == ENOENT) {
 1431         regular_file = true;
 1432         POST(regular_file);
 1433     } else {
 1434         result = ISC_R_INVALIDFILE;
 1435     }
 1436 
 1437     /*
 1438      * Version control.
 1439      */
 1440     if (result == ISC_R_SUCCESS && roll) {
 1441         if (FILE_VERSIONS(channel) == ISC_LOG_ROLLNEVER) {
 1442             return (ISC_R_MAXSIZE);
 1443         }
 1444         result = isc_logfile_roll(&channel->destination.file);
 1445         if (result != ISC_R_SUCCESS) {
 1446             if ((channel->flags & ISC_LOG_OPENERR) == 0) {
 1447                 syslog(LOG_ERR,
 1448                        "isc_log_open: isc_logfile_roll '%s' "
 1449                        "failed: %s",
 1450                        FILE_NAME(channel),
 1451                        isc_result_totext(result));
 1452                 channel->flags |= ISC_LOG_OPENERR;
 1453             }
 1454             return (result);
 1455         }
 1456     }
 1457 
 1458     result = isc_stdio_open(path, "a", &FILE_STREAM(channel));
 1459 
 1460     return (result);
 1461 }
 1462 
 1463 bool
 1464 isc_log_wouldlog(isc_log_t *lctx, int level) {
 1465     /*
 1466      * Try to avoid locking the mutex for messages which can't
 1467      * possibly be logged to any channels -- primarily debugging
 1468      * messages that the debug level is not high enough to print.
 1469      *
 1470      * If the level is (mathematically) less than or equal to the
 1471      * highest_level, or if there is a dynamic channel and the level is
 1472      * less than or equal to the debug level, the main loop must be
 1473      * entered to see if the message should really be output.
 1474      */
 1475     if (lctx == NULL) {
 1476         return (false);
 1477     }
 1478     if (forcelog) {
 1479         return (true);
 1480     }
 1481 
 1482     int highest_level = atomic_load_acquire(&lctx->highest_level);
 1483     if (level <= highest_level) {
 1484         return (true);
 1485     }
 1486     if (atomic_load_acquire(&lctx->dynamic)) {
 1487         int debug_level = atomic_load_acquire(&lctx->debug_level);
 1488         if (level <= debug_level) {
 1489             return (true);
 1490         }
 1491     }
 1492 
 1493     return (false);
 1494 }
 1495 
 1496 static void
 1497 isc_log_doit(isc_log_t *lctx, isc_logcategory_t *category,
 1498          isc_logmodule_t *module, int level, bool write_once,
 1499          const char *format, va_list args) {
 1500     int syslog_level;
 1501     const char *time_string;
 1502     char local_time[64];
 1503     char iso8601z_string[64];
 1504     char iso8601l_string[64];
 1505     char level_string[24] = { 0 };
 1506     struct stat statbuf;
 1507     bool matched = false;
 1508     bool printtime, iso8601, utc, printtag, printcolon;
 1509     bool printcategory, printmodule, printlevel, buffered;
 1510     isc_logchannel_t *channel;
 1511     isc_logchannellist_t *category_channels;
 1512     int_fast32_t dlevel;
 1513     isc_result_t result;
 1514 
 1515     REQUIRE(lctx == NULL || VALID_CONTEXT(lctx));
 1516     REQUIRE(category != NULL);
 1517     REQUIRE(module != NULL);
 1518     REQUIRE(level != ISC_LOG_DYNAMIC);
 1519     REQUIRE(format != NULL);
 1520 
 1521     /*
 1522      * Programs can use libraries that use this logging code without
 1523      * wanting to do any logging, thus the log context is allowed to
 1524      * be non-existent.
 1525      */
 1526     if (lctx == NULL) {
 1527         return;
 1528     }
 1529 
 1530     REQUIRE(category->id < lctx->category_count);
 1531     REQUIRE(module->id < lctx->module_count);
 1532 
 1533     if (!isc_log_wouldlog(lctx, level)) {
 1534         return;
 1535     }
 1536 
 1537     local_time[0] = '\0';
 1538     iso8601l_string[0] = '\0';
 1539     iso8601z_string[0] = '\0';
 1540 
 1541     RDLOCK(&lctx->lcfg_rwl);
 1542     LOCK(&lctx->lock);
 1543 
 1544     lctx->buffer[0] = '\0';
 1545 
 1546     isc_logconfig_t *lcfg = lctx->logconfig;
 1547 
 1548     category_channels = ISC_LIST_HEAD(lcfg->channellists[category->id]);
 1549 
 1550     /*
 1551      * XXXDCL add duplicate filtering? (To not write multiple times
 1552      * to the same source via various channels).
 1553      */
 1554     do {
 1555         /*
 1556          * If the channel list end was reached and a match was
 1557          * made, everything is finished.
 1558          */
 1559         if (category_channels == NULL && matched) {
 1560             break;
 1561         }
 1562 
 1563         if (category_channels == NULL && !matched &&
 1564             category_channels != ISC_LIST_HEAD(lcfg->channellists[0]))
 1565         {
 1566             /*
 1567              * No category/module pair was explicitly
 1568              * configured. Try the category named "default".
 1569              */
 1570             category_channels =
 1571                 ISC_LIST_HEAD(lcfg->channellists[0]);
 1572         }
 1573 
 1574         if (category_channels == NULL && !matched) {
 1575             /*
 1576              * No matching module was explicitly configured
 1577              * for the category named "default".  Use the
 1578              * internal default channel.
 1579              */
 1580             category_channels = &default_channel;
 1581         }
 1582 
 1583         if (category_channels->module != NULL &&
 1584             category_channels->module != module) {
 1585             category_channels = ISC_LIST_NEXT(category_channels,
 1586                               link);
 1587             continue;
 1588         }
 1589 
 1590         matched = true;
 1591 
 1592         channel = category_channels->channel;
 1593         category_channels = ISC_LIST_NEXT(category_channels, link);
 1594 
 1595         if (!forcelog) {
 1596             dlevel = atomic_load_acquire(&lctx->debug_level);
 1597             if (((channel->flags & ISC_LOG_DEBUGONLY) != 0) &&
 1598                 dlevel == 0) {
 1599                 continue;
 1600             }
 1601 
 1602             if (channel->level == ISC_LOG_DYNAMIC) {
 1603                 if (dlevel < level) {
 1604                     continue;
 1605                 }
 1606             } else if (channel->level < level) {
 1607                 continue;
 1608             }
 1609         }
 1610 
 1611         if ((channel->flags & ISC_LOG_PRINTTIME) != 0 &&
 1612             local_time[0] == '\0') {
 1613             isc_time_t isctime;
 1614 
 1615             TIME_NOW(&isctime);
 1616 
 1617             isc_time_formattimestamp(&isctime, local_time,
 1618                          sizeof(local_time));
 1619             isc_time_formatISO8601ms(&isctime, iso8601z_string,
 1620                          sizeof(iso8601z_string));
 1621             isc_time_formatISO8601Lms(&isctime, iso8601l_string,
 1622                           sizeof(iso8601l_string));
 1623         }
 1624 
 1625         if ((channel->flags & ISC_LOG_PRINTLEVEL) != 0 &&
 1626             level_string[0] == '\0') {
 1627             if (level < ISC_LOG_CRITICAL) {
 1628                 snprintf(level_string, sizeof(level_string),
 1629                      "level %d: ", level);
 1630             } else if (level > ISC_LOG_DYNAMIC) {
 1631                 snprintf(level_string, sizeof(level_string),
 1632                      "%s %d: ", log_level_strings[0],
 1633                      level);
 1634             } else {
 1635                 snprintf(level_string, sizeof(level_string),
 1636                      "%s: ", log_level_strings[-level]);
 1637             }
 1638         }
 1639 
 1640         /*
 1641          * Only format the message once.
 1642          */
 1643         if (lctx->buffer[0] == '\0') {
 1644             (void)vsnprintf(lctx->buffer, sizeof(lctx->buffer),
 1645                     format, args);
 1646 
 1647             /*
 1648              * Check for duplicates.
 1649              */
 1650             if (write_once) {
 1651                 isc_logmessage_t *message, *next;
 1652                 isc_time_t oldest;
 1653                 isc_interval_t interval;
 1654                 size_t size;
 1655 
 1656                 isc_interval_set(&interval,
 1657                          lcfg->duplicate_interval, 0);
 1658 
 1659                 /*
 1660                  * 'oldest' is the age of the oldest
 1661                  * messages which fall within the
 1662                  * duplicate_interval range.
 1663                  */
 1664                 TIME_NOW(&oldest);
 1665                 if (isc_time_subtract(&oldest, &interval,
 1666                               &oldest) != ISC_R_SUCCESS)
 1667                 {
 1668                     /*
 1669                      * Can't effectively do the
 1670                      * checking without having a
 1671                      * valid time.
 1672                      */
 1673                     message = NULL;
 1674                 } else {
 1675                     message = ISC_LIST_HEAD(lctx->messages);
 1676                 }
 1677 
 1678                 while (message != NULL) {
 1679                     if (isc_time_compare(&message->time,
 1680                                  &oldest) < 0) {
 1681                         /*
 1682                          * This message is older
 1683                          * than the
 1684                          * duplicate_interval,
 1685                          * so it should be
 1686                          * dropped from the
 1687                          * history.
 1688                          *
 1689                          * Setting the interval
 1690                          * to be to be longer
 1691                          * will obviously not
 1692                          * cause the expired
 1693                          * message to spring
 1694                          * back into existence.
 1695                          */
 1696                         next = ISC_LIST_NEXT(message,
 1697                                      link);
 1698 
 1699                         ISC_LIST_UNLINK(lctx->messages,
 1700                                 message, link);
 1701 
 1702                         isc_mem_put(
 1703                             lctx->mctx, message,
 1704                             sizeof(*message) + 1 +
 1705                                 strlen(message->text));
 1706 
 1707                         message = next;
 1708                         continue;
 1709                     }
 1710 
 1711                     /*
 1712                      * This message is in the
 1713                      * duplicate filtering interval
 1714                      * ...
 1715                      */
 1716                     if (strcmp(lctx->buffer,
 1717                            message->text) == 0) {
 1718                         /*
 1719                          * ... and it is a
 1720                          * duplicate. Unlock the
 1721                          * mutex and get the
 1722                          * hell out of Dodge.
 1723                          */
 1724                         goto unlock;
 1725                     }
 1726 
 1727                     message = ISC_LIST_NEXT(message, link);
 1728                 }
 1729 
 1730                 /*
 1731                  * It wasn't in the duplicate interval,
 1732                  * so add it to the message list.
 1733                  */
 1734                 size = sizeof(isc_logmessage_t) +
 1735                        strlen(lctx->buffer) + 1;
 1736                 message = isc_mem_get(lctx->mctx, size);
 1737                 message->text = (char *)(message + 1);
 1738                 size -= sizeof(isc_logmessage_t);
 1739                 strlcpy(message->text, lctx->buffer, size);
 1740                 TIME_NOW(&message->time);
 1741                 ISC_LINK_INIT(message, link);
 1742                 ISC_LIST_APPEND(lctx->messages, message, link);
 1743             }
 1744         }
 1745 
 1746         utc = ((channel->flags & ISC_LOG_UTC) != 0);
 1747         iso8601 = ((channel->flags & ISC_LOG_ISO8601) != 0);
 1748         printtime = ((channel->flags & ISC_LOG_PRINTTIME) != 0);
 1749         printtag = ((channel->flags &
 1750                  (ISC_LOG_PRINTTAG | ISC_LOG_PRINTPREFIX)) != 0 &&
 1751                 lcfg->tag != NULL);
 1752         printcolon = ((channel->flags & ISC_LOG_PRINTTAG) != 0 &&
 1753                   lcfg->tag != NULL);
 1754         printcategory = ((channel->flags & ISC_LOG_PRINTCATEGORY) != 0);
 1755         printmodule = ((channel->flags & ISC_LOG_PRINTMODULE) != 0);
 1756         printlevel = ((channel->flags & ISC_LOG_PRINTLEVEL) != 0);
 1757         buffered = ((channel->flags & ISC_LOG_BUFFERED) != 0);
 1758 
 1759         if (printtime) {
 1760             if (iso8601) {
 1761                 if (utc) {
 1762                     time_string = iso8601z_string;
 1763                 } else {
 1764                     time_string = iso8601l_string;
 1765                 }
 1766             } else {
 1767                 time_string = local_time;
 1768             }
 1769         } else {
 1770             time_string = "";
 1771         }
 1772 
 1773         switch (channel->type) {
 1774         case ISC_LOG_TOFILE:
 1775             if (FILE_MAXREACHED(channel)) {
 1776                 /*
 1777                  * If the file can be rolled, OR
 1778                  * If the file no longer exists, OR
 1779                  * If the file is less than the maximum
 1780                  * size, (such as if it had been renamed
 1781                  * and a new one touched, or it was
 1782                  * truncated in place)
 1783                  * ... then close it to trigger
 1784                  * reopening.
 1785                  */
 1786                 if (FILE_VERSIONS(channel) !=
 1787                         ISC_LOG_ROLLNEVER ||
 1788                     (stat(FILE_NAME(channel), &statbuf) != 0 &&
 1789                      errno == ENOENT) ||
 1790                     statbuf.st_size < FILE_MAXSIZE(channel))
 1791                 {
 1792                     (void)fclose(FILE_STREAM(channel));
 1793                     FILE_STREAM(channel) = NULL;
 1794                     FILE_MAXREACHED(channel) = false;
 1795                 } else {
 1796                     /*
 1797                      * Eh, skip it.
 1798                      */
 1799                     break;
 1800                 }
 1801             }
 1802 
 1803             if (FILE_STREAM(channel) == NULL) {
 1804                 result = isc_log_open(channel);
 1805                 if (result != ISC_R_SUCCESS &&
 1806                     result != ISC_R_MAXSIZE &&
 1807                     (channel->flags & ISC_LOG_OPENERR) == 0)
 1808                 {
 1809                     syslog(LOG_ERR,
 1810                            "isc_log_open '%s' "
 1811                            "failed: %s",
 1812                            FILE_NAME(channel),
 1813                            isc_result_totext(result));
 1814                     channel->flags |= ISC_LOG_OPENERR;
 1815                 }
 1816                 if (result != ISC_R_SUCCESS) {
 1817                     break;
 1818                 }
 1819                 channel->flags &= ~ISC_LOG_OPENERR;
 1820             }
 1821             /* FALLTHROUGH */
 1822 
 1823         case ISC_LOG_TOFILEDESC:
 1824             fprintf(FILE_STREAM(channel), "%s%s%s%s%s%s%s%s%s%s\n",
 1825                 printtime ? time_string : "",
 1826                 printtime ? " " : "", printtag ? lcfg->tag : "",
 1827                 printcolon ? ": " : "",
 1828                 printcategory ? category->name : "",
 1829                 printcategory ? ": " : "",
 1830                 printmodule ? (module != NULL ? module->name
 1831                                   : "no_module")
 1832                         : "",
 1833                 printmodule ? ": " : "",
 1834                 printlevel ? level_string : "", lctx->buffer);
 1835 
 1836             if (!buffered) {
 1837                 fflush(FILE_STREAM(channel));
 1838             }
 1839 
 1840             /*
 1841              * If the file now exceeds its maximum size
 1842              * threshold, note it so that it will not be
 1843              * logged to any more.
 1844              */
 1845             if (FILE_MAXSIZE(channel) > 0) {
 1846                 INSIST(channel->type == ISC_LOG_TOFILE);
 1847 
 1848                 /* XXXDCL NT fstat/fileno */
 1849                 /* XXXDCL complain if fstat fails? */
 1850                 if (fstat(fileno(FILE_STREAM(channel)),
 1851                       &statbuf) >= 0 &&
 1852                     statbuf.st_size > FILE_MAXSIZE(channel))
 1853                 {
 1854                     FILE_MAXREACHED(channel) = true;
 1855                 }
 1856             }
 1857 
 1858             break;
 1859 
 1860         case ISC_LOG_TOSYSLOG:
 1861             if (level > 0) {
 1862                 syslog_level = LOG_DEBUG;
 1863             } else if (level < ISC_LOG_CRITICAL) {
 1864                 syslog_level = LOG_CRIT;
 1865             } else {
 1866                 syslog_level = syslog_map[-level];
 1867             }
 1868 
 1869             (void)syslog(
 1870                 FACILITY(channel) | syslog_level,
 1871                 "%s%s%s%s%s%s%s%s%s%s",
 1872                 printtime ? time_string : "",
 1873                 printtime ? " " : "", printtag ? lcfg->tag : "",
 1874                 printcolon ? ": " : "",
 1875                 printcategory ? category->name : "",
 1876                 printcategory ? ": " : "",
 1877                 printmodule ? (module != NULL ? module->name
 1878                                   : "no_module")
 1879                         : "",
 1880                 printmodule ? ": " : "",
 1881                 printlevel ? level_string : "", lctx->buffer);
 1882             break;
 1883 
 1884         case ISC_LOG_TONULL:
 1885             break;
 1886         }
 1887     } while (1);
 1888 
 1889 unlock:
 1890     UNLOCK(&lctx->lock);
 1891     RDUNLOCK(&lctx->lcfg_rwl);
 1892 }
 1893 
 1894 void
 1895 isc_log_setforcelog(bool v) {
 1896     forcelog = v;
 1897 }