Implement garbage collector of launchpad 25/319525/7
authorHwankyu Jhun <h.jhun@samsung.com>
Fri, 25 Oct 2024 04:14:08 +0000 (13:14 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Sun, 27 Oct 2024 22:27:52 +0000 (07:27 +0900)
The codes related to the terminated process are moved from
the launchpad-process-pool. To make the launchpad is a single thread,
sub threads are removed.

Change-Id: I54967e9a78af0425a546dba84858309bc5ec1c1e
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
14 files changed:
src/lib/CMakeLists.txt
src/lib/amd_launchpad.cc
src/lib/amd_main.cc
src/lib/amd_socket.h
src/lib/api/amd_api_socket.h
src/lib/api/amd_api_types.h [new file with mode: 0644]
src/lib/app_status/app_status_manager.cc
src/lib/app_status/app_status_manager.hh
src/lib/launchpad/client_channel.cc [new file with mode: 0644]
src/lib/launchpad/client_channel.hh [new file with mode: 0644]
src/lib/launchpad/launchpad.cc [new file with mode: 0644]
src/lib/launchpad/launchpad.hh [new file with mode: 0644]
src/lib/launchpad/worker.cc [new file with mode: 0644]
src/lib/launchpad/worker.hh [new file with mode: 0644]

index 62ef78c638fc2dc007be1e6e2e7e8cd091d1f17c..465d6169fe57125f5fb08a246baa53c2f1f6f690 100644 (file)
@@ -18,6 +18,8 @@ AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/common
   LIB_COMMON_SRCS)
 AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/launch
   LIB_LAUNCH_SRCS)
+AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/launchpad
+  LIB_LAUNCHPAD_SRCS)
 AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/lifecycle
   LIB_LIFECYCLE_SRCS)
 AUX_SOURCE_DIRECTORY(${CMAKE_CURRENT_SOURCE_DIR}/pkgmgr_event
@@ -44,6 +46,7 @@ ADD_LIBRARY(${TARGET_LIB_AMD} SHARED
   ${LIB_BOOT_SEQUENCER_SRCS}
   ${LIB_COMMON_SRCS}
   ${LIB_LAUNCH_SRCS}
+  ${LIB_LAUNCHPAD_SRCS}
   ${LIB_LIFECYCLE_SRCS}
   ${LIB_PKGMGR_EVENT_SRCS}
   ${LIB_RESTART_MANAGER_SRCS}
@@ -78,6 +81,7 @@ APPLY_PKG_CONFIG(${TARGET_LIB_AMD} PUBLIC
   INIPARSER_DEPS
   LIBSYSTEMD_DEPS
   LIBTZPLATFORM_CONFIG_DEPS
+  SECURITY_MANAGER_DEPS
   PARCEL_DEPS
   PKGMGR_DEPS
   PKGMGR_INFO_DEPS
index 6a88973dda79d79e035f88950ec3d4773aea56ec..c4a49dc85a3cbb004488af420ee6dbac4702ba19 100644 (file)
 
 #include "lib/amd_launchpad.h"
 
-#include <signal.h>
-#include <stdio.h>
 #include <sys/types.h>
 #include <unistd.h>
-#include <gio/gio.h>
-#include <glib.h>
-#include <aul.h>
-#include <aul_sock.h>
 #include <bundle_cpp.h>
 #include <bundle_internal.h>
 
-#include <memory>
 #include <string>
-#include <unordered_map>
-#include <utility>
 
-#include <parcel.hh>
-
-#include "lib/amd_config.h"
-#include "lib/amd_login_monitor.h"
 #include "lib/amd_socket.h"
-#include "lib/amd_util.h"
-
-#include "lib/common/exception.hh"
-#include "lib/common/log_private.hh"
-#include "lib/socket/client_socket.hh"
-
-namespace {
-
-tizen_base::Parcel CreateParcel(int cmd, int opt, bundle* request) {
-  tizen_base::Bundle b(request, false, false);
-  auto raw = b.ToRaw();
-  int len = raw.second;
-  auto* data = reinterpret_cast<const void*>(raw.first.get());
-  tizen_base::Parcel parcel;
-  parcel.WriteInt32(cmd);
-  parcel.WriteInt32(len);
-  parcel.WriteInt32(opt | AUL_SOCK_BUNDLE);
-  parcel.Write(data, len);
-  return parcel;
-}
-
-class ClientChannel : public amd::ClientSocket {
- public:
-  class IEvent {
-   public:
-    virtual ~IEvent() = default;
-    virtual void OnResultReceived(unsigned int id, int result) = 0;
-  };
-
-  ClientChannel(uid_t uid, unsigned int id, launchpad_result_cb cb,
-      void* user_data, IEvent* listener)
-      : uid_(uid), id_(id), cb_(cb), user_data_(user_data),
-        listener_(listener) {
-    auto* channel = g_io_channel_unix_new(GetFd());
-    if (channel == nullptr) {
-      _E("g_io_channel_unix_new() is failed");
-      THROW(-ENOMEM);
-    }
-
-    auto source = g_io_add_watch_full(channel, AMD_PRIORITY_MAX, G_IO_IN,
-        OnGIOEvent, this, nullptr);
-    if (source == 0) {
-      _E("g_io_add_watch() is failed");
-      g_io_channel_unref(channel);
-      THROW(-EIO);
-    }
-
-    channel_ = channel;
-    source_ = source;
-  }
-
-  ~ClientChannel() {
-    if (channel_)
-      g_io_channel_unref(channel_);
-
-    if (source_)
-      g_source_remove(source_);
-  }
-
-  uid_t GetUid() const {
-    return uid_;
-  }
-
-  unsigned int GetId() const {
-    return id_;
-  }
-
- private:
-  void Invoke(int result) {
-    _D("[LAUNCHPAD] id: %u, result: %d", id_, result);
-    cb_(id_, result, user_data_);
-  }
-
-  static gboolean OnGIOEvent(GIOChannel* channel,
-      GIOCondition condition, gpointer user_data) {
-    auto* client_channel = static_cast<ClientChannel*>(user_data);
-    client_channel->source_ = 0;
-    int result = -1;
-    int ret = client_channel->Receive(reinterpret_cast<void*>(&result),
-        sizeof(result));
-    if (ret != 0) {
-      _E("Receive() is failed. error(%d), result(%d)", ret, result);
-      result = -ECOMM;
-    }
-
-    client_channel->Invoke(result);
-
-    auto* listener = client_channel->listener_;
-    listener->OnResultReceived(client_channel->GetId(), result);
-    return G_SOURCE_REMOVE;
-  }
-
- private:
-  uid_t uid_;
-  unsigned int id_;
-  launchpad_result_cb cb_;
-  void* user_data_;
-  IEvent* listener_;
-  GIOChannel* channel_ = nullptr;
-  guint source_ = 0;
-};
-
-class Launchpad : public ClientChannel::IEvent {
- public:
-  static Launchpad& GetInst() {
-    static Launchpad inst;
-    return inst;
-  }
-
-  int SendRequest(unsigned int id, int cmd, bundle* request, uid_t uid,
-      launchpad_result_cb cb, void* data) {
-    try {
-      auto client_channel =
-          std::make_shared<ClientChannel>(uid, id, cb, data, this);
-      std::string endpoint = GetEndpoint(uid, LAUNCHPAD_PROCESS_POOL_SOCK);
-      client_channel->Connect(endpoint);
-      client_channel->SetSendBufferSize(AUL_SOCK_MAXBUFF);
-      client_channel->SetReceiveBufferSize(AUL_SOCK_MAXBUFF);
-      client_channel->SetReceiveTimeout(5000);
-
-      tizen_base::Parcel parcel = CreateParcel(cmd, AUL_SOCK_ASYNC, request);
-      int ret = client_channel->Send(parcel.GetData(), parcel.GetDataSize());
-      if (ret != 0) {
-        _E("Send() is failed. error(%d)", ret);
-        return -ECOMM;
-      }
-
-      channels_[id] = std::move(client_channel);
-    } catch (const amd::Exception& e) {
-      _E("Exception occurs. error(%s)", e.what());
-      return e.GetErrorCode();
-    }
-
-    return 0;
-  }
-
-  int Launch(unsigned int id, bundle* request, uid_t uid,
-      launchpad_result_cb cb, void* data) {
-    return SendRequest(id, PAD_CMD_LAUNCH, request, uid, cb, data);
-  }
-
-  static std::string GetEndpoint(uid_t uid, const std::string& name) {
-    return "/run/aul/daemons/" + std::to_string(uid) + "/" + name;
-  }
-
- private:
-  Launchpad() = default;
-  ~Launchpad() = default;
-
-  void OnResultReceived(unsigned int id, int result) override {
-    channels_.erase(id);
-  }
-
- private:
-  std::unordered_map<unsigned int, std::shared_ptr<ClientChannel>> channels_;
-};
-
-int SendAndReceive(bundle* request, uid_t uid, int cmd) {
-  int result = -1;
-  try {
-    auto client = std::make_shared<amd::ClientSocket>();
-    std::string endpoint = Launchpad::GetEndpoint(uid,
-        LAUNCHPAD_PROCESS_POOL_SOCK);
-    client->Connect(endpoint);
-    client->SetSendBufferSize(AUL_SOCK_MAXBUFF);
-    client->SetReceiveBufferSize(AUL_SOCK_MAXBUFF);
-    client->SetReceiveTimeout(5000);
-
-    tizen_base::Parcel parcel = CreateParcel(cmd, AUL_SOCK_ASYNC, request);
-    int ret = client->Send(parcel.GetData(), parcel.GetDataSize());
-    if (ret != 0) {
-      _E("Send() is failed. error(%d)", ret);
-      return -ECOMM;
-    }
-
-    ret = client->Receive(reinterpret_cast<void*>(&result), sizeof(result));
-    if (ret != 0) {
-      _E("Receive() is failed. error(%d)", ret);
-      return -ECOMM;
-    }
-  } catch (const amd::Exception& e) {
-    _E("Exception occurs. error(%s)", e.what());
-    return -ECOMM;
-  }
-
-  return result;
-}
-
-}  // namespace
+#include "lib/launchpad/launchpad.hh"
 
 int _launchpad_launch_with_result_cb(unsigned int id, bundle* request,
-    uid_t uid, launchpad_result_cb callback, void* user_data) {
-  return Launchpad::GetInst().Launch(id, request, uid, callback, user_data);
+                                     uid_t uid, launchpad_result_cb callback,
+                                     void* user_data) {
+  return amd::Launchpad::GetInst().SendRequest(id, PAD_CMD_LAUNCH, request, uid,
+                                               callback, user_data);
 }
 
 int _launchpad_send_hint(uid_t uid, int hint) {
   tizen_base::Bundle b;
-  return SendAndReceive(b.GetHandle(), uid, hint);
+  return amd::Launchpad::GetInst().SendAndReceive(hint, b.GetHandle(), uid);
 }
 
 int _launchpad_send_request_with_result_cb(unsigned int id, int cmd,
                                            bundle* request, uid_t uid,
                                            launchpad_result_cb callback,
                                            void* user_data) {
-  return Launchpad::GetInst().SendRequest(id, cmd, request, uid, callback,
-                                          user_data);
+  return amd::Launchpad::GetInst().SendRequest(id, cmd, request, uid, callback,
+                                               user_data);
 }
index 621924a06329a6b5f75a265b37c2054ab71bc9ff..1ec6ca379c484ddbe4120bc7610b4d8ddfc285bb 100644 (file)
@@ -61,6 +61,7 @@
 
 #include "lib/common/cpu_boost_controller.hh"
 #include "lib/common/exception.hh"
+#include "lib/launchpad/launchpad.hh"
 #include "lib/res_info/res_info_manager.hh"
 
 typedef int (*amd_mod_init_func)(void);
@@ -251,6 +252,7 @@ static int Initialize() {
   _comp_status_init();
   _app_com_broker_init();
   _boot_manager_init();
+  amd::Launchpad::GetInst().Init();
   _launch_init();
   _suspend_init();
   _app_property_init();
@@ -282,6 +284,7 @@ static void Finalize() {
   _login_monitor_fini();
   _app_property_fini();
   _suspend_fini();
+  amd::Launchpad::GetInst().Dispose();
   _boot_manager_fini();
   _app_com_broker_fini();
   _comp_status_fini();
index cf5ac8f1fc26ea2a90a956366051fa9f62b8a7df..477292b61f80f03aa0e2f60962fdbbf824ad364b 100644 (file)
 #include <sys/un.h>
 #include <bundle.h>
 
+#include "lib/api/amd_api_types.h"
+
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 #define LAUNCHPAD_PROCESS_POOL_SOCK ".launchpad-process-pool-sock"
 
-#define PAD_CMD_LAUNCH 0
-
-#define PAD_CMD_VISIBILITY 10
-
-#define PAD_CMD_ADD_LOADER 11
-
-#define PAD_CMD_REMOVE_LOADER 12
-
-#define PAD_CMD_MAKE_DEFAULT_SLOTS 13
-
-#define PAD_CMD_DEMAND 14
-
-#define PAD_CMD_PING 15
-
-#define PAD_CMD_UPDATE_APP_TYPE 16
-
-#define PAD_CMD_CONNECT 18
-
 int _create_sock_activation(void);
 
 int _create_server_sock(void);
index 4cf1e63858b240e19595261d77ba3070b6dce6b8..750d8da3d4d78ac2a73cbd55c81dc61bbe4a8fc8 100644 (file)
 #include <bundle.h>
 #include <stdbool.h>
 #include <sys/types.h>
+#include <amd_api_types.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
-typedef enum {
-       PAD_CMD_PREPARE_APP_DEFINED_LOADER = 17,
-} pad_cmd_e;
-
 void amd_socket_send_result(int fd, int res, bool close);
 
 int amd_socket_send_cmd_to_launchpad(uid_t uid, pad_cmd_e cmd, bundle *b);
diff --git a/src/lib/api/amd_api_types.h b/src/lib/api/amd_api_types.h
new file mode 100644 (file)
index 0000000..fcd4c15
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 __AMD_API_TYPES_H__
+#define __AMD_API_TYPES_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef enum {
+  PAD_CMD_LAUNCH = 0,
+  PAD_CMD_VISIBILITY = 10,
+  PAD_CMD_ADD_LOADER = 11,
+  PAD_CMD_REMOVE_LOADER = 12,
+  PAD_CMD_MAKE_DEFAULT_SLOTS = 13,
+  PAD_CMD_DEMAND = 14,
+  PAD_CMD_PING = 15,
+  PAD_CMD_UPDATE_APP_TYPE = 16,
+  PAD_CMD_PREPARE_APP_DEFINED_LOADER = 17,
+  PAD_CMD_CONNECT = 18,
+  PAD_CMD_KILL_LOADER = 19,
+  PAD_CMD_RESTART_LOADER = 20,
+  PAD_CMD_DISPOSE_LOADER = 21,
+  PAD_CMD_LISTEN_SIGCHLD = 22,
+} pad_cmd_e;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __AMD_API_TYPES_H__ */
index 3b5da78ada262a3672f9c53432ada9f3c73ac36c..100960e8efad524c8dd9457519f5bb7abe9457ff 100644 (file)
@@ -65,12 +65,11 @@ pid_t ProcGetPPIDByPID(pid_t pid) {
   if (!is.is_open())
     return -1;
 
-  std::string key;
-  pid_t ppid;
-  while (is >> key >> ppid) {
-    if (key == "PPid:") {
-      is.close();
-      return ppid;
+  std::string line;
+  while (std::getline(is, line)) {
+    if (line.find("PPid:") == 0) {
+      std::string ppid_str = line.substr(6);
+      return std::stoi(ppid_str);
     }
   }
 
@@ -574,6 +573,7 @@ int AppStatusManager::UserInit(uid_t uid) {
             handle, amd_inotify_rm_watch);
   }
 
+  Launchpad::GetInst().ListenSigchld(uid, this);
   return 0;
 }
 
@@ -584,6 +584,7 @@ void AppStatusManager::UserFinish(uid_t uid) {
   else
     _D("Watch fd doesn't exist - uid(%d)", uid);
 
+  Launchpad::GetInst().IgnoreSigchld();
   auto iter = app_status_list_.begin();
   while (iter != app_status_list_.end()) {
     auto app_status = *iter;
@@ -992,37 +993,7 @@ void AppStatusManager::FinishVconf() {
 }
 
 void AppStatusManager::AppDeadHandler(int pid, int status, void* data) {
-  if (pid <= 0)
-    return;
-
-  auto app_status = GetInst().Find(pid);
-  if (app_status == nullptr)
-    return;
-
-  _E("APP_DEAD_SIGNAL appid(%s), pid(%d), status(%d)",
-      app_status->GetAppID().c_str(), pid, status);
-
-  uid_t uid = app_status->GetUID();
-  std::string appid = app_status->GetAppID();
-  _noti_send(AMD_NOTI_MSG_MAIN_APP_DEAD, pid, uid, app_status.get(), nullptr);
-  _request_flush_pending_request(pid);
-  GetInst().CleanUp(std::move(app_status));
-  int crash_count = 0;
-  if (IsCrashStatus(status)) {
-    _E("pid(%d) is crashed by signal(%d)", pid, WTERMSIG(status));
-    crash_count = ++GetInst().crash_count_map_[uid][appid];
-  } else {
-    GetInst().crash_count_map_[uid].erase(appid);
-  }
-
-  if (crash_count < kMaxCrashCount) {
-    _restart_manager_restart_app(uid, appid.c_str());
-  } else {
-    _E("%s has been crashed %d times in a row", appid.c_str(), crash_count);
-    GetInst().crash_count_map_[uid].erase(appid);
-  }
-
-  _util_save_log("TERMINATED", std::to_string(pid).c_str());
+  _E("pid(%d), status(%d)", pid, status);
 }
 
 void AppStatusManager::LoadAppStatusFromDB(uid_t uid) {
@@ -1058,6 +1029,39 @@ int AppStatusManager::OnLaunchPrepareEnd(const char* msg, pid_t pid, int uid,
   return 0;
 }
 
+void AppStatusManager::OnSigchldEvent(pid_t pid, int status) {
+  if (pid <= 0)
+    return;
+
+  auto app_status = Find(pid);
+  if (app_status == nullptr) return;
+
+  _E("appid(%s), pid(%d), status(%d)",
+     app_status->GetAppID().c_str(), pid, status);
+
+  uid_t uid = app_status->GetUID();
+  std::string appid = app_status->GetAppID();
+  _noti_send(AMD_NOTI_MSG_MAIN_APP_DEAD, pid, uid, app_status.get(), nullptr);
+  _request_flush_pending_request(pid);
+  GetInst().CleanUp(std::move(app_status));
+  int crash_count = 0;
+  if (IsCrashStatus(status)) {
+    _E("pid(%d) is crashed by signal(%d)", pid, WTERMSIG(status));
+    crash_count = ++GetInst().crash_count_map_[uid][appid];
+  } else {
+    GetInst().crash_count_map_[uid].erase(appid);
+  }
+
+  if (crash_count < kMaxCrashCount) {
+    _restart_manager_restart_app(uid, appid.c_str());
+  } else {
+    _E("%s has been crashed %d times in a row", appid.c_str(), crash_count);
+    GetInst().crash_count_map_[uid].erase(appid);
+  }
+
+  _util_save_log("TERMINATED", std::to_string(pid).c_str());
+}
+
 void AppStatusManager::AddSignalReadyCb() {
   _signal_add_ready_cb(
       [](void* data) -> int {
index 48f8749303ff20c51c4a5a9772a236ed5c1cfc03..580fc9049199bb12bf4910a70775de287316166d 100644 (file)
 #include "lib/app_status/app_status.hh"
 #include "lib/app_status/app_status_dao.hh"
 #include "lib/app_status/pkg_status.hh"
+#include "lib/launchpad/launchpad.hh"
 #include "lib/launch/launch_context.hh"
 
 namespace amd {
 
 using AppStatusPtr = std::shared_ptr<AppStatus>;
 
-class AppStatusManager : public AppStatus::IEvent {
+class AppStatusManager : public AppStatus::IEvent, public Launchpad::IEvent {
   using ForeachCb = std::function<void(AppStatus*, void*)>;
 
  public:
@@ -124,6 +125,8 @@ class AppStatusManager : public AppStatus::IEvent {
   static int OnLaunchPrepareEnd(const char *msg, pid_t pid, int uid,
       void* arg3, bundle* data);
 
+  void OnSigchldEvent(pid_t pid, int status) override;
+
  private:
   static constexpr int kMaxCrashCount = 3;
   bool disposed_ = true;
diff --git a/src/lib/launchpad/client_channel.cc b/src/lib/launchpad/client_channel.cc
new file mode 100644 (file)
index 0000000..58f288b
--- /dev/null
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "lib/launchpad/client_channel.hh"
+
+#include <glib.h>
+#include <glib-unix.h>
+
+#include <memory>
+#include <string>
+
+#include "lib/amd_util.h"
+#include "lib/common/log_private.hh"
+
+namespace amd {
+
+ClientChannel::ClientChannel(IEvent* listener) : listener_(listener) {
+  source_ = g_unix_fd_add_full(AMD_PRIORITY_MAX, GetFd(), G_IO_IN, OnGIOEvent,
+                               this, nullptr);
+  if (source_ == 0) {
+    _E("g_unix_fd_add_full() is failed");
+    THROW(-ENOMEM);
+  }
+}
+
+ClientChannel::~ClientChannel() {
+  if (source_) g_source_remove(source_);
+}
+
+void ClientChannel::SetExtraData(void* extra_data) { extra_data_ = extra_data; }
+
+void* ClientChannel::GetExtraData() const { return extra_data_; }
+
+gboolean ClientChannel::OnGIOEvent(gint fd, GIOCondition condition,
+                                   gpointer user_data) {
+  auto* client_channel = static_cast<ClientChannel*>(user_data);
+  client_channel->source_ = 0;
+  int result = -1;
+  int ret =
+      client_channel->Receive(reinterpret_cast<void*>(&result), sizeof(result));
+  if (ret != 0) {
+    _E("Receive() is failed. error(%d), result(%d)", ret, result);
+    result = -ECOMM;
+  }
+
+  auto* listener = client_channel->listener_;
+  listener->OnResultReceived(fd, result);
+  return G_SOURCE_REMOVE;
+}
+
+}  // namespace amd
diff --git a/src/lib/launchpad/client_channel.hh b/src/lib/launchpad/client_channel.hh
new file mode 100644 (file)
index 0000000..48ae1df
--- /dev/null
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 LIB_LAUNCHPAD_CLIENT_CHANNEL_HH_
+#define LIB_LAUNCHPAD_CLIENT_CHANNEL_HH_
+
+#include <glib.h>
+#include <glib-unix.h>
+
+#include <memory>
+#include <string>
+
+#include "lib/socket/client_socket.hh"
+
+namespace amd {
+
+class ClientChannel : public ClientSocket {
+ public:
+  class IEvent {
+   public:
+    virtual ~IEvent() = default;
+    virtual void OnResultReceived(int fd, int result) = 0;
+  };
+
+  explicit ClientChannel(IEvent* listener);
+  ~ClientChannel();
+
+  void SetExtraData(void* extra_data);
+  void* GetExtraData() const;
+
+ private:
+  static gboolean OnGIOEvent(gint fd, GIOCondition condition,
+                             gpointer user_data);
+
+ private:
+  IEvent* listener_;
+  guint source_ = 0;
+  void* extra_data_ = nullptr;
+};
+
+}  // namespace amd
+
+#endif  // LIB_LAUNCHPAD_CLIENT_CHANNEL_HH_
diff --git a/src/lib/launchpad/launchpad.cc b/src/lib/launchpad/launchpad.cc
new file mode 100644 (file)
index 0000000..8593b0b
--- /dev/null
@@ -0,0 +1,402 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "lib/launchpad/launchpad.hh"
+
+#include <aul.h>
+#include <aul_sock.h>
+#include <bundle_cpp.h>
+#include <security-manager.h>
+
+#include <filesystem>
+#include <fstream>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <parcel.hh>
+
+#include "lib/amd_socket.h"
+#include "lib/app_status/app_status_manager.hh"
+#include "lib/common/exception.hh"
+#include "lib/common/log_private.hh"
+#include "lib/signal/dbus_broker.hh"
+
+namespace amd {
+namespace {
+namespace fs = std::filesystem;
+
+constexpr const char AUL_DBUS_PATH[] = "/aul/dbus_handler";
+constexpr const char AUL_DBUS_SIGNAL_INTERFACE[] = "org.tizen.aul.signal";
+constexpr const char AUL_DBUS_APPDEAD_SIGNAL[] = "app_dead";
+constexpr const char AUL_DBUS_APPLAUNCH_SIGNAL[] = "app_launch";
+
+class GarbageCollector : public Worker::Job {
+ public:
+  GarbageCollector(pid_t pid, uid_t uid, std::string appid)
+      : pid_(pid), uid_(uid), appid_(std::move(appid)) {}
+
+  void Do() override {
+    _D("pid=%d, appid=%s", pid_, appid_.c_str());
+    try {
+      std::string path =
+          "/run/aul/apps/" + std::to_string(uid_) + "/" + std::to_string(pid_);
+      DeleteSocketPath(fs::path(path));
+
+      DeleteUnusedFiles();
+      SocketGarbadgeCollector();
+    } catch (const std::filesystem::filesystem_error& e) {
+      _E("Exception occurs. error(%s:%d)", e.what(), e.code().value());
+    }
+
+    _W("security_manager_cleanup_app() ++ %s(%d)", appid_.c_str(), pid_);
+    security_manager_cleanup_app(appid_.c_str(), uid_, pid_);
+    _W("security_manager_cleanup_app() -- %s(%d)", appid_.c_str(), pid_);
+  }
+
+ private:
+  void DeleteSocketPath(const fs::path& path) {
+    try {
+      fs::remove_all(path);
+    } catch (const fs::filesystem_error& e) {
+      _E("Exception occurs. error(%s)", e.what());
+    }
+  }
+
+  void SocketGarbadgeCollector() {
+    std::string path = "/run/aul/apps/" + std::to_string(uid_);
+    for (const auto& entry : fs::directory_iterator(path)) {
+      if (!isdigit(entry.path().filename().string()[0])) continue;
+
+      std::string proc_path = "/proc/" + entry.path().filename().string();
+      if (!fs::exists(proc_path)) DeleteSocketPath(entry.path());
+    }
+  }
+
+  void DeleteUnusedFiles() {
+    DeleteMCJFile();
+    DeleteTmpFiles();
+  }
+
+  void DeleteMCJFile() {
+    std::string path = "/tmp/.dotnet/mcj/MCJ.TMP." + std::to_string(pid_);
+    if (access(path.c_str(), F_OK) == 0) {
+      _W("Remove file: %s", path.c_str());
+      fs::remove(path);
+    }
+  }
+
+  void DeleteTmpFiles() {
+    std::vector<std::string> files = {
+        "clr-debug-pipe-" + std::to_string(pid_) + "-",
+        "dotnet-diagnostic-" + std::to_string(pid_) + "-"};
+
+    fs::path tmp_path = "/tmp";
+    for (const auto& entry : fs::directory_iterator(tmp_path)) {
+      if (entry.is_directory() || entry.path().filename().string()[0] == '.')
+        continue;
+
+      bool found = false;
+      for (const auto& file : files) {
+        if (entry.path().filename().string().find(file) == 0) {
+          fs::remove(entry.path());
+          _W("Removed file: %s", entry.path().c_str());
+          found = true;
+          break;
+        }
+      }
+
+      if (!found) continue;
+    }
+  }
+
+ private:
+  pid_t pid_;
+  uid_t uid_;
+  std::string appid_;
+};
+
+class ResultCb {
+ public:
+  ResultCb(unsigned int id, int cmd, std::string appid, launchpad_result_cb cb,
+           void* user_data)
+      : id_(id),
+        cmd_(cmd),
+        appid_(std::move(appid)),
+        cb_(cb),
+        user_data_(user_data) {}
+
+  void Invoke(int result) {
+    if (cb_) cb_(id_, result, user_data_);
+  }
+
+  int GetCmd() const { return cmd_; }
+
+  const std::string& GetAppId() const { return appid_; }
+
+ private:
+  unsigned int id_;
+  int cmd_;
+  std::string appid_;
+  launchpad_result_cb cb_;
+  void* user_data_;
+};
+
+std::string GetEndpoint(uid_t uid, const std::string& name) {
+  return "/run/aul/daemons/" + std::to_string(uid) + "/" + name;
+}
+
+tizen_base::Parcel CreateParcel(int cmd, int opt, bundle* request) {
+  tizen_base::Bundle b(request, false, false);
+  auto raw = b.ToRaw();
+  int len = raw.second;
+  auto* data = reinterpret_cast<const void*>(raw.first.get());
+  tizen_base::Parcel parcel;
+  parcel.WriteInt32(cmd);
+  parcel.WriteInt32(len);
+  parcel.WriteInt32(opt | AUL_SOCK_BUNDLE);
+  parcel.Write(data, len);
+  return parcel;
+}
+
+}  // namespace
+
+Launchpad::Launchpad() = default;
+
+Launchpad::~Launchpad() { Dispose(); }
+
+Launchpad& Launchpad::GetInst() {
+  static Launchpad inst;
+  return inst;
+}
+
+void Launchpad::Init() {
+  if (!disposed_) return;
+
+  try {
+    garbage_collector_.reset(new Worker("LaunchpadGC+"));
+  } catch (const Exception& e) {
+    _E("Exception occurs. error=%s", e.what());
+    return;
+  }
+
+  disposed_ = false;
+}
+
+void Launchpad::Dispose() {
+  if (disposed_) return;
+
+  IgnoreSigchld();
+  garbage_collector_.reset();
+  disposed_ = true;
+}
+
+int Launchpad::SendRequest(unsigned int id, int cmd, bundle* request, uid_t uid,
+                           launchpad_result_cb cb, void* user_data) {
+  try {
+    auto client_channel = std::make_shared<ClientChannel>(this);
+    std::string endpoint = GetEndpoint(uid, LAUNCHPAD_PROCESS_POOL_SOCK);
+    client_channel->Connect(endpoint);
+    client_channel->SetSendBufferSize(AUL_SOCK_MAXBUFF);
+    client_channel->SetReceiveBufferSize(AUL_SOCK_MAXBUFF);
+    client_channel->SetReceiveTimeout(5000);
+
+    tizen_base::Parcel parcel = CreateParcel(cmd, AUL_SOCK_ASYNC, request);
+    int ret = client_channel->Send(parcel.GetData(), parcel.GetDataSize());
+    if (ret != 0) {
+      _E("Send() is failed. error(%d)", ret);
+      return -ECOMM;
+    }
+
+    auto* appid = bundle_get_val(request, AUL_K_APPID);
+    if (appid == nullptr) appid = "";
+
+    client_channel->SetExtraData(new ResultCb(id, cmd, appid, cb, user_data));
+    int fd = client_channel->GetFd();
+    channels_[fd] = std::move(client_channel);
+  } catch (const Exception& e) {
+    _E("Exception occurs. error(%s)", e.what());
+    return e.GetErrorCode();
+  }
+
+  return 0;
+}
+
+int Launchpad::SendAndReceive(int cmd, bundle* request, uid_t uid) {
+  int result = -1;
+  try {
+    auto client = std::make_shared<ClientSocket>();
+    std::string endpoint = GetEndpoint(uid, LAUNCHPAD_PROCESS_POOL_SOCK);
+    client->Connect(endpoint);
+    client->SetSendBufferSize(AUL_SOCK_MAXBUFF);
+    client->SetReceiveBufferSize(AUL_SOCK_MAXBUFF);
+    client->SetReceiveTimeout(5000);
+
+    tizen_base::Parcel parcel = CreateParcel(cmd, AUL_SOCK_ASYNC, request);
+    int ret = client->Send(parcel.GetData(), parcel.GetDataSize());
+    if (ret != 0) {
+      _E("Send() is failed. error(%d)", ret);
+      return -ECOMM;
+    }
+
+    ret = client->Receive(reinterpret_cast<void*>(&result), sizeof(result));
+    if (ret != 0) {
+      _E("Receive() is failed. error(%d)", ret);
+      return -ECOMM;
+    }
+  } catch (const Exception& e) {
+    _E("Exception occurs. error(%s)", e.what());
+    return -ECOMM;
+  }
+
+  return result;
+}
+
+bool Launchpad::ListenSigchld() {
+  try {
+    sigchld_socket_.reset(new ClientSocket());
+    std::string endpoint = GetEndpoint(uid_, LAUNCHPAD_PROCESS_POOL_SOCK);
+    sigchld_socket_->Connect(endpoint);
+    sigchld_socket_->SetSendBufferSize(AUL_SOCK_MAXBUFF);
+    sigchld_socket_->SetReceiveBufferSize(AUL_SOCK_MAXBUFF);
+    sigchld_socket_->SetReceiveTimeout(5000);
+
+    tizen_base::Bundle envelope;
+    tizen_base::Parcel parcel = CreateParcel(
+        PAD_CMD_LISTEN_SIGCHLD, AUL_SOCK_ASYNC, envelope.GetHandle());
+    int ret = sigchld_socket_->Send(parcel.GetData(), parcel.GetDataSize());
+    if (ret != 0) {
+      _E("Send() is failed. error=%d", ret);
+      return false;
+    }
+
+    sigchld_source_ =
+        g_unix_fd_add(sigchld_socket_->GetFd(), G_IO_IN, UnixFdFunc, this);
+    if (sigchld_source_ == 0) _E("g_unix_fd_add() is failed");
+  } catch (const Exception& e) {
+    _E("Exception occurs. error=%s", e.what());
+    return false;
+  }
+  return true;
+}
+
+gboolean Launchpad::RetryFunc(gpointer user_data) {
+  auto* self = static_cast<Launchpad*>(user_data);
+  if (!self->ListenSigchld()) return G_SOURCE_CONTINUE;
+
+  self->retry_source_ = 0;
+  return G_SOURCE_REMOVE;
+}
+
+void Launchpad::ListenSigchld(uid_t uid, IEvent* listener) {
+  uid_ = uid;
+  listener_ = listener;
+  if (sigchld_source_ != 0) return;
+
+  if (!ListenSigchld()) {
+    if (retry_source_) g_source_remove(retry_source_);
+
+    retry_source_ = g_timeout_add(100, RetryFunc, this);
+  }
+}
+
+void Launchpad::IgnoreSigchld() {
+  if (retry_source_) {
+    g_source_remove(retry_source_);
+    retry_source_ = 0;
+  }
+
+  if (sigchld_source_) {
+    g_source_remove(sigchld_source_);
+    sigchld_source_ = 0;
+  }
+
+  sigchld_socket_.reset();
+  listener_ = nullptr;
+}
+
+void Launchpad::SendAppLaunchSignal(pid_t pid, const std::string& appid) {
+  std::string log_message = "[AppLaunch] " + std::to_string(pid) + ":" + appid;
+  DBusBroker::GetInst().SendSignal(std::make_shared<DBusRequest>(
+      AUL_DBUS_PATH, AUL_DBUS_SIGNAL_INTERFACE, AUL_DBUS_APPLAUNCH_SIGNAL,
+      std::move(log_message), g_variant_new("(us)", pid, appid.c_str())));
+}
+
+void Launchpad::SendAppDeadSignal(pid_t pid, int status) {
+  std::string log_message =
+      "[AppDead] " + std::to_string(pid) + ":" + std::to_string(status);
+  DBusBroker::GetInst().SendSignal(std::make_shared<DBusRequest>(
+      AUL_DBUS_PATH, AUL_DBUS_SIGNAL_INTERFACE, AUL_DBUS_APPDEAD_SIGNAL,
+      std::move(log_message), g_variant_new("(ii)", pid, status)));
+}
+
+void Launchpad::OnResultReceived(int fd, int result) {
+  auto found = channels_.find(fd);
+  if (found == channels_.end()) return;
+
+  auto client_channel = found->second;
+  auto* result_cb = static_cast<ResultCb*>(client_channel->GetExtraData());
+  if (result_cb) {
+    if (result_cb->GetCmd() == PAD_CMD_LAUNCH)
+      SendAppLaunchSignal(result, result_cb->GetAppId());
+    result_cb->Invoke(result);
+    delete result_cb;
+  }
+  channels_.erase(found);
+}
+
+void Launchpad::HandleSigchldEvent() {
+  size_t data_size = 0;
+  int ret = sigchld_socket_->Receive(static_cast<void*>(&data_size),
+                                     sizeof(data_size));
+  if (ret != 0) {
+    _E("Receive() is failed. error=%d", ret);
+    return;
+  }
+
+  std::vector<uint8_t> data(data_size);
+  ret = sigchld_socket_->Receive(data.data(), data_size);
+  if (ret != 0) {
+    _E("Receive() is failed. error=%d", ret);
+    return;
+  }
+
+  tizen_base::Parcel parcel(data.data(), data.size());
+  pid_t pid = -1;
+  parcel.ReadInt32(&pid);
+  int status = 0;
+  parcel.ReadInt32(&status);
+  _W("[SIGCHLD] pid=%d, status=%d", pid, status);
+
+  auto app_status = AppStatusManager::GetInst().Find(pid);
+  if (app_status) {
+    SendAppDeadSignal(pid, status);
+    garbage_collector_->Post(std::make_shared<GarbageCollector>(
+        pid, app_status->GetUID(), app_status->GetAppID()));
+  }
+
+  if (listener_) listener_->OnSigchldEvent(pid, status);
+}
+
+gboolean Launchpad::UnixFdFunc(gint fd, GIOCondition cond, gpointer user_data) {
+  _D("fd=%d, cond=%d", fd, static_cast<int>(cond));
+  auto* self = static_cast<Launchpad*>(user_data);
+  self->HandleSigchldEvent();
+  return G_SOURCE_CONTINUE;
+}
+
+}  // namespace amd
diff --git a/src/lib/launchpad/launchpad.hh b/src/lib/launchpad/launchpad.hh
new file mode 100644 (file)
index 0000000..66322ce
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 LIB_LAUNCHPAD_LAUNCHPAD_HH_
+#define LIB_LAUNCHPAD_LAUNCHPAD_HH_
+
+#include <glib.h>
+#include <glib-unix.h>
+#include <bundle.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+
+#include "lib/amd_launchpad.h"
+#include "lib/launchpad/client_channel.hh"
+#include "lib/launchpad/worker.hh"
+
+namespace amd {
+
+class Launchpad : public ClientChannel::IEvent {
+ public:
+  class IEvent {
+   public:
+    virtual ~IEvent() = default;
+    virtual void OnSigchldEvent(pid_t pid, int status) = 0;
+  };
+
+  Launchpad(const Launchpad&) = delete;
+  Launchpad& operator=(const Launchpad&) = delete;
+  Launchpad(Launchpad&&) = delete;
+  Launchpad& operator=(Launchpad&&) = delete;
+
+  static Launchpad& GetInst();
+
+  void Init();
+  void Dispose();
+  int SendRequest(unsigned int id, int cmd, bundle* request, uid_t uid,
+                  launchpad_result_cb cb, void* user_data);
+  int SendAndReceive(int cmd, bundle* request, uid_t uid);
+  void ListenSigchld(uid_t uid, IEvent* listener);
+  void IgnoreSigchld();
+
+ private:
+  Launchpad();
+  ~Launchpad();
+
+  void SendAppLaunchSignal(pid_t pid, const std::string& appid);
+  void SendAppDeadSignal(pid_t pid, int status);
+  void OnResultReceived(int fd, int result) override;
+  void HandleSigchldEvent();
+  bool ListenSigchld();
+  static gboolean UnixFdFunc(gint fd, GIOCondition cond, gpointer user_data);
+  static gboolean RetryFunc(gpointer user_data);
+
+ private:
+  bool disposed_ = true;
+  std::unique_ptr<Worker> garbage_collector_;
+  std::unordered_map<int, std::shared_ptr<ClientChannel>> channels_;
+  std::unique_ptr<ClientSocket> sigchld_socket_{nullptr};
+  guint sigchld_source_ = 0;
+  uid_t uid_ = 0;
+  IEvent* listener_ = nullptr;
+  guint retry_source_ = 0;
+};
+
+}  // namespace amd
+
+#endif  // LIB_LAUNCHPAD_LAUNCHPAD_HH_
diff --git a/src/lib/launchpad/worker.cc b/src/lib/launchpad/worker.cc
new file mode 100644 (file)
index 0000000..be570e5
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 "lib/launchpad/worker.hh"
+
+#include <utility>
+
+#include "lib/common/exception.hh"
+#include "lib/common/log_private.hh"
+
+namespace amd {
+
+Worker::Worker(std::string name) : name_(std::move(name)) {
+  int ret = tizen_core_task_create(name_.c_str(), true, &task_);
+  if (ret != TIZEN_CORE_ERROR_NONE) {
+    _E("tizen_core_task_create() is failed. error=%d", ret);
+    THROW(ret);
+  }
+
+  tizen_core_task_get_tizen_core(task_, &core_);
+  tizen_core_task_run(task_);
+}
+
+Worker::~Worker() {
+  bool running = false;
+  tizen_core_task_is_running(task_, &running);
+  if (running) tizen_core_task_quit(task_);
+
+  tizen_core_task_destroy(task_);
+}
+
+
+void Worker::Post(std::shared_ptr<Job> job) {
+  queue_.Push(std::move(job));
+  tizen_core_source_h source = nullptr;
+  tizen_core_add_idle_job(core_, JobCb, this, &source);
+  if (source == nullptr) _E("Failed to add idle job");
+}
+
+bool Worker::JobCb(void* user_data) {
+  auto* worker = static_cast<Worker*>(user_data);
+  while (!worker->queue_.IsEmpty()) {
+    auto job = worker->queue_.WaitAndPop();
+    if (job) job->Do();
+  }
+
+  return false;
+}
+
+}  // namespace amd
diff --git a/src/lib/launchpad/worker.hh b/src/lib/launchpad/worker.hh
new file mode 100644 (file)
index 0000000..7631758
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (c) 2024 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * 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 LIB_LAUNCHPAD_WORKER_HH_
+#define LIB_LAUNCHPAD_WORKER_HH_
+
+#include <tizen_core.h>
+
+#include <memory>
+#include <string>
+
+#include <shared-queue.hpp>
+
+namespace amd {
+
+class Worker {
+ public:
+  class Job {
+   public:
+    virtual ~Job() = default;
+    virtual void Do() {}
+  };
+
+  explicit Worker(std::string name);
+  virtual ~Worker();
+
+  void Post(std::shared_ptr<Job> job);
+ private:
+  static bool JobCb(void* user_data);
+
+ private:
+  std::string name_;
+  tizen_core_task_h task_ = nullptr;
+  tizen_core_h core_ = nullptr;
+  tizen_base::SharedQueue<std::shared_ptr<Job>> queue_;
+};
+
+}  // namespace amd
+
+#endif  // LIB_LAUNCHPAD_WORKER_HH_
\ No newline at end of file