]> git.ipfire.org Git - thirdparty/bacula.git/commitdiff
BEE Backport bacula/src/lib/bsock_meeting.c/h
authorEric Bollengier <eric@baculasystems.com>
Tue, 12 May 2020 19:31:34 +0000 (21:31 +0200)
committerEric Bollengier <eric@baculasystems.com>
Thu, 29 Apr 2021 08:44:18 +0000 (10:44 +0200)
bacula/src/lib/bsock_meeting.c [new file with mode: 0644]
bacula/src/lib/bsock_meeting.h [new file with mode: 0644]

diff --git a/bacula/src/lib/bsock_meeting.c b/bacula/src/lib/bsock_meeting.c
new file mode 100644 (file)
index 0000000..f56ad3e
--- /dev/null
@@ -0,0 +1,171 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2020 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+/*
+ * Written by Eric Bollengier MMXVIII
+ */
+
+/* Code to handle Bacula Client behind a NAT without a VPN 
+ * We keep a connection idle with a short TCP keepalive to detect
+ * any problem very quickly.
+ *
+ * A thread can be responsible to listen all sockets and reply to
+ * a ISALIVE request. A ISALIVE request can be send after a wake up
+ * for example.
+ */
+
+#include "bacula.h"
+#include "bsock_meeting.h"
+
+static int dbglvl = DT_NETWORK|50;
+
+BsockMeeting::BsockMeeting(): socket(NULL), keepidle(0), keepintvl(0) {
+   pthread_mutex_init(&mutex, NULL);
+   pthread_cond_init(&cond, NULL);
+};
+
+BsockMeeting::~BsockMeeting() {
+   pthread_mutex_destroy(&mutex);
+   pthread_cond_destroy(&cond);
+   free_bsock(socket);
+};
+
+void BsockMeeting::set(BSOCK *s) {
+   int turnon=1;
+   P(mutex);
+   free_bsock(socket);          /* We are the owner of the socket object, we can use free */
+   socket = s;
+
+   /*
+    * Keep socket from timing out from inactivity
+    */
+   if (setsockopt(socket->m_fd, SOL_SOCKET, SO_KEEPALIVE, (sockopt_val_t)&turnon, sizeof(turnon)) < 0) {
+      berrno be;
+      Dmsg1(dbglvl, _("Cannot set SO_KEEPALIVE on socket: %s\n"), be.bstrerror());
+   }
+#if defined(TCP_KEEPIDLE)
+   if (getsockopt(socket->m_fd, SOL_TCP, TCP_KEEPIDLE, (sockopt_val_t)&keepidle, sizeof(keepidle)) < 0) {
+      berrno be;
+      Dmsg1(dbglvl, _("Cannot get TCP_KEEPIDLE on socket: %s\n"), be.bstrerror());
+   }
+   opt = 10000;
+   if (setsockopt(socket->m_fd, SOL_TCP, TCP_KEEPIDLE, (sockopt_val_t)&opt, sizeof(opt)) < 0) {
+      berrno be;
+      Dmsg1(dbglvl, _("Cannot set TCP_KEEPIDLE on socket: %s\n"), be.bstrerror());
+   }
+#endif
+#if defined(TCP_KEEPINTVL)
+   if (getsockopt(socket->m_fd, SOL_TCP, TCP_KEEPINTVL, (sockopt_val_t)&keepintvl, sizeof(keepintvl)) < 0) {
+      berrno be;
+      Dmsg1(dbglvl, _("Cannot get TCP_KEEPINTVL on socket: %s\n"), be.bstrerror());
+   }
+   opt = 5000;
+   if (setsockopt(socket->m_fd, SOL_TCP, TCP_KEEPINTVL, (sockopt_val_t)&opt, sizeof(opt)) < 0) {
+      berrno be;
+      Dmsg1(dbglvl, _("Cannot set TCP_KEEPINTVL on socket: %s\n"), be.bstrerror());
+   }
+#endif
+   pthread_cond_signal(&cond);
+   V(mutex);
+};
+
+/* Get the current Socket, wait timeout to get it */
+BSOCK *BsockMeeting::get(int timeout) {
+   BSOCK *ret=NULL;
+   struct timespec to;
+   btimer_t *t;
+   int32_t sig=0;
+
+   P(mutex);
+   to.tv_sec = time(NULL) + timeout;
+   to.tv_nsec = 0;
+   while (socket == NULL) {
+      Dmsg0(dbglvl, "socket is null...\n");
+      int err = pthread_cond_timedwait(&cond, &mutex, &to);
+      if (err == ETIMEDOUT) {
+         Dmsg0(dbglvl, "Timeout\n");
+         /* timeout, do something */
+         break;
+      } else {
+         Dmsg2(dbglvl, "timedwait=%d socket=%p\n", err, socket);
+      }
+   }
+   /* TODO: We need to test if the socket is still valid, and we need a read() to raise
+    * an error. We don't want to give a broken socket to the job
+    */
+   if (socket) {
+      Dmsg0(dbglvl, "Found a socket in the proxy\n");
+      t = start_bsock_timer(socket, 10);
+      socket->signal(BNET_ISALIVE);
+      sig = socket->recv();
+      stop_bsock_timer(t);
+
+      if (sig != -1 || socket->msglen != BNET_ISALIVE) {
+         Dmsg2(dbglvl, "Socket seems broken sig=%d msglen=%d\n", sig, socket->msglen);
+         free_bsock(socket);
+         V(mutex);           /* Let's get a new one */
+         return get(timeout);
+      }
+      Dmsg0(dbglvl, "Socket seems OK\n");
+   }
+   if (socket) {
+#if defined(TCP_KEEPIDLE)
+      if (setsockopt(socket->m_fd, SOL_TCP, TCP_KEEPIDLE, (sockopt_val_t)&keepidle, sizeof(keepidle)) < 0) {
+         berrno be;
+         Dmsg1(dbglvl, _("Cannot set TCP_KEEPIDLE on socket: %s\n"), be.bstrerror());
+      }
+#endif
+#if defined(TCP_KEEPINTVL)
+      if (setsockopt(socket->m_fd, SOL_TCP, TCP_KEEPINTVL, (sockopt_val_t)&keepintvl, sizeof(keepintvl)) < 0) {
+         berrno be;
+         Dmsg1(dbglvl, _("Cannot set TCP_KEEPINTVL on socket: %s\n"), be.bstrerror());
+      }
+#endif
+   }
+   ret = socket;
+   socket = NULL;
+   V(mutex);          
+   return ret;
+};
+
+bool BsockMeeting::is_set(POOLMEM *&address)
+{
+   lock_guard g(mutex);
+   if (address) {
+      *address = 0;
+   }
+   if (!socket) {
+      return false;
+   }
+   if (address) {
+      pm_strcpy(address, socket->host());
+   }
+   return true;
+}
+
+void BsockMeeting::wait_request(BSOCK *dir) {
+   int32_t sig = dir->recv();
+   if (sig == -1 && dir->msglen == BNET_ISALIVE) {
+      /* nop */
+      dir->signal(BNET_ISALIVE);
+   } else {
+      Dmsg1(dbglvl, "got incorrect message sig=%d\n", sig);
+      dir->close();
+   }
+};
diff --git a/bacula/src/lib/bsock_meeting.h b/bacula/src/lib/bsock_meeting.h
new file mode 100644 (file)
index 0000000..f1c7baf
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+   Bacula(R) - The Network Backup Solution
+
+   Copyright (C) 2000-2020 Kern Sibbald
+
+   The original author of Bacula is Kern Sibbald, with contributions
+   from many others, a complete list can be found in the file AUTHORS.
+
+   You may use this file and others of this release according to the
+   license defined in the LICENSE file, which includes the Affero General
+   Public License, v3.0 ("AGPLv3") and some additional permissions and
+   terms pursuant to its AGPLv3 Section 7.
+
+   This notice must be preserved when any source code is
+   conveyed and/or propagated.
+
+   Bacula(R) is a registered trademark of Kern Sibbald.
+*/
+
+#ifndef BSOCK_MEETING_H
+#define BSOCK_MEETING_H
+
+/*
+ * Written by Eric Bollengier MMXVIII
+ */
+
+/*
+ * The BsockMeeting class is reponsible to create a meeting 
+ * point between a client and a server.
+ *
+ * It can be used for example for Client that are behind a
+ * firewall and cannot be contacted by the Director.
+ *
+ * The Client can initiate the connection, it will be stored inside a
+ * BsockMeeting object. The Director can query the BsockMeeting object at any
+ * time and will wait to get the BSOCK object, or get the current one if any.
+ *
+ * The TCP keepalive settings are modified to maintain the connection alive and
+ * detect problems. The TCP settings are restored automatically.
+ */
+class BsockMeeting: public SMARTALLOC
+{
+private:
+   BSOCK *socket;               /* Socket stored, NULL if empty */
+   int keepidle;                /* original keepidle setting */
+   int keepintvl;               /* original keepintvl setting */
+
+   /* Initialized in the construction body */
+   pthread_mutex_t mutex;
+   pthread_cond_t  cond;
+
+public:
+   BsockMeeting();
+   ~BsockMeeting();
+
+   void set(BSOCK *s);         /* Assign a BSOCK object */
+   bool is_set(POOLMEM *&host);/* Query if we have a BSOCK, and from who */
+   BSOCK *get(int timeout);   /* Get the current BSOCK Object, wait if needed */
+   void wait_request(BSOCK *dir); /* Wait for a Director request (client side) */
+};
+
+#endif