"Fossies" - the Fresh Open Source Software Archive

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