Index: mojo/edk/embedder/named_platform_handle_utils_posix.cc |
diff --git a/mojo/edk/embedder/named_platform_handle_utils_posix.cc b/mojo/edk/embedder/named_platform_handle_utils_posix.cc |
new file mode 100644 |
index 0000000000000000000000000000000000000000..97572c0590377b89a1128d55c904923ec0be1865 |
--- /dev/null |
+++ b/mojo/edk/embedder/named_platform_handle_utils_posix.cc |
@@ -0,0 +1,147 @@ |
+// Copyright 2016 The Chromium Authors. All rights reserved. |
+// Use of this source code is governed by a BSD-style license that can be |
+// found in the LICENSE file. |
+ |
+#include "mojo/edk/embedder/named_platform_handle_utils.h" |
+ |
+#include <errno.h> |
+#include <sys/socket.h> |
+#include <sys/un.h> |
+#include <unistd.h> |
+ |
+#include "base/files/file_path.h" |
+#include "base/files/file_util.h" |
+#include "base/logging.h" |
+#include "base/posix/eintr_wrapper.h" |
+#include "mojo/edk/embedder/named_platform_handle.h" |
+ |
+namespace mojo { |
+namespace edk { |
+namespace { |
+ |
+// The maximum length of the name of a socket for MODE_NAMED_SERVER or |
+// MODE_NAMED_CLIENT if you want to pass in your own socket. |
+// The standard size on linux is 108, mac is 104. To maintain consistency |
+// across platforms we standardize on the smaller value. |
+const size_t kMaxSocketNameLength = 104; |
+ |
+// This function fills in |unix_addr| with the appropriate data for the socket, |
+// and sets |unix_addr_len| to the length of the data therein. |
+// Returns true on success, or false on failure (typically because |handle.name| |
+// violated the naming rules). |
+bool MakeUnixAddr(const NamedPlatformHandle& handle, |
+ struct sockaddr_un* unix_addr, |
+ size_t* unix_addr_len) { |
+ DCHECK(unix_addr); |
+ DCHECK(unix_addr_len); |
+ DCHECK(handle.is_valid()); |
+ |
+ // We reject handle.name.length() == kMaxSocketNameLength to make room for the |
+ // NUL terminator at the end of the string. |
+ if (handle.name.length() >= kMaxSocketNameLength) { |
+ LOG(ERROR) << "Socket name too long: " << handle.name; |
+ return false; |
+ } |
+ |
+ // Create unix_addr structure. |
+ memset(unix_addr, 0, sizeof(struct sockaddr_un)); |
+ unix_addr->sun_family = AF_UNIX; |
+ strncpy(unix_addr->sun_path, handle.name.c_str(), kMaxSocketNameLength); |
+ *unix_addr_len = |
+ offsetof(struct sockaddr_un, sun_path) + handle.name.length(); |
+ return true; |
+} |
+ |
+// This function creates a unix domain socket, and set it as non-blocking. |
+// If successful, this returns a ScopedPlatformHandle containing the socket. |
+// Otherwise, this returns an invalid ScopedPlatformHandle. |
+ScopedPlatformHandle CreateUnixDomainSocket(bool needs_connection) { |
+ // Create the unix domain socket. |
+ PlatformHandle socket_handle(socket(AF_UNIX, SOCK_STREAM, 0)); |
+ socket_handle.needs_connection = needs_connection; |
+ ScopedPlatformHandle handle(socket_handle); |
+ if (!handle.is_valid()) { |
+ PLOG(ERROR) << "Failed to create AF_UNIX socket."; |
+ return ScopedPlatformHandle(); |
+ } |
+ |
+ // Now set it as non-blocking. |
+ if (!base::SetNonBlocking(handle.get().handle)) { |
+ PLOG(ERROR) << "base::SetNonBlocking() failed " << handle.get().handle; |
+ return ScopedPlatformHandle(); |
+ } |
+ return handle; |
+} |
+ |
+} // namespace |
+ |
+ScopedPlatformHandle CreateClientHandle( |
+ const NamedPlatformHandle& named_handle) { |
+ if (!named_handle.is_valid()) |
+ return ScopedPlatformHandle(); |
+ |
+ struct sockaddr_un unix_addr; |
+ size_t unix_addr_len; |
+ if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len)) |
+ return ScopedPlatformHandle(); |
+ |
+ ScopedPlatformHandle handle = CreateUnixDomainSocket(false); |
+ if (!handle.is_valid()) |
+ return ScopedPlatformHandle(); |
+ |
+ if (HANDLE_EINTR(connect(handle.get().handle, |
+ reinterpret_cast<sockaddr*>(&unix_addr), |
+ unix_addr_len)) < 0) { |
+ PLOG(ERROR) << "connect " << named_handle.name; |
+ return ScopedPlatformHandle(); |
+ } |
+ |
+ return handle; |
+} |
+ |
+ScopedPlatformHandle CreateServerHandle(const NamedPlatformHandle& named_handle, |
+ bool enforce_uniqueness) { |
+ CHECK(!enforce_uniqueness); |
+ if (!named_handle.is_valid()) |
+ return ScopedPlatformHandle(); |
+ |
+ // Make sure the path we need exists. |
+ base::FilePath socket_dir = base::FilePath(named_handle.name).DirName(); |
+ if (!base::CreateDirectory(socket_dir)) { |
+ LOG(ERROR) << "Couldn't create directory: " << socket_dir.value(); |
+ return ScopedPlatformHandle(); |
+ } |
+ |
+ // Delete any old FS instances. |
+ if (unlink(named_handle.name.c_str()) < 0 && errno != ENOENT) { |
+ PLOG(ERROR) << "unlink " << named_handle.name; |
+ return ScopedPlatformHandle(); |
+ } |
+ |
+ struct sockaddr_un unix_addr; |
+ size_t unix_addr_len; |
+ if (!MakeUnixAddr(named_handle, &unix_addr, &unix_addr_len)) |
+ return ScopedPlatformHandle(); |
+ |
+ ScopedPlatformHandle handle = CreateUnixDomainSocket(true); |
+ if (!handle.is_valid()) |
+ return ScopedPlatformHandle(); |
+ |
+ // Bind the socket. |
+ if (bind(handle.get().handle, reinterpret_cast<const sockaddr*>(&unix_addr), |
+ unix_addr_len) < 0) { |
+ PLOG(ERROR) << "bind " << named_handle.name; |
+ return ScopedPlatformHandle(); |
+ } |
+ |
+ // Start listening on the socket. |
+ if (listen(handle.get().handle, SOMAXCONN) < 0) { |
+ PLOG(ERROR) << "listen " << named_handle.name; |
+ unlink(named_handle.name.c_str()); |
+ return ScopedPlatformHandle(); |
+ } |
+ return handle; |
+} |
+ |
+} // namespace edk |
+} // namespace mojo |