2 * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
4 * Licensed under the Apache License, Version 2.0 (the License);
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an AS IS BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "launchpad-process-pool/loader_context.hh"
23 #include <sys/capability.h>
24 #include <sys/smack.h>
25 #include <sys/types.h>
34 #include <exception.hh>
35 #include <peer_credentials.hh>
37 #include <user_tracer.hh>
41 #include "launchpad-process-pool/app_labels_monitor.hh"
42 #include "launchpad-process-pool/config.hh"
43 #include "launchpad-process-pool/loader_executor.hh"
44 #include "launchpad-process-pool/log.hh"
45 #include "launchpad-process-pool/log_private.hh"
46 #include "launchpad-process-pool/memory_monitor.hh"
47 #include "launchpad-process-pool/util.hh"
49 namespace fs = std::filesystem;
54 constexpr const char kLaunchpadType[] = ".launchpad-type";
55 const int kSocketMaxBufferSize = 131071;
56 const unsigned int kWinScore = 100;
57 const float kLoseScoreRate = 0.7f;
59 int VerifyLoaderCaps(const std::string& executable_file) {
60 std::vector<cap_value_t> values { CAP_SETGID, CAP_MAC_ADMIN };
62 // If Dytransition feature is enabled, CAP_MAC_ADMIN is unnecessary.
63 if (!AppLabelsMonitor::GetInst().IsDisposed())
66 _W("Executable file: %s", executable_file.c_str());
67 cap_t cap = cap_get_file(executable_file.c_str());
69 _E("Failed to get cap from file(%s)", executable_file.c_str());
73 auto cap_auto = std::unique_ptr<std::remove_pointer<cap_t>::type,
74 decltype(cap_free)*>(cap, cap_free);
76 for (size_t i = 0; i < values.size(); i++) {
77 cap_flag_value_t inh_state;
78 int ret = cap_get_flag(cap, values[i], CAP_INHERITABLE, &inh_state);
80 _E("Failed to get cap inhertiable. errno(%d)", errno);
84 cap_flag_value_t eff_state;
85 ret = cap_get_flag(cap, values[i], CAP_EFFECTIVE, &eff_state);
87 _E("Failed to get cap effective. errno(%d)", errno);
91 if ((inh_state != CAP_SET) || (eff_state != CAP_SET)) {
92 _E("%d capability is needed", values[i]);
102 LoaderContext::Builder& LoaderContext::Builder::SetLoaderInfo(
103 std::shared_ptr<LoaderInfo> loader_info) {
104 loader_info_ = std::move(loader_info);
108 LoaderContext::Builder& LoaderContext::Builder::SetLoaderId(int loader_id) {
109 loader_id_ = loader_id;
113 LoaderContext::Builder& LoaderContext::Builder::SetCallerPid(pid_t caller_pid) {
114 caller_pid_ = caller_pid;
118 LoaderContext::Builder& LoaderContext::Builder::SetActive() {
119 if ((static_cast<int>(loader_info_->GetDeactivationMethod()) &
120 static_cast<int>(LoaderMethod::OutOfMemory)) &&
121 MemoryMonitor::GetInst().IsLowMemory())
127 LoaderContext::Builder& LoaderContext::Builder::SetLoaderMount(
128 std::shared_ptr<LoaderMount> loader_mount) {
129 loader_mount_ = std::move(loader_mount);
133 LoaderContext::Builder::operator LoaderContext*() {
134 return new LoaderContext(std::move(loader_info_), loader_id_, caller_pid_,
135 activated_, std::move(loader_mount_));
138 LoaderContext::LoaderContext(std::shared_ptr<LoaderInfo> loader_info,
139 int loader_id, pid_t caller_pid, bool activated,
140 std::shared_ptr<LoaderMount> loader_mount)
141 : loader_info_(std::move(loader_info)),
142 loader_id_(loader_id),
143 caller_pid_(caller_pid),
144 activated_(activated),
145 loader_mount_(std::move(loader_mount)),
146 cpu_checker_(new CPUChecker(loader_info_->GetCpuThresholdMax(),
147 loader_info_->GetCpuThresholdMin())),
149 auto& executable_file = loader_info_->GetExe();
150 if (executable_file != "null") {
151 if (access(executable_file.c_str(), F_OK | X_OK) != 0) {
153 _E("access() is failed. path: %s, errno: %d",
154 executable_file.c_str(), errno);
158 VerifyLoaderCaps(executable_file);
161 loader_extra_ = reinterpret_cast<const char*>(
162 loader_info_->GetExtra().ToRaw().first.get());
164 condition_path_exists_ = loader_info_->GetConditionPathExists();
167 LoaderContext::~LoaderContext() {
171 void LoaderContext::Listen() {
173 std::string socket_path = "/run/aul/daemons/" + std::to_string(getuid()) +
174 "/" + std::string(kLaunchpadType) + std::to_string(GetType()) + "-" +
175 std::to_string(loader_id_);
176 if (fs::exists(socket_path))
177 fs::remove(socket_path);
179 server_socket_.reset(new ServerSocket());
180 server_socket_->Bind(socket_path);
181 server_socket_->Listen(128);
183 server_channel_.reset(new IOChannel(server_socket_->GetFd(),
184 IOChannel::IOCondition::IO_IN, this));
185 server_channel_->SetCloseOnDestroy(false);
186 } catch (const Exception& e) {
187 _E("Exception occurs. error: %s", e.what());
188 THROW(e.GetErrorCode());
192 void LoaderContext::Dispose() {
193 _E("Dispose. type(%d), name(%s), pid(%d)",
194 GetType(), GetLoaderName().c_str(), GetPid());
197 _D("Kill process(%d)", pid_);
198 if (kill(pid_, SIGKILL) != 0)
199 _E("kill() is failed. pid(%d), errno(%d)", pid_, errno);
204 if (live_timer_ > 0) {
205 g_source_remove(live_timer_);
210 g_source_remove(timer_);
214 if (on_boot_timer_ > 0) {
215 g_source_remove(on_boot_timer_);
219 client_channel_.reset();
220 client_socket_.reset();
224 pid_t LoaderContext::Prepare() {
225 bool set_priority = (Config::GetInst().GetLaunchMode().GetMode() !=
226 Config::LaunchMode::Mode::AlwaysLoaderWithLowPriority);
227 pid_ = LoaderExecutor::GetInst().Execute(this,
228 set_priority ? loader_info_->GetSchedPriority() : 0);
229 _W("Prepare. type(%d), name(%s), pid(%d)",
230 GetType(), GetLoaderName().c_str(), pid_);
232 _E("Failed to create a child process");
236 Log::Print("[CANDIDATE]", "pid(%7d) | type(%d) | loader(%s)",
237 GetPid(), GetLoaderId(), GetLoaderName().c_str());
242 MemoryMonitor::GetInst().Reset();
247 pid_t LoaderContext::Deploy(const AppInfo* app_info) {
249 if (loader_mount_->Mount(pid_, app_info) != 0)
250 _E("Failed to attach resources to loader process");
253 Util::DeleteSocketPath(pid_, getuid());
254 tizen_base::Parcel parcel;
255 parcel.WriteParcelable(*app_info);
256 size_t data_size = parcel.GetDataSize();
257 int ret = client_socket_->Send(static_cast<void*>(&data_size),
260 _E("Failed to send request. pid(%d)", pid_);
264 ret = client_socket_->Send(parcel.GetData(), data_size);
266 _E("Failed to send request. pid(%d)", pid_);
270 _W("Request to the loader process. pid(%d), path(%s)",
271 pid_, app_info->GetAppPath().c_str());
275 LoaderContext::Dispose();
281 const std::string& LoaderContext::GetLoaderName() const {
282 return loader_info_->GetName();
285 const std::string& LoaderContext::GetLoaderPath() const {
286 return loader_info_->GetExe();
289 const std::string& LoaderContext::GetLoaderExtra() const {
290 return loader_extra_;
293 int LoaderContext::GetType() const {
294 return static_cast<int>(loader_info_->GetType());
297 bool LoaderContext::IsPrepared() const {
301 void LoaderContext::SetPrepared(bool prepared) {
302 prepared_ = prepared;
305 int LoaderContext::GetSchedPriority() const {
306 return loader_info_->GetSchedPriority();
309 pid_t LoaderContext::GetPid() const {
313 void LoaderContext::SetPid(pid_t pid) {
316 if (listener_ != nullptr)
317 listener_->OnLoaderLaunched(this);
321 pid_t LoaderContext::GetCallerPid() const {
325 int LoaderContext::GetLoaderId() const {
329 bool LoaderContext::IsHydraMode() const {
330 return loader_info_->IsHydraMode();
333 bool LoaderContext::IsActivated() const {
337 bool LoaderContext::IsTouched() const {
341 bool LoaderContext::IsOnBoot() const {
342 return loader_info_->IsOnBoot();
345 int LoaderContext::GetOnBootTimeout() const {
346 return loader_info_->GetOnBootTimeout();
349 LoaderMethod LoaderContext::GetDetectionMethod() const {
350 return loader_info_->GetDetectionMethod();
353 void LoaderContext::SetLoaderMethod(LoaderMethod method) {
357 bool LoaderContext::ShouldCheckAppInstallation() const {
358 return loader_info_->ShouldCheckAppInstallation();
361 void LoaderContext::SetAppInstalled(bool installed) {
362 loader_info_->SetAppInstalled(installed);
365 bool LoaderContext::IsAppInstalled() const {
366 return loader_info_->IsAppInstalled();
369 CPUChecker* LoaderContext::GetCPUChecker() const {
370 return cpu_checker_.get();
373 void LoaderContext::UnsetTimer() {
375 g_source_remove(timer_);
380 void LoaderContext::SetTimer() {
382 _W("Already registered");
386 if (!IsActivated()) {
387 _W("The loader is deacitvated. type(%d)", GetType());
391 if (static_cast<int>(loader_info_->GetDetectionMethod()) &
392 static_cast<int>(LoaderMethod::Timeout)) {
393 timer_ = g_timeout_add(loader_info_->GetTimeout(), TimeoutCb, this);
395 _E("g_timeout_add() is failed");
399 void LoaderContext::SetOnBootTimer(int timeout) {
400 if (on_boot_timer_ != 0)
403 on_boot_timer_ = g_timeout_add(timeout, OnBootTimeoutCb, this);
404 if (on_boot_timer_ == 0)
405 _E("g_timeout_add() is failed");
408 bool LoaderContext::ShouldWaitForFileCreation() {
409 auto iter = condition_path_exists_.begin();
410 while (iter != condition_path_exists_.end()) {
412 if (!fs::exists(path)) {
413 _D("%s does not exist", path.c_str());
417 iter = condition_path_exists_.erase(iter);
423 int LoaderContext::IncreaseCPUCheckCount() {
424 return ++cpu_check_count_;
427 void LoaderContext::ResetCPUCheckCount() {
428 cpu_check_count_ = 0;
431 void LoaderContext::UpdatePssMemory() {
432 Procfs::GetPssMemory(pid_, &pss_);
435 uint64_t LoaderContext::GetPssMemory() const {
439 unsigned int LoaderContext::GetScore() const {
443 gboolean LoaderContext::TimeoutCb(gpointer user_data) {
444 auto* context = static_cast<LoaderContext*>(user_data);
446 auto* listener = context->listener_;
447 if (listener != nullptr)
448 listener->OnTimeoutEvent(context);
450 return G_SOURCE_REMOVE;
453 gboolean LoaderContext::TimeToLiveCb(gpointer user_data) {
454 auto* context = static_cast<LoaderContext*>(user_data);
455 context->UpdateState(LoaderMethod::TimeToLive, false);
456 return G_SOURCE_REMOVE;
459 gboolean LoaderContext::OnBootTimeoutCb(gpointer user_data) {
460 auto* context = static_cast<LoaderContext*>(user_data);
461 _W("type(%d), loader_name(%s)",
462 context->GetType(), context->GetLoaderName().c_str());
463 context->on_boot_timer_ = 0;
464 if (context->GetPid() > 0) {
465 _E("Loader is already running. pid(%d)", context->GetPid());
466 return G_SOURCE_REMOVE;
469 if (context->ShouldWaitForFileCreation()) {
470 context->SetOnBootTimer(100);
471 return G_SOURCE_REMOVE;
474 _W("Loader(%d:%s) is starting by the on-boot timer option",
475 context->GetType(), context->GetLoaderName().c_str());
477 return G_SOURCE_REMOVE;
480 void LoaderContext::UpdateScore() {
481 score_ = score_ * kLoseScoreRate + kWinScore;
484 void LoaderContext::Activate() {
485 _W("type(%d), loader_name(%s), activated(%d)",
486 GetType(), GetLoaderName().c_str(), IsActivated());
491 if (!touched_ && !loader_info_->IsOnBoot())
497 if (loader_info_->ShouldCheckAppInstallation() &&
498 !loader_info_->IsAppInstalled())
501 if (CanActivate(LoaderMethod::Timeout))
505 void LoaderContext::Deactivate() {
506 _W("type(%d), loader_name(%s), activated(%d)",
507 GetType(), GetLoaderName().c_str(), IsActivated());
515 bool LoaderContext::CanBeDetected(LoaderMethod method) const {
516 return (static_cast<int>(loader_info_->GetDetectionMethod()) &
517 static_cast<int>(method));
520 bool LoaderContext::CanActivate(LoaderMethod method) const {
521 return (static_cast<int>(loader_info_->GetActivationMethod()) &
522 static_cast<int>(method));
525 bool LoaderContext::CanDeactivate(LoaderMethod method) const {
526 return (static_cast<int>(loader_info_->GetDeactivationMethod()) &
527 static_cast<int>(method));
530 void LoaderContext::UpdateState(LoaderMethod method, bool force) {
531 _W("type(%d), loader_name(%s), activated(%d), method(%d), force(%d)",
532 GetType(), GetLoaderName().c_str(), activated_, static_cast<int>(method),
534 SetLoaderMethod(method);
536 case LoaderMethod::OutOfMemory:
537 if ((force || CanDeactivate(method)) &&
538 MemoryMonitor::GetInst().IsLowMemory()) {
545 case LoaderMethod::TimeToLive:
546 if (force || CanDeactivate(method))
549 case LoaderMethod::AvailableMemory:
550 if (force || CanActivate(method))
553 case LoaderMethod::Request:
554 if (force || CanActivate(method))
555 UpdateState(LoaderMethod::OutOfMemory, force);
563 bool LoaderContext::IsLaunchable() {
564 if (ShouldCheckAppInstallation() && !IsAppInstalled()) {
565 _W("The application is not installed. type(%d)", GetType());
569 if (ShouldWaitForFileCreation()) {
570 _W("The load should wait for file creation. type(%d)", GetType());
574 if (!IsActivated()) {
575 _W("The loader is deactivated. type(%d)", GetType());
580 _W("The loader is already running. type(%d), pid(%d)", GetType(), GetPid());
587 void LoaderContext::SetEventListener(IEvent* listener) {
588 listener_ = listener;
591 void LoaderContext::Ref() {
595 void LoaderContext::Unref() {
600 uint32_t LoaderContext::RefCount() const {
604 void LoaderContext::SetLiveTimer() {
605 _W("type(%d), loader_name(%s), deactivation_method(%d)",
606 GetType(), GetLoaderName().c_str(),
607 static_cast<int>(loader_info_->GetDeactivationMethod()));
608 if (static_cast<int>(loader_info_->GetDeactivationMethod()) &
609 static_cast<int>(LoaderMethod::TimeToLive)) {
610 if (live_timer_ == 0) {
611 live_timer_ = g_timeout_add_seconds(loader_info_->GetTimeToLive(),
617 void LoaderContext::HandleLoaderEvent() {
620 client_socket_ = server_socket_->Accept();
621 client_socket_->SetReceiveBufferSize(kSocketMaxBufferSize);
622 client_socket_->SetReceiveTimeout(5200);
623 client_socket_->SetSendBufferSize(kSocketMaxBufferSize);
626 int ret = client_socket_->Receive(static_cast<void*>(&pid), sizeof(pid));
628 _E("Receive() is failed. error: %d", ret);
629 client_socket_.reset();
633 auto peer_cred = PeerCredentials::Get(client_socket_->GetFd());
634 if (peer_cred->GetPid() != pid) {
635 _E("The peer information does not match. %d : %d",
636 peer_cred->GetPid(), pid);
637 client_socket_.reset();
641 client_channel_.reset(
643 client_socket_->GetFd(),
644 IOChannel::IOCondition::IO_IN | IOChannel::IOCondition::IO_HUP,
646 client_channel_->SetCloseOnDestroy(false);
649 SetPid(peer_cred->GetPid());
652 if (listener_ != nullptr) {
654 listener_->OnLoaderPrepared(this);
657 SECURE_LOGI("Type %d loader was connected. pid: %d", GetType(), pid_);
658 UserTracer::Print("Type " + std::to_string(GetType()) +
659 " loader was connected. pid: " + std::to_string(pid_));
660 } catch (const Exception& e) {
661 _E("Exception occurs. error: %s", e.what());
665 auto client_socket = server_socket_->Accept();
666 _E("Refuse loader process connection");
670 void LoaderContext::HandleLoaderClientEvent(int condition) {
672 (IOChannel::IOCondition::IO_HUP | IOChannel::IOCondition::IO_NVAL)) {
673 SECURE_LOGE("Type %d loader was disconnected. pid: %d", GetType(), pid_);
680 void LoaderContext::OnIOEventReceived(int fd, int condition) {
681 _D("[DEBUG] fd(%d), condition(%d)", fd, condition);
682 if (server_socket_.get() != nullptr && server_socket_->GetFd() == fd)
684 else if (client_socket_.get() != nullptr && client_socket_->GetFd() == fd)
685 HandleLoaderClientEvent(condition);
688 void LoaderContext::CreateReadyFile() {
689 if (ready_file_created_)
692 std::string path = "/tmp/." + std::to_string(getuid()) + "-" +
693 GetLoaderName() + ".ready";
694 std::filesystem::path file_path(path);
695 if (std::filesystem::exists(file_path)) {
696 ready_file_created_ = true;
700 std::ofstream file_stream(file_path);
701 if (!file_stream.is_open()) {
702 _E("Failed to create the file. path(%s)", path.c_str());
708 if (smack_setlabel(path.c_str(), "*", SMACK_LABEL_ACCESS) != 0)
709 _E("smack_setlabel() is failed. path: %s, errno: %d", path.c_str(), errno);
711 _W("File(%s) created successfully", path.c_str());
712 ready_file_created_ = true;
715 } // namespace launchpad