Support socket activation by fd passing
Client: cpp
Patch: Federico Giovanardi

This closes #3211
diff --git a/test/cpp/src/TestServer.cpp b/test/cpp/src/TestServer.cpp
index 858fffa..dc95af6 100644
--- a/test/cpp/src/TestServer.cpp
+++ b/test/cpp/src/TestServer.cpp
@@ -31,6 +31,7 @@
 #include <thrift/server/TSimpleServer.h>
 #include <thrift/server/TThreadPoolServer.h>
 #include <thrift/server/TThreadedServer.h>
+#include <thrift/transport/PlatformSocket.h>
 #include <thrift/transport/THttpServer.h>
 #include <thrift/transport/THttpTransport.h>
 #include <thrift/transport/TNonblockingSSLServerSocket.h>
@@ -54,14 +55,21 @@
 #ifdef HAVE_SIGNAL_H
 #include <signal.h>
 #endif
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_UN_H
+#include <sys/un.h>
+#endif
 
 #include <iostream>
-#include <stdexcept>
+#include <memory>
 #include <sstream>
+#include <stdexcept>
 
 #include <boost/algorithm/string.hpp>
-#include <boost/program_options.hpp>
 #include <boost/filesystem.hpp>
+#include <boost/program_options.hpp>
 
 #if _WIN32
 #include <thrift/windows/TWinsockSingleton.h>
@@ -570,6 +578,47 @@
   std::shared_ptr<TestHandler> _delegate;
 };
 
+struct DomainSocketFd {
+  THRIFT_SOCKET socket_fd;
+  std::string path;
+  DomainSocketFd(const std::string& path) : path(path) {
+#ifdef HAVE_SYS_UN_H
+    unlink(path.c_str());
+    socket_fd = socket(AF_UNIX, SOCK_STREAM, IPPROTO_IP);
+    if (socket_fd == -1) {
+      std::ostringstream os;
+      os << "Cannot create domain socket: " << strerror(errno);
+      throw std::runtime_error(os.str());
+    }
+    if (path.size() > sizeof(sockaddr_un::sun_path) - 1)
+      throw std::runtime_error("Path size on domain socket too big");
+    struct sockaddr_un sa;
+    memset(&sa, 0, sizeof(sa));
+    sa.sun_family = AF_UNIX;
+    strcpy(sa.sun_path, path.c_str());
+    int rv = bind(socket_fd, (struct sockaddr*)&sa, sizeof(sa));
+    if (rv == -1) {
+      std::ostringstream os;
+      os << "Cannot bind domain socket: " << strerror(errno);
+      throw std::runtime_error(os.str());
+    }
+
+    rv = ::listen(socket_fd, 16);
+    if (rv == -1) {
+      std::ostringstream os;
+      os << "Cannot listen on domain socket: " << strerror(errno);
+      throw std::runtime_error(os.str());
+    }
+#else
+    throw std::runtime_error("Cannot create a domain socket without AF_UNIX");
+#endif
+  }
+  ~DomainSocketFd() {
+    ::THRIFT_CLOSESOCKET(socket_fd);
+    unlink(path.c_str());
+  }
+};
+
 namespace po = boost::program_options;
 
 int main(int argc, char** argv) {
@@ -589,6 +638,8 @@
   string server_type = "simple";
   string domain_socket = "";
   bool abstract_namespace = false;
+  bool emulate_socketactivation = false;
+  std::unique_ptr<DomainSocketFd> domain_socket_fd;
   size_t workers = 4;
   int string_limit = 0;
   int container_limit = 0;
@@ -599,6 +650,7 @@
     ("port", po::value<int>(&port)->default_value(port), "Port number to listen")
     ("domain-socket", po::value<string>(&domain_socket) ->default_value(domain_socket), "Unix Domain Socket (e.g. /tmp/ThriftTest.thrift)")
     ("abstract-namespace", "Create the domain socket in the Abstract Namespace (no connection with filesystem pathnames)")
+    ("emulate-socketactivation","Open the socket from the tester program and pass the library an already open fd")
     ("server-type", po::value<string>(&server_type)->default_value(server_type), "type of server, \"simple\", \"thread-pool\", \"threaded\", or \"nonblocking\"")
     ("transport", po::value<string>(&transport_type)->default_value(transport_type), "transport: buffered, framed, http, websocket, zlib")
     ("protocol", po::value<string>(&protocol_type)->default_value(protocol_type), "protocol: binary, compact, header, json, multi, multic, multih, multij")
@@ -678,6 +730,9 @@
   if (vm.count("abstract-namespace")) {
     abstract_namespace = true;
   }
+  if (vm.count("emulate-socketactivation")) {
+    emulate_socketactivation = true;
+  }
 
   // Dispatcher
   std::shared_ptr<TProtocolFactory> protocolFactory;
@@ -727,8 +782,16 @@
         abstract_socket += domain_socket;
         serverSocket = std::shared_ptr<TServerSocket>(new TServerSocket(abstract_socket));
       } else {
-        unlink(domain_socket.c_str());
-        serverSocket = std::shared_ptr<TServerSocket>(new TServerSocket(domain_socket));
+        if (emulate_socketactivation) {
+          unlink(domain_socket.c_str());
+          // open and bind the socket
+          domain_socket_fd.reset(new DomainSocketFd(domain_socket));
+          serverSocket = std::shared_ptr<TServerSocket>(
+              new TServerSocket(domain_socket_fd->socket_fd, SocketType::UNIX));
+        } else {
+          unlink(domain_socket.c_str());
+          serverSocket = std::shared_ptr<TServerSocket>(new TServerSocket(domain_socket));
+        }
       }
       port = 0;
     } else {
diff --git a/test/crossrunner/run.py b/test/crossrunner/run.py
index 3ccc6e3..e532417 100644
--- a/test/crossrunner/run.py
+++ b/test/crossrunner/run.py
@@ -306,7 +306,7 @@
         return port if ok else self._get_domain_port()
 
     def alloc_port(self, socket_type):
-        if socket_type in ('domain', 'abstract'):
+        if socket_type in ('domain', 'abstract','domain-socketactivated'):
             return self._get_domain_port()
         else:
             return self._get_tcp_port()
@@ -323,7 +323,7 @@
         self._log.debug('free_port')
         self._lock.acquire()
         try:
-            if socket_type == 'domain':
+            if socket_type in ['domain','domain-socketactivated']:
                 self._dom_ports.remove(port)
                 path = domain_socket_path(port)
                 if os.path.exists(path):
diff --git a/test/crossrunner/test.py b/test/crossrunner/test.py
index 2a1a4da..3da38f4 100644
--- a/test/crossrunner/test.py
+++ b/test/crossrunner/test.py
@@ -59,9 +59,11 @@
         return cmd
 
     def _socket_args(self, socket, port):
+        support_socket_activation = self.kind == 'server' and sys.platform != "win32"
         return {
             'ip-ssl': ['--ssl'],
             'domain': ['--domain-socket=%s' % domain_socket_path(port)],
+            'domain-socketactivated': (['--emulate-socketactivation'] if support_socket_activation else []) + ['--domain-socket=%s' % domain_socket_path(port)],
             'abstract': ['--abstract-namespace', '--domain-socket=%s' % domain_socket_path(port)],
         }.get(socket, None)
 
diff --git a/test/tests.json b/test/tests.json
index 16b47ac..9731a88 100644
--- a/test/tests.json
+++ b/test/tests.json
@@ -404,13 +404,13 @@
       "buffered",
       "http",
       "framed",
-      "zlib",
-      "websocket"
+      "zlib"
     ],
     "sockets": [
       "ip",
       "ip-ssl",
-      "domain"
+      "domain",
+      "domain-socketactivated"
     ],
     "protocols": [
       "compact",