Send Guest and Host event FDs over to QEmu

Change-Id: I20132c5babaed27a086c8599c97619562b5cac07
(cherry picked from commit 1bf152026ecd1d8565e2ef9b32090f7825f3ea97)
diff --git a/ivserver/hald_client.cc b/ivserver/hald_client.cc
index 2b535b8..d7eb8e2 100644
--- a/ivserver/hald_client.cc
+++ b/ivserver/hald_client.cc
@@ -99,7 +99,7 @@
   };
   avd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
   avd::SharedFD fds[3] = {guest_to_host_efd, host_to_guest_efd,
-                          shared_mem.shared_mem_fd()};
+                          shared_mem.SharedMemFD()};
   rval = client_socket_->SendMsgAndFDs<3>(hdr, MSG_NOSIGNAL, fds);
   if (rval == -1) {
     LOG(ERROR) << "failed to send Host FD: " << client_socket_->StrError();
diff --git a/ivserver/hald_client_test.cc b/ivserver/hald_client_test.cc
index bde3dc5..02b33cc 100644
--- a/ivserver/hald_client_test.cc
+++ b/ivserver/hald_client_test.cc
@@ -11,11 +11,11 @@
 #include "host/ivserver/hald_client.h"
 #include "host/ivserver/vsocsharedmem_mock.h"
 
-using ::testing::_;
 using ::testing::DoAll;
 using ::testing::Return;
 using ::testing::ReturnRef;
 using ::testing::SetArgPointee;
+using ::testing::_;
 
 namespace ivserver {
 namespace test {
@@ -24,14 +24,16 @@
  public:
   void SetUp() override {
     std::string socket_location;
-    ASSERT_TRUE(GetTempLocation(&socket_location)) << "Could not create temp file";
+    ASSERT_TRUE(GetTempLocation(&socket_location))
+        << "Could not create temp file";
     LOG(INFO) << "Temp file location: " << socket_location;
 
     hald_server_socket_ = avd::SharedFD::SocketLocalServer(
         socket_location.c_str(), false, SOCK_STREAM, 0666);
-    test_socket_ =
-        avd::SharedFD::SocketLocalClient(socket_location.c_str(), false, SOCK_STREAM);
-    hald_socket_ = avd::SharedFD::Accept(*hald_server_socket_, nullptr, nullptr);
+    test_socket_ = avd::SharedFD::SocketLocalClient(socket_location.c_str(),
+                                                    false, SOCK_STREAM);
+    hald_socket_ =
+        avd::SharedFD::Accept(*hald_server_socket_, nullptr, nullptr);
 
     EXPECT_TRUE(hald_server_socket_->IsOpen());
     EXPECT_TRUE(test_socket_->IsOpen());
@@ -71,24 +73,25 @@
 };
 
 TEST_F(HaldClientTest, HandshakeTerminatedByHald) {
-  std::thread thread([this]() {
-    auto client(HaldClient::New(vsoc_, hald_socket_));
-    EXPECT_FALSE(client)
-        << "Handshake should not complete when client terminates early.";
-  });
+  std::thread thread(
+      [this]() {
+        auto client(HaldClient::New(vsoc_, hald_socket_));
+        EXPECT_FALSE(client)
+            << "Handshake should not complete when client terminates early.";
+      });
 
   test_socket_->Close();
   thread.join();
 }
 
 TEST_F(HaldClientTest, HandshakeTerminatedByInvalidRegionSize) {
-  uint16_t sizes[] = { 0, VSoCSharedMemory::kMaxRegionNameLength + 1, 0xffff };
+  uint16_t sizes[] = {0, VSoCSharedMemory::kMaxRegionNameLength + 1, 0xffff};
 
   for (uint16_t size : sizes) {
     std::thread thread([this, size]() {
-    auto client(HaldClient::New(vsoc_,hald_socket_));
-    EXPECT_FALSE(client) << "Handle should not be created when size is "
-                         << size;
+      auto client(HaldClient::New(vsoc_, hald_socket_));
+      EXPECT_FALSE(client) << "Handle should not be created when size is "
+                           << size;
     });
 
     int32_t proto_version;
@@ -104,28 +107,28 @@
 TEST_F(HaldClientTest, FullSaneHandshake) {
   std::string temp;
   ASSERT_TRUE(GetTempLocation(&temp));
-  avd::SharedFD host_fd(avd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+  avd::SharedFD host_fd(
+      avd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
   EXPECT_TRUE(host_fd->IsOpen());
 
   ASSERT_TRUE(GetTempLocation(&temp));
-  avd::SharedFD guest_fd(avd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+  avd::SharedFD guest_fd(
+      avd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
   EXPECT_TRUE(guest_fd->IsOpen());
 
   ASSERT_TRUE(GetTempLocation(&temp));
-  avd::SharedFD shmem_fd(avd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
+  avd::SharedFD shmem_fd(
+      avd::SharedFD::Open(temp.c_str(), O_CREAT | O_RDWR, 0666));
   EXPECT_TRUE(shmem_fd->IsOpen());
 
   const std::string test_location("testing");
   EXPECT_CALL(vsoc_, GetEventFdPairForRegion(test_location, _, _))
-      .WillOnce(DoAll(
-        SetArgPointee<1>(host_fd),
-        SetArgPointee<2>(guest_fd),
-        Return(true)));
-  EXPECT_CALL(vsoc_, shared_mem_fd())
-      .WillOnce(ReturnRef(shmem_fd));
+      .WillOnce(DoAll(SetArgPointee<1>(host_fd), SetArgPointee<2>(guest_fd),
+                      Return(true)));
+  EXPECT_CALL(vsoc_, SharedMemFD()).WillOnce(ReturnRef(shmem_fd));
 
   std::thread thread([this]() {
-    auto client(HaldClient::New(vsoc_,hald_socket_));
+    auto client(HaldClient::New(vsoc_, hald_socket_));
     EXPECT_TRUE(client);
   });
 
@@ -135,7 +138,8 @@
       test_socket_->Recv(&proto_version, sizeof(proto_version), MSG_NOSIGNAL));
 
   uint16_t size = test_location.size();
-  EXPECT_EQ(sizeof(size), test_socket_->Send(&size, sizeof(size), MSG_NOSIGNAL));
+  EXPECT_EQ(sizeof(size),
+            test_socket_->Send(&size, sizeof(size), MSG_NOSIGNAL));
   EXPECT_EQ(size, test_socket_->Send(test_location.data(), size, MSG_NOSIGNAL));
 
   // TODO(ender): delete this once no longer necessary. Currently, absence of
@@ -155,6 +159,5 @@
   thread.join();
 }
 
-
 }  // namespace test
 }  // namespace ivserver
diff --git a/ivserver/ivserver.cc b/ivserver/ivserver.cc
index 92cd7cc..a3e3a30 100644
--- a/ivserver/ivserver.cc
+++ b/ivserver/ivserver.cc
@@ -53,15 +53,10 @@
 }
 
 void IVServer::HandleNewQemuConnection() {
-  std::unique_ptr<QemuClient> res =
-      QemuClient::New(vsoc_shmem_->shared_mem_fd(),
-                      avd::SharedFD::Accept(*qemu_channel_, nullptr, nullptr));
+  std::unique_ptr<QemuClient> res = QemuClient::New(
+      *vsoc_shmem_, avd::SharedFD::Accept(*qemu_channel_, nullptr, nullptr));
 
-  if (res) {
-    // TODO(romitd): how to recover if some clients failed? should we retry?
-    // Why are we doing this?
-    vsoc_shmem_->BroadcastQemuSocket(res->client_socket());
-  } else {
+  if (!res) {
     LOG(WARNING) << "Could not accept new QEmu client.";
   }
 }
diff --git a/ivserver/qemu_client.cc b/ivserver/qemu_client.cc
index a18f8af..83164ec 100644
--- a/ivserver/qemu_client.cc
+++ b/ivserver/qemu_client.cc
@@ -3,14 +3,9 @@
 #include <glog/logging.h>
 
 namespace ivserver {
-namespace {
-// QEMU expects version 0 of the QEMU <--> ivserver protocol.
-const uint64_t kQemuIvshMemProtocolVersion = 0;
-const uint64_t kQemuVMId = 1;
-}  // anonymous namespace
 
-std::unique_ptr<QemuClient> QemuClient::New(const avd::SharedFD& socket,
-                                            avd::SharedFD shmemfd) {
+std::unique_ptr<QemuClient> QemuClient::New(const VSoCSharedMemory& shmem,
+                                            const avd::SharedFD& socket) {
   std::unique_ptr<QemuClient> res;
   if (!socket->IsOpen()) {
     LOG(WARNING) << "Invalid socket passed to QemuClient: "
@@ -19,8 +14,8 @@
   }
 
   res.reset(new QemuClient(std::move(socket)));
-  if (!res->PerformHandshake(shmemfd)) {
-    LOG(ERROR) << "QEmu handshake failed. Dropping connection.";
+  if (!res->PerformHandshake(shmem)) {
+    LOG(ERROR) << "Qemu handshake failed. Dropping connection.";
     res.reset();
   }
 
@@ -31,37 +26,82 @@
 
 // Once the QemuClient object is constructed, invoking the following
 // method will perform the actual handshake with a QEMU instance.
-bool QemuClient::PerformHandshake(const avd::SharedFD& shmem_fd) {
-  int rval =
-      client_socket_->Send(&kQemuIvshMemProtocolVersion,
-                           sizeof(kQemuIvshMemProtocolVersion), MSG_NOSIGNAL);
-  if (rval != sizeof(kQemuIvshMemProtocolVersion)) {
+bool QemuClient::PerformHandshake(const VSoCSharedMemory& shmem) {
+  // 1. The protocol version number, currently zero.  The client should
+  //    close the connection on receipt of versions it can't handle.
+  int64_t msg = QemuConstants::kIvshMemProtocolVersion;
+  int rval = client_socket_->Send(&msg, sizeof(msg), MSG_NOSIGNAL);
+  if (rval != sizeof(msg)) {
     LOG(ERROR) << "Failed to send protocol version: "
                << client_socket_->StrError();
     return false;
   }
 
-  rval = client_socket_->Send(&kQemuVMId, sizeof(kQemuVMId), MSG_NOSIGNAL);
-  if (rval != sizeof(kQemuVMId)) {
+  // 2. The client's ID.  This is unique among all clients of this server.
+  //    IDs must be between 0 and 65535, because the Doorbell register
+  //    provides only 16 bits for them.
+  msg = QemuConstants::kGuestID;
+  rval = client_socket_->Send(&msg, sizeof(msg), MSG_NOSIGNAL);
+  if (rval != sizeof(msg)) {
     LOG(ERROR) << "Failed to send VM Id: " << client_socket_->StrError();
     return false;
   }
 
-  // Send FD to remote process over unix domain socket using control message.
-  uint64_t control_data = ~0;
-  struct iovec vec {
-    &control_data, sizeof(control_data)
-  };
-  avd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
-  avd::SharedFD fds[] = {shmem_fd};
-  rval = client_socket_->SendMsgAndFDs(hdr, 0, fds);
-  if (rval == -1) {
-    LOG(ERROR) << "failed to send shared_mem_fd: "
+  // 3. The number -1, accompanied by the file descriptor for the shared
+  //    memory.
+  if (!SendSocketInfo(QemuFDMsg::kSharedMem, shmem.SharedMemFD())) {
+    LOG(ERROR) << "Failed to send Shared Memory socket: "
                << client_socket_->StrError();
     return false;
   }
 
+  // 4. Connect notifications for existing other clients, if any.  This is
+  //    a peer ID (number between 0 and 65535 other than the client's ID),
+  //    repeated N times.  Each repetition is accompanied by one file
+  //    descriptor.  These are for interrupting the peer with that ID using
+  //    vector 0,..,N-1, in order.  If the client is configured for fewer
+  //    vectors, it closes the extra file descriptors.  If it is configured
+  //    for more, the extra vectors remain unconnected.
+  for (const auto region_pair : shmem.Regions()) {
+    if (!SendSocketInfo(QemuFDMsg::kHostSideHald, region_pair.second.host_fd)) {
+      LOG(ERROR) << "Failed to send Host Side FD for region "
+                 << region_pair.first << ": " << client_socket_->StrError();
+      return false;
+    }
+  }
+
+  // 5. Interrupt setup.  This is the client's own ID, repeated N times.
+  //    Each repetition is accompanied by one file descriptor.  These are
+  //    for receiving interrupts from peers using vector 0,..,N-1, in
+  //    order.  If the client is configured for fewer vectors, it closes
+  //    the extra file descriptors.  If it is configured for more, the
+  //    extra vectors remain unconnected.
+  for (const auto region_pair : shmem.Regions()) {
+    if (!SendSocketInfo(QemuFDMsg::kGuestSideHal,
+                        region_pair.second.guest_fd)) {
+      LOG(ERROR) << "Failed to send Guest Side FD for region "
+                 << region_pair.first << ": " << client_socket_->StrError();
+      return false;
+    }
+  }
+
   return true;
 }
 
-}  // namespace ivserver
+bool QemuClient::SendSocketInfo(QemuFDMsg message,
+                                const avd::SharedFD& socket) {
+  struct iovec vec {
+    &message, sizeof(message)
+  };
+  avd::InbandMessageHeader hdr{nullptr, 0, &vec, 1, 0};
+  avd::SharedFD fds[] = {socket};
+  int rval = client_socket_->SendMsgAndFDs(hdr, 0, fds);
+  if (rval == -1) {
+    LOG(ERROR) << "failed to send shared_mem_fd: "
+               << client_socket_->StrError();
+    return false;
+  }
+  return true;
+}
+
+}  // namespace ivserver
\ No newline at end of file
diff --git a/ivserver/qemu_client.h b/ivserver/qemu_client.h
index f8c4c59..d0f80c1 100644
--- a/ivserver/qemu_client.h
+++ b/ivserver/qemu_client.h
@@ -14,12 +14,37 @@
 // https://github.com/qemu/qemu/blob/stable-2.8/docs/specs/ivshmem-spec.txt
 class QemuClient final {
  public:
-  static std::unique_ptr<QemuClient> New(const avd::SharedFD &connection,
-                                         avd::SharedFD shmemfd);
+  static std::unique_ptr<QemuClient> New(const VSoCSharedMemory &shmem,
+                                         const avd::SharedFD &connection);
 
   avd::SharedFD client_socket() const { return client_socket_; }
 
  private:
+  enum QemuConstants : int64_t {
+    kIvshMemProtocolVersion = 0,
+    // HostID is in fact a Peer ID and can take multiple values, depending on
+    // how many subsystems we would like Guest to talk to.
+    kHostBaseID = 0,
+    // GuestID is a unique form of Peer ID (see above), that identifies newly
+    // created quest in IvSharedMem world.
+    kGuestID = 1024
+  };
+
+  static_assert(QemuConstants::kHostBaseID < QemuConstants::kGuestID,
+                "Guest and host should have different IDs");
+  // Type of QEmu FD messages.
+  // QEmu uses these messages to identify purpose of socket it is
+  // receiving.
+  enum class QemuFDMsg : int64_t {
+    // Represents SharedMemory FD.
+    kSharedMem = -1,
+    // Represents primary (and currently only) FD that is owned and managed by
+    // Host side.
+    kHostSideHald = QemuConstants::kHostBaseID,
+    // Represents FDs that are owned by Guest.
+    kGuestSideHal = QemuConstants::kGuestID,
+  };
+
   avd::SharedFD client_socket_;
 
   // Initialize new instance of QemuClient.
@@ -27,7 +52,10 @@
 
   // Once the QemuClient object is constructed, invoking the following
   // method will perform the actual handshake with a QEMU instance.
-  bool PerformHandshake(const avd::SharedFD &shmem_fd);
+  bool PerformHandshake(const VSoCSharedMemory &shmem_fd);
+
+  // Send socket data to Qemu.
+  bool SendSocketInfo(QemuFDMsg message, const avd::SharedFD &socket);
 
   QemuClient(const QemuClient &) = delete;
   QemuClient &operator=(const QemuClient &) = delete;
diff --git a/ivserver/vsocsharedmem.cc b/ivserver/vsocsharedmem.cc
index 120c248..dce7f2a 100644
--- a/ivserver/vsocsharedmem.cc
+++ b/ivserver/vsocsharedmem.cc
@@ -30,9 +30,9 @@
                                avd::SharedFD *guest_to_host,
                                avd::SharedFD *host_to_guest) const override;
 
-  const avd::SharedFD &shared_mem_fd() const override;
+  const avd::SharedFD &SharedMemFD() const override;
 
-  void BroadcastQemuSocket(const avd::SharedFD &qemu_socket) const override;
+  const std::map<std::string, Region> &Regions() const override;
 
  private:
   void CreateLayout();
@@ -40,10 +40,10 @@
   const uint32_t size_;
   const Json::Value &json_root_;
   avd::SharedFD shared_mem_fd_;
-  std::map<std::string, std::pair<avd::SharedFD, avd::SharedFD>> eventfd_data_;
+  std::map<std::string, Region> eventfd_data_;
 
   VSoCSharedMemoryImpl(const VSoCSharedMemoryImpl &) = delete;
-  VSoCSharedMemoryImpl& operator=(const VSoCSharedMemoryImpl& other) = delete;
+  VSoCSharedMemoryImpl &operator=(const VSoCSharedMemoryImpl &other) = delete;
 };
 
 VSoCSharedMemoryImpl::VSoCSharedMemoryImpl(const uint32_t size_mib,
@@ -63,10 +63,15 @@
   CreateLayout();
 }
 
-const avd::SharedFD &VSoCSharedMemoryImpl::shared_mem_fd() const {
+const avd::SharedFD &VSoCSharedMemoryImpl::SharedMemFD() const {
   return shared_mem_fd_;
 }
 
+const std::map<std::string, VSoCSharedMemory::Region>
+    &VSoCSharedMemoryImpl::Regions() const {
+  return eventfd_data_;
+}
+
 void VSoCSharedMemoryImpl::CreateLayout() {
   uint32_t offset = 0;
   void *mmap_addr =
@@ -147,19 +152,17 @@
     // Create one pair of eventfds for this region. Note that the guest to host
     // eventfd is non-blocking, whereas the host to guest eventfd is blocking.
     // This is in anticipation of blocking semantics for the host side locks.
-    avd::SharedFD host_efd(avd::SharedFD::Event());
+    avd::SharedFD host_efd(avd::SharedFD::Event(0, EFD_NONBLOCK));
     LOG_IF(FATAL, !host_efd->IsOpen())
         << "Failed to create host eventfd for " << device_name << ": "
         << host_efd->StrError();
 
-    avd::SharedFD guest_efd(avd::SharedFD::Event());
+    avd::SharedFD guest_efd(avd::SharedFD::Event(0, EFD_NONBLOCK));
     LOG_IF(FATAL, !guest_efd->IsOpen())
         << "Failed to create guest eventfd for " << device_name << ": "
         << guest_efd->StrError();
 
-    eventfd_data_.emplace(
-        region["device_name"].asString(),
-        std::pair<avd::SharedFD, avd::SharedFD>{host_efd, guest_efd});
+    eventfd_data_.emplace(device_name, Region{host_efd, guest_efd});
   }
 
   munmap(mmap_addr, size_);
@@ -171,41 +174,17 @@
   auto it = eventfd_data_.find(region_name);
   if (it == eventfd_data_.end()) return false;
 
-  *guest_to_host = it->second.first;
-  *host_to_guest = it->second.second;
+  *guest_to_host = it->second.host_fd;
+  *host_to_guest = it->second.guest_fd;
   return true;
 }
 
-void VSoCSharedMemoryImpl::BroadcastQemuSocket(const avd::SharedFD &qemu_fd) const {
-  uint64_t control_data = 0;
-  struct iovec vec {
-    &control_data, sizeof(control_data)
-  };
-  avd::InbandMessageHeader hdr{nullptr, 0, &vec, sizeof(vec), 0};
-
-  avd::SharedFD fds[] = {qemu_fd};
-  // TODO(ghartman, romitd): how should we recover from these?
-  for (const auto it : eventfd_data_) {
-    int result;
-    result = it.second.first->SendMsgAndFDs(hdr, 0, fds);
-    if (result == -1) {
-      LOG(ERROR) << "failed to send QEmu FD to " << it.first
-                 << " Host: " << it.second.first->StrError();
-    }
-
-    result = it.second.second->SendMsgAndFDs(hdr, 0, fds);
-    if (result == -1) {
-      LOG(ERROR) << "failed to send QEmu FD to " << it.first
-                 << " Guest: " << it.second.second->StrError();
-    }
-  }
-}
-
 }  // anonymous namespace
 
 std::unique_ptr<VSoCSharedMemory> VSoCSharedMemory::New(
-    const uint32_t size_mb, const std::string& name, const Json::Value& root) {
-  return std::unique_ptr<VSoCSharedMemory>(new VSoCSharedMemoryImpl(size_mb, name, root));
+    const uint32_t size_mb, const std::string &name, const Json::Value &root) {
+  return std::unique_ptr<VSoCSharedMemory>(
+      new VSoCSharedMemoryImpl(size_mb, name, root));
 }
 
 }  // namespace ivserver
diff --git a/ivserver/vsocsharedmem.h b/ivserver/vsocsharedmem.h
index 8f6f051..0669ba3 100644
--- a/ivserver/vsocsharedmem.h
+++ b/ivserver/vsocsharedmem.h
@@ -2,20 +2,25 @@
 
 #include <inttypes.h>
 #include <json/json.h>
-#include <unistd.h>
 #include <map>
 #include <memory>
 #include <string>
 
 #include "common/libs/fs/shared_fd.h"
+#include "uapi/vsoc_shm.h"
 
 namespace ivserver {
 
 class VSoCSharedMemory {
  public:
+  // Region describes a VSoCSharedMem region.
+  struct Region {
+    avd::SharedFD host_fd;
+    avd::SharedFD guest_fd;
+  };
+
   // Max name length of a memory region.
-  // TODO(ender): set this value from trusted source.
-  static constexpr int32_t kMaxRegionNameLength = 16;
+  static constexpr int32_t kMaxRegionNameLength = sizeof(vsoc_device_name);
 
   VSoCSharedMemory() = default;
   virtual ~VSoCSharedMemory() = default;
@@ -25,16 +30,15 @@
                                                const Json::Value &json_root);
 
   virtual bool GetEventFdPairForRegion(const std::string &region_name,
-                               avd::SharedFD *guest_to_host,
-                               avd::SharedFD *host_to_guest) const = 0;
+                                       avd::SharedFD *guest_to_host,
+                                       avd::SharedFD *host_to_guest) const = 0;
 
-  virtual const avd::SharedFD &shared_mem_fd() const = 0;
-
-  virtual void BroadcastQemuSocket(const avd::SharedFD &qemu_socket) const = 0;
+  virtual const avd::SharedFD &SharedMemFD() const = 0;
+  virtual const std::map<std::string, Region> &Regions() const = 0;
 
  private:
   VSoCSharedMemory(const VSoCSharedMemory &) = delete;
-  VSoCSharedMemory& operator=(const VSoCSharedMemory& other) = delete;
+  VSoCSharedMemory &operator=(const VSoCSharedMemory &other) = delete;
 };
 
 }  // namespace ivserver
diff --git a/ivserver/vsocsharedmem_mock.h b/ivserver/vsocsharedmem_mock.h
index 58ec680..8f96250 100644
--- a/ivserver/vsocsharedmem_mock.h
+++ b/ivserver/vsocsharedmem_mock.h
@@ -8,9 +8,11 @@
 namespace test {
 class VSoCSharedMemoryMock : public VSoCSharedMemory {
  public:
-  MOCK_CONST_METHOD3(GetEventFdPairForRegion, bool(const std::string&, avd::SharedFD*, avd::SharedFD*));
-  MOCK_CONST_METHOD0(shared_mem_fd, const avd::SharedFD&());
-  MOCK_CONST_METHOD1(BroadcastQemuSocket, void(const avd::SharedFD&));
+  MOCK_CONST_METHOD3(GetEventFdPairForRegion,
+                     bool(const std::string&, avd::SharedFD*, avd::SharedFD*));
+  MOCK_CONST_METHOD0(SharedMemFD, const avd::SharedFD&());
+  MOCK_CONST_METHOD0(Regions,
+                     const std::map<std::string, VSoCSharedMemory::Region>&());
 };
-}
-}
\ No newline at end of file
+}  // namespace test
+}  // namespace ivserver
\ No newline at end of file