]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
- Fix to decouple file descriptor activity and cache lookups in
authorW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 14 Aug 2025 10:20:10 +0000 (12:20 +0200)
committerW.C.A. Wijngaards <wouter@nlnetlabs.nl>
Thu, 14 Aug 2025 10:20:10 +0000 (12:20 +0200)
  dump_cache.

daemon/cachedump.c

index daa4e0ab137e2693d504d6175ae1511f87e06ca8..941cb9dfc9fde23833b78c55445a478d8852a186 100644 (file)
 #include "sldns/wire2str.h"
 #include "sldns/str2wire.h"
 
+static void spool_txt_printf(struct config_strlist_head* txt,
+       const char* format, ...) ATTR_FORMAT(printf, 2, 3);
+
+/** Append to strlist at end, and log error if out of memory. */
+static void
+spool_txt_string(struct config_strlist_head* txt, char* str)
+{
+       if(!cfg_strlist_append(txt, strdup(str))) {
+               log_err("out of memory in spool text");
+       }
+}
+
+/** Spool txt to spool list. */
+static void
+spool_txt_vmsg(struct config_strlist_head* txt, const char* format,
+       va_list args)
+{
+       char msg[65535];
+       vsnprintf(msg, sizeof(msg), format, args);
+       spool_txt_string(txt, msg);
+}
+
+/** Print item to spool list. On alloc failure the list is as before. */
+static void
+spool_txt_printf(struct config_strlist_head* txt, const char* format, ...)
+{
+       va_list args;
+       va_start(args, format);
+       spool_txt_vmsg(txt, format, args);
+       va_end(args);
+}
+
 /** dump one rrset zonefile line */
-static int
-dump_rrset_line(RES* ssl, struct ub_packed_rrset_key* k, time_t now, size_t i)
+static void
+dump_rrset_line(struct config_strlist_head* txt, struct ub_packed_rrset_key* k,
+       time_t now, size_t i)
 {
        char s[65535];
        if(!packed_rr_to_string(k, i, now, s, sizeof(s))) {
-               return ssl_printf(ssl, "BADRR\n");
+               spool_txt_string(txt, "BADRR\n");
+               return;
        }
-       return ssl_printf(ssl, "%s", s);
+       spool_txt_string(txt, s);
 }
 
 /** dump rrset key and data info */
-static int
-dump_rrset(RES* ssl, struct ub_packed_rrset_key* k, 
+static void
+dump_rrset(struct config_strlist_head* txt, struct ub_packed_rrset_key* k,
        struct packed_rrset_data* d, time_t now)
 {
        size_t i;
        /* rd lock held by caller */
-       if(!k || !d) return 1;
-       if(k->id == 0) return 1; /* deleted */
-       if(d->ttl < now) return 1; /* expired */
+       if(!k || !d) return;
+       if(k->id == 0) return; /* deleted */
+       if(d->ttl < now) return; /* expired */
 
        /* meta line */
-       if(!ssl_printf(ssl, ";rrset%s " ARG_LL "d %u %u %d %d\n",
+       spool_txt_printf(txt, ";rrset%s " ARG_LL "d %u %u %d %d\n",
                (k->rk.flags & PACKED_RRSET_NSEC_AT_APEX)?" nsec_apex":"",
                (long long)(d->ttl - now),
                (unsigned)d->count, (unsigned)d->rrsig_count,
                (int)d->trust, (int)d->security
-               )) 
-               return 0;
+               );
        for(i=0; i<d->count + d->rrsig_count; i++) {
-               if(!dump_rrset_line(ssl, k, now, i))
+               dump_rrset_line(txt, k, now, i);
+       }
+}
+
+/** Spool strlist to the output. */
+static int
+spool_strlist(RES* ssl, struct config_strlist* list)
+{
+       struct config_strlist* s;
+       for(s=list; s; s=s->next) {
+               if(!ssl_printf(ssl, "%s", s->str))
                        return 0;
        }
        return 1;
@@ -101,13 +144,17 @@ dump_rrset(RES* ssl, struct ub_packed_rrset_key* k,
 
 /** dump lruhash cache and call callback for every item. */
 static int
-dump_lruhash(struct lruhash* table, int (*func)(void*, void*), void* arg)
+dump_lruhash(struct lruhash* table,
+       void (*func)(struct lruhash_entry*, struct config_strlist_head*, void*),
+       RES* ssl, void* arg)
 {
        int just_started = 1;
        int not_done = 1;
        hashvalue_type hash;
        size_t num = 0; /* number of entries processed. */
        size_t max = 2; /* number of entries after which it unlocks. */
+       struct config_strlist_head txt; /* Text strings spooled. */
+       memset(&txt, 0, sizeof(txt));
 
        while(not_done) {
                size_t i; /* hash bin. */
@@ -128,11 +175,7 @@ dump_lruhash(struct lruhash* table, int (*func)(void*, void*), void* arg)
                        lock_quick_lock(&bin->lock);
                        for(e = bin->overflow_list; e; e = e->overflow_next) {
                                /* Entry e is locked by the func. */
-                               if(!(*func)(e, arg)) {
-                                       lock_quick_unlock(&bin->lock);
-                                       lock_quick_unlock(&table->lock);
-                                       return 0;
-                               }
+                               func(e, &txt, arg);
                                num_bin++;
                        }
                        lock_quick_unlock(&bin->lock);
@@ -165,13 +208,34 @@ dump_lruhash(struct lruhash* table, int (*func)(void*, void*), void* arg)
                        }
                }
                lock_quick_unlock(&table->lock);
+               /* Print the spooled items, that are collected while the
+                * locks are locked. The print happens while they are not
+                * locked. */
+               if(txt.first) {
+                       if(!spool_strlist(ssl, txt.first)) {
+                               config_delstrlist(txt.first);
+                               return 0;
+                       }
+                       config_delstrlist(txt.first);
+                       memset(&txt, 0, sizeof(txt));
+               }
+       }
+       /* Print the final spooled items. */
+       if(txt.first) {
+               if(!spool_strlist(ssl, txt.first)) {
+                       config_delstrlist(txt.first);
+                       return 0;
+               }
+               config_delstrlist(txt.first);
        }
        return 1;
 }
 
 /** dump slabhash cache and call callback for every item. */
 static int
-dump_slabhash(struct slabhash* sh, int (*func)(void*, void*), void* arg)
+dump_slabhash(struct slabhash* sh,
+       void (*func)(struct lruhash_entry*, struct config_strlist_head*, void*),
+       RES* ssl, void* arg)
 {
        /* Process a number of items at a time, then unlock the cache,
         * so that ordinary processing can continue. Keep an iteration marker
@@ -180,7 +244,7 @@ dump_slabhash(struct slabhash* sh, int (*func)(void*, void*), void* arg)
         * can grow. */
        size_t slab;
        for(slab=0; slab<sh->size; slab++) {
-               if(!dump_lruhash(sh->array[slab], func, arg))
+               if(!dump_lruhash(sh->array[slab], func, ssl, arg))
                        return 0;
        }
        return 1;
@@ -195,20 +259,16 @@ struct dump_info {
 };
 
 /** Dump the rrset cache entry */
-static int
-dump_rrset_entry(void* entryp, void* arg)
+static void
+dump_rrset_entry(struct lruhash_entry* e, struct config_strlist_head* txt,
+       void* arg)
 {
        struct dump_info* dump_info = (struct dump_info*)arg;
-       struct lruhash_entry* e = (struct lruhash_entry*)entryp;
        lock_rw_rdlock(&e->lock);
-       if(!dump_rrset(dump_info->ssl, (struct ub_packed_rrset_key*)e->key,
+       dump_rrset(txt, (struct ub_packed_rrset_key*)e->key,
                (struct packed_rrset_data*)e->data,
-               *dump_info->worker->env.now)) {
-               lock_rw_unlock(&e->lock);
-               return 0;
-       }
+               *dump_info->worker->env.now);
        lock_rw_unlock(&e->lock);
-       return 1;
 }
 
 /** dump rrset cache */
@@ -220,14 +280,14 @@ dump_rrset_cache(RES* ssl, struct worker* worker)
        dump_info.worker = worker;
        dump_info.ssl = ssl;
        if(!ssl_printf(ssl, "START_RRSET_CACHE\n")) return 0;
-       if(!dump_slabhash(&r->table, &dump_rrset_entry, &dump_info))
+       if(!dump_slabhash(&r->table, &dump_rrset_entry, ssl, &dump_info))
                return 0;
        return ssl_printf(ssl, "END_RRSET_CACHE\n");
 }
 
 /** dump message to rrset reference */
-static int
-dump_msg_ref(RES* ssl, struct ub_packed_rrset_key* k)
+static void
+dump_msg_ref(struct config_strlist_head* txt, struct ub_packed_rrset_key* k)
 {
        char* nm, *tp, *cl;
        nm = sldns_wire2str_dname(k->rk.dname, k->rk.dname_len);
@@ -237,30 +297,25 @@ dump_msg_ref(RES* ssl, struct ub_packed_rrset_key* k)
                free(nm);
                free(tp);
                free(cl);
-               return ssl_printf(ssl, "BADREF\n");
-       }
-       if(!ssl_printf(ssl, "%s %s %s %d\n", nm, cl, tp, (int)k->rk.flags)) {
-               free(nm);
-               free(tp);
-               free(cl);
-               return 0;
+               spool_txt_string(txt, "BADREF\n");
+               return;
        }
+       spool_txt_printf(txt, "%s %s %s %d\n", nm, cl, tp, (int)k->rk.flags);
        free(nm);
        free(tp);
        free(cl);
-
-       return 1;
 }
 
 /** dump message entry */
-static int
-dump_msg(RES* ssl, struct query_info* k, struct reply_info* d, time_t now)
+static void
+dump_msg(struct config_strlist_head* txt, struct query_info* k,
+       struct reply_info* d, time_t now)
 {
        size_t i;
        char* nm, *tp, *cl;
-       if(!k || !d) return 1;
-       if(d->ttl < now) return 1; /* expired */
-       
+       if(!k || !d) return;
+       if(d->ttl < now) return; /* expired */
+
        nm = sldns_wire2str_dname(k->qname, k->qname_len);
        tp = sldns_wire2str_type(k->qtype);
        cl = sldns_wire2str_class(k->qclass);
@@ -268,45 +323,35 @@ dump_msg(RES* ssl, struct query_info* k, struct reply_info* d, time_t now)
                free(nm);
                free(tp);
                free(cl);
-               return 1; /* skip this entry */
+               return; /* skip this entry */
        }
        if(!rrset_array_lock(d->ref, d->rrset_count, now)) {
                /* rrsets have timed out or do not exist */
                free(nm);
                free(tp);
                free(cl);
-               return 1; /* skip this entry */
+               return; /* skip this entry */
        }
-       
+
        /* meta line */
-       if(!ssl_printf(ssl, "msg %s %s %s %d %d " ARG_LL "d %d %u %u %u %d %s\n",
-                       nm, cl, tp,
-                       (int)d->flags, (int)d->qdcount, 
-                       (long long)(d->ttl-now), (int)d->security,
-                       (unsigned)d->an_numrrsets,
-                       (unsigned)d->ns_numrrsets,
-                       (unsigned)d->ar_numrrsets,
-                       (int)d->reason_bogus,
-                       d->reason_bogus_str?d->reason_bogus_str:"")) {
-               free(nm);
-               free(tp);
-               free(cl);
-               rrset_array_unlock(d->ref, d->rrset_count);
-               return 0;
-       }
+       spool_txt_printf(txt,
+               "msg %s %s %s %d %d " ARG_LL "d %d %u %u %u %d %s\n",
+               nm, cl, tp,
+               (int)d->flags, (int)d->qdcount,
+               (long long)(d->ttl-now), (int)d->security,
+               (unsigned)d->an_numrrsets,
+               (unsigned)d->ns_numrrsets,
+               (unsigned)d->ar_numrrsets,
+               (int)d->reason_bogus,
+               d->reason_bogus_str?d->reason_bogus_str:"");
        free(nm);
        free(tp);
        free(cl);
        
        for(i=0; i<d->rrset_count; i++) {
-               if(!dump_msg_ref(ssl, d->rrsets[i])) {
-                       rrset_array_unlock(d->ref, d->rrset_count);
-                       return 0;
-               }
+               dump_msg_ref(txt, d->rrsets[i]);
        }
        rrset_array_unlock(d->ref, d->rrset_count);
-
-       return 1;
 }
 
 /** copy msg to worker pad */
@@ -336,11 +381,11 @@ copy_msg(struct regional* region, struct lruhash_entry* e,
 }
 
 /** Dump the msg entry. */
-static int
-dump_msg_entry(void* entryp, void* arg)
+static void
+dump_msg_entry(struct lruhash_entry* e, struct config_strlist_head* txt,
+       void* arg)
 {
        struct dump_info* dump_info = (struct dump_info*)arg;
-       struct lruhash_entry* e = (struct lruhash_entry*)entryp;
        struct query_info* k;
        struct reply_info* d;
 
@@ -349,15 +394,13 @@ dump_msg_entry(void* entryp, void* arg)
        lock_rw_rdlock(&e->lock);
        if(!copy_msg(dump_info->worker->scratchpad, e, &k, &d)) {
                lock_rw_unlock(&e->lock);
-               return 0;
+               log_err("out of memory in dump_msg_entry");
+               return;
        }
        lock_rw_unlock(&e->lock);
        /* Release lock so we can lookup the rrset references
         * in the rrset cache. */
-       if(!dump_msg(dump_info->ssl, k, d, *dump_info->worker->env.now)) {
-               return 0;
-       }
-       return 1;
+       dump_msg(txt, k, d, *dump_info->worker->env.now);
 }
 
 /** dump msg cache */
@@ -368,7 +411,8 @@ dump_msg_cache(RES* ssl, struct worker* worker)
        dump_info.worker = worker;
        dump_info.ssl = ssl;
        if(!ssl_printf(ssl, "START_MSG_CACHE\n")) return 0;
-       if(!dump_slabhash(worker->env.msg_cache, &dump_msg_entry, &dump_info))
+       if(!dump_slabhash(worker->env.msg_cache, &dump_msg_entry, ssl,
+               &dump_info))
                return 0;
        return ssl_printf(ssl, "END_MSG_CACHE\n");
 }
@@ -891,12 +935,18 @@ print_dp_main(RES* ssl, struct delegpt* dp, struct dns_msg* msg)
                struct ub_packed_rrset_key* k = msg->rep->rrsets[i];
                struct packed_rrset_data* d = 
                        (struct packed_rrset_data*)k->entry.data;
+               struct config_strlist_head txt;
+               memset(&txt, 0, sizeof(txt));
                if(d->security == sec_status_bogus) {
                        if(!ssl_printf(ssl, "Address is BOGUS:\n"))
                                return;
                }
-               if(!dump_rrset(ssl, k, d, 0))
+               dump_rrset(&txt, k, d, 0);
+               if(!spool_strlist(ssl, txt.first)) {
+                       config_delstrlist(txt.first);
                        return;
+               }
+               config_delstrlist(txt.first);
            }
        delegpt_count_ns(dp, &n_ns, &n_miss);
        delegpt_count_addr(dp, &n_addr, &n_res, &n_avail);