Fix Unix socket usage on macOS

macOS does not support abstract namespaces for sockets.
This patch fixes the socket usage by using a real path instead
of abstract namespaces.

Change-Id: I434e92e86fb87d1085c35b5d707d1238295192fb
Reviewed-on: http://gerrit.cloudera.org:8080/16458
Tested-by: Grant Henke <granthenke@apache.org>
Reviewed-by: Alexey Serbin <aserbin@cloudera.com>
diff --git a/src/kudu/rpc/rpc-test.cc b/src/kudu/rpc/rpc-test.cc
index 92279f2..ce16f0d 100644
--- a/src/kudu/rpc/rpc-test.cc
+++ b/src/kudu/rpc/rpc-test.cc
@@ -112,9 +112,11 @@
   }
   Sockaddr bind_addr() const {
     if (use_unix_socket()) {
+      // Ensure multiple calls to bind_addr work by unlinking the socket file.
+      // The only way to reuse a socket file is to remove it with unlink().
+      unlink(socket_path_.c_str());
       Sockaddr addr;
-      string path = Substitute("@kudu-test-$0", getpid());
-      CHECK_OK(addr.ParseUnixDomainPath(path));
+      CHECK_OK(addr.ParseUnixDomainPath(socket_path_));
       return addr;
     }
     return Sockaddr::Wildcard();
@@ -125,6 +127,13 @@
     }
     return bound_addr.ToString();
   }
+  void TearDown() override {
+    RpcTestBase::TearDown();
+    // Ensure we cleanup the socket file on teardown.
+    unlink(socket_path_.c_str());
+  }
+
+  std::string socket_path_ = GetTestSocketPath("rpc-test");
 };
 
 // This is used to run all parameterized tests with and without SSL, on Unix sockets
diff --git a/src/kudu/server/server_base.cc b/src/kudu/server/server_base.cc
index c030e22..dcc8afd 100644
--- a/src/kudu/server/server_base.cc
+++ b/src/kudu/server/server_base.cc
@@ -536,8 +536,13 @@
   if (FLAGS_rpc_listen_on_unix_domain_socket) {
     VLOG(1) << "Enabling listening on unix domain socket.";
     Sockaddr addr;
+#if !defined(__APPLE__)
     RETURN_NOT_OK_PREPEND(addr.ParseUnixDomainPath(Substitute("@kudu-$0", fs_manager_->uuid())),
                           "unable to parse provided UNIX socket path");
+#else
+    RETURN_NOT_OK_PREPEND(addr.ParseUnixDomainPath(Substitute("/tmp/kudu-$0", fs_manager_->uuid())),
+                          "unable to parse provided UNIX socket path");
+#endif
     RETURN_NOT_OK_PREPEND(rpc_server_->AddBindAddress(addr),
                           "unable to add configured UNIX socket path to list of bind addresses "
                           "for RPC server");
diff --git a/src/kudu/util/net/socket-test.cc b/src/kudu/util/net/socket-test.cc
index df966d5..fb22db0 100644
--- a/src/kudu/util/net/socket-test.cc
+++ b/src/kudu/util/net/socket-test.cc
@@ -29,6 +29,7 @@
 #include <gtest/gtest.h>
 
 #include "kudu/gutil/strings/substitute.h"
+#include "kudu/gutil/strings/util.h"
 #include "kudu/util/monotime.h"
 #include "kudu/util/net/sockaddr.h"
 #include "kudu/util/scoped_cleanup.h"
@@ -112,8 +113,7 @@
           // Test GetPeerAddress from server side.
           Sockaddr peer_addr;
           CHECK_OK(sock.GetPeerAddress(&peer_addr));
-          CHECK_EQ("unix:<unnamed>", peer_addr.ToString());
-
+          CHECK(HasPrefixString(peer_addr.ToString(), "unix:"));
           size_t n_written;
           CHECK_OK(sock.BlockingWrite(
               reinterpret_cast<const uint8_t*>(kData.data()), kData.size(), &n_written,
@@ -149,17 +149,18 @@
   DoTestServerDisconnects(true, "recv got EOF from 127.0.0.1:[0-9]+");
 }
 
+// Apple does not support abstract namespaces in sockets.
+#if !defined(__APPLE__)
 TEST_F(SocketTest, TestUnixSocketAbstractNamespace) {
   DoUnixSocketTest(strings::Substitute("@kudu-test-pid-$0", getpid()));
 }
+#endif
+
 TEST_F(SocketTest, TestUnixSocketFilesystemPath) {
-  // Use a path in /tmp/ instead of the normal GetTestPath approach because
-  // unix domain socket paths are limited in length. The test directory
-  // may be too long.
-  string path = strings::Substitute("/tmp/kudu-test-pid-$0", getpid());
+  string path = GetTestSocketPath("socket-test");
   SCOPED_CLEANUP({
-      unlink(path.c_str());
-    });
+    unlink(path.c_str());
+  });
   DoUnixSocketTest(path);
 }
 
diff --git a/src/kudu/util/test_util.cc b/src/kudu/util/test_util.cc
index bce1693..48fa73a 100644
--- a/src/kudu/util/test_util.cc
+++ b/src/kudu/util/test_util.cc
@@ -51,6 +51,7 @@
 #include "kudu/gutil/walltime.h"
 #include "kudu/util/env.h"
 #include "kudu/util/faststring.h"
+#include "kudu/util/oid_generator.h"
 #include "kudu/util/path_util.h"
 #include "kudu/util/scoped_cleanup.h"
 #include "kudu/util/slice.h"
@@ -280,6 +281,14 @@
   return dir;
 }
 
+string GetTestSocketPath(const string& name) {
+  string dir;
+  CHECK_OK(Env::Default()->GetTestDirectory(&dir));
+  ObjectIdGenerator generator;
+  string uuid = generator.Next();
+  return JoinPathSegments(dir, Substitute("$0-$1.sock", name, uuid));
+}
+
 string GetTestExecutableDirectory() {
   string exec;
   CHECK_OK(Env::Default()->GetExecutablePath(&exec));
diff --git a/src/kudu/util/test_util.h b/src/kudu/util/test_util.h
index 9f60aef..a8139f8 100644
--- a/src/kudu/util/test_util.h
+++ b/src/kudu/util/test_util.h
@@ -112,6 +112,13 @@
 // if a KuduTest instance is available.
 std::string GetTestDataDirectory();
 
+// Returns a unique socket path for use in tests in the form of:
+//   <test-dir>/<name>-<uuid>.sock
+//
+// The path is in the base test directory because the path to Unix domain
+// socket file cannot be longer than ~100 bytes.
+std::string GetTestSocketPath(const std::string& name);
+
 // Return the directory which contains the test's executable.
 std::string GetTestExecutableDirectory();