Commit c76b89b6 authored by Nikita Lutsenko's avatar Nikita Lutsenko Committed by Facebook GitHub Bot

Stub out sockets API for xros

Summary: As per title, stub it out in a way that will just trigger exceptions for everything not yet implemented.

Reviewed By: yfeldblum

Differential Revision: D26579892

fbshipit-source-id: 5bfc20b1f6737bbda80616ea1407a5cf108be3c3
parent 54b8e62e
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <cerrno> #include <cerrno>
#include <cstddef> #include <cstddef>
#include <stdexcept>
#include <folly/Portability.h> #include <folly/Portability.h>
#include <folly/net/detail/SocketFileDescriptorMap.h> #include <folly/net/detail/SocketFileDescriptorMap.h>
...@@ -85,11 +86,18 @@ static R wrapSocketFunction(F f, NetworkSocket s, Args... args) { ...@@ -85,11 +86,18 @@ static R wrapSocketFunction(F f, NetworkSocket s, Args... args) {
} // namespace } // namespace
NetworkSocket accept(NetworkSocket s, sockaddr* addr, socklen_t* addrlen) { NetworkSocket accept(NetworkSocket s, sockaddr* addr, socklen_t* addrlen) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
return NetworkSocket(wrapSocketFunction<NetworkSocket::native_handle_type>( return NetworkSocket(wrapSocketFunction<NetworkSocket::native_handle_type>(
::accept, s, addr, addrlen)); ::accept, s, addr, addrlen));
#endif
} }
int bind(NetworkSocket s, const sockaddr* name, socklen_t namelen) { int bind(NetworkSocket s, const sockaddr* name, socklen_t namelen) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
if (kIsWindows && name->sa_family == AF_UNIX) { if (kIsWindows && name->sa_family == AF_UNIX) {
// Windows added support for AF_UNIX sockets, but didn't add // Windows added support for AF_UNIX sockets, but didn't add
// support for autobind sockets, so detect requests for autobind // support for autobind sockets, so detect requests for autobind
...@@ -101,13 +109,21 @@ int bind(NetworkSocket s, const sockaddr* name, socklen_t namelen) { ...@@ -101,13 +109,21 @@ int bind(NetworkSocket s, const sockaddr* name, socklen_t namelen) {
} }
} }
return wrapSocketFunction<int>(::bind, s, name, namelen); return wrapSocketFunction<int>(::bind, s, name, namelen);
#endif
} }
int close(NetworkSocket s) { int close(NetworkSocket s) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
return netops::detail::SocketFileDescriptorMap::close(s.data); return netops::detail::SocketFileDescriptorMap::close(s.data);
#endif
} }
int connect(NetworkSocket s, const sockaddr* name, socklen_t namelen) { int connect(NetworkSocket s, const sockaddr* name, socklen_t namelen) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
auto r = wrapSocketFunction<int>(::connect, s, name, namelen); auto r = wrapSocketFunction<int>(::connect, s, name, namelen);
#ifdef _WIN32 #ifdef _WIN32
if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) { if (r == -1 && WSAGetLastError() == WSAEWOULDBLOCK) {
...@@ -115,18 +131,30 @@ int connect(NetworkSocket s, const sockaddr* name, socklen_t namelen) { ...@@ -115,18 +131,30 @@ int connect(NetworkSocket s, const sockaddr* name, socklen_t namelen) {
} }
#endif #endif
return r; return r;
#endif
} }
int getpeername(NetworkSocket s, sockaddr* name, socklen_t* namelen) { int getpeername(NetworkSocket s, sockaddr* name, socklen_t* namelen) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
return wrapSocketFunction<int>(::getpeername, s, name, namelen); return wrapSocketFunction<int>(::getpeername, s, name, namelen);
#endif
} }
int getsockname(NetworkSocket s, sockaddr* name, socklen_t* namelen) { int getsockname(NetworkSocket s, sockaddr* name, socklen_t* namelen) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
return wrapSocketFunction<int>(::getsockname, s, name, namelen); return wrapSocketFunction<int>(::getsockname, s, name, namelen);
#endif
} }
int getsockopt( int getsockopt(
NetworkSocket s, int level, int optname, void* optval, socklen_t* optlen) { NetworkSocket s, int level, int optname, void* optval, socklen_t* optlen) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
auto ret = wrapSocketFunction<int>( auto ret = wrapSocketFunction<int>(
::getsockopt, s, level, optname, (char*)optval, optlen); ::getsockopt, s, level, optname, (char*)optval, optlen);
#ifdef _WIN32 #ifdef _WIN32
...@@ -139,18 +167,30 @@ int getsockopt( ...@@ -139,18 +167,30 @@ int getsockopt(
} }
#endif #endif
return ret; return ret;
#endif
} }
int inet_aton(const char* cp, in_addr* inp) { int inet_aton(const char* cp, in_addr* inp) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
inp->s_addr = inet_addr(cp); inp->s_addr = inet_addr(cp);
return inp->s_addr == INADDR_NONE ? 0 : 1; return inp->s_addr == INADDR_NONE ? 0 : 1;
#endif
} }
int listen(NetworkSocket s, int backlog) { int listen(NetworkSocket s, int backlog) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
return wrapSocketFunction<int>(::listen, s, backlog); return wrapSocketFunction<int>(::listen, s, backlog);
#endif
} }
int poll(PollDescriptor fds[], nfds_t nfds, int timeout) { int poll(PollDescriptor fds[], nfds_t nfds, int timeout) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
// Make sure that PollDescriptor is byte-for-byte identical to pollfd, // Make sure that PollDescriptor is byte-for-byte identical to pollfd,
// so we don't need extra allocations just for the safety of this shim. // so we don't need extra allocations just for the safety of this shim.
static_assert( static_assert(
...@@ -187,6 +227,7 @@ int poll(PollDescriptor fds[], nfds_t nfds, int timeout) { ...@@ -187,6 +227,7 @@ int poll(PollDescriptor fds[], nfds_t nfds, int timeout) {
#else #else
return ::poll(files, nfds, timeout); return ::poll(files, nfds, timeout);
#endif #endif
#endif // defined(__XROS__)
} }
ssize_t recv(NetworkSocket s, void* buf, size_t len, int flags) { ssize_t recv(NetworkSocket s, void* buf, size_t len, int flags) {
...@@ -211,6 +252,8 @@ ssize_t recv(NetworkSocket s, void* buf, size_t len, int flags) { ...@@ -211,6 +252,8 @@ ssize_t recv(NetworkSocket s, void* buf, size_t len, int flags) {
} }
} }
return wrapSocketFunction<ssize_t>(::recv, s, (char*)buf, (int)len, flags); return wrapSocketFunction<ssize_t>(::recv, s, (char*)buf, (int)len, flags);
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
return wrapSocketFunction<ssize_t>(::recv, s, buf, len, flags); return wrapSocketFunction<ssize_t>(::recv, s, buf, len, flags);
#endif #endif
...@@ -271,6 +314,8 @@ ssize_t recvfrom( ...@@ -271,6 +314,8 @@ ssize_t recvfrom(
} }
return wrapSocketFunction<ssize_t>( return wrapSocketFunction<ssize_t>(
::recvfrom, s, (char*)buf, (int)len, flags, from, fromlen); ::recvfrom, s, (char*)buf, (int)len, flags, from, fromlen);
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
return wrapSocketFunction<ssize_t>( return wrapSocketFunction<ssize_t>(
::recvfrom, s, buf, len, flags, from, fromlen); ::recvfrom, s, buf, len, flags, from, fromlen);
...@@ -321,6 +366,8 @@ ssize_t recvmsg(NetworkSocket s, msghdr* message, int flags) { ...@@ -321,6 +366,8 @@ ssize_t recvmsg(NetworkSocket s, msghdr* message, int flags) {
int res = WSARecvMsg(h, &msg, &bytesReceived, nullptr, nullptr); int res = WSARecvMsg(h, &msg, &bytesReceived, nullptr, nullptr);
errno = translate_wsa_error(WSAGetLastError()); errno = translate_wsa_error(WSAGetLastError());
return res == 0 ? (ssize_t)bytesReceived : -1; return res == 0 ? (ssize_t)bytesReceived : -1;
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
return wrapSocketFunction<ssize_t>(::recvmsg, s, message, flags); return wrapSocketFunction<ssize_t>(::recvmsg, s, message, flags);
#endif #endif
...@@ -332,6 +379,9 @@ int recvmmsg( ...@@ -332,6 +379,9 @@ int recvmmsg(
unsigned int vlen, unsigned int vlen,
unsigned int flags, unsigned int flags,
timespec* timeout) { timespec* timeout) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
if (reinterpret_cast<void*>(::recvmmsg) != nullptr) { if (reinterpret_cast<void*>(::recvmmsg) != nullptr) {
return wrapSocketFunction<int>(::recvmmsg, s, msgvec, vlen, flags, timeout); return wrapSocketFunction<int>(::recvmmsg, s, msgvec, vlen, flags, timeout);
} }
...@@ -351,12 +401,15 @@ int recvmmsg( ...@@ -351,12 +401,15 @@ int recvmmsg(
} }
} }
return static_cast<int>(vlen); return static_cast<int>(vlen);
#endif
} }
ssize_t send(NetworkSocket s, const void* buf, size_t len, int flags) { ssize_t send(NetworkSocket s, const void* buf, size_t len, int flags) {
#ifdef _WIN32 #ifdef _WIN32
return wrapSocketFunction<ssize_t>( return wrapSocketFunction<ssize_t>(
::send, s, (const char*)buf, (int)len, flags); ::send, s, (const char*)buf, (int)len, flags);
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
return wrapSocketFunction<ssize_t>(::send, s, buf, len, flags); return wrapSocketFunction<ssize_t>(::send, s, buf, len, flags);
#endif #endif
...@@ -398,6 +451,8 @@ ssize_t sendmsg(NetworkSocket socket, const msghdr* message, int flags) { ...@@ -398,6 +451,8 @@ ssize_t sendmsg(NetworkSocket socket, const msghdr* message, int flags) {
bytesSent += r; bytesSent += r;
} }
return bytesSent; return bytesSent;
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
return wrapSocketFunction<ssize_t>(::sendmsg, socket, message, flags); return wrapSocketFunction<ssize_t>(::sendmsg, socket, message, flags);
#endif #endif
...@@ -407,6 +462,8 @@ int sendmmsg( ...@@ -407,6 +462,8 @@ int sendmmsg(
NetworkSocket socket, mmsghdr* msgvec, unsigned int vlen, int flags) { NetworkSocket socket, mmsghdr* msgvec, unsigned int vlen, int flags) {
#if FOLLY_HAVE_SENDMMSG #if FOLLY_HAVE_SENDMMSG
return wrapSocketFunction<int>(::sendmmsg, socket, msgvec, vlen, flags); return wrapSocketFunction<int>(::sendmmsg, socket, msgvec, vlen, flags);
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
// implement via sendmsg // implement via sendmsg
for (unsigned int i = 0; i < vlen; i++) { for (unsigned int i = 0; i < vlen; i++) {
...@@ -439,6 +496,8 @@ ssize_t sendto( ...@@ -439,6 +496,8 @@ ssize_t sendto(
#ifdef _WIN32 #ifdef _WIN32
return wrapSocketFunction<ssize_t>( return wrapSocketFunction<ssize_t>(
::sendto, s, (const char*)buf, (int)len, flags, to, (int)tolen); ::sendto, s, (const char*)buf, (int)len, flags, to, (int)tolen);
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
return wrapSocketFunction<ssize_t>(::sendto, s, buf, len, flags, to, tolen); return wrapSocketFunction<ssize_t>(::sendto, s, buf, len, flags, to, tolen);
#endif #endif
...@@ -462,6 +521,8 @@ int setsockopt( ...@@ -462,6 +521,8 @@ int setsockopt(
} }
return wrapSocketFunction<int>( return wrapSocketFunction<int>(
::setsockopt, s, level, optname, (char*)optval, optlen); ::setsockopt, s, level, optname, (char*)optval, optlen);
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
return wrapSocketFunction<int>( return wrapSocketFunction<int>(
::setsockopt, s, level, optname, optval, optlen); ::setsockopt, s, level, optname, optval, optlen);
...@@ -469,11 +530,19 @@ int setsockopt( ...@@ -469,11 +530,19 @@ int setsockopt(
} }
int shutdown(NetworkSocket s, int how) { int shutdown(NetworkSocket s, int how) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
return wrapSocketFunction<int>(::shutdown, s, how); return wrapSocketFunction<int>(::shutdown, s, how);
#endif
} }
NetworkSocket socket(int af, int type, int protocol) { NetworkSocket socket(int af, int type, int protocol) {
#if defined(__XROS__)
throw std::logic_error("Not implemented!");
#else
return NetworkSocket(::socket(af, type, protocol)); return NetworkSocket(::socket(af, type, protocol));
#endif
} }
int socketpair(int domain, int type, int protocol, NetworkSocket sv[2]) { int socketpair(int domain, int type, int protocol, NetworkSocket sv[2]) {
...@@ -489,6 +558,8 @@ int socketpair(int domain, int type, int protocol, NetworkSocket sv[2]) { ...@@ -489,6 +558,8 @@ int socketpair(int domain, int type, int protocol, NetworkSocket sv[2]) {
sv[0] = NetworkSocket(static_cast<SOCKET>(pair[0])); sv[0] = NetworkSocket(static_cast<SOCKET>(pair[0]));
sv[1] = NetworkSocket(static_cast<SOCKET>(pair[1])); sv[1] = NetworkSocket(static_cast<SOCKET>(pair[1]));
return r; return r;
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
int pair[2]; int pair[2];
auto r = ::socketpair(domain, type, protocol, pair); auto r = ::socketpair(domain, type, protocol, pair);
...@@ -505,6 +576,8 @@ int set_socket_non_blocking(NetworkSocket s) { ...@@ -505,6 +576,8 @@ int set_socket_non_blocking(NetworkSocket s) {
#ifdef _WIN32 #ifdef _WIN32
u_long nonBlockingEnabled = 1; u_long nonBlockingEnabled = 1;
return ioctlsocket(s.data, FIONBIO, &nonBlockingEnabled); return ioctlsocket(s.data, FIONBIO, &nonBlockingEnabled);
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
int flags = fcntl(s.data, F_GETFL, 0); int flags = fcntl(s.data, F_GETFL, 0);
if (flags == -1) { if (flags == -1) {
...@@ -520,6 +593,8 @@ int set_socket_close_on_exec(NetworkSocket s) { ...@@ -520,6 +593,8 @@ int set_socket_close_on_exec(NetworkSocket s) {
return 0; return 0;
} }
return -1; return -1;
#elif defined(__XROS__)
throw std::logic_error("Not implemented!");
#else #else
return fcntl(s.data, F_SETFD, FD_CLOEXEC); return fcntl(s.data, F_SETFD, FD_CLOEXEC);
#endif #endif
......
...@@ -25,7 +25,85 @@ ...@@ -25,7 +25,85 @@
#include <folly/portability/Time.h> #include <folly/portability/Time.h>
#include <folly/portability/Windows.h> #include <folly/portability/Windows.h>
#ifndef _WIN32 #ifdef _WIN32
#include <WS2tcpip.h> // @manual
using nfds_t = int;
using sa_family_t = ADDRESS_FAMILY;
// these are not supported
#define SO_EE_ORIGIN_ZEROCOPY 0
#define SO_ZEROCOPY 0
#define SO_TXTIME 0
#define MSG_ZEROCOPY 0x0
#define SOL_UDP 0x0
#define UDP_SEGMENT 0x0
#define IP_BIND_ADDRESS_NO_PORT 0
// We don't actually support either of these flags
// currently.
#define MSG_DONTWAIT 0x1000
#define MSG_EOR 0
struct msghdr {
void* msg_name;
socklen_t msg_namelen;
struct iovec* msg_iov;
size_t msg_iovlen;
void* msg_control;
size_t msg_controllen;
int msg_flags;
};
struct mmsghdr {
struct msghdr msg_hdr;
unsigned int msg_len;
};
struct sockaddr_un {
sa_family_t sun_family;
char sun_path[108];
};
#define SHUT_RD SD_RECEIVE
#define SHUT_WR SD_SEND
#define SHUT_RDWR SD_BOTH
// These are the same, but PF_LOCAL
// isn't defined by WinSock.
#define AF_LOCAL PF_UNIX
#define PF_LOCAL PF_UNIX
// This isn't defined by Windows, and we need to
// distinguish it from SO_REUSEADDR
#define SO_REUSEPORT 0x7001
// Someone thought it would be a good idea
// to define a field via a macro...
#undef s_host
#elif defined(__XROS__)
// Stub this out for now.
using nfds_t = int;
using socklen_t = int;
struct sockaddr {};
struct in_addr {};
struct msghdr {
void* msg_name;
socklen_t msg_namelen;
struct iovec* msg_iov;
size_t msg_iovlen;
void* msg_control;
size_t msg_controllen;
int msg_flags;
};
struct mmsghdr {
struct msghdr msg_hdr;
unsigned int msg_len;
};
#else
#include <netdb.h> #include <netdb.h>
#include <poll.h> #include <poll.h>
...@@ -121,61 +199,6 @@ struct mmsghdr { ...@@ -121,61 +199,6 @@ struct mmsghdr {
#define IP_BIND_ADDRESS_NO_PORT 24 #define IP_BIND_ADDRESS_NO_PORT 24
#endif #endif
#else
#include <WS2tcpip.h> // @manual
using nfds_t = int;
using sa_family_t = ADDRESS_FAMILY;
// these are not supported
#define SO_EE_ORIGIN_ZEROCOPY 0
#define SO_ZEROCOPY 0
#define SO_TXTIME 0
#define MSG_ZEROCOPY 0x0
#define SOL_UDP 0x0
#define UDP_SEGMENT 0x0
#define IP_BIND_ADDRESS_NO_PORT 0
// We don't actually support either of these flags
// currently.
#define MSG_DONTWAIT 0x1000
#define MSG_EOR 0
struct msghdr {
void* msg_name;
socklen_t msg_namelen;
struct iovec* msg_iov;
size_t msg_iovlen;
void* msg_control;
size_t msg_controllen;
int msg_flags;
};
struct mmsghdr {
struct msghdr msg_hdr;
unsigned int msg_len;
};
struct sockaddr_un {
sa_family_t sun_family;
char sun_path[108];
};
#define SHUT_RD SD_RECEIVE
#define SHUT_WR SD_SEND
#define SHUT_RDWR SD_BOTH
// These are the same, but PF_LOCAL
// isn't defined by WinSock.
#define AF_LOCAL PF_UNIX
#define PF_LOCAL PF_UNIX
// This isn't defined by Windows, and we need to
// distinguish it from SO_REUSEADDR
#define SO_REUSEPORT 0x7001
// Someone thought it would be a good idea
// to define a field via a macro...
#undef s_host
#endif #endif
namespace folly { namespace folly {
......
...@@ -31,6 +31,9 @@ struct NetworkSocket { ...@@ -31,6 +31,9 @@ struct NetworkSocket {
#ifdef _WIN32 #ifdef _WIN32
using native_handle_type = SOCKET; using native_handle_type = SOCKET;
static constexpr native_handle_type invalid_handle_value = INVALID_SOCKET; static constexpr native_handle_type invalid_handle_value = INVALID_SOCKET;
#elif defined(__XROS__)
using native_handle_type = void*;
static constexpr native_handle_type invalid_handle_value = nullptr;
#else #else
using native_handle_type = int; using native_handle_type = int;
static constexpr native_handle_type invalid_handle_value = -1; static constexpr native_handle_type invalid_handle_value = -1;
......
...@@ -145,4 +145,34 @@ int SocketFileDescriptorMap::socketToFd(SOCKET sock) noexcept { ...@@ -145,4 +145,34 @@ int SocketFileDescriptorMap::socketToFd(SOCKET sock) noexcept {
} // namespace detail } // namespace detail
} // namespace netops } // namespace netops
} // namespace folly } // namespace folly
#elif defined(__XROS__)
// Stub this out for now.
#include <stdexcept>
namespace folly {
namespace netops {
namespace detail {
int SocketFileDescriptorMap::close(int fd) noexcept {
throw std::logic_error("Not implemented!");
}
int SocketFileDescriptorMap::close(void* sock) noexcept {
throw std::logic_error("Not implemented!");
}
void* SocketFileDescriptorMap::fdToSocket(int fd) noexcept {
throw std::logic_error("Not implemented!");
}
int SocketFileDescriptorMap::socketToFd(void* sock) noexcept {
throw std::logic_error("Not implemented!");
}
} // namespace detail
} // namespace netops
} // namespace folly
#endif #endif
...@@ -34,6 +34,12 @@ struct SocketFileDescriptorMap { ...@@ -34,6 +34,12 @@ struct SocketFileDescriptorMap {
static SOCKET fdToSocket(int fd) noexcept; static SOCKET fdToSocket(int fd) noexcept;
static int socketToFd(SOCKET sock) noexcept; static int socketToFd(SOCKET sock) noexcept;
#elif defined(__XROS__)
static int close(int fd) noexcept;
static int close(void* sock) noexcept;
static void* fdToSocket(int fd) noexcept;
static int socketToFd(void* sock) noexcept;
#else #else
static int close(int fd) noexcept { return ::close(fd); } static int close(int fd) noexcept { return ::close(fd); }
......
...@@ -23,25 +23,9 @@ ...@@ -23,25 +23,9 @@
namespace folly { namespace folly {
namespace portability { namespace portability {
namespace sockets { namespace sockets {
#ifndef _WIN32
using ::accept; #ifdef _WIN32
using ::bind;
using ::connect;
using ::getpeername;
using ::getsockname;
using ::getsockopt;
using ::inet_ntop;
using ::listen;
using ::poll;
using ::recv;
using ::recvfrom;
using ::send;
using ::sendmsg;
using ::sendto;
using ::setsockopt;
using ::shutdown;
using ::socket;
#else
// Some Windows specific helper functions. // Some Windows specific helper functions.
bool is_fh_socket(int fh); bool is_fh_socket(int fh);
SOCKET fd_to_socket(int fd); SOCKET fd_to_socket(int fd);
...@@ -131,7 +115,33 @@ ssize_t sendto( ...@@ -131,7 +115,33 @@ ssize_t sendto(
socklen_t tolen); socklen_t tolen);
int setsockopt( int setsockopt(
int s, int level, int optname, const char* optval, socklen_t optlen); int s, int level, int optname, const char* optval, socklen_t optlen);
#elif defined(__XROS__)
// None of these are implemented or referenced right now.
#else
using ::accept;
using ::bind;
using ::connect;
using ::getpeername;
using ::getsockname;
using ::getsockopt;
using ::inet_ntop;
using ::listen;
using ::poll;
using ::recv;
using ::recvfrom;
using ::send;
using ::sendmsg;
using ::sendto;
using ::setsockopt;
using ::shutdown;
using ::socket;
#endif #endif
} // namespace sockets } // namespace sockets
} // namespace portability } // namespace portability
} // namespace folly } // namespace folly
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment