Mount gadget resource paths for NUIGadget
[platform/core/appfw/launchpad.git] / src / launchpad-process-pool / loader_context.cc
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd All Rights Reserved
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 #include "launchpad-process-pool/loader_context.hh"
18
19 #include <errno.h>
20 #include <libgen.h>
21 #include <malloc.h>
22 #include <stdio.h>
23 #include <sys/capability.h>
24 #include <sys/smack.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27
28 #include <filesystem>
29 #include <fstream>
30 #include <iostream>
31 #include <string>
32 #include <utility>
33
34 #include <exception.hh>
35 #include <peer_credentials.hh>
36 #include <procfs.hh>
37 #include <user_tracer.hh>
38 #include <util.hh>
39 #include <types.hh>
40
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"
48
49 namespace fs = std::filesystem;
50
51 namespace launchpad {
52 namespace {
53
54 constexpr const char kLaunchpadType[] = ".launchpad-type";
55 const int kSocketMaxBufferSize = 131071;
56 const unsigned int kWinScore = 100;
57 const float kLoseScoreRate = 0.7f;
58
59 int VerifyLoaderCaps(const std::string& executable_file) {
60   std::vector<cap_value_t> values { CAP_SETGID, CAP_MAC_ADMIN };
61
62   // If Dytransition feature is enabled, CAP_MAC_ADMIN is unnecessary.
63   if (!AppLabelsMonitor::GetInst().IsDisposed())
64     values.pop_back();
65
66   _W("Executable file: %s", executable_file.c_str());
67   cap_t cap = cap_get_file(executable_file.c_str());
68   if (cap == nullptr) {
69     _E("Failed to get cap from file(%s)", executable_file.c_str());
70     return -1;
71   }
72
73   auto cap_auto = std::unique_ptr<std::remove_pointer<cap_t>::type,
74       decltype(cap_free)*>(cap, cap_free);
75
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);
79     if (ret != 0) {
80       _E("Failed to get cap inhertiable. errno(%d)", errno);
81       return -1;
82     }
83
84     cap_flag_value_t eff_state;
85     ret = cap_get_flag(cap, values[i], CAP_EFFECTIVE, &eff_state);
86     if (ret != 0) {
87       _E("Failed to get cap effective. errno(%d)", errno);
88       return -1;
89     }
90
91     if ((inh_state != CAP_SET) || (eff_state != CAP_SET)) {
92       _E("%d capability is needed", values[i]);
93       return -1;
94     }
95   }
96
97   return 0;
98 }
99
100 }  // namespace
101
102 LoaderContext::Builder& LoaderContext::Builder::SetLoaderInfo(
103     std::shared_ptr<LoaderInfo> loader_info) {
104   loader_info_ = std::move(loader_info);
105   return *this;
106 }
107
108 LoaderContext::Builder& LoaderContext::Builder::SetLoaderId(int loader_id) {
109   loader_id_ = loader_id;
110   return *this;
111 }
112
113 LoaderContext::Builder& LoaderContext::Builder::SetCallerPid(pid_t caller_pid) {
114   caller_pid_ = caller_pid;
115   return *this;
116 }
117
118 LoaderContext::Builder& LoaderContext::Builder::SetActive() {
119   if ((static_cast<int>(loader_info_->GetDeactivationMethod()) &
120        static_cast<int>(LoaderMethod::OutOfMemory)) &&
121       MemoryMonitor::GetInst().IsLowMemory())
122     activated_ = false;
123
124   return *this;
125 }
126
127 LoaderContext::Builder& LoaderContext::Builder::SetLoaderMount(
128     std::shared_ptr<LoaderMount> loader_mount) {
129   loader_mount_ = std::move(loader_mount);
130   return *this;
131 }
132
133 LoaderContext::Builder::operator LoaderContext*() {
134   return new LoaderContext(std::move(loader_info_), loader_id_, caller_pid_,
135                            activated_, std::move(loader_mount_));
136 }
137
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())),
148       score_(kWinScore) {
149   auto& executable_file = loader_info_->GetExe();
150   if (executable_file != "null") {
151     if (access(executable_file.c_str(), F_OK | X_OK) != 0) {
152       int error = -errno;
153       _E("access() is failed. path: %s, errno: %d",
154           executable_file.c_str(), errno);
155       THROW(error);
156     }
157
158     VerifyLoaderCaps(executable_file);
159   }
160
161   loader_extra_ = reinterpret_cast<const char*>(
162       loader_info_->GetExtra().ToRaw().first.get());
163   Listen();
164   condition_path_exists_ = loader_info_->GetConditionPathExists();
165 }
166
167 LoaderContext::~LoaderContext() {
168   Dispose();
169 }
170
171 void LoaderContext::Listen() {
172   try {
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);
178
179     server_socket_.reset(new ServerSocket());
180     server_socket_->Bind(socket_path);
181     server_socket_->Listen(128);
182
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());
189   }
190 }
191
192 void LoaderContext::Dispose() {
193   _E("Dispose. type(%d), name(%s), pid(%d)",
194       GetType(), GetLoaderName().c_str(), GetPid());
195
196   if (pid_ > 0) {
197     _D("Kill process(%d)", pid_);
198     if (kill(pid_, SIGKILL) != 0)
199       _E("kill() is failed. pid(%d), errno(%d)", pid_, errno);
200
201     pid_ = 0;
202   }
203
204   if (live_timer_ > 0) {
205     g_source_remove(live_timer_);
206     live_timer_ = 0;
207   }
208
209   if (timer_ > 0) {
210     g_source_remove(timer_);
211     timer_ = 0;
212   }
213
214   if (on_boot_timer_ > 0) {
215     g_source_remove(on_boot_timer_);
216     on_boot_timer_ = 0;
217   }
218
219   client_channel_.reset();
220   client_socket_.reset();
221   prepared_ = false;
222 }
223
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_);
231   if (pid_ == -1) {
232     _E("Failed to create a child process");
233     return -1;
234   }
235
236   Log::Print("[CANDIDATE]", "pid(%7d) | type(%d) | loader(%s)",
237       GetPid(), GetLoaderId(), GetLoaderName().c_str());
238
239   if (!IsHydraMode())
240     SetLiveTimer();
241
242   MemoryMonitor::GetInst().Reset();
243   touched_ = true;
244   return pid_;
245 }
246
247 pid_t LoaderContext::Deploy(const AppInfo* app_info) {
248   if (loader_mount_) {
249     if (loader_mount_->Mount(pid_, app_info) != 0)
250       _E("Failed to attach resources to loader process");
251   }
252
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),
258       sizeof(data_size));
259   if (ret != 0) {
260     _E("Failed to send request. pid(%d)", pid_);
261     return ret;
262   }
263
264   ret = client_socket_->Send(parcel.GetData(), data_size);
265   if (ret != 0) {
266     _E("Failed to send request. pid(%d)", pid_);
267     return ret;
268   }
269
270   _W("Request to the loader process. pid(%d), path(%s)",
271       pid_, app_info->GetAppPath().c_str());
272
273   pid_t pid = pid_;
274   pid_ = 0;
275   LoaderContext::Dispose();
276   SetTimer();
277   UpdateScore();
278   return pid;
279 }
280
281 const std::string& LoaderContext::GetLoaderName() const {
282   return loader_info_->GetName();
283 }
284
285 const std::string& LoaderContext::GetLoaderPath() const {
286   return loader_info_->GetExe();
287 }
288
289 const std::string& LoaderContext::GetLoaderExtra() const {
290   return loader_extra_;
291 }
292
293 int LoaderContext::GetType() const {
294   return static_cast<int>(loader_info_->GetType());
295 }
296
297 bool LoaderContext::IsPrepared() const {
298   return prepared_;
299 }
300
301 void LoaderContext::SetPrepared(bool prepared) {
302   prepared_ = prepared;
303 }
304
305 int LoaderContext::GetSchedPriority() const {
306   return loader_info_->GetSchedPriority();
307 }
308
309 pid_t LoaderContext::GetPid() const {
310   return pid_;
311 }
312
313 void LoaderContext::SetPid(pid_t pid) {
314   pid_ = pid;
315   if (pid_ > 0) {
316     if (listener_ != nullptr)
317       listener_->OnLoaderLaunched(this);
318   }
319 }
320
321 pid_t LoaderContext::GetCallerPid() const {
322   return caller_pid_;
323 }
324
325 int LoaderContext::GetLoaderId() const {
326   return loader_id_;
327 }
328
329 bool LoaderContext::IsHydraMode() const {
330   return loader_info_->IsHydraMode();
331 }
332
333 bool LoaderContext::IsActivated() const {
334   return activated_;
335 }
336
337 bool LoaderContext::IsTouched() const {
338   return touched_;
339 }
340
341 bool LoaderContext::IsOnBoot() const {
342   return loader_info_->IsOnBoot();
343 }
344
345 int LoaderContext::GetOnBootTimeout() const {
346   return loader_info_->GetOnBootTimeout();
347 }
348
349 LoaderMethod LoaderContext::GetDetectionMethod() const {
350   return loader_info_->GetDetectionMethod();
351 }
352
353 void LoaderContext::SetLoaderMethod(LoaderMethod method) {
354   method_ = method;
355 }
356
357 bool LoaderContext::ShouldCheckAppInstallation() const {
358   return loader_info_->ShouldCheckAppInstallation();
359 }
360
361 void LoaderContext::SetAppInstalled(bool installed) {
362   loader_info_->SetAppInstalled(installed);
363 }
364
365 bool LoaderContext::IsAppInstalled() const {
366   return loader_info_->IsAppInstalled();
367 }
368
369 CPUChecker* LoaderContext::GetCPUChecker() const {
370   return cpu_checker_.get();
371 }
372
373 void LoaderContext::UnsetTimer() {
374   if (timer_ > 0) {
375     g_source_remove(timer_);
376     timer_ = 0;
377   }
378 }
379
380 void LoaderContext::SetTimer() {
381   if (timer_ > 0) {
382     _W("Already registered");
383     return;
384   }
385
386   if (!IsActivated()) {
387     _W("The loader is deacitvated. type(%d)", GetType());
388     return;
389   }
390
391   if (static_cast<int>(loader_info_->GetDetectionMethod()) &
392       static_cast<int>(LoaderMethod::Timeout)) {
393     timer_ = g_timeout_add(loader_info_->GetTimeout(), TimeoutCb, this);
394     if (timer_ == 0)
395       _E("g_timeout_add() is failed");
396   }
397 }
398
399 void LoaderContext::SetOnBootTimer(int timeout) {
400   if (on_boot_timer_ != 0)
401     return;
402
403   on_boot_timer_ = g_timeout_add(timeout, OnBootTimeoutCb, this);
404   if (on_boot_timer_ == 0)
405     _E("g_timeout_add() is failed");
406 }
407
408 bool LoaderContext::ShouldWaitForFileCreation() {
409   auto iter = condition_path_exists_.begin();
410   while (iter != condition_path_exists_.end()) {
411     auto& path = *iter;
412     if (!fs::exists(path)) {
413       _D("%s does not exist", path.c_str());
414       return true;
415     }
416
417     iter = condition_path_exists_.erase(iter);
418   }
419
420   return false;
421 }
422
423 int LoaderContext::IncreaseCPUCheckCount() {
424   return ++cpu_check_count_;
425 }
426
427 void LoaderContext::ResetCPUCheckCount() {
428   cpu_check_count_ = 0;
429 }
430
431 void LoaderContext::UpdatePssMemory() {
432   Procfs::GetPssMemory(pid_, &pss_);
433 }
434
435 uint64_t LoaderContext::GetPssMemory() const {
436   return pss_;
437 }
438
439 unsigned int LoaderContext::GetScore() const {
440   return score_;
441 }
442
443 gboolean LoaderContext::TimeoutCb(gpointer user_data) {
444   auto* context = static_cast<LoaderContext*>(user_data);
445   context->timer_ = 0;
446   auto* listener = context->listener_;
447   if (listener != nullptr)
448     listener->OnTimeoutEvent(context);
449
450   return G_SOURCE_REMOVE;
451 }
452
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;
457 }
458
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;
467   }
468
469   if (context->ShouldWaitForFileCreation()) {
470     context->SetOnBootTimer(100);
471     return G_SOURCE_REMOVE;
472   }
473
474   _W("Loader(%d:%s) is starting by the on-boot timer option",
475       context->GetType(), context->GetLoaderName().c_str());
476   context->Prepare();
477   return G_SOURCE_REMOVE;
478 }
479
480 void LoaderContext::UpdateScore() {
481   score_ = score_ * kLoseScoreRate + kWinScore;
482 }
483
484 void LoaderContext::Activate() {
485   _W("type(%d), loader_name(%s), activated(%d)",
486       GetType(), GetLoaderName().c_str(), IsActivated());
487   if (IsActivated())
488     return;
489
490   activated_ = true;
491   if (!touched_ && !loader_info_->IsOnBoot())
492     return;
493
494   if (pid_ > 0)
495     return;
496
497   if (loader_info_->ShouldCheckAppInstallation() &&
498       !loader_info_->IsAppInstalled())
499     return;
500
501   if (CanActivate(LoaderMethod::Timeout))
502     SetTimer();
503 }
504
505 void LoaderContext::Deactivate() {
506   _W("type(%d), loader_name(%s), activated(%d)",
507       GetType(), GetLoaderName().c_str(), IsActivated());
508   if (!IsActivated())
509     return;
510
511   activated_ = false;
512   Dispose();
513 }
514
515 bool LoaderContext::CanBeDetected(LoaderMethod method) const {
516   return (static_cast<int>(loader_info_->GetDetectionMethod()) &
517       static_cast<int>(method));
518 }
519
520 bool LoaderContext::CanActivate(LoaderMethod method) const {
521   return (static_cast<int>(loader_info_->GetActivationMethod()) &
522       static_cast<int>(method));
523 }
524
525 bool LoaderContext::CanDeactivate(LoaderMethod method) const {
526   return (static_cast<int>(loader_info_->GetDeactivationMethod()) &
527       static_cast<int>(method));
528 }
529
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),
533       force);
534   SetLoaderMethod(method);
535   switch (method) {
536     case LoaderMethod::OutOfMemory:
537       if ((force || CanDeactivate(method)) &&
538           MemoryMonitor::GetInst().IsLowMemory()) {
539         _W("Low memory");
540         Deactivate();
541       } else {
542         Activate();
543       }
544       break;
545     case LoaderMethod::TimeToLive:
546       if (force || CanDeactivate(method))
547         Deactivate();
548       break;
549     case LoaderMethod::AvailableMemory:
550       if (force || CanActivate(method))
551         Activate();
552       break;
553     case LoaderMethod::Request:
554       if (force || CanActivate(method))
555         UpdateState(LoaderMethod::OutOfMemory, force);
556       break;
557     default:
558       Activate();
559       break;
560   }
561 }
562
563 bool LoaderContext::IsLaunchable() {
564   if (ShouldCheckAppInstallation() && !IsAppInstalled()) {
565     _W("The application is not installed. type(%d)", GetType());
566     return false;
567   }
568
569   if (ShouldWaitForFileCreation()) {
570     _W("The load should wait for file creation. type(%d)", GetType());
571     return false;
572   }
573
574   if (!IsActivated()) {
575     _W("The loader is deactivated. type(%d)", GetType());
576     return false;
577   }
578
579   if (GetPid() > 0) {
580     _W("The loader is already running. type(%d), pid(%d)", GetType(), GetPid());
581     return false;
582   }
583
584   return true;
585 }
586
587 void LoaderContext::SetEventListener(IEvent* listener) {
588   listener_ = listener;
589 }
590
591 void LoaderContext::Ref() {
592   ref_count_++;
593 }
594
595 void LoaderContext::Unref() {
596   if (ref_count_ > 0)
597     ref_count_--;
598 }
599
600 uint32_t LoaderContext::RefCount() const {
601   return ref_count_;
602 }
603
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(),
612           TimeToLiveCb, this);
613     }
614   }
615 }
616
617 void LoaderContext::HandleLoaderEvent() {
618   if (!prepared_) {
619     try {
620       client_socket_ = server_socket_->Accept();
621       client_socket_->SetReceiveBufferSize(kSocketMaxBufferSize);
622       client_socket_->SetReceiveTimeout(5200);
623       client_socket_->SetSendBufferSize(kSocketMaxBufferSize);
624
625       int pid = -1;
626       int ret = client_socket_->Receive(static_cast<void*>(&pid), sizeof(pid));
627       if (ret != 0) {
628         _E("Receive() is failed. error: %d", ret);
629         client_socket_.reset();
630         return;
631       }
632
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();
638         return;
639       }
640
641       client_channel_.reset(
642           new IOChannel(
643               client_socket_->GetFd(),
644               IOChannel::IOCondition::IO_IN | IOChannel::IOCondition::IO_HUP,
645               this));
646       client_channel_->SetCloseOnDestroy(false);
647
648       if (IsHydraMode())
649         SetPid(peer_cred->GetPid());
650
651       prepared_ = true;
652       if (listener_ != nullptr) {
653         CreateReadyFile();
654         listener_->OnLoaderPrepared(this);
655       }
656
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());
662       return;
663     }
664   } else {
665     auto client_socket = server_socket_->Accept();
666     _E("Refuse loader process connection");
667   }
668 }
669
670 void LoaderContext::HandleLoaderClientEvent(int condition) {
671   if (condition &
672       (IOChannel::IOCondition::IO_HUP | IOChannel::IOCondition::IO_NVAL)) {
673     SECURE_LOGE("Type %d loader was disconnected. pid: %d", GetType(), pid_);
674     SetPid(0);
675     Dispose();
676     Prepare();
677   }
678 }
679
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)
683     HandleLoaderEvent();
684   else if (client_socket_.get() != nullptr && client_socket_->GetFd() == fd)
685     HandleLoaderClientEvent(condition);
686 }
687
688 void LoaderContext::CreateReadyFile() {
689   if (ready_file_created_)
690     return;
691
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;
697     return;
698   }
699
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());
703     return;
704   }
705
706   file_stream.close();
707
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);
710
711   _W("File(%s) created successfully", path.c_str());
712   ready_file_created_ = true;
713 }
714
715 }  // namespace launchpad