+++ /dev/null
-/*
- * 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);
-}
--- /dev/null
+/*
+ * 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");
+}