Add rpc-port-util tool 41/235241/19
authorHwankyu Jhun <h.jhun@samsung.com>
Wed, 3 Jun 2020 04:18:16 +0000 (13:18 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Wed, 10 Jun 2020 04:04:40 +0000 (13:04 +0900)
Usage:
 $ rpc-port-util -n <port_name>

e.g. $ rpc-port-util -n Message

Change-Id: I19ffecef1648a3e1ba61891de606a75796b1d5be
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
26 files changed:
CMakeLists.txt
conf/rpc-port.conf.in [new file with mode: 0644]
packaging/rpc-port.manifest
packaging/rpc-port.spec
src/debug-port-internal.cc [new file with mode: 0644]
src/debug-port-internal.hh [new file with mode: 0644]
src/log-private.hh [new file with mode: 0644]
src/port-internal.cc
src/port-internal.h
src/proxy-internal.cc
src/proxy-internal.h
src/rpc-port.cc
src/shared-queue-internal.hh [new file with mode: 0644]
src/stub-internal.cc
src/stub-internal.h
utils/CMakeLists.txt [new file with mode: 0644]
utils/src/debug-port.cc [new file with mode: 0644]
utils/src/debug-port.hh [new file with mode: 0644]
utils/src/log-private.hh [new file with mode: 0644]
utils/src/logger.cc [new file with mode: 0644]
utils/src/logger.hh [new file with mode: 0644]
utils/src/main.cc [new file with mode: 0644]
utils/src/message.cc [new file with mode: 0644]
utils/src/message.hh [new file with mode: 0644]
utils/src/options.cc [new file with mode: 0644]
utils/src/options.hh [new file with mode: 0644]

index bdc8943..82d5c9e 100644 (file)
@@ -56,6 +56,9 @@ INSTALL(TARGETS ${this_target} DESTINATION ${LIB_INSTALL_DIR})
 INSTALL(DIRECTORY ${CMAKE_SOURCE_DIR}/include/ DESTINATION include/rpc-port FILES_MATCHING PATTERN "*.h")
 INSTALL(DIRECTORY ${LIBRARY_OUTPUT_PATH}/ DESTINATION ${LIB_INSTALL_DIR} FILES_MATCHING PATTERN "*.so*")
 
+CONFIGURE_FILE(${CMAKE_SOURCE_DIR}/conf/rpc-port.conf.in ${CMAKE_SOURCE_DIR}/conf/rpc-port.conf @ONLY)
+INSTALL(FILES ${CMAKE_SOURCE_DIR}/conf/rpc-port.conf DESTINATION /etc/dbus-1/system.d)
+
 IF(NOT DEFINED MINIMUM_BUILD)
 ENABLE_TESTING()
 SET(RPC_PORT_UNITTESTS rpc-port_unittests)
@@ -65,3 +68,5 @@ ADD_TEST(NAME ${RPC_PORT_UNITTESTS} COMMAND ${RPC_PORT_UNITTESTS}
 ADD_SUBDIRECTORY(unit_tests)
 ADD_DEPENDENCIES(${RPC_PORT_UNITTESTS} rpc-port)
 ENDIF(NOT DEFINED MINIMUM_BUILD)
+
+ADD_SUBDIRECTORY(utils)
diff --git a/conf/rpc-port.conf.in b/conf/rpc-port.conf.in
new file mode 100644 (file)
index 0000000..2447548
--- /dev/null
@@ -0,0 +1,17 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+    <policy context="default">
+        <allow own="org.tizen.rpc.port.signal"/>
+        <allow send_type="signal" send_interface="org.tizen.rpc.port.signal" send_member="New"/>
+        <check send_type="signal" send_interface="org.tizen.rpc.port.signal" send_member="Debug" privilege="http://tizen.org/privilege/internal/default/platform"/>
+    </policy>
+    <policy user="app_fw">
+        <allow own="org.tizen.rpc.port.signal"/>
+        <allow send_type="signal" send_interface="org.tizen.rpc.port.signal"/>
+    </policy>
+    <policy user="root">
+        <allow own="org.tizen.rpc.port.signal"/>
+        <allow send_type="signal" send_interface="org.tizen.rpc.port.signal"/>
+    </policy>
+</busconfig>
index 75b0fa5..2aab1dc 100644 (file)
@@ -2,4 +2,7 @@
     <request>
         <domain name="_"/>
     </request>
+    <assign>
+        <filesystem path="/usr/bin/rpc-port-util" label="User::Shell" exec_label="User"/>
+    </assign>
 </manifest>
index 28cf833..494c1a3 100644 (file)
@@ -117,6 +117,8 @@ install -m 0644 gcov-obj/* %{buildroot}%{_datadir}/gcov/obj
 %manifest %{name}.manifest
 %attr(0644,root,root) %{_libdir}/lib%{name}.so.*
 %license LICENSE.APLv2
+%{_bindir}/rpc-port-util
+%config %{_sysconfdir}/dbus-1/system.d/rpc-port.conf
 
 %files devel
 %{_includedir}/rpc-port/*.h
diff --git a/src/debug-port-internal.cc b/src/debug-port-internal.cc
new file mode 100644 (file)
index 0000000..dc1b0ba
--- /dev/null
@@ -0,0 +1,358 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <time.h>
+
+#include "debug-port-internal.hh"
+#include "log-private.hh"
+#include "parcel-internal.h"
+
+namespace rpc_port {
+namespace internal {
+
+static const char PATH_RPC_PORT_UTIL_SOCK[] =
+    "/run/aul/daemons/.rpc-port-util-sock";
+static const char RPC_PORT_SIGNAL_PATH[] = "/Org/Tizen/RPC/Port/Signal";
+static const char RPC_PORT_SIGNAL_INTERFACE[] = "org.tizen.rpc.port.signal";
+static const char RPC_PORT_SIGNAL_DEBUG[] = "Debug";
+static const char RPC_PORT_SIGNAL_NEW[] = "New";
+
+DebugPort::~DebugPort() {
+  if (!disposed_)
+    Dispose();
+}
+
+void DebugPort::Dispose() {
+  Unwatch();
+  Unsubscribe();
+  JoinThread();
+  disposed_ = true;
+}
+
+bool DebugPort::IsConnected() {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  return connected_;
+}
+
+void DebugPort::AddSession(std::string port_name, std::string destination,
+    int main_port, int delegate_port) {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  sessions_.emplace_back(
+      new DebugPort::Session(
+        port_name, destination, main_port, delegate_port));
+}
+
+void DebugPort::RemoveSession(int port) {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  auto iter = sessions_.begin();
+  while (iter != sessions_.end()) {
+    if ((*iter)->GetMainPort() == port ||
+        (*iter)->GetDelegatePort() == port) {
+      _W("Remove session. port(%d)", port);
+      iter = sessions_.erase(iter);
+      break;
+    }
+
+    ++iter;
+  }
+}
+
+std::shared_ptr<DebugPort::Session> DebugPort::FindSession(int port) {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  for (auto& s : sessions_) {
+    if (s->GetMainPort() == port || s->GetDelegatePort() == port)
+      return s;
+  }
+
+  return {};
+}
+
+int DebugPort::Send(int port, bool is_read, uint32_t seq,
+    const void* buf, unsigned int size) {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  if (!IsConnected())
+    return -1;
+
+  auto session = FindSession(port);
+  if (session.get() == nullptr) {
+    _E("Failed to find session. port(%d)", port);
+    return -1;
+  }
+
+  // time + port_name + destination + is_delegate + port + is_read + seq + size + data
+  Parcel parcel;
+  parcel.WriteInt64(time(nullptr));
+  parcel.WriteString(session->GetPortName().c_str());
+  parcel.WriteString(session->GetDestination().c_str());
+  parcel.WriteBool(session->GetDelegatePort() == port);
+  parcel.WriteInt32(port);
+  parcel.WriteBool(is_read);
+  parcel.WriteInt32(seq);
+  parcel.WriteInt32(size);
+  parcel.Write(static_cast<const unsigned char*>(buf), size);
+
+  queue_.Push(std::make_shared<Parcel>(parcel));
+  return 0;
+}
+
+void DebugPort::Init() {
+  Subscribe();
+  EmitSignal(RPC_PORT_SIGNAL_NEW);
+  is_running_ = false;
+  disposed_ = false;
+}
+
+int DebugPort::Connect() {
+  int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  if (fd < 0) {
+    _E("socket() is failed. errno(%d)", errno);
+    return -1;
+  }
+
+  struct sockaddr_un addr = { 0, };
+  addr.sun_family = AF_UNIX;
+  snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", PATH_RPC_PORT_UTIL_SOCK);
+
+  int ret = connect(fd, reinterpret_cast<struct sockaddr*>(&addr),
+      sizeof(addr));
+  if (ret < 0) {
+    _E("connect() is failed. fd(%d), errno(%d)", fd, errno);
+    close(fd);
+    return -1;
+  }
+
+  return fd;
+}
+
+int DebugPort::Watch(int fd) {
+  GIOChannel* io = g_io_channel_unix_new(fd);
+  if (io == nullptr) {
+    _E("g_io_channel_unix_new() is failed");
+    return -1;
+  }
+
+  GIOCondition cond = static_cast<GIOCondition>(
+      (G_IO_ERR | G_IO_HUP | G_IO_NVAL));
+  guint tag = g_io_add_watch(io, cond, OnDebugPortDisconnectedCb, this);
+  if (tag == 0) {
+    _E("g_io_add_watch() is failed");
+    g_io_channel_unref(io);
+    return -1;
+  }
+
+  io_ = io;
+  watch_tag_ = tag;
+  return 0;
+}
+
+void DebugPort::Unwatch() {
+  if (watch_tag_) {
+    g_source_remove(watch_tag_);
+    watch_tag_ = 0;
+  }
+
+  if (io_) {
+    g_io_channel_unref(io_);
+    io_ = nullptr;
+  }
+}
+
+gboolean DebugPort::OnDebugPortDisconnectedCb(GIOChannel* io,
+    GIOCondition cond, gpointer data) {
+  _W("cond(%d)", static_cast<int>(cond));
+  auto* debug_port = static_cast<DebugPort*>(data);
+  std::lock_guard<std::recursive_mutex> lock(debug_port->GetMutex());
+  debug_port->SetConnectionStatus(false);
+  debug_port->watch_tag_ = 0;
+  debug_port->Unwatch();
+  debug_port->port_.reset();
+  _W("Disconnected");
+  return G_SOURCE_REMOVE;
+}
+
+GDBusConnection* DebugPort::GetConnection() {
+  if (conn_)
+    return conn_;
+
+  GError* error = nullptr;
+  conn_ = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
+  if (conn_ == nullptr) {
+    _E("g_bus_get_sync() is failed. error(%s)",
+        error ? error->message : "unknown");
+    g_clear_error(&error);
+    return nullptr;
+  }
+
+  return conn_;
+}
+
+void DebugPort::Subscribe() {
+  GDBusConnection* conn = GetConnection();
+  if (conn == nullptr)
+    return;
+
+  subs_tag_ = g_dbus_connection_signal_subscribe(conn,
+      nullptr,
+      RPC_PORT_SIGNAL_INTERFACE,
+      RPC_PORT_SIGNAL_DEBUG,
+      RPC_PORT_SIGNAL_PATH,
+      nullptr,
+      G_DBUS_SIGNAL_FLAGS_NONE,
+      OnGDBusSignalCb,
+      this,
+      nullptr);
+  if (subs_tag_ == 0) {
+    _E("g_dbus_connection_signal_subscribe() is failed");
+    return;
+  }
+
+  _D("tag(%u)", subs_tag_);
+}
+
+void DebugPort::Unsubscribe() {
+  if (subs_tag_)
+    g_dbus_connection_signal_unsubscribe(conn_, subs_tag_);
+
+  if (conn_)
+    g_object_unref(conn_);
+}
+
+void DebugPort::EmitSignal(std::string signal) {
+  GDBusConnection* conn = GetConnection();
+  if (conn == nullptr)
+    return;
+
+  GError* error = nullptr;
+  gboolean ret = g_dbus_connection_emit_signal(conn,
+      nullptr,
+      RPC_PORT_SIGNAL_PATH,
+      RPC_PORT_SIGNAL_INTERFACE,
+      signal.c_str(),
+      nullptr,
+      &error);
+  if (ret != TRUE) {
+    _E("g_dbus_connection_emit_signal() is failed. error(%s)",
+        error ? error->message : "unknown");
+    g_clear_error(&error);
+    return;
+  }
+
+  ret = g_dbus_connection_flush_sync(conn, nullptr, &error);
+  if (ret != TRUE) {
+    _E("g_dbus_connection_flush_sync() is failed. error(%s)",
+        error ? error->message : "unknown");
+    g_clear_error(&error);
+    return;
+  }
+
+  _W("EmitSignal(%s)", signal.c_str());
+}
+
+void DebugPort::OnGDBusSignalCb(GDBusConnection *connection,
+    const gchar* sender_name,
+    const gchar* object_path,
+    const gchar* interface_name,
+    const gchar* signal_name,
+    GVariant* parameters,
+    gpointer user_data) {
+  _W("signal_name(%s)", signal_name);
+  std::string signal(signal_name);
+  if (signal != RPC_PORT_SIGNAL_DEBUG)
+    return;
+
+  gchar* port_name = nullptr;
+  gint pid = -1;
+  g_variant_get(parameters, "(&si)", &port_name, &pid);
+  _W("port_name(%s), pid(%d)", port_name, pid);
+
+  if (pid != 0 && pid != getpid()) {
+    _W("Invalid pid(%d)", pid);
+    return;
+  }
+
+  auto* debug_port = static_cast<DebugPort*>(user_data);
+  int fd = debug_port->Connect();
+  if (fd < 0)
+    return;
+
+  std::lock_guard<std::recursive_mutex> lock(debug_port->GetMutex());
+  debug_port->port_.reset(new Port(fd, signal));
+  int ret = debug_port->Watch(fd);
+  if (ret < 0)
+    return;
+
+  debug_port->CreateThread();
+  debug_port->SetConnectionStatus(true);
+  _W("Connected");
+}
+
+void DebugPort::SetConnectionStatus(bool status) {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  connected_ = status;
+}
+
+void DebugPort::CreateThread() {
+  if (is_running_)
+    return;
+
+  thread_ = std::thread([&]() {
+      _W("START");
+      do {
+        std::shared_ptr<Parcel> parcel;
+        queue_.WaitAndPop(parcel);
+        int len = parcel->GetRaw().size();
+        if (len == 0) {
+          _W("Done");
+          break;
+        }
+
+        if (!IsConnected())
+          continue;
+
+        int ret = port_->Write(reinterpret_cast<void*>(&len), sizeof(len));
+        if (ret < 0) {
+          _E("Failed to write size");
+          SetConnectionStatus(false);
+          continue;
+        }
+
+        ret = port_->Write(&*parcel->GetRaw().cbegin(), len);
+        if (ret < 0) {
+          _E("Failed to write data");
+          SetConnectionStatus(false);
+        }
+      } while (true);
+      _W("END");
+    });
+
+  is_running_ = true;
+}
+
+void DebugPort::JoinThread() {
+  if (is_running_)
+    queue_.Push(std::shared_ptr<Parcel>(new Parcel()));
+
+  if (thread_.joinable()) {
+    _W("Join thread");
+    thread_.join();
+  }
+}
+
+}  // namespace internal
+}  // namespace rpc_port
diff --git a/src/debug-port-internal.hh b/src/debug-port-internal.hh
new file mode 100644 (file)
index 0000000..3fa25e7
--- /dev/null
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEBUG_PORT_INTERNAL_HH_
+#define DEBUG_PORT_INTERNAL_HH_
+
+#include <gio/gio.h>
+#include <glib.h>
+
+#include <atomic>
+#include <list>
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+
+#include "parcel-internal.h"
+#include "port-internal.h"
+#include "shared-queue-internal.hh"
+
+namespace rpc_port {
+namespace internal {
+
+class DebugPort {
+ private:
+  DebugPort() = default;
+  ~DebugPort();
+
+ public:
+  class Session {
+   public:
+    Session(std::string port_name, std::string destination,
+         int main_port, int delegate_port)
+      : port_name_(std::move(port_name)),
+        destination_(std::move(destination)),
+        main_port_(main_port),
+        delegate_port_(delegate_port) {
+    }
+    virtual ~Session() = default;
+
+    const std::string& GetPortName() {
+      return port_name_;
+    }
+
+    const std::string& GetDestination() {
+      return destination_;
+    }
+
+    int GetMainPort() {
+      return main_port_;
+    }
+
+    int GetDelegatePort() {
+      return delegate_port_;
+    }
+
+   private:
+    std::string port_name_;
+    std::string destination_;
+    int main_port_;
+    int delegate_port_;
+  };
+
+  static DebugPort& GetInst() {
+    static DebugPort inst;
+
+    std::lock_guard<std::recursive_mutex> lock(inst.GetMutex());
+    if (inst.disposed_)
+      inst.Init();
+    return inst;
+  }
+
+  void Dispose();
+  bool IsConnected();
+
+  void AddSession(std::string port_name, std::string destination,
+      int main_port, int delegate_port);
+  void RemoveSession(int port);
+
+  int Send(int port, bool is_read, uint32_t seq,
+      const void* buf, unsigned int size);
+
+ private:
+  std::recursive_mutex& GetMutex() const {
+    return mutex_;
+  }
+
+  void Init();
+  int Connect();
+  int Watch(int fd);
+  void Unwatch();
+  GDBusConnection* GetConnection();
+  void Subscribe();
+  void Unsubscribe();
+  void EmitSignal(std::string signal);
+  void SetConnectionStatus(bool status);
+  void CreateThread();
+  void JoinThread();
+
+  std::shared_ptr<DebugPort::Session> FindSession(int port);
+
+  static gboolean OnDebugPortDisconnectedCb(GIOChannel* io,
+      GIOCondition cond, gpointer data);
+  static void OnGDBusSignalCb(GDBusConnection *connection,
+      const gchar* sender_name,
+      const gchar* object_path,
+      const gchar* interface_name,
+      const gchar* signal_name,
+      GVariant* parameters,
+      gpointer user_data);
+
+ private:
+  bool disposed_ = true;
+  bool connected_ = false;
+  GDBusConnection* conn_ = nullptr;
+  guint subs_tag_ = 0;
+  std::unique_ptr<Port> port_;
+  GIOChannel* io_ = nullptr;
+  guint watch_tag_ = 0;
+  std::list<std::shared_ptr<DebugPort::Session>> sessions_;
+  std::thread thread_;
+  std::atomic<bool> is_running_;
+  SharedQueue<std::shared_ptr<Parcel>> queue_;
+  mutable std::recursive_mutex mutex_;
+};
+
+}  // namespace internal
+}  // namespace rpc_port
+
+#endif  // DEBUG_PORT_INTERNAL_HH_
diff --git a/src/log-private.hh b/src/log-private.hh
new file mode 100644 (file)
index 0000000..6c6b2d6
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOG_PRIVATE_HH_
+#define LOG_PRIVATE_HH_
+
+#include <dlog.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RPC_PORT"
+
+#undef _E
+#define _E LOGE
+
+#undef _W
+#define _W LOGW
+
+#undef _I
+#define _I LOGI
+
+#undef _D
+#define _D LOGD
+
+#endif  // LOG_PRIVATE_HH_
index 8619a99..b1adf36 100644 (file)
@@ -45,7 +45,7 @@ namespace rpc_port {
 namespace internal {
 
 Port::Port(int fd, std::string id)
-    : fd_(fd), id_(std::move(id)) {
+    : fd_(fd), id_(std::move(id)), instance_(""), seq_(0) {
   char uuid[37];
   uuid_t u;
   uuid_generate(u);
@@ -54,7 +54,7 @@ Port::Port(int fd, std::string id)
 }
 
 Port::Port(int fd, std::string id, std::string instance)
-    : fd_(fd), id_(std::move(id)), instance_(std::move(instance)) {}
+    : fd_(fd), id_(std::move(id)), instance_(std::move(instance)), seq_(0) {}
 
 Port::~Port() {
   close(fd_);
index db94da9..5c4fb1f 100644 (file)
@@ -21,6 +21,7 @@
 #include <mutex>
 #include <string>
 #include <memory>
+#include <atomic>
 
 namespace rpc_port {
 namespace internal {
@@ -53,10 +54,15 @@ class Port {
     return instance_;
   }
 
+  uint32_t GetSeq() {
+    return ++seq_;
+  }
+
  private:
   int fd_;
   std::string id_;
   std::string instance_;
+  std::atomic<uint32_t> seq_;
   mutable std::recursive_mutex mutex_;
 };
 
index 3d45bc2..dcbe1bf 100644 (file)
 #include <sys/socket.h>
 #include <dlog.h>
 
+#include "debug-port-internal.hh"
+#include "log-private.hh"
 #include "proxy-internal.h"
 
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-
-#define LOG_TAG "RPC_PORT"
-
 #define EILLEGALACCESS 127
 
 namespace rpc_port {
@@ -39,7 +35,7 @@ Proxy::Proxy(bool mock)
     : fd_broker_(mock) {}
 
 Proxy::~Proxy() {
-  LOGD("Proxy::~Proxy");
+  _D("Proxy::~Proxy");
   if (conn_timer_) {
     g_source_remove(conn_timer_);  // LCOV_EXCL_LINE
     conn_timer_ = 0;  // LCOV_EXCL_LINE
@@ -52,7 +48,7 @@ gboolean Proxy::OnSocketDisconnected(GIOChannel *gio, GIOCondition cond,
   IEventListener* listener = proxy->listener_;
   int fd = g_io_channel_unix_get_fd(gio);
 
-  LOGW("Socket was disconnected. fd(%d)", fd);
+  _W("Socket was disconnected. fd(%d)", fd);
 
   if (proxy->main_port_.get()->GetFd() == fd) {
     proxy->listener_ = nullptr;
@@ -71,6 +67,7 @@ gboolean Proxy::OnSocketDisconnected(GIOChannel *gio, GIOCondition cond,
     proxy->main_port_.reset();
     proxy->delegate_port_.reset();
   }
+  DebugPort::GetInst().RemoveSession(fd);
 
   return FALSE;
 }
@@ -83,13 +80,14 @@ gboolean Proxy::OnDataReceived(GIOChannel *gio, GIOCondition cond,
 
   if (proxy->delegate_port_.get()->GetFd() == fd) {
     if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
-      LOGW("Socket was disconnected by stub. fd(%d)", fd);
+      _W("Socket was disconnected by stub. fd(%d)", fd);
       IEventListener* listener = proxy->listener_;
       proxy->listener_ = nullptr;
       proxy->delegate_port_.get()->SetSource(0);
       if (listener)
         listener->OnDisconnected(proxy->target_appid_);
 
+      DebugPort::GetInst().RemoveSession(proxy->main_port_->GetFd());
       proxy->main_port_.reset();
       proxy->delegate_port_.reset();
       return FALSE;
@@ -113,7 +111,7 @@ void Proxy::OnPortRejected(const std::string& appid) {
 
 void Proxy::OnPortAppeared(const std::string& appid,
                            const std::string& port_name) {
-  LOGD("endpoint(%s), port_name(%s)", appid.c_str(), port_name.c_str());
+  _D("endpoint(%s), port_name(%s)", appid.c_str(), port_name.c_str());
   if (conn_timer_) {
     g_source_remove(conn_timer_);
     conn_timer_ = 0;
@@ -137,33 +135,34 @@ void Proxy::OnPortAppeared(const std::string& appid,
     // LCOV_EXCL_STOP
   }
 
-  LOGW("[__OnPortAppeared__] fds[0]: %d, fds[1]: %d", fds_[0], fds_[1]);
+  _W("[__OnPortAppeared__] fds[0]: %d, fds[1]: %d", fds_[0], fds_[1]);
 }
 
 void Proxy::OnPortVanished(const std::string& appid,
                            const std::string& port_name) {
-  LOGW("[__OnPortVanished__] endpoint(%s), port_name(%s)",
+  _W("[__OnPortVanished__] endpoint(%s), port_name(%s)",
       appid.c_str(), port_name.c_str());
 }
 
 void Proxy::OnPortConnected(const std::string& appid,
                             const std::string& port_name) {
-  LOGW("[__OnPortConnected__] endpoint(%s), port_name(%s)",
+  _W("[__OnPortConnected__] endpoint(%s), port_name(%s)",
       appid.c_str(), port_name.c_str());
   if (!listener_) {
-    LOGW("listener is null");  // LCOV_EXCL_LINE
+    _W("listener is null");  // LCOV_EXCL_LINE
     return;  // LCOV_EXCL_LINE
   }
 
   main_port_.reset(new ProxyPort(this, fds_[0], appid, false));
   delegate_port_.reset(new ProxyPort(this, fds_[1], appid));
   listener_->OnConnected(appid, main_port_.get());
+  DebugPort::GetInst().AddSession(port_name, appid, fds_[0], fds_[1]);
 }
 
 // LCOV_EXCL_START
 void Proxy::OnPortDisconnected(const std::string& appid,
                                const std::string& port_name, bool cancel) {
-  LOGW("[__OnPortDisconnected__] endporint(%s), port_name(%s)",
+  _W("[__OnPortDisconnected__] endporint(%s), port_name(%s)",
       appid.c_str(), port_name.c_str());
 
   if (cancel) {
@@ -172,13 +171,14 @@ void Proxy::OnPortDisconnected(const std::string& appid,
   }
 
   if (!listener_) {
-    LOGW("listener is null");
+    _W("listener is null");
     return;
   }
 
   IEventListener* listener = listener_;
   listener_ = nullptr;
   listener->OnDisconnected(appid);
+  DebugPort::GetInst().RemoveSession(fds_[0]);
 }
 // LCOV_EXCL_STOP
 
@@ -188,7 +188,7 @@ int Proxy::Connect(std::string appid, std::string port_name,
     return RPC_PORT_ERROR_INVALID_PARAMETER;
 
   if (listener_ != nullptr) {
-    LOGD("Already connected");  // LCOV_EXCL_LINE
+    _D("Already connected");  // LCOV_EXCL_LINE
     return RPC_PORT_ERROR_INVALID_PARAMETER;  // LCOV_EXCL_LINE
   }
 
@@ -222,7 +222,7 @@ int Proxy::ConnectSync(std::string appid, std::string port_name,
     return RPC_PORT_ERROR_INVALID_PARAMETER;
 
   if (listener_ != nullptr) {
-    LOGW("Already connected");
+    _W("Already connected");
     return RPC_PORT_ERROR_INVALID_PARAMETER;
   }
 
@@ -253,6 +253,7 @@ int Proxy::ConnectSync(std::string appid, std::string port_name,
   main_port_.reset(new ProxyPort(this, fds_[0], target_appid_, false));
   delegate_port_.reset(new ProxyPort(this, fds_[1], target_appid_));
   listener_->OnConnected(target_appid_, main_port_.get());
+  DebugPort::GetInst().AddSession(port_name, appid, fds_[0], fds_[1]);
 
   return RPC_PORT_ERROR_NONE;
 }
@@ -262,7 +263,7 @@ int Proxy::ConnectSync(std::string appid, std::string port_name,
 gboolean Proxy::DbusNameTimeout(gpointer user_data) {
   Proxy* obj = static_cast<Proxy*>(user_data);
 
-  LOGW("[__DbusNameTimeout__] endpoint(%s)", obj->target_appid_.c_str());
+  _W("[__DbusNameTimeout__] endpoint(%s)", obj->target_appid_.c_str());
   obj->conn_timer_ = 0;
   if (obj->listener_) {
     IEventListener* listener = obj->listener_;
@@ -297,7 +298,7 @@ int Proxy::ProxyPort::Watch(bool receive) {
 
   gioc_ = g_io_channel_unix_new(fd);
   if (!gioc_) {
-    LOGE("Error is %s", strerror_r(errno, buf, sizeof(buf)));  // LCOV_EXCL_LINE
+    _E("Error is %s", strerror_r(errno, buf, sizeof(buf)));  // LCOV_EXCL_LINE
     return -1;  // LCOV_EXCL_LINE
   }
 
@@ -306,7 +307,7 @@ int Proxy::ProxyPort::Watch(bool receive) {
       Proxy::OnSocketDisconnected, parent_);
   if (disconn_src_ == 0) {
     // LCOV_EXCL_START
-    LOGE("Failed to add watch on socket");
+    _E("Failed to add watch on socket");
     g_io_channel_unref(gioc_);
     gioc_ = nullptr;
     return -1;
@@ -321,7 +322,7 @@ int Proxy::ProxyPort::Watch(bool receive) {
       Proxy::OnDataReceived, parent_);
   if (src_ == 0) {
     // LCOV_EXCL_START
-    LOGE("Failed to add watch on socket");
+    _E("Failed to add watch on socket");
     g_source_remove(disconn_src_);
     disconn_src_ = 0;
     g_io_channel_unref(gioc_);
index 61c1318..63cbf52 100644 (file)
@@ -24,8 +24,8 @@
 #include <string>
 #include <memory>
 
-#include "port-internal.h"
 #include "fdbroker-internal.h"
+#include "port-internal.h"
 
 namespace rpc_port {
 namespace internal {
index 9964b96..cba0110 100644 (file)
@@ -182,8 +182,18 @@ RPC_API int rpc_port_read(rpc_port_h h, void* buf, unsigned int size) {
     return RPC_PORT_ERROR_INVALID_PARAMETER;
 
   auto port = static_cast<Port*>(h);
+  uint32_t seq = 0;
+  int ret = port->Read(reinterpret_cast<uint32_t*>(&seq), sizeof(seq));
+  if (ret < 0)
+    return ret;
 
-  return port->Read(buf, size);
+  ret = port->Read(buf, size);
+  if (ret < 0)
+    return ret;
+
+  auto& debug_port = DebugPort::GetInst();
+  debug_port.Send(port->GetFd(), true, seq, buf, size);
+  return RPC_PORT_ERROR_NONE;
 }
 
 RPC_API int rpc_port_write(rpc_port_h h, const void* buf, unsigned int size) {
@@ -191,8 +201,18 @@ RPC_API int rpc_port_write(rpc_port_h h, const void* buf, unsigned int size) {
     return RPC_PORT_ERROR_INVALID_PARAMETER;
 
   auto port = static_cast<Port*>(h);
+  uint32_t seq = port->GetSeq();
+  int ret = port->Write(reinterpret_cast<void*>(&seq), sizeof(seq));
+  if (ret < 0)
+    return ret;
+
+  ret = port->Write(buf, size);
+  if (ret < 0)
+    return ret;
 
-  return port->Write(buf, size);
+  auto& debug_port = DebugPort::GetInst();
+  debug_port.Send(port->GetFd(), false, seq, buf, size);
+  return RPC_PORT_ERROR_NONE;
 }
 
 RPC_API int rpc_port_proxy_create(rpc_port_proxy_h* h) {
@@ -480,4 +500,4 @@ RPC_API int rpc_port_unset_private_sharing(rpc_port_h h) {
   auto port = static_cast<Port*>(h);
 
   return port->UnsetPrivateSharing();
-}
\ No newline at end of file
+}
diff --git a/src/shared-queue-internal.hh b/src/shared-queue-internal.hh
new file mode 100644 (file)
index 0000000..2ea1a87
--- /dev/null
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SHARED_QUEUE_INTERNAL_HH_
+#define SHARED_QUEUE_INTERNAL_HH_
+
+#include <condition_variable>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+namespace rpc_port {
+namespace internal {
+
+template <class T>
+class SharedQueue {
+ public:
+  SharedQueue() = default;
+  virtual ~SharedQueue() = default;
+
+  void Push(T item) {
+    std::lock_guard<std::mutex> lock(mutex_);
+    queue_.push(item);
+    cond_var_.notify_one();
+  }
+
+  bool TryAndPop(T& item) {
+    std::lock_guard<std::mutex> lock(mutex_);
+    if (queue_.empty())
+       return false;
+
+    item = queue_.front();
+    queue_.pop_front();
+
+    return true;
+  }
+
+  void WaitAndPop(T& item) {
+    std::unique_lock<std::mutex> lock(mutex_);
+    while (queue_.empty())
+      cond_var_.wait(lock);
+
+    item = queue_.front();
+    queue_.pop();
+  }
+
+  bool Empty() {
+    std::lock_guard<std::mutex> lock(mutex_);
+    return queue_.empty();
+  }
+
+  int Size() {
+    std::lock_guard<std::mutex> lock(mutex_);
+    return queue_.size();
+  }
+
+ private:
+  std::queue<T> queue_;
+  mutable std::mutex mutex_;
+  std::condition_variable cond_var_;
+};
+
+}  // namespace internal
+}  // namespace rpc_port
+
+#endif  // SHARED_QUEUE_INTERNAL_HH_
index 8b0476a..f73f3d2 100644 (file)
  * limitations under the License.
  */
 
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-
 #include <sys/types.h>
 #include <sys/socket.h>
 #include <dlog.h>
 #include <aul.h>
 #include <aul_rpc_port.h>
 
+#include "debug-port-internal.hh"
+#include "log-private.hh"
 #include "stub-internal.h"
 
-#ifdef LOG_TAG
-#undef LOG_TAG
-#endif
-
-#define LOG_TAG "RPC_PORT"
-
 namespace rpc_port {
 namespace internal {
 
 Stub::Stub(const std::string& port_name, bool mock)
-    : fd_broker_(mock), port_name_(port_name) {}
+  : fd_broker_(mock),
+    port_name_(port_name) {
+}
 
 Stub::~Stub() {
-  LOGD("Stub::~Stub");
+  _D("Stub::~Stub");
 }
 
 int Stub::Listen(IEventListener* ev) {
@@ -86,6 +80,7 @@ void Stub::RemoveAcceptedPorts(std::string instance) {
   while (iter != ports_.end()) {
     if ((*iter)->GetInstance().compare(instance) == 0) {
       LOGI("Close: fd(%d)", (*iter)->GetFd());
+      DebugPort::GetInst().RemoveSession((*iter)->GetFd());
       iter = ports_.erase(iter);
     } else {
       iter++;
@@ -102,12 +97,12 @@ gboolean Stub::OnDataReceived(GIOChannel *gio, GIOCondition cond,
   for (auto& p : stub->ports_) {
     if (p->GetFd() == fd && !p->IsDelegate()) {
       if (recv(fd, buffer, sizeof(buffer), MSG_PEEK | MSG_DONTWAIT) == 0) {
-        LOGW("Socket was disconnected from proxy. fd(%d)", fd);
+        _W("Socket was disconnected from proxy. fd(%d)", fd);
         stub->listener_->OnDisconnected(p->GetId(), p->GetInstance());
         stub->RemoveAcceptedPorts(p->GetInstance());
 
         if (aul_rpc_port_notify_rpc_finished() != AUL_R_OK)
-          LOGW("Failed to notify rpc finished");  // LCOV_EXCL_LINE
+          _W("Failed to notify rpc finished");  // LCOV_EXCL_LINE
 
         return FALSE;
       }
@@ -116,12 +111,12 @@ gboolean Stub::OnDataReceived(GIOChannel *gio, GIOCondition cond,
                                             p.get());
 
       if (ret != 0) {
-        LOGW("Invalid protocol");
+        _W("Invalid protocol");
         stub->listener_->OnDisconnected(p->GetId(), p->GetInstance());
         stub->RemoveAcceptedPorts(p->GetInstance());
 
         if (aul_rpc_port_notify_rpc_finished() != AUL_R_OK)
-          LOGW("Failed to notify rpc finished");  // LCOV_EXCL_LINE
+          _W("Failed to notify rpc finished");  // LCOV_EXCL_LINE
 
         return FALSE;
       }
@@ -138,12 +133,12 @@ gboolean Stub::OnSocketDisconnected(GIOChannel *gio, GIOCondition cond,
   Stub* stub = static_cast<Stub*>(data);
   int fd = g_io_channel_unix_get_fd(gio);
 
-  LOGW("Socket was disconnected. fd(%d)", fd);
+  _W("Socket was disconnected. fd(%d)", fd);
   for (auto& p : stub->ports_) {
     if (p->GetFd() == fd) {
       stub->listener_->OnDisconnected(p->GetId(), p->GetInstance());
       if (aul_rpc_port_notify_rpc_finished() != AUL_R_OK)
-        LOGW("Failed to notify rpc finished");  // LCOV_EXCL_LINE
+        _W("Failed to notify rpc finished");  // LCOV_EXCL_LINE
       stub->RemoveAcceptedPorts(p->GetInstance());
       break;
     }
@@ -153,7 +148,7 @@ gboolean Stub::OnSocketDisconnected(GIOChannel *gio, GIOCondition cond,
 }
 
 void Stub::OnFdReceived(const std::string& sender, int fds[2]) {
-  LOGW("[__OnFdReceived__] fds[0]: %d, fds[1]: %d", fds[0], fds[1]);
+  _W("[__OnFdReceived__] fds[0]: %d, fds[1]: %d", fds[0], fds[1]);
   auto* main_port = new AcceptedPort(this, false, fds[0], sender, true);
   ports_.emplace_back(main_port);
   ports_.emplace_back(new AcceptedPort(this, true, fds[1], sender,
@@ -164,6 +159,7 @@ void Stub::OnFdReceived(const std::string& sender, int fds[2]) {
       break;
     }
   }
+  DebugPort::GetInst().AddSession(port_name_, sender, fds[0], fds[1]);
 }
 
 Stub::AcceptedPort::AcceptedPort(Stub* parent, bool isDelegate, int fd,
@@ -198,7 +194,7 @@ int Stub::AcceptedPort::Watch(bool receive) {
   gioc_ = g_io_channel_unix_new(fd);
   if (!gioc_) {
     // LCOV_EXCL_START
-    LOGE("Error is %s", strerror_r(errno, buf, sizeof(buf)));
+    _E("Error is %s", strerror_r(errno, buf, sizeof(buf)));
     return -1;
     // LCOV_EXCL_STOP
   }
@@ -208,7 +204,7 @@ int Stub::AcceptedPort::Watch(bool receive) {
                                 Stub::OnSocketDisconnected, parent_);
   if (disconn_src_ == 0) {
     // LCOV_EXCL_START
-    LOGE("fail to add watch on socket");
+    _E("fail to add watch on socket");
     g_io_channel_unref(gioc_);
     gioc_ = nullptr;
     return -1;
@@ -223,7 +219,7 @@ int Stub::AcceptedPort::Watch(bool receive) {
                         Stub::OnDataReceived, parent_);
   if (src_ == 0) {
     // LCOV_EXCL_START
-    LOGE("fail to add watch on socket");
+    _E("fail to add watch on socket");
     g_source_remove(disconn_src_);
     disconn_src_ = 0;
     g_io_channel_unref(gioc_);
index cff2e7b..54022d2 100644 (file)
@@ -25,8 +25,9 @@
 #include <list>
 #include <memory>
 
-#include "port-internal.h"
+#include "debug-port-internal.hh"
 #include "fdbroker-internal.h"
+#include "port-internal.h"
 
 namespace rpc_port {
 namespace internal {
diff --git a/utils/CMakeLists.txt b/utils/CMakeLists.txt
new file mode 100644 (file)
index 0000000..c639ff2
--- /dev/null
@@ -0,0 +1,34 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.8)
+PROJECT(rpc-port-util CXX)
+
+INCLUDE(FindPkgConfig)
+pkg_check_modules(rpc-port-util REQUIRED
+    aul
+    bundle
+    dlog
+    glib-2.0
+)
+
+FOREACH(flag ${rpc-port-util_CFLAGS})
+       SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} ${flag}")
+ENDFOREACH(flag)
+SET(EXTRA_CFLAGS "${EXTRA_CFLAGS} -fvisibility=hidden -Wall -Werror -Winline -fPIE")
+
+SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CFLAGS} -std=c++14")
+SET(CMAKE_CXX_FLAGS_DEBUG "-O0 -g")
+SET(CMAKE_CXX_FLAGS_RELEASE "-O2")
+SET(SOURCES "")
+
+ADD_DEFINITIONS("-DFULLVER=\"${FULLVER}\"")
+
+INCLUDE_DIRECTORIES(
+       ${CMAKE_CURRENT_SOURCE_DIR}/../
+       ${CMAKE_CURRENT_SOURCE_DIR}/../include
+       )
+
+AUX_SOURCE_DIRECTORY(src SOURCES)
+ADD_EXECUTABLE(${PROJECT_NAME} ${SOURCES})
+
+TARGET_LINK_LIBRARIES(${PROJECT_NAME} ${rpc-port-util_LDFLAGS} "-pie" rpc-port)
+
+INSTALL(TARGETS ${PROJECT_NAME} DESTINATION /usr/bin)
diff --git a/utils/src/debug-port.cc b/utils/src/debug-port.cc
new file mode 100644 (file)
index 0000000..7a5229b
--- /dev/null
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+
+#include <iostream>
+#include <vector>
+
+#include "debug-port.hh"
+#include "log-private.hh"
+#include "message.hh"
+
+namespace rpc_port {
+namespace util {
+
+static const char PATH_RPC_PORT_UTIL_SOCK[] =
+    "/run/aul/daemons/.rpc-port-util-sock";
+static const char RPC_PORT_SIGNAL_PATH[] = "/Org/Tizen/RPC/Port/Signal";
+static const char RPC_PORT_SIGNAL_INTERFACE[] = "org.tizen.rpc.port.signal";
+static const char RPC_PORT_SIGNAL_DEBUG[] = "Debug";
+static const char RPC_PORT_SIGNAL_NEW[] = "New";
+
+class DisconnectedEvent {
+ public:
+  DisconnectedEvent(int pid, int fd, void* data)
+    : pid_(pid), fd_(fd), data_(data) {
+  }
+
+  int GetPid() {
+    return pid_;
+  }
+
+  int GetFd() {
+    return fd_;
+  }
+
+  void* GetData() {
+    return data_;
+  }
+
+ private:
+  int pid_;
+  int fd_;
+  void* data_;
+};
+
+DebugPort::DebugPort(std::string port_name, int pid)
+  : port_name_(std::move(port_name)),
+    pid_(pid) {
+  Subscribe();
+
+  int fd = CreateSocket();
+  if (fd < 0) {
+    _E("Failed to create socket");
+    exit(EXIT_FAILURE);
+  }
+
+  fd_ = fd;
+  Watch(fd_);
+}
+
+DebugPort::~DebugPort() {
+  Unwatch();
+
+  if (fd_ > 0)
+    close(fd_);
+
+  Unsubscribe();
+}
+
+void DebugPort::EmitSignal() {
+  GDBusConnection* conn = GetConnection();
+  if (conn == nullptr)
+    return;
+
+  GError* error = nullptr;
+  gboolean ret = g_dbus_connection_emit_signal(conn,
+      nullptr,
+      RPC_PORT_SIGNAL_PATH,
+      RPC_PORT_SIGNAL_INTERFACE,
+      RPC_PORT_SIGNAL_DEBUG,
+      g_variant_new("(si)", port_name_.c_str(), pid_),
+      &error);
+  if (ret != TRUE) {
+    _E("g_dbus_connection_emit_signal() is failed. error(%s)",
+        error ? error->message : "Unknown");
+    g_clear_error(&error);
+    return;
+  }
+
+  ret = g_dbus_connection_flush_sync(conn, nullptr, &error);
+  if (ret != TRUE) {
+    _E("g_dbus_connection_flush_sync() is failed. error(%s)",
+        error ? error->message : "Unknown");
+    g_clear_error(&error);
+    return;
+  }
+
+  _W("EmitSignal");
+}
+
+GDBusConnection* DebugPort::GetConnection() {
+  if (conn_)
+    return conn_;
+
+  GError* error = nullptr;
+  conn_ = g_bus_get_sync(G_BUS_TYPE_SYSTEM, nullptr, &error);
+  if (conn_ == nullptr) {
+    _E("g_bus_get_sync() is failed. error(%s)",
+        error ? error->message : "Unknown");
+    g_clear_error(&error);
+    return nullptr;
+  }
+
+  return conn_;
+}
+
+void DebugPort::Subscribe() {
+  GDBusConnection* conn = GetConnection();
+  if (conn == nullptr)
+    return;
+
+  subs_tag_ = g_dbus_connection_signal_subscribe(conn,
+      nullptr,
+      RPC_PORT_SIGNAL_INTERFACE,
+      RPC_PORT_SIGNAL_NEW,
+      RPC_PORT_SIGNAL_PATH,
+      nullptr,
+      G_DBUS_SIGNAL_FLAGS_NONE,
+      OnGDBusSignalCb,
+      this,
+      nullptr);
+  if (subs_tag_ == 0) {
+    _E("g_dbus_connection_signal_subscribe() is failed");
+    return;
+  }
+
+  _W("tag(%u)", subs_tag_);
+}
+
+void DebugPort::Unsubscribe() {
+  if (subs_tag_)
+    g_dbus_connection_signal_unsubscribe(conn_, subs_tag_);
+
+  if (conn_)
+    g_object_unref(conn_);
+}
+
+int DebugPort::CreateSocket() {
+  int fd = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+  if (fd < 0) {
+    _E("socket() is failed. errno(%d)", errno);
+    return -1;
+  }
+
+  struct sockaddr_un addr = { 0, };
+  addr.sun_family = AF_UNIX;
+  snprintf(addr.sun_path, sizeof(addr.sun_path), "%s", PATH_RPC_PORT_UTIL_SOCK);
+  unlink(addr.sun_path);
+
+  int ret = bind(fd, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr));
+  if (ret < 0) {
+    _E("bind() is failed. fd(%d), errno(%d)", fd, errno);
+    close(fd);
+    return -1;
+  }
+
+  ret = listen(fd, 128);
+  if (ret < 0) {
+    _E("listen() is failed. fd(%d), errno(%d)", fd, errno);
+    close(fd);
+    return -1;
+  }
+
+  ret = chmod(addr.sun_path, S_ISVTX | S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP |
+      S_IWGRP | S_IXGRP | S_IROTH | S_IWOTH | S_IXOTH);
+  if (ret < 0)
+    _E("chmod() is failed. errno(%d)", errno);
+
+  return fd;
+}
+
+void DebugPort::OnDataReceived(int pid, rpc_port_parcel_h parcel) {
+  if (pid_ != 0 && pid_ != pid)
+    return;
+
+  long long time = 0;
+  rpc_port_parcel_read_int64(parcel, &time);
+  time_t sent_time = static_cast<time_t>(time);
+
+  char* port_name = nullptr;
+  rpc_port_parcel_read_string(parcel, &port_name);
+  std::unique_ptr<char, decltype(std::free)*> port_name_ptr(port_name,
+      std::free);
+  if (port_name == nullptr)
+    return;
+
+  if (port_name != port_name_)
+    return;
+
+  char* destination = nullptr;
+  rpc_port_parcel_read_string(parcel, &destination);
+  std::unique_ptr<char, decltype(std::free)*> destination_ptr(destination,
+      std::free);
+
+  bool is_delegate = false;
+  rpc_port_parcel_read_bool(parcel, &is_delegate);
+
+  int port = -1;
+  rpc_port_parcel_read_int32(parcel, &port);
+
+  bool is_read = false;
+  rpc_port_parcel_read_bool(parcel, &is_read);
+
+  uint32_t seq = 0;
+  rpc_port_parcel_read_int32(parcel, reinterpret_cast<int*>(&seq));
+
+  unsigned int size = 0;
+  rpc_port_parcel_read_int32(parcel, reinterpret_cast<int*>(&size));
+  if (size == 0) {
+    _E("Invalid size");
+    return;
+  }
+
+  auto* buf = new unsigned char[size];
+  rpc_port_parcel_burst_read(parcel, buf, size);
+  std::vector<unsigned char> data(buf, buf + size);
+  delete[] buf;
+
+  auto* msg = new Message(sent_time, pid, port_name, destination, is_delegate,
+      port, is_read, seq, data);
+  g_idle_add([](gpointer data) -> gboolean {
+        auto* msg = static_cast<Message*>(data);
+        msg->Print();
+        delete msg;
+        return G_SOURCE_REMOVE;
+      }, msg);
+}
+
+void DebugPort::OnDisconnected(int pid, int fd) {
+  auto* event = new DisconnectedEvent(pid, fd, this);
+  g_idle_add([](gpointer data) -> gboolean {
+        auto* event = static_cast<DisconnectedEvent*>(data);
+        auto* debug_port = static_cast<DebugPort*>(event->GetData());
+        debug_port->RemoveLogger(event->GetPid(), event->GetFd());
+        delete event;
+        return G_SOURCE_REMOVE;
+      }, event);
+}
+
+void DebugPort::OnGDBusSignalCb(GDBusConnection *connection,
+    const gchar* sender_name,
+    const gchar* object_path,
+    const gchar* interface_name,
+    const gchar* signal_name,
+    GVariant* parameters,
+    gpointer user_data) {
+  _E("signal_name(%s)", signal_name);
+  std::string signal(signal_name);
+  if (signal != RPC_PORT_SIGNAL_NEW)
+    return;
+
+  auto* debug_port = static_cast<DebugPort*>(user_data);
+  debug_port->EmitSignal();
+}
+
+int DebugPort::Watch(int fd) {
+  GIOChannel* io = g_io_channel_unix_new(fd);
+  if (io == nullptr) {
+    _E("g_io_channel_unix_new() is failed");
+    return -1;
+  }
+
+  GIOCondition cond = static_cast<GIOCondition>(
+      (G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP | G_IO_NVAL));
+  guint tag = g_io_add_watch(io, cond, OnDebugPortConnectedCb, this);
+  if (tag == 0) {
+    _E("g_io_add_watch() is failed");
+    g_io_channel_unref(io);
+    return -1;
+  }
+
+  io_ = io;
+  watch_tag_ = tag;
+  return 0;
+}
+
+void DebugPort::Unwatch() {
+  if (watch_tag_) {
+    g_source_remove(watch_tag_);
+    watch_tag_ = 0;
+  }
+
+  if (io_) {
+    g_io_channel_unref(io_);
+    io_ = nullptr;
+  }
+}
+
+int DebugPort::Accept(struct ucred* cred) {
+  struct sockaddr_un addr = { 0, };
+  socklen_t socklen = sizeof(addr);
+  int client_fd = accept(fd_, reinterpret_cast<struct sockaddr*>(&addr),
+      &socklen);
+  if (client_fd < 0) {
+    _E("accept() is failed. errno(%d)", errno);
+    return -1;
+  }
+
+  socklen = sizeof(struct ucred);
+  int ret = getsockopt(client_fd, SOL_SOCKET, SO_PEERCRED,
+      cred, &socklen);
+  if (ret < 0) {
+    _E("Failed to get peer information. client_fd(%d), errno(%d)",
+        client_fd, errno);
+    close(client_fd);
+    return -1;
+  }
+
+  struct timeval tv = { 5, 200 * 1000 };
+  ret = setsockopt(client_fd, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
+  if (ret < 0) {
+    _E("Failed to set socket option. client_fd(%d), errno(%d)",
+        client_fd, errno);
+    close(client_fd);
+    return -1;
+  }
+
+  return client_fd;
+}
+
+gboolean DebugPort::OnDebugPortConnectedCb(GIOChannel* io,
+    GIOCondition cond,
+    gpointer data) {
+  _E("cond(%d)", static_cast<int>(cond));
+
+  if (cond & (G_IO_IN | G_IO_PRI)) {
+    auto* debug_port = static_cast<DebugPort*>(data);
+    struct ucred cred = { 0, };
+    int client_fd = debug_port->Accept(&cred);
+    if (client_fd < 0)
+      return G_SOURCE_CONTINUE;
+
+    debug_port->AddLogger(cred.pid, client_fd);
+  }
+
+  return G_SOURCE_CONTINUE;
+}
+
+void DebugPort::AddLogger(int pid, int fd) {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  loggers_.emplace_back(new Logger(pid, fd, this));
+  _W("Logger added. pid(%d), fd(%d)", pid, fd);
+}
+
+void DebugPort::RemoveLogger(int pid, int fd) {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  auto iter = loggers_.begin();
+  while (iter != loggers_.end()) {
+    if ((*iter)->GetPid() == pid &&
+        (*iter)->GetFd() == fd) {
+      _W("Logger removed. pid(%d), fd(%d)", pid, fd);
+      iter = loggers_.erase(iter);
+      break;
+    }
+
+    ++iter;
+  }
+}
+
+}  // namespace util
+}  // namespace rpc_port
diff --git a/utils/src/debug-port.hh b/utils/src/debug-port.hh
new file mode 100644 (file)
index 0000000..5c85bb4
--- /dev/null
@@ -0,0 +1,87 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef DEBUG_PORT_HH_
+#define DEBUG_PORT_HH_
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <memory>
+#include <mutex>
+#include <string>
+#include <thread>
+#include <list>
+
+#include "logger.hh"
+
+namespace rpc_port {
+namespace util {
+
+class DebugPort : public Logger::IEvent {
+ public:
+  DebugPort(std::string port_name, int pid);
+  virtual ~DebugPort();
+
+  void EmitSignal();
+
+ private:
+  GDBusConnection* GetConnection();
+  void Subscribe();
+  void Unsubscribe();
+  int CreateSocket();
+  int Watch(int fd);
+  void Unwatch();
+  int Accept(struct ucred* cred);
+  void AddLogger(int pid, int fd);
+  void RemoveLogger(int pid, int fd);
+
+  void OnDataReceived(int pid, rpc_port_parcel_h parcel) override;
+  void OnDisconnected(int pid, int fd) override;
+
+  static void OnGDBusSignalCb(GDBusConnection *connection,
+      const gchar* sender_name,
+      const gchar* object_path,
+      const gchar* interface_name,
+      const gchar* signal_name,
+      GVariant* parameters,
+      gpointer user_data);
+  static gboolean OnDebugPortConnectedCb(GIOChannel* io,
+      GIOCondition cond,
+      gpointer data);
+
+  std::recursive_mutex& GetMutex() const {
+    return mutex_;
+  }
+
+ private:
+  std::string port_name_;
+  int pid_;
+  GDBusConnection* conn_ = nullptr;
+  guint subs_tag_ = 0;
+  int fd_ = -1;
+  GIOChannel* io_ = nullptr;
+  guint watch_tag_ = 0;
+  std::list<std::unique_ptr<Logger>> loggers_;
+  mutable std::recursive_mutex mutex_;
+};
+
+}  // namespace util
+}  // namespace rpc_port
+
+#endif  // DEBUG_PORT_HH_
diff --git a/utils/src/log-private.hh b/utils/src/log-private.hh
new file mode 100644 (file)
index 0000000..c959ebf
--- /dev/null
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOG_PRIVATE_HH_
+#define LOG_PRIVATE_HH_
+
+#include <dlog.h>
+
+#undef LOG_TAG
+#define LOG_TAG "RPC_PORT_UTIL"
+
+#undef _E
+#define _E LOGE
+
+#undef _W
+#define _W LOGW
+
+#undef _I
+#define _I LOGI
+
+#undef _D
+#define _D LOGD
+
+#endif  // LOG_PRIVATE_HH_
diff --git a/utils/src/logger.cc b/utils/src/logger.cc
new file mode 100644 (file)
index 0000000..c9c9a92
--- /dev/null
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "log-private.hh"
+#include "logger.hh"
+
+namespace rpc_port {
+namespace util {
+
+Logger::Logger(int pid, int fd, IEvent* listener)
+  : pid_(pid), fd_(fd), listener_(listener) {
+  thread_ = std::thread([&]() {
+      GMainContext* context = g_main_context_new();
+      GIOChannel* io = g_io_channel_unix_new(fd_);
+      GIOCondition cond = static_cast<GIOCondition>(
+          G_IO_IN | G_IO_PRI | G_IO_HUP | G_IO_ERR | G_IO_NVAL);
+      GSource* source = g_io_create_watch(io, cond);
+      g_source_set_callback(source, (GSourceFunc)OnDataReceivedCb, this,
+          nullptr);
+      g_source_set_priority(source, G_PRIORITY_DEFAULT);
+      g_source_attach(source, context);
+      g_source_unref(source);
+
+      loop_ = g_main_loop_new(context, FALSE);
+      g_main_context_push_thread_default(context);
+
+      g_main_loop_run(loop_);
+
+      if (!g_source_is_destroyed(source))
+        g_source_destroy(source);
+
+      g_io_channel_unref(io);
+
+      g_main_context_pop_thread_default(context);
+      g_main_loop_unref(loop_);
+      g_main_context_unref(context);
+    });
+}
+
+Logger::~Logger() {
+  if (g_main_loop_is_running(loop_))
+    g_main_loop_quit(loop_);
+
+  thread_.join();
+
+  if (fd_ > 0)
+    close(fd_);
+}
+
+int Logger::GetPid() {
+  return pid_;
+}
+
+int Logger::GetFd() {
+  return fd_;
+}
+
+int Logger::Read(void* buf, unsigned int size) {
+  char* buffer = static_cast<char*>(buf);
+  unsigned int left = size;
+  while (left) {
+    ssize_t read_size = read(fd_, buffer, left);
+    if (read_size <= 0) {
+      _E("Failed to read data. fd(%d), errno(%d)", fd_, errno);
+      return -1;
+    }
+
+    left -= read_size;
+    buffer += read_size;
+  }
+  return 0;
+}
+
+rpc_port_parcel_h Logger::Read()
+{
+  int size = 0;
+  int ret = Read(reinterpret_cast<void*>(&size), sizeof(size));
+  if (ret < 0)
+    return nullptr;
+
+  auto* buf = new unsigned char[size];
+  ret = Read(static_cast<void*>(buf), size);
+  if (ret < 0) {
+    delete[] buf;
+    return nullptr;
+  }
+
+  rpc_port_parcel_h parcel = nullptr;
+  rpc_port_parcel_create(&parcel);
+  rpc_port_parcel_burst_write(parcel, buf, size);
+  delete[] buf;
+  return parcel;
+}
+
+gboolean Logger::OnDataReceivedCb(GIOChannel* io,
+    GIOCondition cond, gpointer data) {
+  auto* logger = static_cast<Logger*>(data);
+  auto* listener = logger->listener_;
+  int fd = g_io_channel_unix_get_fd(io);
+  int pid = logger->pid_;
+
+  if (cond & (G_IO_HUP | G_IO_ERR | G_IO_NVAL)) {
+    _E("Error(%d), fd(%d)", cond, fd);
+    listener->OnDisconnected(pid, fd);
+    return G_SOURCE_REMOVE;
+  }
+
+  rpc_port_parcel_h parcel = logger->Read();
+  listener->OnDataReceived(pid, parcel);
+  rpc_port_parcel_destroy(parcel);
+  return G_SOURCE_CONTINUE;
+}
+
+}  // namespace util
+}  // namespace rpc_port
diff --git a/utils/src/logger.hh b/utils/src/logger.hh
new file mode 100644 (file)
index 0000000..325536b
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef LOGGER_HH_
+#define LOGGER_HH_
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <rpc-port-parcel.h>
+
+#include <string>
+#include <memory>
+#include <thread>
+
+namespace rpc_port {
+namespace util {
+
+class Logger {
+ public:
+  class IEvent {
+   public:
+    virtual void OnDataReceived(int pid, rpc_port_parcel_h parcel) = 0;
+    virtual void OnDisconnected(int pid, int fd) = 0;
+  };
+
+  Logger(int pid, int fd, IEvent* listener);
+  virtual ~Logger();
+
+  int GetPid();
+  int GetFd();
+
+ private:
+  int Read(void* buf, unsigned int size);
+  rpc_port_parcel_h Read();
+  static gboolean OnDataReceivedCb(GIOChannel* io,
+      GIOCondition cond, gpointer data);
+
+ private:
+  int pid_;
+  int fd_;
+  IEvent* listener_;
+  std::thread thread_;
+  GMainLoop* loop_ = nullptr;
+};
+
+}  // namespace util
+}  // namespace rpc_port
+
+#endif  // LOGGER_HH_
diff --git a/utils/src/main.cc b/utils/src/main.cc
new file mode 100644 (file)
index 0000000..d038565
--- /dev/null
@@ -0,0 +1,66 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <glib.h>
+
+#include "debug-port.hh"
+#include "log-private.hh"
+#include "options.hh"
+
+using namespace rpc_port;
+using namespace rpc_port::util;
+
+namespace {
+
+class MainLoop {
+ public:
+  MainLoop() {
+    loop_ = g_main_loop_new(nullptr, FALSE);
+  }
+
+  ~MainLoop() {
+    g_main_loop_unref(loop_);
+  }
+
+  void Run() {
+    g_main_loop_run(loop_);
+  }
+
+  void Quit() {
+    g_main_loop_quit(loop_);
+  }
+
+ private:
+  GMainLoop* loop_;
+};
+
+}  // namespace
+
+int main(int argc, char** argv) {
+  std::unique_ptr<Options> options = Options::Parse(argc, argv);
+  if (!options) {
+    _E("options is nullptr");
+    exit(1);
+  }
+
+  DebugPort debug_port(options->GetPortName(), options->GetPid());
+  debug_port.EmitSignal();
+
+  MainLoop loop;
+  loop.Run();
+
+  return 0;
+}
diff --git a/utils/src/message.cc b/utils/src/message.cc
new file mode 100644 (file)
index 0000000..17fa647
--- /dev/null
@@ -0,0 +1,103 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <stdio.h>
+
+#include <chrono>
+#include <ctime>
+#include <iostream>
+#include <iomanip>
+
+#include "message.hh"
+
+namespace rpc_port {
+namespace util {
+
+Message::Message(time_t time, int pid, std::string port_name,
+    std::string destination, bool is_delegate, int port, bool is_read,
+    uint32_t seq, std::vector<unsigned char> data)
+  : time_(time),
+    pid_(pid),
+    port_name_(std::move(port_name)),
+    destination_(std::move(destination)),
+    is_delegate_(is_delegate),
+    port_(port),
+    is_read_(is_read),
+    seq_(seq),
+    data_(std::move(data)) {
+}
+
+Message::~Message() = default;
+
+void Message::Print() {
+  struct tm sent_time;
+  localtime_r(&time_, &sent_time);
+  char buf[50];
+  fprintf(stdout, "%s", asctime_r(&sent_time, buf));
+  fprintf(stdout, "[%s / %s:%d] [%u] ",
+      port_name_.c_str(),
+      is_delegate_ ? "delegate" : "main",
+      port_,
+      seq_);
+  if (is_read_)
+    fprintf(stdout, "%s -> %d \n", destination_.c_str(), pid_);
+  else
+    fprintf(stdout, "%d -> %s \n", pid_, destination_.c_str());
+
+  Hexdump();
+}
+
+void Message::Hexdump() {
+  unsigned int address = 0;
+  unsigned int row = 0;
+  unsigned int nread = 0;
+  std::cout << std::hex << std::setfill('0');
+  while (true) {
+    if (address >= data_.size())
+      break;
+
+    std::cout << std::setw(8) << address;
+    nread = ((data_.size() - address) > 16) ? 16 : (data_.size() - address);
+
+    for (unsigned int i = 0; i < 16; ++i) {
+      if (i % 8 == 0)
+        std::cout << ' ';
+
+      if (i < nread) {
+        std::cout << ' ' << std::setw(2) <<
+            static_cast<int>(data_[16 * row + i]);
+      } else {
+        std::cout << "   ";
+      }
+    }
+
+    std::cout << "  ";
+    for (unsigned int i = 0; i < nread ; ++i) {
+      if (data_[16 * row + i] < 32)
+        std::cout << '.';
+      else
+        std::cout << data_[16 * row + i];
+    }
+
+    std::cout << std::endl;
+    address += 16;
+    row++;
+  }
+  std::cout << std::endl;
+}
+
+}  // namespace util
+}  // namespace rpc_port
diff --git a/utils/src/message.hh b/utils/src/message.hh
new file mode 100644 (file)
index 0000000..9157b2b
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef MESSAGE_HH_
+#define MESSAGE_HH_
+
+#include <time.h>
+
+#include <string>
+#include <vector>
+
+namespace rpc_port {
+namespace util {
+
+class Message {
+ public:
+  Message(time_t time, int pid, std::string port_name, std::string destination,
+      bool is_delegate, int port, bool is_read, uint32_t seq,
+      std::vector<unsigned char> data);
+  virtual ~Message();
+
+  void Print();
+
+ private:
+  void Hexdump();
+
+ private:
+  time_t time_;
+  int pid_;
+  std::string port_name_;
+  std::string destination_;
+  bool is_delegate_;
+  int port_;
+  bool is_read_;
+  uint32_t seq_;
+  std::vector<unsigned char> data_;
+};
+
+}  // namespace util
+}  // namespace rpc_port
+
+#endif  // MESSAGE_HH_
diff --git a/utils/src/options.cc b/utils/src/options.cc
new file mode 100644 (file)
index 0000000..69bc185
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <getopt.h>
+
+#include <iostream>
+#include <cstring>
+#include <memory>
+
+#include "options.hh"
+
+namespace rpc_port {
+namespace util {
+
+Options::Options() {
+  help_ = R"__option_cb(
+Usage:
+  rpc-port-util [OPTION...]
+
+Help Options:
+  -h, --help                  Show help options
+
+Additional Options:
+  -n, --name=<Port Name>      Port name
+  -p, --pid=<Process ID>      Process ID
+
+Application Options:
+  -v, --version               Show version information
+)__option_cb";
+}
+
+void Options::PrintUsage() {
+  std::cerr << help_ << std::endl;
+}
+
+void Options::PrintVersion() {
+  std::cerr << "rpc-port-util " << FULLVER << std::endl;
+}
+
+void Options::PrintSample() {
+  std::cerr << "rpc-port-util -n Message " << std::endl;
+}
+
+std::unique_ptr<Options> Options::Parse(int argc, char** argv) {
+  int cmd[CMD_MAX] = { 0, };
+  int opt[OPT_MAX] = { 0, };
+  auto options = std::unique_ptr<Options>(new Options());
+  int option_index = 0;
+
+  struct option long_options[] = {
+    {"version", no_argument, NULL, 'v'},
+    {"help", no_argument, NULL, 'h'},
+    {"name", required_argument, NULL, 'n'},
+    {"pid", required_argument, NULL, 'p'},
+    {0, 0, 0, 0}
+  };
+
+  while (true) {
+    int c = getopt_long(argc, argv, "vhn:p:", long_options,
+        &option_index);
+    if (c == -1)
+      break;
+
+    switch (c) {
+      case 0:
+        break;
+
+      case 'v':
+        cmd[CMD_VERSION] = 1;
+        break;
+
+      case 'h':
+        cmd[CMD_HELP] = 1;
+        break;
+
+      case 'n':
+        opt[OPT_NAME] = 1;
+        options->port_name_ = optarg;
+        break;
+
+      case 'p':
+        opt[OPT_PID] = 1;
+        options->pid_ = std::stoi(optarg);
+        break;
+
+      default:
+        cmd[CMD_HELP] = 1;
+    }
+  }
+
+  if (cmd[CMD_VERSION]) {
+    options->PrintVersion();
+    return std::unique_ptr<Options>(nullptr);
+  }
+
+  if (cmd[CMD_HELP]) {
+    options->PrintUsage();
+    return std::unique_ptr<Options>(nullptr);
+  } else if (!opt[OPT_NAME]) {
+    std::cerr << "Select a port name" << std::endl;
+    options->PrintSample();
+    return std::unique_ptr<Options>(nullptr);
+  }
+
+  return options;
+}
+
+}  // namespace util
+}  // namespace rpc_port
diff --git a/utils/src/options.hh b/utils/src/options.hh
new file mode 100644 (file)
index 0000000..4e4612c
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * Copyright (c) 2020 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef OPTIONS_HH_
+#define OPTIONS_HH_
+
+#include <string>
+#include <memory>
+
+namespace rpc_port {
+namespace util {
+
+class Options {
+ public:
+  Options();
+  ~Options() = default;
+
+  static std::unique_ptr<Options> Parse(int argc, char** argv);
+
+  std::string GetPortName() const {
+    return port_name_;
+  }
+
+  int GetPid() {
+    return pid_;
+  }
+
+ private:
+  enum Cmd {
+    CMD_VERSION,
+    CMD_HELP,
+    CMD_MAX
+  };
+
+  enum Opt {
+    OPT_NAME,
+    OPT_PID,
+    OPT_MAX
+  };
+
+  void PrintUsage();
+  void PrintVersion();
+  void PrintSample();
+
+ private:
+  std::string port_name_;
+  int pid_ = 0;
+  std::string help_;
+};
+
+}  // namespace util
+}  // namespace rpc_port
+
+#endif  // OPTIONS_HH_