Support socket activation of systemd 88/308788/16
authorHwankyu Jhun <h.jhun@samsung.com>
Mon, 1 Apr 2024 05:55:44 +0000 (14:55 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Wed, 3 Apr 2024 05:06:16 +0000 (14:06 +0900)
This patch is for linux daemon and service.
The rpc-port creates the socket path without communication with amd if
the caller is a daemon or service. The process name must have "d::" prefix
as below:
 e.g. "d::Benchmark"
After this is applied, the rpc-port supports socket activation of system.
The socket path of the daemon is "/run/aul/rpcport/.<proc_name>::<port_name>".

Requires:
 - https://review.tizen.org/gerrit/#/c/platform/core/appfw/aul-1/+/308786/

Change-Id: Ifdd411b2e8b9c745cb9c1cdd9e930df7ab628b9a
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
20 files changed:
CMakeLists.txt
benchmark/server/tidl/main.cc
benchmark/tool/main.cc
packaging/rpc-port.spec
src/CMakeLists.txt
src/aul-internal.cc
src/aul-internal.hh
src/file-monitor-internal.cc [new file with mode: 0644]
src/file-monitor-internal.hh [new file with mode: 0644]
src/proxy-internal.cc
src/proxy-internal.hh
src/rpc-port.cc
src/server-socket-internal.cc
src/server-socket-internal.hh
src/stub-internal.cc
src/stub-internal.hh
test/unit_tests/main.cc
test/unit_tests/mock/inotify_mock.cc [new file with mode: 0644]
test/unit_tests/mock/inotify_mock.hh [new file with mode: 0644]
test/unit_tests/rpc_port_test.cc

index 1509ed8..8b8f0e7 100644 (file)
@@ -51,6 +51,7 @@ PKG_CHECK_MODULES(GIO_DEPS REQUIRED gio-2.0)
 PKG_CHECK_MODULES(GIO_UNIX_DEPS REQUIRED gio-unix-2.0)
 PKG_CHECK_MODULES(GLIB_DEPS REQUIRED glib-2.0)
 PKG_CHECK_MODULES(GMOCK_DEPS REQUIRED gmock)
+PKG_CHECK_MODULES(LIBSYSTEMD_DEPS REQUIRED libsystemd)
 PKG_CHECK_MODULES(LIBTZPLATFORM_CONFIG_DEPS REQUIRED libtzplatform-config)
 PKG_CHECK_MODULES(PARCEL_DEPS REQUIRED parcel)
 PKG_CHECK_MODULES(PKGMGR_INFO_DEPS REQUIRED pkgmgr-info)
index daac54c..7a084f6 100644 (file)
  */
 
 #include <glib.h>
+#include <sys/types.h>
+#include <unistd.h>
 
-#include <aul_proc.h>
+#include <rpc-port-internal.h>
 
 #include "BenchmarkStub.h"
 #include "log-private.hh"
 
 namespace {
-constexpr const char SERVER_PROC_NAME[] = "org.tizen.appfw.rpc_port.benchmark";
+constexpr const char SERVER_PROC_NAME[] =
+    "d::org.tizen.appfw.rpc_port.benchmark";
 
 namespace bs = rpc_port::BenchmarkStub::stub;
 
@@ -66,9 +69,12 @@ class MainLoop {
   }
 
   void Run() {
-    int ret = aul_proc_register(SERVER_PROC_NAME, nullptr);
-    if (ret != AUL_R_OK) {
-      _E("aul_proc_register() failed (%d)", ret);
+    std::string proc_name = std::string(SERVER_PROC_NAME);
+    if (getuid() >= 5000) proc_name = "u" + proc_name;
+
+    int ret = rpc_port_register_proc_info(proc_name.c_str(), nullptr);
+    if (ret != RPC_PORT_ERROR_NONE) {
+      _E("rpc_port_register_proc_info() failed (%d)", ret);
       return;
     }
 
index cf42bd4..b9bece1 100644 (file)
@@ -31,7 +31,7 @@
 
 namespace {
 
-#define SERVER_PROC_NAME "org.tizen.appfw.rpc_port.benchmark"
+#define SERVER_PROC_NAME "d::org.tizen.appfw.rpc_port.benchmark"
 #define SERVER_BIN_TIDL "/usr/bin/rpc-port-benchmark-server-tidl"
 #define SERVER_BIN_DBUS "/usr/bin/rpc-port-benchmark-server-dbus"
 #define SERVER_BIN_GRPC "/usr/bin/rpc-port-benchmark-server-grpc"
@@ -79,7 +79,10 @@ class Tester {
     } else if (options_->IsGrpc()) {
       grpc_proxy_.Connect();
     } else {
-      proxy_.reset(new bp::Benchmark(&listener_, SERVER_PROC_NAME));
+      std::string proc_name(SERVER_PROC_NAME);
+      if (getuid() >= 5000) proc_name = "u" + proc_name;
+
+      proxy_.reset(new bp::Benchmark(&listener_, proc_name.c_str()));
 
       try {
         proxy_->Connect(true);
index 30d4ad4..e4fc222 100644 (file)
@@ -21,6 +21,7 @@ BuildRequires:  pkgconfig(tizen-shared-queue)
 BuildRequires:  pkgconfig(uuid)
 BuildRequires:  pkgconfig(grpc)
 BuildRequires:  pkgconfig(protobuf)
+BuildRequires:  pkgconfig(libsystemd)
 
 %if 0%{?gcov:1}
 BuildRequires:  lcov
index a783f49..3e45a73 100644 (file)
@@ -18,6 +18,7 @@ APPLY_PKG_CONFIG(${TARGET_RPC_PORT} PUBLIC
   GIO_DEPS
   GIO_UNIX_DEPS
   GLIB_DEPS
+  LIBSYSTEMD_DEPS
   LIBTZPLATFORM_CONFIG_DEPS
   PARCEL_DEPS
   PKGMGR_INFO_DEPS
index f77b6d3..628cd5f 100644 (file)
 namespace rpc_port {
 namespace internal {
 
+std::string Aul::GetName(int pid) {
+  char* name = nullptr;
+  if (aul_proc_get_name(pid, &name) != AUL_R_OK) return "";
+
+  std::unique_ptr<char, decltype(std::free)*> name_auto(name, std::free);
+  return std::string(name);
+}
+
 std::string Aul::GetAppId(int pid) {
   char app_id[256] = { 0, };
   int ret = aul_app_get_appid_bypid(pid, app_id, sizeof(app_id));
   if (ret != AUL_R_OK) {
     // LCOV_EXCL_START
     _E("aul_app_get_appid_bypid() is failed. pid(%d), error(%d)", pid, ret);
-    char* name = nullptr;
-    ret = aul_proc_get_name(pid, &name);
-    if (ret != AUL_R_OK)
-      return "";
-
-    std::unique_ptr<char, decltype(std::free)*> name_auto(name, std::free);
-    return std::string(name);
+    return GetName(pid);
     // LCOV_EXCL_STOP
   }
 
index 4859109..3d74be4 100644 (file)
@@ -24,6 +24,7 @@ namespace internal {
 
 class Aul {
  public:
+  static std::string GetName(int pid);
   static std::string GetAppId(int pid);
   static std::string GetPortPath(const std::string& app_id,
       const std::string& port_name, uid_t uid);
diff --git a/src/file-monitor-internal.cc b/src/file-monitor-internal.cc
new file mode 100644 (file)
index 0000000..ad41da5
--- /dev/null
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 2024 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 "file-monitor-internal.hh"
+
+#include <sys/inotify.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <filesystem>
+#include <utility>
+
+#include "exception-internal.hh"
+#include "log-private.hh"
+
+namespace rpc_port {
+namespace internal {
+namespace fs = std::filesystem;
+
+FileMonitor::FileMonitor(std::string path, IEvent* listener)
+    : path_(std::move(path)), listener_(listener) {
+  const auto fs_path = fs::path(path_);
+  parent_path_ = fs_path.parent_path().string();
+  file_name_ = fs_path.filename().string();
+  _W("path=%s, parent_path=%s, file_name=%s",
+     path_.c_str(), parent_path_.c_str(), file_name_.c_str());
+}
+
+FileMonitor::~FileMonitor() {
+  _W("path=%s", path_.c_str());
+  Stop();
+}
+
+bool FileMonitor::Exist() {
+  if (access(path_.c_str(), F_OK) == 0) return true;
+
+  return false;
+}
+
+void FileMonitor::Start() {
+  fd_ = inotify_init1(IN_NONBLOCK | IN_CLOEXEC);
+  if (fd_ < 0) {
+    _E("inotify_init1() is failed. errno(%d)", errno);
+    THROW(-1);
+  }
+
+  wd_ = inotify_add_watch(fd_, parent_path_.c_str(), IN_CREATE | IN_DELETE);
+  if (wd_ < 0) {
+    _E("inotify_add_watch() is failed. errno(%d)", errno);
+    Stop();
+    THROW(-1);
+  }
+
+  channel_ = g_io_channel_unix_new(fd_);
+  if (channel_ == nullptr) {
+    _E("g_io_channel_unix_new() is failed");
+    Stop();
+    THROW(-1);
+  }
+
+  source_ = g_io_create_watch(channel_, G_IO_IN);
+  if (source_ == nullptr) {
+    _E("g_io_create_watch() is failed");
+    Stop();
+    THROW(-1);
+  }
+
+  g_source_set_callback(source_, reinterpret_cast<GSourceFunc>(GIOFunc), this,
+                        nullptr);
+  g_source_attach(source_, nullptr);
+  g_source_unref(source_);
+}
+
+void FileMonitor::Stop() {
+  if (source_ && !g_source_is_destroyed(source_)) {
+    g_source_destroy(source_);
+    source_ = nullptr;
+  }
+
+  if (channel_) {
+    g_io_channel_unref(channel_);
+    channel_ = nullptr;
+  }
+
+  if (wd_ > -1) {
+    inotify_rm_watch(fd_, wd_);
+    wd_ = -1;
+  }
+
+  if (fd_ > -1) {
+    close(fd_);
+    fd_ = -1;
+  }
+}
+
+gboolean FileMonitor::GIOFunc(GIOChannel* channel, GIOCondition condition,
+                              gpointer user_data) {
+  char buf[4096] __attribute__((aligned(__alignof__(struct inotify_event))));
+  auto* monitor = static_cast<FileMonitor*>(user_data);
+  auto* listener = monitor->listener_;
+  int fd = g_io_channel_unix_get_fd(channel);
+  struct inotify_event* event;
+  ssize_t len;
+  char* ptr;
+  char* nptr;
+
+  while ((len = read(fd, buf, sizeof(buf))) > 0) {
+    for (ptr = buf; ptr < buf + len;
+         ptr += sizeof(struct inotify_event) + event->len) {
+      event = reinterpret_cast<struct inotify_event*>(ptr);
+      nptr = ptr + sizeof(struct inotify_event) + event->len;
+      if (nptr > buf + len) break;
+
+      if (monitor->file_name_ != event->name) continue;
+
+      if (event->mask & IN_CREATE)
+        listener->OnFileCreated(monitor->path_);
+      else if (event->mask & IN_DELETE)
+        listener->OnFileDeleted(monitor->path_);
+    }
+  }
+
+  return G_SOURCE_CONTINUE;
+}
+
+}  // namespace internal
+}  // namespace rpc_port
diff --git a/src/file-monitor-internal.hh b/src/file-monitor-internal.hh
new file mode 100644 (file)
index 0000000..2058402
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Copyright (c) 2024 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 FILE_MONITOR_INTERNAL_HH_
+#define FILE_MONITOR_INTERNAL_HH_
+
+#include <gio/gio.h>
+#include <glib.h>
+
+#include <string>
+
+namespace rpc_port {
+namespace internal {
+
+class FileMonitor {
+ public:
+  class IEvent {
+   public:
+    virtual ~IEvent() = default;
+    virtual void OnFileCreated(const std::string& path) = 0;
+    virtual void OnFileDeleted(const std::string& path) = 0;
+  };
+
+  explicit FileMonitor(std::string path, IEvent* listener = nullptr);
+  ~FileMonitor();
+
+  void Start();
+  void Stop();
+  bool Exist();
+
+ private:
+  static gboolean GIOFunc(GIOChannel* channel, GIOCondition condition,
+                          gpointer user_data);
+
+ private:
+  std::string path_;
+  std::string parent_path_;
+  std::string file_name_;
+  IEvent* listener_ = nullptr;
+  int fd_ = -1;
+  int wd_ = -1;
+  GIOChannel* channel_ = nullptr;
+  GSource* source_ = nullptr;
+};
+
+}  // namespace internal
+}  // namespace rpc_port
+
+#endif  // FILE_MONITOR_INTERNAL_HH_
index c35ff8c..1978829 100644 (file)
@@ -41,6 +41,8 @@ namespace {
 
 constexpr const char kPortTypeMain[] = "main";
 constexpr const char kPortTypeDelegate[] = "delegate";
+constexpr const char kDPrefix[] = "d::";
+constexpr const char kUdPrefix[] = "ud::";
 
 std::string GenInstance() {
   uuid_t u;
@@ -70,17 +72,26 @@ int SendRequest(ClientSocket* client, const Request& request) {
 }
 
 int ReceiveResponse(ClientSocket* client, Response** response) {
+  int flags = fcntl(client->GetFd(), F_GETFL, 0);
+  fcntl(client->GetFd(), F_SETFL, flags & ~O_NONBLOCK);
+
   size_t size = 0;
   int ret = client->Receive(reinterpret_cast<void*>(&size), sizeof(size));
   if (ret != 0) {
-    _E("Receive() is failed. error(%d)", ret);  // LCOV_EXCL_LINE
-    return -1;  // LCOV_EXCL_LINE
+    // LCOV_EXCL_START
+    _E("Receive() is failed. error(%d)", ret);
+    fcntl(client->GetFd(), F_SETFL, flags);
+    return -1;
+    // LCOV_EXCL_STOP
   }
 
   uint8_t* buf = static_cast<uint8_t*>(malloc(size));
   if (buf == nullptr) {
-    _E("Out of memory");  // LCOV_EXCL_LINE
-    return -1;  // LCOV_EXCL_LINE
+    // LCOV_EXCL_START
+    _E("Out of memory");
+    fcntl(client->GetFd(), F_SETFL, flags);
+    return -1;
+    // LCOV_EXCL_STOP
   }
 
   ret = client->Receive(buf, size);
@@ -88,6 +99,7 @@ int ReceiveResponse(ClientSocket* client, Response** response) {
     // LCOV_EXCL_START
     _E("Receive() is failed. error(%d)", ret);
     free(buf);
+    fcntl(client->GetFd(), F_SETFL, flags);
     return -1;
     // LCOV_EXCL_STOP
   }
@@ -95,14 +107,26 @@ int ReceiveResponse(ClientSocket* client, Response** response) {
   tizen_base::Parcel parcel(buf, size, false);
   *response = new (std::nothrow) Response();
   if (*response == nullptr) {
-    _E("Out of memory");  // LCOV_EXCL_LINE
-    return -1;  // LCOV_EXCL_LINE
+    // LCOV_EXCL_START
+    _E("Out of memory");
+    fcntl(client->GetFd(), F_SETFL, flags);
+    return -1;
+    // LCOV_EXCL_STOP
   }
 
   parcel.ReadParcelable(*response);
+  fcntl(client->GetFd(), F_SETFL, flags);
   return 0;
 }
 
+bool IsDaemon(const std::string& name) {
+  if (name.compare(0, strlen(kDPrefix), kDPrefix) == 0) return true;
+
+  if (name.compare(0, strlen(kUdPrefix), kUdPrefix) == 0) return true;
+
+  return false;
+}
+
 }  // namespace
 
 Proxy::Proxy() {
@@ -121,78 +145,91 @@ Proxy::~Proxy() {
   Cancel();
 }
 
-int Proxy::Connect(bool sync) {
+int Proxy::MainPortConnect(const std::string& instance, bool sync) {
   std::lock_guard<std::recursive_mutex> lock(GetMutex());
   fds_[0] = 0;
-  fds_[1] = 0;
-
-  std::string port_path = Aul::GetPortPath(real_appid_, port_name_,
-      rpc_port_get_target_uid());
-  std::string instance = GenInstance();
-
-  // Main Port
-  main_client_.reset(Client::Create(this, port_path));
+  main_client_.reset(Client::Create(this, port_path_));
   if (main_client_.get() == nullptr)
     return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
 
   Request request(instance.c_str(), kPortTypeMain);
   int ret = SendRequest(main_client_.get(), request);
-  if (ret != 0)
-    return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
+  if (ret != 0) return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
 
   if (sync) {
     Response* response = nullptr;
     ret = ReceiveResponse(main_client_.get(), &response);
-    if (ret != 0)
-      return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
+    if (ret != 0) return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
 
     std::unique_ptr<Response> response_auto(response);
     if (response->GetResult() != 0) {
-      _E("Permission denied");  // LCOV_EXCL_LINE
+      _E("Permission denied");                  // LCOV_EXCL_LINE
       return RPC_PORT_ERROR_PERMISSION_DENIED;  // LCOV_EXCL_LINE
     }
 
     main_client_->SetNonblock();
     fds_[0] = main_client_->RemoveFd();
   } else {
+    main_client_->SetNonblock();
     ret = main_client_->Watch();
-    if (ret != 0)
-      return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
+    if (ret != 0) return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
   }
 
-  // Delegate Port
-  delegate_client_.reset(Client::Create(this, port_path));
+  return RPC_PORT_ERROR_NONE;
+}
+
+int Proxy::DelegatePortConnect(const std::string& instance, bool sync) {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  fds_[1] = 0;
+  delegate_client_.reset(Client::Create(this, port_path_));
   if (delegate_client_.get() == nullptr)
     return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
 
-  request.SetPortType(kPortTypeDelegate);
-  ret = SendRequest(delegate_client_.get(), request);
-  if (ret != 0)
-    return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
+  Request request(instance.c_str(), kPortTypeDelegate);
+  int ret = SendRequest(delegate_client_.get(), request);
+  if (ret != 0) return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
 
   if (sync) {
     Response* response = nullptr;
     ret = ReceiveResponse(delegate_client_.get(), &response);
-    if (ret != 0)
-      return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
+    if (ret != 0) return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
 
     std::unique_ptr<Response> response_auto(response);
     if (response->GetResult() != 0) {
-      _E("Permission denied");  // LCOV_EXCL_LINE
+      _E("Permission denied");                  // LCOV_EXCL_LINE
       return RPC_PORT_ERROR_PERMISSION_DENIED;  // LCOV_EXCL_LINE
     }
 
     delegate_client_->SetNonblock();
     fds_[1] = delegate_client_->RemoveFd();
   } else {
+    delegate_client_->SetNonblock();
     ret = delegate_client_->Watch();
-    if (ret != 0)
-      return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
+    if (ret != 0) return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
   }
 
   return RPC_PORT_ERROR_NONE;
 }
 
+int Proxy::Connect(bool sync) {
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  std::string instance = GenInstance();
+  int ret = MainPortConnect(instance, sync);
+  if (ret != RPC_PORT_ERROR_NONE) return ret;
+
+  ret = DelegatePortConnect(instance, sync);
+  if (ret != RPC_PORT_ERROR_NONE) return ret;
+
+  if (sync) {
+    main_port_.reset(new ProxyPort(this, fds_[0], target_appid_, false));
+    delegate_port_.reset(new ProxyPort(this, fds_[1], target_appid_));
+    DebugPort::AddSession(port_name_, target_appid_, fds_[0], fds_[1]);
+    listener_->OnConnected(target_appid_, main_port_.get());
+  }
+
+  return ret;
+}
+
 int Proxy::Connect(std::string appid, std::string port_name,
     IEventListener* listener) {
   if (listener == nullptr)
@@ -210,18 +247,21 @@ int Proxy::Connect(std::string appid, std::string port_name,
   SetRealAppId(target_appid_);
   main_port_.reset();
   delegate_port_.reset();
+  port_path_ =
+      Aul::GetPortPath(real_appid_, port_name_, rpc_port_get_target_uid());
 
   Cancel();
   UnsetConnTimer();
-  int ret = Aul::PrepareStub(real_appid_, port_name_,
-      rpc_port_get_target_uid());
-  if (ret != RPC_PORT_ERROR_NONE) {
-    listener_ = nullptr;
-    return ret;
+  if (!IsDaemon(real_appid_)) {
+    int ret = Aul::PrepareStub(real_appid_, port_name_,
+        rpc_port_get_target_uid());
+    if (ret != RPC_PORT_ERROR_NONE) {
+      listener_ = nullptr;
+      return ret;
+    }
   }
 
-  ret = Watch();
-  if (ret != 0) {
+  if (Watch() != 0) {
     listener_ = nullptr;  // LCOV_EXCL_LINE
     return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
   }
@@ -244,44 +284,51 @@ int Proxy::ConnectSync(std::string appid, std::string port_name,
   target_appid_ = std::move(appid);
   port_name_ = std::move(port_name);
   SetRealAppId(target_appid_);
+  port_path_ =
+      Aul::GetPortPath(real_appid_, port_name_, rpc_port_get_target_uid());
+
+  if (!IsDaemon(real_appid_)) {
+    int ret = Aul::PrepareStub(real_appid_, port_name_,
+        rpc_port_get_target_uid());
+    if (ret != RPC_PORT_ERROR_NONE) {
+      listener_ = nullptr;
+      return ret;
+    }
+  }
 
-  int ret = Aul::PrepareStub(real_appid_, port_name_,
-      rpc_port_get_target_uid());
+  if (!WaitUntilPortCreation())
+    return RPC_PORT_ERROR_IO_ERROR;
+
+  int ret = Connect(true);
   if (ret != RPC_PORT_ERROR_NONE) {
+    // LCOV_EXCL_START
     listener_ = nullptr;
     return ret;
+    // LCOV_EXCL_STOP
   }
 
-  bool exist = false;
-  int retry_count = 20;
+  return RPC_PORT_ERROR_NONE;
+}
+
+bool Proxy::WaitUntilPortCreation() {
+  file_monitor_.reset(new FileMonitor(port_path_));
+  int retry_count = 100;
   do {
-    exist = Aul::ExistPort(real_appid_, port_name_, rpc_port_get_target_uid());
-    if (exist)
-      break;
+    if (file_monitor_->Exist()) return true;
 
-    usleep(500 * 1000);
+    usleep(100 * 1000);
     retry_count--;
   } while (retry_count > 0);
 
-  if (!exist) {
+  if (!file_monitor_->Exist()) {
     // LCOV_EXCL_START
-    _E("%s:%s is not ready", real_appid_.c_str(), port_name_.c_str());
-    listener_ = nullptr;
-    return RPC_PORT_ERROR_IO_ERROR;
+    _E("port(%s) of appid(%s) is not ready",
+        port_name_.c_str(), target_appid_.c_str());
+    return false;
     // LCOV_EXCL_STOP
   }
 
-  ret = Connect(true);
-  if (ret != RPC_PORT_ERROR_NONE) {
-    listener_ = nullptr;  // LCOV_EXCL_LINE
-    return ret;  // LCOV_EXCL_LINE
-  }
-
-  main_port_.reset(new ProxyPort(this, fds_[0], target_appid_, false));
-  delegate_port_.reset(new ProxyPort(this, fds_[1], target_appid_));
-  DebugPort::AddSession(port_name_, target_appid_, fds_[0], fds_[1]);
-  listener_->OnConnected(target_appid_, main_port_.get());
-  return RPC_PORT_ERROR_NONE;
+  return true;
 }
 
 void Proxy::DisconnectPort() {
@@ -294,28 +341,22 @@ void Proxy::DisconnectPort() {
 
 int Proxy::Watch() {
   std::lock_guard<std::recursive_mutex> lock(GetMutex());
-  int ret = aul_rpc_port_usr_add_watch(real_appid_.c_str(), port_name_.c_str(),
-      OnPortAppeared, OnPortVanished, this, rpc_port_get_target_uid(),
-      &watch_handle_);
-  if (ret != AUL_R_OK) {
-    _E("aul_rpc_port_usr_add_watch() is failed. error(%d)", ret);  // LCOV_EXCL_LINE
-    return -1;  // LCOV_EXCL_LINE
+  try {
+    file_monitor_.reset(new FileMonitor(port_path_, this));
+    file_monitor_->Start();
+    SetConnTimer();
+    SetIdler();
+  } catch (const Exception& e) {
+    LOGE("Exception occurs. error(%s)", e.what());
+    return -1;
   }
 
-  port_exist_ = Aul::ExistPort(real_appid_, port_name_,
-      rpc_port_get_target_uid());
-
-  SetConnTimer();
-  SetIdler();
   return 0;
 }
 
 void Proxy::Cancel() {
   std::lock_guard<std::recursive_mutex> lock(GetMutex());
-  if (watch_handle_) {
-    aul_rpc_port_remove_watch(watch_handle_);
-    watch_handle_ = nullptr;
-  }
+  file_monitor_.reset();
 }
 
 void Proxy::SetRealAppId(const std::string& alias_appid) {
@@ -323,6 +364,11 @@ void Proxy::SetRealAppId(const std::string& alias_appid) {
   if (!real_appid_.empty())
     return;
 
+  if (IsDaemon(alias_appid)) {
+    real_appid_ = alias_appid;
+    return;
+  }
+
   char* appid = nullptr;
   int ret = aul_svc_get_appid_by_alias_appid(alias_appid.c_str(), &appid);
   if (ret != AUL_SVC_RET_OK) {
@@ -397,45 +443,33 @@ void Proxy::UnsetIdler() {
   idler_data_ = nullptr;
 }
 
-void Proxy::OnPortAppeared(const char* app_id, const char* port_name, int pid,
-    void* user_data) {
-  _W("app_id(%s), port_name(%s), pid(%d)", app_id, port_name, pid);
-  auto* proxy = static_cast<Proxy*>(user_data);
-  std::lock_guard<std::recursive_mutex> lock(proxy->GetMutex());
-  proxy->UnsetIdler();
-
-  if (proxy->watch_handle_ == nullptr) {
-    _E("Invalid context");
-    return;
-  }
-
-  auto* listener = proxy->listener_;
-  if (listener == nullptr) {
-    _E("Invalid context");  // LCOV_EXCL_START
-    return;  // LCOV_EXCL_START
-  }
-
-  proxy->Cancel();
+void Proxy::OnFileCreated(const std::string& path) {
+  _W("appid=%s, port_name=%s, port_path=%s",
+      target_appid_.c_str(), port_name_.c_str(), path.c_str());
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  UnsetIdler();
+  Cancel();
+  if (listener_ == nullptr) return;  // LCOV_EXCL_LINE
 
-  int ret = proxy->Connect(false);
+  int ret = Connect(false);
   if (ret != RPC_PORT_ERROR_NONE) {
     // LCOV_EXCL_START
-    proxy->UnsetConnTimer();
-    proxy->listener_ = nullptr;
+    UnsetConnTimer();
+    auto* listener = listener_;
+    listener_ = nullptr;
     if (ret == RPC_PORT_ERROR_PERMISSION_DENIED)
-      listener->OnRejected(proxy->target_appid_, ret);
+      listener->OnRejected(target_appid_, ret);
     else
-      listener->OnDisconnected(proxy->target_appid_);
+      listener->OnDisconnected(target_appid_);
     // LCOV_EXCL_STOP
   }
 }
 
-void Proxy::OnPortVanished(const char* app_id, const char* port_name, int pid,
-    void* user_data) {
-  _W("app_id(%s), port_name(%s), pid(%d)", app_id, port_name, pid);
-  auto* proxy = static_cast<Proxy*>(user_data);
-  std::lock_guard<std::recursive_mutex> lock(proxy->GetMutex());
-  proxy->UnsetIdler();
+void Proxy::OnFileDeleted(const std::string& path) {
+  _W("appid=%s, port_name=%s, port_path=%s",
+      target_appid_.c_str(), port_name_.c_str(), path.c_str());
+  std::lock_guard<std::recursive_mutex> lock(GetMutex());
+  UnsetIdler();
 }
 
 gboolean Proxy::OnTimedOut(gpointer user_data) {
@@ -487,12 +521,10 @@ gboolean Proxy::OnIdle(gpointer user_data) {
   DestroyWeakPtr(proxy->idler_data_);
   proxy->idler_data_ = nullptr;
 
-  if (proxy->port_exist_) {
-    proxy->OnPortAppeared(proxy->real_appid_.c_str(),
-        proxy->port_name_.c_str(), -1, proxy.get());
+  if (proxy->file_monitor_->Exist()) {
+    proxy->OnFileCreated(proxy->port_path_);
   } else {
-    proxy->OnPortVanished(proxy->real_appid_.c_str(),
-        proxy->port_name_.c_str(), -1, proxy.get());
+    proxy->OnFileDeleted(proxy->port_path_);
   }
 
   return G_SOURCE_REMOVE;
index 8da58ff..2ef4dcc 100644 (file)
 #define PROXY_INTERNAL_HH_
 
 #include <aul_rpc_port.h>
-#include <glib.h>
 #include <gio/gio.h>
 #include <glib-unix.h>
+#include <glib.h>
 
-#include <string>
 #include <memory>
 #include <mutex>
+#include <string>
 
 #include "client-socket-internal.hh"
+#include "file-monitor-internal.hh"
 #include "port-internal.hh"
 
 namespace rpc_port {
 namespace internal {
 
-class Proxy : public std::enable_shared_from_this<Proxy> {
+class Proxy : public std::enable_shared_from_this<Proxy>,
+              public FileMonitor::IEvent {
  public:
   Proxy();
   virtual ~Proxy();
@@ -110,16 +112,17 @@ class Proxy : public std::enable_shared_from_this<Proxy> {
   };
 
  private:
-  static void OnPortAppeared(const char* app_id, const char* port_name, int pid,
-      void* user_data);
-  static void OnPortVanished(const char* app_id, const char* port_name, int pid,
-      void* user_data);
+  void OnFileCreated(const std::string& path) override;
+  void OnFileDeleted(const std::string& path) override;
   static gboolean OnTimedOut(gpointer user_data);
   static gboolean OnIdle(gpointer user_data);
 
   void SetRealAppId(const std::string& alias_appid);
   std::recursive_mutex& GetMutex() const;
+  int MainPortConnect(const std::string& instance, bool sync);
+  int DelegatePortConnect(const std::string& instance, bool sync);
   int Connect(bool sync);
+  bool WaitUntilPortCreation();
   int Watch();
   void Cancel();
   void SetConnTimer();
@@ -134,6 +137,7 @@ class Proxy : public std::enable_shared_from_this<Proxy> {
 
  private:
   std::string port_name_;
+  std::string port_path_;
   std::shared_ptr<ProxyPort> main_port_;
   std::shared_ptr<ProxyPort> delegate_port_;
   IEventListener* listener_ = nullptr;
@@ -142,11 +146,10 @@ class Proxy : public std::enable_shared_from_this<Proxy> {
   int fds_[2];
   std::unique_ptr<Client> main_client_;
   std::unique_ptr<Client> delegate_client_;
-  aul_rpc_port_watch_h watch_handle_ = nullptr;
   gpointer conn_timer_data_ = nullptr;
   gpointer idler_data_ = nullptr;
   mutable std::recursive_mutex mutex_;
-  bool port_exist_ = false;
+  std::unique_ptr<FileMonitor> file_monitor_;
 };
 
 }  // namespace internal
index b9d04cd..023a9ab 100644 (file)
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "include/rpc-port.h"
+
 #include <aul.h>
 #include <aul_rpc_port.h>
 #include <glib.h>
@@ -24,7 +26,6 @@
 #include <utility>
 
 #include "include/rpc-port-internal.h"
-#include "include/rpc-port.h"
 #include "log-private.hh"
 #include "port-internal.hh"
 #include "proxy-internal.hh"
@@ -460,13 +461,9 @@ RPC_API int rpc_port_stub_listen(rpc_port_stub_h h) {
   auto p = static_cast<::StubExt*>(h);
   std::lock_guard<std::recursive_mutex> lock(p->GetMutex());
 
-  int fd = -1;
-  int ret = aul_rpc_port_usr_create(p->GetPortName().c_str(),
-      rpc_port_get_target_uid(), &fd);
-  if (ret != AUL_R_OK) {
-    _E("aul_rpc_port_usr_create() is failed. error(%d)", ret);  // LCOV_EXCL_LINE
+  int fd = p->CreatePort();
+  if (fd < 0)
     return RPC_PORT_ERROR_IO_ERROR;  // LCOV_EXCL_LINE
-  }
 
   return p->Listen(p, fd);
 }
index ed01137..3aca86b 100644 (file)
@@ -14,6 +14,8 @@
  * limitations under the License.
  */
 
+#include "server-socket-internal.hh"
+
 #include <errno.h>
 #include <fcntl.h>
 #include <limits.h>
 #include <sys/un.h>
 #include <unistd.h>
 
+#include "exception-internal.hh"
 #include "log-private.hh"
-#include "server-socket-internal.hh"
 
 namespace rpc_port {
 namespace internal {
 
+ServerSocket::ServerSocket() {
+  fd_ = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0);
+  if (fd_ < 0) {
+    fd_ = -errno;
+    _E("socket() is failed. errno(%d)", errno);
+    THROW(fd_);
+  }
+}
+
 ServerSocket::ServerSocket(int fd) : fd_(fd) {
   SetCloseOnExec();
 }
@@ -58,6 +69,23 @@ int ServerSocket::GetFd() const {
   return fd_;
 }
 
+void ServerSocket::Bind(const std::string& bindpoint) {
+  struct sockaddr_un sockaddr = { 0, };
+  sockaddr.sun_family = AF_UNIX;
+  snprintf(sockaddr.sun_path, sizeof(sockaddr.sun_path), "%s",
+      bindpoint.c_str());
+  struct sockaddr* sockaddr_ptr = reinterpret_cast<struct sockaddr*>(&sockaddr);
+  socklen_t len = static_cast<socklen_t>(sizeof(sockaddr));
+
+  unlink(bindpoint.c_str());
+  int ret = bind(GetFd(), sockaddr_ptr, len);
+  if (ret < 0) {
+    ret = -errno;
+    _E("bind() is failed. errno(%d)", errno);
+    THROW(ret);
+  }
+}
+
 // LCOV_EXCL_START
 int ServerSocket::Listen(int backlog) {
   int ret = listen(GetFd(), backlog);
@@ -83,5 +111,11 @@ void ServerSocket::Close() {
   }
 }
 
+int ServerSocket::RemoveFd() {
+  int fd = fd_;
+  fd_ = -1;
+  return fd;
+}
+
 }  // namespace internal
 }  // namespace rpc_port
index 43e392a..ee96155 100644 (file)
@@ -17,6 +17,8 @@
 #ifndef SERVER_SOCKET_INTERNAL_HH_
 #define SERVER_SOCKET_INTERNAL_HH_
 
+#include <string>
+
 #include "client-socket-internal.hh"
 
 namespace rpc_port {
@@ -24,15 +26,18 @@ namespace internal {
 
 class ServerSocket {
  public:
+  ServerSocket();
   explicit ServerSocket(int fd);
   virtual ~ServerSocket();
 
   bool IsClosed();
   ClientSocket* Accept();
   int GetFd() const;
+  void Bind(const std::string& endpoint);
   int Listen(int backlog);
   void Close();
   void SetCloseOnExec();
+  int RemoveFd();
 
  private:
   int fd_;
index d968ff9..df135a1 100644 (file)
  * limitations under the License.
  */
 
+#include "stub-internal.hh"
+
 #include <aul.h>
 #include <aul_rpc_port.h>
 #include <dlog.h>
 #include <sys/socket.h>
 #include <sys/types.h>
+#include <sys/un.h>
+#include <systemd/sd-daemon.h>
 
 #include <utility>
 #include <vector>
 
 #include "aul-internal.hh"
 #include "debug-port-internal.hh"
+#include "exception-internal.hh"
 #include "include/rpc-port.h"
 #include "log-private.hh"
 #include "peer-cred-internal.hh"
 #include "request-internal.hh"
 #include "response-internal.hh"
-#include "stub-internal.hh"
 
 namespace rpc_port {
 namespace internal {
@@ -180,6 +184,54 @@ void Stub::RemoveAcceptedPorts(std::string instance) {
   }
 }
 
+int Stub::CreatePort() {
+  if (getenv("AUL_APPID") == nullptr) {
+    std::string name = Aul::GetName(getpid());
+    if (!name.empty()) {
+      std::string endpoint = Aul::GetPortPath(name, GetPortName(), getuid());
+      int fd = GetFdFromSystemd(endpoint);
+      if (fd > -1) return fd;
+
+      fd = CreateServerSocket(endpoint);
+      if (fd > -1) return fd;
+    }
+  }
+
+  int fd = -1;
+  int ret = aul_rpc_port_usr_create(GetPortName().c_str(), getuid(), &fd);
+  if (ret != AUL_R_OK) {
+    // LCOV_EXCL_START
+    _E("aul_rpc_port_usr_create() is failed. error(%d)", ret);
+    return RPC_PORT_ERROR_IO_ERROR;
+    // LCOV_EXCL_STOP
+  }
+
+  return fd;
+}
+
+int Stub::GetFdFromSystemd(const std::string& endpoint) {
+  int fds = sd_listen_fds(0);
+  for (int fd = SD_LISTEN_FDS_START; fd < SD_LISTEN_FDS_START + fds; ++fd) {
+    if (sd_is_socket_unix(fd, SOCK_STREAM, 1, endpoint.c_str(), 0) > 0)
+      return fd;
+  }
+
+  _E("There is no socket stream");
+  return -1;
+}
+
+int Stub::CreateServerSocket(const std::string& endpoint) {
+  try {
+    ServerSocket socket;
+    socket.Bind(endpoint);
+    socket.Listen(128);
+    return socket.RemoveFd();
+  } catch (const Exception& e) {
+    _E("Exception occurs. error(%s)", e.what());
+    return -1;
+  }
+}
+
 gboolean Stub::AcceptedPort::OnDataReceived(GIOChannel* channel,
     GIOCondition cond, gpointer user_data) {
   auto* stub = static_cast<Stub*>(user_data);
@@ -400,8 +452,9 @@ gboolean Stub::Server::OnRequestReceived(GIOChannel* channel, GIOCondition cond,
       return;  // LCOV_EXCL_LINE
 
     if (res != 0) {
-      _E("Access denied. fd(%d), pid(%d)", client->GetFd(), cred->GetPid());  // LCOV_EXCL_LINE
-      return;  // LCOV_EXCL_LINE
+      _E("Access denied. fd(%d), pid(%d)",
+          client->GetFd(), cred->GetPid());  // LCOV_EXCL_LINE
+      return;              // LCOV_EXCL_LINE
     }
 
     client->SetNonblock();
index f58413f..71aaefc 100644 (file)
@@ -57,6 +57,7 @@ class Stub {
   std::shared_ptr<Port> FindPort(const std::string& instance) const;
   std::shared_ptr<Port> FindDelegatePort(const std::string& instance) const;
   const std::string& GetPortName() const;
+  int CreatePort();
 
  private:
   class AcceptedPort : public Port {
@@ -99,13 +100,15 @@ class Stub {
    private:
     Stub* parent_;
     GIOChannel* channel_ = nullptr;
-    guint source_ = 0;;
+    guint source_ = 0;
   };
 
   void AddAcceptedPort(const std::string& sender_appid,
       const std::string& instance, const std::string& port_type, int fd);
   void RemoveAcceptedPorts(std::string instance);
   std::recursive_mutex& GetMutex() const;
+  int GetFdFromSystemd(const std::string& endpoint);
+  int CreateServerSocket(const std::string& endpoint);
 
  private:
   std::shared_ptr<AccessController> access_controller_ =
index fcdb392..232421c 100644 (file)
  * limitations under the License.
  */
 
-#include <gtest/gtest.h>
+#include <dlog.h>
 #include <gmock/gmock.h>
+#include <gtest/gtest.h>
+#include <stdarg.h>
+#include <stdio.h>
+
+// LCOV_EXCL_START
+extern "C" int __dlog_sec_print(log_id_t log_id, int prio, const char* tag,
+                                const char* fmt, ...) {
+  printf("%s:", tag);
+  va_list ap;
+  va_start(ap, fmt);
+  vprintf(fmt, ap);
+  va_end(ap);
+  printf("\n");
+  return 0;
+}
+
+extern "C" int dlog_vprint(log_priority prio, const char* tag, const char* fmt,
+                           va_list ap) {
+  printf("%s:", tag);
+  vprintf(fmt, ap);
+  printf("\n");
+  return 0;
+}
+
+extern "C" int __dlog_print(log_id_t log_id, int prio, const char* tag,
+                            const char* fmt, ...) {
+  printf("%s:", tag);
+  va_list ap;
+  va_start(ap, fmt);
+  vprintf(fmt, ap);
+  va_end(ap);
+  printf("\n");
+  return 0;
+}
+// LCOV_EXCL_STOP
 
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
diff --git a/test/unit_tests/mock/inotify_mock.cc b/test/unit_tests/mock/inotify_mock.cc
new file mode 100644 (file)
index 0000000..05141ec
--- /dev/null
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2024 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 "mock/inotify_mock.hh"
+
+#include "mock/mock_hook.hh"
+#include "mock/test_fixture.hh"
+
+extern "C" int inotify_add_watch(int fd, const char *pathname, uint32_t mask) {
+  return 1;
+}
+
+extern "C" int inotify_rm_watch(int fd, int wd) {
+  return 0;
+}
diff --git a/test/unit_tests/mock/inotify_mock.hh b/test/unit_tests/mock/inotify_mock.hh
new file mode 100644 (file)
index 0000000..241d8f9
--- /dev/null
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2024 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 UNIT_TESTS_MOCK_INOTIFY_MOCK_HH_
+#define UNIT_TESTS_MOCK_INOTIFY_MOCK_HH_
+
+#include <gmock/gmock.h>
+#include <sys/inotify.h>
+
+#include "mock/module_mock.hh"
+
+class InotifyMock : public virtual ModuleMock {
+ public:
+  virtual ~InotifyMock() {}
+
+  MOCK_METHOD3(inotify_add_watch, int(int, const char*, uint32_t));
+  MOCK_METHOD2(inotify_rm_watch, int(int, int));
+};
+
+#endif  // UNIT_TESTS_MOCK_INOTIFY_MOCK_HH_
index 9dd7b2c..a5f93e7 100644 (file)
@@ -32,6 +32,7 @@
 
 #include "include/rpc-port-internal.h"
 #include "unit_tests/mock/aul_mock.hh"
+#include "unit_tests/mock/inotify_mock.hh"
 #include "unit_tests/mock/test_fixture.hh"
 
 using ::testing::_;
@@ -229,7 +230,8 @@ int FakeAulProcDeregister() {
 
 }  // namespace
 
-class Mocks : public ::testing::NiceMock<AulMock> {};
+class Mocks : public ::testing::NiceMock<AulMock>,
+              public ::testing::NiceMock<InotifyMock> {};
 
 class RpcPortBase : public TestFixture {
  public: