]> git.ipfire.org Git - thirdparty/pdns.git/commitdiff
dnsdist: Refactoring of the SNMP code with channels
authorRemi Gacogne <remi.gacogne@powerdns.com>
Fri, 8 Apr 2022 15:17:00 +0000 (17:17 +0200)
committerRemi Gacogne <remi.gacogne@powerdns.com>
Tue, 13 Jun 2023 07:59:35 +0000 (09:59 +0200)
pdns/dnsdist-snmp.cc
pdns/dnsdistdist/channel.hh
pdns/recursordist/rec-snmp.cc
pdns/snmp-agent.cc
pdns/snmp-agent.hh

index d853a32d04eaddae84be3a8f367c7704d6b76c8e..149acbc511bbddec41e7e7db3fde1e40098c698b 100644 (file)
@@ -411,7 +411,7 @@ bool DNSDistSNMPAgent::sendBackendStatusChangeTrap(const DownstreamState& dss)
                             backendStatus.c_str(),
                             backendStatus.size());
 
-  return sendTrap(d_trapPipe[1], varList);
+  return sendTrap(d_sender, varList);
 #else
   return true;
 #endif /* HAVE_NET_SNMP */
@@ -436,7 +436,7 @@ bool DNSDistSNMPAgent::sendCustomTrap(const std::string& reason)
                             reason.c_str(),
                             reason.size());
 
-  return sendTrap(d_trapPipe[1], varList);
+  return sendTrap(d_sender, varList);
 #else
   return true;
 #endif /* HAVE_NET_SNMP */
@@ -542,7 +542,7 @@ bool DNSDistSNMPAgent::sendDNSTrap(const DNSQuestion& dq, const std::string& rea
                             reason.c_str(),
                             reason.size());
 
-  return sendTrap(d_trapPipe[1], varList);
+  return sendTrap(d_sender, varList);
 #else
   return true;
 #endif /* HAVE_NET_SNMP */
index 5698fb7ba3e7cbd4d21986646633f15e199af028..5c352870ed6051363ad53d86432833b0707bff66 100644 (file)
@@ -34,7 +34,7 @@ namespace channel
    *
    * A sender can be used by several threads in a safe way.
    */
-  template <typename T>
+  template <typename T, typename D = std::default_delete<T>>
   class Sender
   {
   public:
@@ -56,7 +56,7 @@ namespace channel
      *
      * \throw runtime_error if the channel is broken, for example if the other end has been closed.
      */
-    bool send(std::unique_ptr<T>&&) const;
+    bool send(std::unique_ptr<T, D>&&) const;
 
   private:
     FDWrapper d_fd;
@@ -67,7 +67,7 @@ namespace channel
    *
    * A receiver can be used by several threads in a safe way, but in that case spurious wake up might happen.
    */
-  template <typename T>
+  template <typename T, typename D = std::default_delete<T>>
   class Receiver
   {
   public:
@@ -89,7 +89,8 @@ namespace channel
      *
      * \throw runtime_error if the channel is broken, for example if the other end has been closed.
      */
-    std::optional<std::unique_ptr<T>> receive() const;
+    std::optional<std::unique_ptr<T, D>> receive() const;
+    std::optional<std::unique_ptr<T, D>> receive(D deleter) const;
 
     /**
      * \brief Get a descriptor that can be used with an I/O multiplexer to wait for an object to become available.
@@ -112,8 +113,8 @@ namespace channel
    *
    * \throw runtime_error if the channel creation failed.
    */
-  template <typename T>
-  std::pair<Sender<T>, Receiver<T>> createObjectQueue(bool nonBlocking = true, size_t pipeBufferSize = 0);
+  template <typename T, typename D = std::default_delete<T>>
+  std::pair<Sender<T, D>, Receiver<T, D>> createObjectQueue(bool nonBlocking = true, size_t pipeBufferSize = 0);
 
   /**
    * The notifier's end of a channel used to communicate between threads.
@@ -183,15 +184,19 @@ namespace channel
    */
   std::pair<Notifier, Waiter> createNotificationQueue(bool nonBlocking = true, size_t pipeBufferSize = 0);
 
-  template <typename T>
-  bool Sender<T>::send(std::unique_ptr<T>&& object) const
+  template <typename T, typename D>
+  bool Sender<T, D>::send(std::unique_ptr<T, D>&& object) const
   {
-    auto ptr = object.release();
+    // we do not release right away because we might need the custom deleter later
+    auto ptr = object.get();
     static_assert(sizeof(ptr) <= PIPE_BUF, "Writes up to PIPE_BUF are guaranted not to interleaved and to either fully succeed or fail");
     ssize_t sent = write(d_fd.getHandle(), &ptr, sizeof(ptr));
 
-    if (sent != sizeof(ptr)) {
-      delete ptr;
+    if (sent == sizeof(ptr)) {
+      // we cannot touch it anymore
+      object.release();
+    }
+    else {
       if (errno == EAGAIN || errno == EWOULDBLOCK) {
         return false;
       }
@@ -203,14 +208,37 @@ namespace channel
     return true;
   }
 
-  template <typename T>
-  std::optional<std::unique_ptr<T>> Receiver<T>::receive() const
+  template <typename T, typename D>
+  std::optional<std::unique_ptr<T, D>> Receiver<T, D>::receive() const
+  {
+    std::optional<std::unique_ptr<T, D>> result;
+    T* obj{nullptr};
+    ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj));
+    if (got == sizeof(obj)) {
+      return std::unique_ptr<T, D>(obj);
+    }
+    else if (got == 0) {
+      throw std::runtime_error("EOF while reading from Channel receiver");
+    }
+    else if (got == -1) {
+      if (errno == EAGAIN || errno == EINTR) {
+        return result;
+      }
+      throw std::runtime_error("Error while reading from Channel receiver: " + stringerror());
+    }
+    else {
+      throw std::runtime_error("Partial read from Channel receiver");
+    }
+  }
+
+  template <typename T, typename D>
+  std::optional<std::unique_ptr<T, D>> Receiver<T, D>::receive(D deleter) const
   {
-    std::optional<std::unique_ptr<T>> result;
+    std::optional<std::unique_ptr<T, D>> result;
     T* obj{nullptr};
     ssize_t got = read(d_fd.getHandle(), &obj, sizeof(obj));
     if (got == sizeof(obj)) {
-      return std::unique_ptr<T>(obj);
+      return std::unique_ptr<T, D>(obj, deleter);
     }
     else if (got == 0) {
       throw std::runtime_error("EOF while reading from Channel receiver");
@@ -226,8 +254,8 @@ namespace channel
     }
   }
 
-  template <typename T>
-  std::pair<Sender<T>, Receiver<T>> createObjectQueue(bool nonBlocking, size_t pipeBufferSize)
+  template <typename T, typename D>
+  std::pair<Sender<T, D>, Receiver<T, D>> createObjectQueue(bool nonBlocking, size_t pipeBufferSize)
   {
     int fds[2] = {-1, -1};
     if (pipe(fds) < 0) {
@@ -251,7 +279,7 @@ namespace channel
       setPipeBufferSize(receiver.getHandle(), pipeBufferSize);
     }
 
-    return std::pair(Sender<T>(std::move(sender)), Receiver<T>(std::move(receiver)));
+    return std::pair(Sender<T, D>(std::move(sender)), Receiver<T, D>(std::move(receiver)));
   }
 }
 }
index 8773a158f1c57b903ca106cb64a87b34904cfdae..7a0fbd5f888bfc7d85a3549efe28fd0cdc0f2d50 100644 (file)
@@ -268,7 +268,7 @@ bool RecursorSNMPAgent::sendCustomTrap(const std::string& reason)
                             reason.c_str(),
                             reason.size());
 
-  return sendTrap(d_trapPipe[1], varList);
+  return sendTrap(d_sender, varList);
 #endif /* HAVE_NET_SNMP */
   return true;
 }
index 18a088ea435307a9f3b9c4e3d90f449dd08ff0be..b70a5b280dc4ebea81b4c722616e6c920e68ba26 100644 (file)
@@ -41,32 +41,31 @@ int SNMPAgent::setCounter64Value(netsnmp_request_info* request,
   return SNMP_ERR_NOERROR;
 }
 
-bool SNMPAgent::sendTrap(int fd,
+bool SNMPAgent::sendTrap(pdns::channel::Sender<netsnmp_variable_list, void(*)(netsnmp_variable_list*)>& sender,
                          netsnmp_variable_list* varList)
 {
-  ssize_t written = write(fd, &varList, sizeof(varList));
-
-  if (written != sizeof(varList)) {
-    snmp_free_varbind(varList);
+  try  {
+    auto obj = std::unique_ptr<netsnmp_variable_list, void(*)(netsnmp_variable_list*)>(varList, snmp_free_varbind);
+    return sender.send(std::move(obj));
+  }
+  catch (...) {
     return false;
   }
-  return true;
 }
 
 void SNMPAgent::handleTrapsEvent()
 {
-  netsnmp_variable_list* varList = nullptr;
-  ssize_t got = 0;
-
-  do {
-    got = read(d_trapPipe[0], &varList, sizeof(varList));
-
-    if (got == sizeof(varList)) {
-      send_v2trap(varList);
-      snmp_free_varbind(varList);
+  try {
+    while (true) {
+      auto obj = d_receiver.receive(snmp_free_varbind);
+      if (!obj) {
+        break;
+      }
+      send_v2trap(obj->get());
     }
   }
-  while (got > 0);
+  catch (const std::exception& e) {
+  }
 }
 
 void SNMPAgent::handleSNMPQueryEvent(int fd)
@@ -121,7 +120,7 @@ void SNMPAgent::worker()
 
   /* we want to be notified if a trap is waiting
    to be sent */
-  mplexer->addReadFD(d_trapPipe[0], &handleTrapsCB, this);
+  mplexer->addReadFD(d_receiver.getDescriptor(), &handleTrapsCB, this);
 
   while(true) {
     netsnmp_large_fd_set_init(&fdset, FD_SETSIZE);
@@ -187,20 +186,8 @@ SNMPAgent::SNMPAgent(const std::string& name, const std::string& daemonSocket)
 
   init_snmp(name.c_str());
 
-  if (pipe(d_trapPipe) < 0)
-    unixDie("Creating pipe");
-
-  if (!setNonBlocking(d_trapPipe[0])) {
-    close(d_trapPipe[0]);
-    close(d_trapPipe[1]);
-    unixDie("Setting pipe non-blocking");
-  }
-
-  if (!setNonBlocking(d_trapPipe[1])) {
-    close(d_trapPipe[0]);
-    close(d_trapPipe[1]);
-    unixDie("Setting pipe non-blocking");
-  }
-
+  auto [sender, receiver] = pdns::channel::createObjectQueue<netsnmp_variable_list, void(*)(netsnmp_variable_list*)>(true);
+  d_sender = std::move(sender);
+  d_receiver = std::move(receiver);
 #endif /* HAVE_NET_SNMP */
 }
index e4ba13420dd2d474e699230be4ed5d9763cc95a5..f5df62ba746d7f2a3fa822a025cf6e43b1361b0f 100644 (file)
@@ -16,6 +16,7 @@
 #endif /* HAVE_NET_SNMP */
 
 #include "mplexer.hh"
+#include "channel.hh"
 
 class SNMPAgent
 {
@@ -23,11 +24,6 @@ public:
   SNMPAgent(const std::string& name, const std::string& daemonSocket);
   virtual ~SNMPAgent()
   {
-#ifdef HAVE_NET_SNMP
-    
-    close(d_trapPipe[0]);
-    close(d_trapPipe[1]);
-#endif /* HAVE_NET_SNMP */
   }
 
   void run()
@@ -48,10 +44,11 @@ protected:
   static const oid snmpTrapOID[];
   static const size_t snmpTrapOIDLen;
 
-  static bool sendTrap(int fd,
+  static bool sendTrap(pdns::channel::Sender<netsnmp_variable_list, void(*)(netsnmp_variable_list*)>& sender,
                        netsnmp_variable_list* varList);
 
-  int d_trapPipe[2] = { -1, -1};
+  pdns::channel::Sender<netsnmp_variable_list, void(*)(netsnmp_variable_list*)> d_sender;
+  pdns::channel::Receiver<netsnmp_variable_list, void(*)(netsnmp_variable_list*)> d_receiver;
 #endif /* HAVE_NET_SNMP */
 private:
   void worker();