Fix launchpad recovery 18/266818/8
authorHwankyu Jhun <h.jhun@samsung.com>
Fri, 19 Nov 2021 03:48:46 +0000 (12:48 +0900)
committerHwankyu Jhun <h.jhun@samsung.com>
Sun, 21 Nov 2021 22:18:10 +0000 (07:18 +0900)
While sending the SIGKILL signal to the running applications, AMD  will be busy
if there are a lot of running applications.
After this patch is applied, AMD maintains a connection for the launchpad and
monitors it to handle the recovery. If the failure number exceeds the maximum,
AMD will send the SIGABRT signal to the running launchpad. And then, AMD
tries to connect to the launchpad.
In this time, a launchpad that is a new process will be executed by
socket activation of systemd.

Change-Id: I338196a7fb680beb4a1eb4c508bc9512165e7999
Signed-off-by: Hwankyu Jhun <h.jhun@samsung.com>
src/lib/amd_launch.c
src/lib/amd_launchpad.c [deleted file]
src/lib/amd_launchpad.cc [new file with mode: 0644]
src/lib/amd_launchpad.h
src/lib/amd_login_monitor.c
src/lib/amd_socket.h

index c0cf57a..2d8440e 100644 (file)
@@ -126,7 +126,6 @@ static int __focused_pid;
 static GList *__result_info_list;
 static int __poweroff_state;
 static launch_mode_e __launch_mode = LAUNCH_MODE_NORMAL;
-static unsigned int __failure_count;
 
 static int __nofork_processing(int cmd, int pid, bundle *kb, request_h req);
 
@@ -2209,28 +2208,12 @@ static int __on_app_status_cleanup(const char *msg, int arg1, int arg2,
        return 0;
 }
 
-static int __default_launcher(bundle *b, uid_t uid, void *data)
-{
-       int r;
-
-       if (!b) {
-               _E("Invalid parameter");
-               return -1;
-       }
-
-       r = _send_cmd_to_launchpad(LAUNCHPAD_PROCESS_POOL_SOCK, uid,
-                       PAD_CMD_LAUNCH, b);
-       return r;
-}
-
 int _launch_init(void)
 {
        int ret;
 
        _D("_launch_init");
 
-       _launchpad_set_launcher(__default_launcher, NULL);
-
        _signal_add_ready_cb(__listen_app_status_signal, NULL);
        _signal_add_ready_cb(__listen_poweroff_state_signal, NULL);
 
@@ -3133,18 +3116,9 @@ static int __do_starting_app(struct launch_s *handle, request_h req,
                                handle->appid, ret);
                _noti_send(AMD_NOTI_MSG_LAUNCH_DO_STARTING_APP_CANCEL,
                                ret, 0, NULL, NULL);
-               if (ret != -ECOMM)
-                       return ret;
-
-               __failure_count++;
-               if (__failure_count > _config_get_max_launch_failure()) {
-                       _launchpad_recover_launcher(target_uid);
-                       __failure_count = 0;
-               }
                return ret;
        }
 
-       __failure_count = 0;
        handle->pid = ret;
        *pending = true;
        *bg_launch = handle->bg_launch;
diff --git a/src/lib/amd_launchpad.c b/src/lib/amd_launchpad.c
deleted file mode 100644 (file)
index bc8e136..0000000
+++ /dev/null
@@ -1,195 +0,0 @@
-/*
- * Copyright (c) 2017 - 2020 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.
- */
-
-#define _GNU_SOURCE
-#include <stdio.h>
-#include <unistd.h>
-#include <sys/types.h>
-#include <signal.h>
-
-#include <aul.h>
-#include <glib.h>
-
-#include "amd_app_status.h"
-#include "amd_config.h"
-#include "amd_launch.h"
-#include "amd_launchpad.h"
-#include "amd_login_monitor.h"
-#include "amd_signal.h"
-#include "amd_util.h"
-#include "amd_socket.h"
-
-#define TIMEOUT_INTERVAL 1000
-#define LAUNCHPAD_RECOVERY_SOCK ".launchpad-recovery-sock"
-
-struct launchpad_info {
-       int (*launcher)(bundle *, uid_t t, void *);
-       void *data;
-};
-
-static struct launchpad_info __launchpad;
-static GHashTable *__timer_tbl;
-
-int _launchpad_set_launcher(int (*callback)(bundle *, uid_t, void *),
-               void *user_data)
-{
-       __launchpad.launcher = callback;
-       __launchpad.data = user_data;
-
-       return 0;
-}
-
-int _launchpad_launch(bundle *kb, uid_t uid)
-{
-       if (!__launchpad.launcher) {
-               _E("Launcher is not prepared");
-               return -1;
-       }
-
-       return __launchpad.launcher(kb, uid, __launchpad.data);
-}
-
-static void __remove_recovery_timer(gpointer data)
-{
-       guint timer = GPOINTER_TO_UINT(data);
-
-       g_source_remove(timer);
-}
-
-static void __unset_recovery_timer(uid_t uid)
-{
-       if (!g_hash_table_contains(__timer_tbl, GUINT_TO_POINTER(uid)))
-               return;
-
-       g_hash_table_remove(__timer_tbl, GUINT_TO_POINTER(uid));
-       _W("[__RECOVERY__] timer is removed. uid(%u)", uid);
-}
-
-static gboolean __launchpad_recovery_cb(gpointer data)
-{
-       uid_t uid = GPOINTER_TO_UINT(data);
-       bundle *b;
-       pid_t pgid;
-       pid_t pid;
-       int ret;
-
-       pid = _login_monitor_get_launchpad_pid(uid);
-       if (pid > 0) {
-               pgid = getpgid(pid);
-               if (pgid > 0) {
-                       _W("[__RECOVERY__] launchpad(%d) is running", pid);
-                       return G_SOURCE_CONTINUE;
-               }
-       }
-
-       b = bundle_create();
-       ret = _send_cmd_to_launchpad_async(LAUNCHPAD_RECOVERY_SOCK, uid,
-                       0, b);
-       bundle_free(b);
-       if (ret < 0) {
-               _E("[__RECOVERY__] Failed to send recovery request. error(%d)",
-                               ret);
-               return G_SOURCE_CONTINUE;
-       }
-
-       _W("[__RECOVERY__] Recovery launchpad");
-       __unset_recovery_timer(uid);
-       return G_SOURCE_CONTINUE;
-}
-
-static void __set_recovery_timer(uid_t uid)
-{
-       guint timer;
-
-       timer = g_timeout_add(TIMEOUT_INTERVAL, __launchpad_recovery_cb,
-                       GUINT_TO_POINTER(uid));
-       g_hash_table_insert(__timer_tbl, GUINT_TO_POINTER(uid),
-                       GUINT_TO_POINTER(timer));
-       _W("[__RECOVERY__] timer(%u) is removed. uid(%u)", timer, uid);
-}
-
-static bool __is_recovering(uid_t uid)
-{
-       return g_hash_table_contains(__timer_tbl, GUINT_TO_POINTER(uid));
-}
-
-static void __running_appinfo_cb(app_status_h app_status, void *user_data)
-{
-       const char *appid;
-       uid_t uid;
-       pid_t pid;
-       int ret;
-
-       appid = _app_status_get_appid(app_status);
-       pid = _app_status_get_pid(app_status);
-       uid = _app_status_get_uid(app_status);
-
-       aul_send_app_terminate_request_signal(pid, NULL, NULL, NULL);
-       ret = _launch_send_sigkill(pid, uid);
-       _E("[__RECOVERY__] appid(%s), pid(%d), result(%d)", appid, pid, ret);
-
-       ret = _signal_send_app_dead(pid);
-       if (ret < 0) {
-               _W("Failed to send app dead signal. pid(%d)", pid);
-               _app_status_cleanup(app_status);
-       }
-}
-
-int _launchpad_recover_launcher(uid_t uid)
-{
-       pid_t pid;
-       int ret;
-
-       if (__is_recovering(uid))
-               return 0;
-
-       _W("[__RECOVERY__] uid(%u)", uid);
-       pid = _login_monitor_get_launchpad_pid(uid);
-       if (pid < 0) {
-               _E("Failed to get launchpad pid. uid(%u)", uid);
-               return -1;
-       }
-
-       _app_status_foreach_running_appinfo(__running_appinfo_cb, NULL);
-
-       ret = kill(pid, SIGABRT);
-       _E("[__RECOVERY__] launchpad pid(%d), uid(%d), result(%d)",
-                       pid, uid, ret);
-
-       __set_recovery_timer(uid);
-       return ret;
-}
-
-int _launchpad_init(void)
-{
-       _D("LAUNCHPAD_INIT");
-
-       __timer_tbl = g_hash_table_new_full(g_direct_hash, g_direct_equal,
-                       NULL, __remove_recovery_timer);
-       if (!__timer_tbl) {
-               _E("g_hash_table_new() is failed");
-               return -ENOMEM;
-       }
-
-       return 0;
-}
-
-void _launchpad_fini(void)
-{
-       _D("LAUNCHPAD_FINI");
-       if (__timer_tbl)
-               g_hash_table_destroy(__timer_tbl);
-}
diff --git a/src/lib/amd_launchpad.cc b/src/lib/amd_launchpad.cc
new file mode 100644 (file)
index 0000000..9636680
--- /dev/null
@@ -0,0 +1,319 @@
+/*
+ * Copyright (c) 2017 - 2021 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/amd_launchpad.h"
+
+#include <aul.h>
+#include <aul_sock.h>
+#include <bundle_internal.h>
+#include <glib.h>
+#include <gio/gio.h>
+#include <signal.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <bundle_cpp.h>
+#include <parcel.hh>
+
+#include <map>
+#include <memory>
+#include <string>
+
+#include "lib/amd_config.h"
+#include "lib/amd_login_monitor.h"
+#include "lib/amd_socket.h"
+
+#include "lib/common/exception.hh"
+#include "lib/common/log_private.hh"
+#include "lib/socket/client_socket.hh"
+
+namespace {
+using namespace amd;
+
+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 ClientSocket {
+ public:
+  class IEvent {
+   public:
+    virtual void OnDisconnected(uid_t uid) = 0;
+  };
+
+  ClientChannel(uid_t uid, IEvent* listener) : uid_(uid), listener_(listener) {
+    auto* channel = g_io_channel_unix_new(GetFd());
+    if (channel == nullptr) {
+      _E("g_io_channel_unix_new() is failed");
+      THROW(-ENOMEM);
+    }
+
+    int condition = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+    auto source = g_io_add_watch(channel, static_cast<GIOCondition>(condition),
+        OnSocketDisconnected, this);
+    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_;
+  }
+
+  void IncreaseFailureCount() {
+    failure_count_++;
+    if (failure_count_ > _config_get_max_launch_failure()) {
+      _E("Failure count(%d) exceeds the maximum", failure_count_);
+      Recover();
+      failure_count_ = 0;
+    }
+  }
+
+  void ResetFailureCount() {
+    failure_count_ = 0;
+  }
+
+  bool IsRecovering() const {
+    return recovering_;
+  }
+
+ private:
+  void Recover() {
+    if (pid_ == -1) {
+      pid_ = _login_monitor_get_launchpad_pid(uid_);
+      _W("Launchpad process ID: %d", pid_);
+    }
+
+    if (kill(pid_, SIGABRT) != 0) {
+      _E("kill() is failed. pid(%d), uid(%u), error(%d)",
+          pid_, uid_, errno);
+    }
+
+    recovering_ = true;
+  }
+
+  static gboolean OnSocketDisconnected(GIOChannel* channel,
+      GIOCondition condition, gpointer user_data) {
+    auto* client_channel = static_cast<ClientChannel*>(user_data);
+    _E("[LAUNCHPAD] Disconnected. uid(%u)", client_channel->GetUid());
+    client_channel->source_ = 0;
+    client_channel->listener_->OnDisconnected(client_channel->GetUid());
+    return G_SOURCE_REMOVE;
+  }
+
+ private:
+  pid_t pid_ = -1;
+  uid_t uid_;
+  IEvent* listener_;
+  GIOChannel* channel_ = nullptr;
+  guint source_ = 0;
+  unsigned int failure_count_ = 0;
+  bool recovering_ = false;
+};
+
+class Launchpad : public ClientChannel::IEvent {
+ public:
+  static Launchpad& GetInst() {
+    static Launchpad inst;
+    return inst;
+  }
+
+  void SetLauncher(launchpad_launcher_cb cb, void* user_data) {
+    cb_ = cb;
+    user_data_ = user_data;
+  }
+
+  int Launch(bundle* kb, uid_t uid) {
+    if (cb_ == nullptr) {
+      _E("Launcher is not prepared");
+      return -1;
+    }
+
+    std::shared_ptr<ClientChannel> channel(nullptr);
+    auto found = channels_.find(uid);
+    if (found != channels_.end()) {
+      channel = found->second;
+      if (channel->IsRecovering()) {
+        _E("Launcher is recovering. uid(%u)", uid);
+        return -ECOMM;
+      }
+    } else {
+      Connect(uid);
+    }
+
+    int ret = cb_(kb, uid, user_data_);
+    if (ret < 0) {
+      _E("Launch() is failed. error(%d)", ret);
+      if (channel.get() != nullptr)
+        channel->IncreaseFailureCount();
+    } else {
+      if (channel.get() != nullptr)
+        channel->ResetFailureCount();
+    }
+
+    return ret;
+  }
+
+  int Connect(uid_t uid) {
+    if (channels_.find(uid) != channels_.end())
+      return 0;
+
+    try {
+      auto client_channel = std::make_shared<ClientChannel>(uid, this);
+      std::string endpoint = GetEndpoint(uid, LAUNCHPAD_PROCESS_POOL_SOCK);
+      client_channel->Connect(endpoint);
+      client_channel->SetSendBufferSize(AUL_SOCK_MAXBUFF);
+
+      tizen_base::Bundle request;
+      tizen_base::Parcel parcel = CreateParcel(PAD_CMD_CONNECT,
+          AUL_SOCK_ASYNC, request.GetHandle());
+      auto raw = parcel.GetRaw();
+      int ret = client_channel->Send(reinterpret_cast<void*>(&raw[0]),
+          raw.size());
+      if (ret != 0) {
+        _E("Send() is failed. error(%d)", ret);
+        return ret;
+      }
+
+      channels_[uid] = std::move(client_channel);
+    } catch (const Exception& e) {
+      _E("Exception occurs. error(%s)", e.what());
+      return e.GetErrorCode();
+    }
+
+    return 0;
+  }
+
+  void Disconnect(uid_t uid) {
+    auto found = channels_.find(uid);
+    if (found == channels_.end())
+      return;
+
+    channels_.erase(found);
+  }
+
+  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 OnDisconnected(uid_t uid) override {
+    auto found = channels_.find(uid);
+    if (found == channels_.end()) {
+      _W("No such user(%u)", uid);
+      return;
+    }
+
+    channels_.erase(found);
+    _W("Launcher is disconnected. uid(%u)", uid);
+    int ret = Connect(uid);
+    if (ret != 0)
+      _E("Connect() is failed. uid(%u), error(%d)", uid, ret);
+  }
+
+ private:
+  launchpad_launcher_cb cb_ = nullptr;
+  void* user_data_ = nullptr;
+  std::map<uid_t, std::shared_ptr<ClientChannel>> channels_;
+};
+
+int DefaultLauncher(bundle* request, uid_t uid, void* user_data) {
+  int result = -1;
+  try {
+    auto client = std::make_shared<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(1500);
+
+    tizen_base::Parcel parcel = CreateParcel(PAD_CMD_LAUNCH,
+        AUL_SOCK_ASYNC, request);
+    auto raw = parcel.GetRaw();
+    int ret = client->Send(reinterpret_cast<void*>(&raw[0]), raw.size());
+    if (ret != 0) {
+      _E("Send() is failed. error(%d)", ret);
+      return ret;
+    }
+
+    ret = client->Receive(reinterpret_cast<void*>(&result), sizeof(result));
+    if (ret != 0) {
+      _E("Receive() is failed. error(%d)", ret);
+      return ret;
+    }
+  } catch (const Exception& e) {
+    _E("Exception occurs. error(%s)", e.what());
+    return e.GetErrorCode();
+  }
+
+  return result;
+}
+
+}  // namespace
+
+int _launchpad_set_launcher(launchpad_launcher_cb cb, void* user_data) {
+  Launchpad::GetInst().SetLauncher(cb, user_data);
+  return 0;
+}
+
+int _launchpad_launch(bundle* request, uid_t uid) {
+  return Launchpad::GetInst().Launch(request, uid);
+}
+
+int _launchpad_connect(uid_t uid) {
+  return Launchpad::GetInst().Connect(uid);
+}
+
+void _launchpad_disconnect(uid_t uid) {
+  Launchpad::GetInst().Disconnect(uid);
+}
+
+int _launchpad_init(void) {
+  _D("LAUNCHPAD_INIT");
+  Launchpad::GetInst().SetLauncher(::DefaultLauncher, nullptr);
+  return 0;
+}
+
+void _launchpad_fini(void) {
+  _D("LAUNCHPAD_FINI");
+}
index 0754f7c..5fd128e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2017 - 2020 Samsung Electronics Co., Ltd All Rights Reserved
+ * Copyright (c) 2017 - 2021 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.
 extern "C" {
 #endif
 
-int _launchpad_set_launcher(int (*callback)(bundle *, uid_t, void *),
+typedef int (*launchpad_launcher_cb)(bundle* request, uid_t uid,
                void *user_data);
 
-int _launchpad_launch(bundle *kb, uid_t uid);
+int _launchpad_set_launcher(launchpad_launcher_cb callback, void *user_data);
 
-int _launchpad_recover_launcher(uid_t uid);
+int _launchpad_launch(bundle *request, uid_t uid);
+
+int _launchpad_connect(uid_t uid);
+
+void _launchpad_disconnect(uid_t uid);
 
 int _launchpad_init(void);
 
index 22d71ac..751f6cb 100644 (file)
@@ -36,6 +36,7 @@
 #include "amd_config.h"
 #include "amd_cynara.h"
 #include "amd_launch.h"
+#include "amd_launchpad.h"
 #include "amd_login_monitor.h"
 #include "amd_noti.h"
 #include "amd_request.h"
@@ -100,6 +101,11 @@ static login_handler login_table[] = {
                .login = _app_status_usr_init
        },
        {
+               .state = UID_STATE_OPENING | UID_STATE_ONLINE |
+                       UID_STATE_ACTIVE,
+               .login = _launchpad_connect
+       },
+       {
                .state = UID_STATE_ONLINE | UID_STATE_ACTIVE,
                .login = _request_usr_init
        },
@@ -111,6 +117,10 @@ static login_handler login_table[] = {
 static logout_handler logout_table[] = {
        {
                .state = UID_STATE_OFFLINE,
+               .logout = _launchpad_disconnect
+       },
+       {
+               .state = UID_STATE_OFFLINE,
                .logout = _appinfo_unload
        },
        {
index 2892b96..cf5ac8f 100644 (file)
@@ -48,6 +48,8 @@ extern "C" {
 
 #define PAD_CMD_UPDATE_APP_TYPE 16
 
+#define PAD_CMD_CONNECT 18
+
 int _create_sock_activation(void);
 
 int _create_server_sock(void);