From: Remi Gacogne Date: Fri, 25 Jun 2021 09:51:46 +0000 (+0200) Subject: dnsdist: Implement 'reload()' to rotate Log(Response)Action's log file X-Git-Tag: dnsdist-1.7.0-alpha1~99^2~2 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=ebaa134afe5b2f7196a52c6990c1377421e3e75e;p=thirdparty%2Fpdns.git dnsdist: Implement 'reload()' to rotate Log(Response)Action's log file --- diff --git a/pdns/dnsdist-lua-actions.cc b/pdns/dnsdist-lua-actions.cc index ce1e714be3..754857e1ec 100644 --- a/pdns/dnsdist-lua-actions.cc +++ b/pdns/dnsdist-lua-actions.cc @@ -727,35 +727,26 @@ class LogAction : public DNSAction, public boost::noncopyable { public: // this action does not stop the processing - LogAction(): d_fp(nullptr, fclose) + LogAction() { } - LogAction(const std::string& str, bool binary=true, bool append=false, bool buffered=true, bool verboseOnly=true, bool includeTimestamp=false): d_fname(str), d_binary(binary), d_verboseOnly(verboseOnly), d_includeTimestamp(includeTimestamp) + LogAction(const std::string& str, bool binary=true, bool append=false, bool buffered=true, bool verboseOnly=true, bool includeTimestamp=false): d_fname(str), d_binary(binary), d_verboseOnly(verboseOnly), d_includeTimestamp(includeTimestamp), d_append(append), d_buffered(buffered) { if (str.empty()) { return; } - if(append) { - d_fp = std::unique_ptr(fopen(str.c_str(), "a+"), fclose); - } - else { - d_fp = std::unique_ptr(fopen(str.c_str(), "w"), fclose); - } - + reopenLogFile(); if (!d_fp) { - throw std::runtime_error("Unable to open file '"+str+"' for logging: "+stringerror()); - } - - if (!buffered) { - setbuf(d_fp.get(), 0); + throw std::runtime_error("Unable to open file '" + str + "' for logging: " + stringerror()); } } DNSAction::Action operator()(DNSQuestion* dq, std::string* ruleresult) const override { - if (!d_fp) { + auto fp = std::atomic_load_explicit(&d_fp, std::memory_order_acquire); + if (!fp) { if (!d_verboseOnly || g_verbose) { if (d_includeTimestamp) { infolog("[%u.%u] Packet from %s for %s %s with id %d", static_cast(dq->queryTime->tv_sec), static_cast(dq->queryTime->tv_nsec), dq->remote->toStringWithPort(), dq->qname->toString(), QType(dq->qtype).toString(), dq->getHeader()->id); @@ -771,28 +762,28 @@ public: if (d_includeTimestamp) { uint64_t tv_sec = static_cast(dq->queryTime->tv_sec); uint32_t tv_nsec = static_cast(dq->queryTime->tv_nsec); - fwrite(&tv_sec, sizeof(tv_sec), 1, d_fp.get()); - fwrite(&tv_nsec, sizeof(tv_nsec), 1, d_fp.get()); + fwrite(&tv_sec, sizeof(tv_sec), 1, fp.get()); + fwrite(&tv_nsec, sizeof(tv_nsec), 1, fp.get()); } uint16_t id = dq->getHeader()->id; - fwrite(&id, sizeof(id), 1, d_fp.get()); - fwrite(out.c_str(), 1, out.size(), d_fp.get()); - fwrite(&dq->qtype, sizeof(dq->qtype), 1, d_fp.get()); - fwrite(&dq->remote->sin4.sin_family, sizeof(dq->remote->sin4.sin_family), 1, d_fp.get()); + fwrite(&id, sizeof(id), 1, fp.get()); + fwrite(out.c_str(), 1, out.size(), fp.get()); + fwrite(&dq->qtype, sizeof(dq->qtype), 1, fp.get()); + fwrite(&dq->remote->sin4.sin_family, sizeof(dq->remote->sin4.sin_family), 1, fp.get()); if (dq->remote->sin4.sin_family == AF_INET) { - fwrite(&dq->remote->sin4.sin_addr.s_addr, sizeof(dq->remote->sin4.sin_addr.s_addr), 1, d_fp.get()); + fwrite(&dq->remote->sin4.sin_addr.s_addr, sizeof(dq->remote->sin4.sin_addr.s_addr), 1, fp.get()); } else if (dq->remote->sin4.sin_family == AF_INET6) { - fwrite(&dq->remote->sin6.sin6_addr.s6_addr, sizeof(dq->remote->sin6.sin6_addr.s6_addr), 1, d_fp.get()); + fwrite(&dq->remote->sin6.sin6_addr.s6_addr, sizeof(dq->remote->sin6.sin6_addr.s6_addr), 1, fp.get()); } - fwrite(&dq->remote->sin4.sin_port, sizeof(dq->remote->sin4.sin_port), 1, d_fp.get()); + fwrite(&dq->remote->sin4.sin_port, sizeof(dq->remote->sin4.sin_port), 1, fp.get()); } else { if (d_includeTimestamp) { - fprintf(d_fp.get(), "[%llu.%lu] Packet from %s for %s %s with id %d\n", static_cast(dq->queryTime->tv_sec), static_cast(dq->queryTime->tv_nsec), dq->remote->toStringWithPort().c_str(), dq->qname->toString().c_str(), QType(dq->qtype).toString().c_str(), dq->getHeader()->id); + fprintf(fp.get(), "[%llu.%lu] Packet from %s for %s %s with id %d\n", static_cast(dq->queryTime->tv_sec), static_cast(dq->queryTime->tv_nsec), dq->remote->toStringWithPort().c_str(), dq->qname->toString().c_str(), QType(dq->qtype).toString().c_str(), dq->getHeader()->id); } else { - fprintf(d_fp.get(), "Packet from %s for %s %s with id %d\n", dq->remote->toStringWithPort().c_str(), dq->qname->toString().c_str(), QType(dq->qtype).toString().c_str(), dq->getHeader()->id); + fprintf(fp.get(), "Packet from %s for %s %s with id %d\n", dq->remote->toStringWithPort().c_str(), dq->qname->toString().c_str(), QType(dq->qtype).toString().c_str(), dq->getHeader()->id); } } } @@ -806,46 +797,68 @@ public: } return "log"; } + + void reload() override + { + reopenLogFile(); + } + private: + void reopenLogFile() + { + std::shared_ptr fp = nullptr; + + if (d_append) { + fp = std::shared_ptr(fopen(d_fname.c_str(), "a+"), fclose); + } + else { + fp = std::shared_ptr(fopen(d_fname.c_str(), "w"), fclose); + } + + if (!fp) { + /* don't fall on our sword when reopening */ + return; + } + + if (!d_buffered) { + setbuf(fp.get(), 0); + } + + std::atomic_store_explicit(&d_fp, fp, std::memory_order_release); + } + std::string d_fname; - std::unique_ptr d_fp{nullptr, fclose}; + std::shared_ptr d_fp{nullptr}; bool d_binary{true}; bool d_verboseOnly{true}; bool d_includeTimestamp{false}; + bool d_append{false}; + bool d_buffered{true}; }; class LogResponseAction : public DNSResponseAction, public boost::noncopyable { public: - LogResponseAction(): d_fp(nullptr, fclose) + LogResponseAction() { } - LogResponseAction(const std::string& str, bool append=false, bool buffered=true, bool verboseOnly=true, bool includeTimestamp=false): d_fname(str), d_verboseOnly(verboseOnly), d_includeTimestamp(includeTimestamp) + LogResponseAction(const std::string& str, bool append=false, bool buffered=true, bool verboseOnly=true, bool includeTimestamp=false): d_fname(str), d_verboseOnly(verboseOnly), d_includeTimestamp(includeTimestamp), d_append(append), d_buffered(buffered) { if (str.empty()) { return; } - if (append) { - d_fp = std::unique_ptr(fopen(str.c_str(), "a+"), fclose); - } - else { - d_fp = std::unique_ptr(fopen(str.c_str(), "w"), fclose); - } - + reopenLogFile(); if (!d_fp) { - throw std::runtime_error("Unable to open file '"+str+"' for logging: "+stringerror()); - } - - if (!buffered) { - setbuf(d_fp.get(), 0); + throw std::runtime_error("Unable to open file '" + str + "' for logging: " + stringerror()); } } DNSResponseAction::Action operator()(DNSResponse* dr, std::string* ruleresult) const override { - if (!d_fp) { + auto fp = std::atomic_load_explicit(&d_fp, std::memory_order_acquire); + if (!fp) { if (!d_verboseOnly || g_verbose) { if (d_includeTimestamp) { infolog("[%u.%u] Answer to %s for %s %s (%s) with id %d", static_cast(dr->queryTime->tv_sec), static_cast(dr->queryTime->tv_nsec), dr->remote->toStringWithPort(), dr->qname->toString(), QType(dr->qtype).toString(), RCode::to_s(dr->getHeader()->rcode), dr->getHeader()->id); @@ -857,10 +870,10 @@ public: } else { if (d_includeTimestamp) { - fprintf(d_fp.get(), "[%llu.%lu] Answer to %s for %s %s (%s) with id %d\n", static_cast(dr->queryTime->tv_sec), static_cast(dr->queryTime->tv_nsec), dr->remote->toStringWithPort().c_str(), dr->qname->toString().c_str(), QType(dr->qtype).toString().c_str(), RCode::to_s(dr->getHeader()->rcode).c_str(), dr->getHeader()->id); + fprintf(fp.get(), "[%llu.%lu] Answer to %s for %s %s (%s) with id %d\n", static_cast(dr->queryTime->tv_sec), static_cast(dr->queryTime->tv_nsec), dr->remote->toStringWithPort().c_str(), dr->qname->toString().c_str(), QType(dr->qtype).toString().c_str(), RCode::to_s(dr->getHeader()->rcode).c_str(), dr->getHeader()->id); } else { - fprintf(d_fp.get(), "Answer to %s for %s %s (%s) with id %d\n", dr->remote->toStringWithPort().c_str(), dr->qname->toString().c_str(), QType(dr->qtype).toString().c_str(), RCode::to_s(dr->getHeader()->rcode).c_str(), dr->getHeader()->id); + fprintf(fp.get(), "Answer to %s for %s %s (%s) with id %d\n", dr->remote->toStringWithPort().c_str(), dr->qname->toString().c_str(), QType(dr->qtype).toString().c_str(), RCode::to_s(dr->getHeader()->rcode).c_str(), dr->getHeader()->id); } } return Action::None; @@ -873,11 +886,42 @@ public: } return "log"; } + + void reload() override + { + reopenLogFile(); + } + private: + void reopenLogFile() + { + std::shared_ptr fp = nullptr; + + if (d_append) { + fp = std::shared_ptr(fopen(d_fname.c_str(), "a+"), fclose); + } + else { + fp = std::shared_ptr(fopen(d_fname.c_str(), "w"), fclose); + } + + if (!fp) { + /* don't fall on our sword when reopening */ + return; + } + + if (!d_buffered) { + setbuf(fp.get(), 0); + } + + std::atomic_store_explicit(&d_fp, fp, std::memory_order_release); + } + std::string d_fname; - std::unique_ptr d_fp{nullptr, fclose}; + std::shared_ptr d_fp{nullptr}; bool d_verboseOnly{true}; bool d_includeTimestamp{false}; + bool d_append{false}; + bool d_buffered{true}; }; @@ -1681,6 +1725,8 @@ void setupLuaActions(LuaContext& luaCtx) }); luaCtx.registerFunction("getStats", &DNSAction::getStats); + luaCtx.registerFunction("reload", &DNSAction::reload); + luaCtx.registerFunction("reload", &DNSResponseAction::reload); luaCtx.writeFunction("LuaAction", [](LuaAction::func_t func) { setLuaSideEffect(); diff --git a/pdns/dnsdist.hh b/pdns/dnsdist.hh index 5d6ed9401a..46f40f4078 100644 --- a/pdns/dnsdist.hh +++ b/pdns/dnsdist.hh @@ -223,6 +223,9 @@ public: { return {{}}; } + virtual void reload() + { + } }; class DNSResponseAction @@ -234,6 +237,9 @@ public: { } virtual string toString() const = 0; + virtual void reload() + { + } }; struct DynBlock diff --git a/pdns/dnsdistdist/docs/rules-actions.rst b/pdns/dnsdistdist/docs/rules-actions.rst index 35aa125756..3196db05b3 100644 --- a/pdns/dnsdistdist/docs/rules-actions.rst +++ b/pdns/dnsdistdist/docs/rules-actions.rst @@ -943,6 +943,9 @@ The following actions exist. .. versionchanged:: 1.4.0 Added the optional parameters ``verboseOnly`` and ``includeTimestamp``, made ``filename`` optional. + .. versionchanged:: 1.7.0 + Added the ``reload`` method. + Log a line for each query, to the specified ``file`` if any, to the console (require verbose) if the empty string is given as filename. If an empty string is supplied in the file name, the logging is done to stdout, and only in verbose mode by default. This can be changed by setting ``verboseOnly`` to false. @@ -952,6 +955,8 @@ The following actions exist. The ``append`` optional parameter specifies whether we open the file for appending or truncate each time (default). The ``buffered`` optional parameter specifies whether writes to the file are buffered (default) or not. + Since 1.7.0 calling the ``reload()`` method on the object will cause it to close and re-open the log file, for rotation purposes. + Subsequent rules are processed after this action. :param string filename: File to log to. Set to an empty string to log to the normal stdout log, this only works when ``-v`` is set on the command line. @@ -965,6 +970,9 @@ The following actions exist. .. versionadded:: 1.5.0 + .. versionchanged:: 1.7.0 + Added the ``reload`` method. + Log a line for each response, to the specified ``file`` if any, to the console (require verbose) if the empty string is given as filename. If an empty string is supplied in the file name, the logging is done to stdout, and only in verbose mode by default. This can be changed by setting ``verboseOnly`` to false. @@ -972,6 +980,8 @@ The following actions exist. The ``append`` optional parameter specifies whether we open the file for appending or truncate each time (default). The ``buffered`` optional parameter specifies whether writes to the file are buffered (default) or not. + Since 1.7.0 calling the ``reload()`` method on the object will cause it to close and re-open the log file, for rotation purposes. + Subsequent rules are processed after this action. :param string filename: File to log to. Set to an empty string to log to the normal stdout log, this only works when ``-v`` is set on the command line.