Add setting priority to 0 before OnAdapterLoopBegin()
[platform/core/appfw/launchpad.git] / src / lib / launchpad / launchpad_loader.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/launchpad_loader.hh"
18
19 #include <aul.h>
20 #include <bundle_cpp.h>
21 #include <malloc.h>
22 #include <sys/types.h>
23 #include <unistd.h>
24
25 #include <memory>
26
27 #include <app_info.hh>
28 #include <client_socket.hh>
29 #include <exception.hh>
30 #include <parcel.hh>
31 #include <sched_priority.hh>
32 #include <socket.hh>
33 #include <util.hh>
34 #include <types.hh>
35
36 #include "launchpad/log_private.hh"
37 #include "launchpad/step_prepare_execution.hh"
38 #include "launchpad/thread_control.hh"
39 #include "launchpad-common/sched_priority.hh"
40
41 namespace launchpad {
42 namespace {
43
44 const uint32_t kMaxRetryingCount = 600;
45 constexpr const char kLaunchpadLoaderSocketName[] = ".launchpad-type";
46
47 tizen_base::Bundle loader_bundle;
48 LaunchpadLoader* context = nullptr;
49
50 }  // namespace
51
52 LaunchpadLoader::LaunchpadLoader(int argc, char** argv)
53     : argc_(argc), argv_(argv) {
54   if (context != nullptr) {
55     _E("Already exists");
56     THROW(-EALREADY);
57   }
58
59   if (argc_ < 4) {
60     _E("Too few argument");
61     THROW(-EINVAL);
62   }
63
64   int is_hydra = argv_[LoaderArg::Hydra][0] - '0';
65   if (is_hydra) {
66     _E("Cannot run in hydra mode");
67     THROW(-EINVAL);
68   }
69
70   loader_type_ = atoi(argv_[LoaderArg::Type]);
71   if (loader_type_ < 0 || loader_type_ >= LoaderType::Max) {
72     _E("Invalid argument. type: %d", loader_type_);
73     THROW(-EINVAL);
74   }
75
76   loader_id_ = atoi(argv_[LoaderArg::Id]);
77   _W("loader type: %d, loader id: %d", loader_type_, loader_id_);
78   context = this;
79 }
80
81 LaunchpadLoader::~LaunchpadLoader() {
82   if (app_argv_ != nullptr) {
83     for (int i = 0; i < app_argc_; ++i)
84       free(app_argv_[i]);
85
86     free(app_argv_);
87   }
88
89   context = nullptr;
90 }
91
92 int LaunchpadLoader::Run(loader_lifecycle_callback_s* callback,
93     loader_adapter_s* adapter, void* user_data) {
94   if (callback == nullptr || adapter == nullptr) {
95     _E("Invalid argument");
96     return -EINVAL;
97   }
98
99   if (adapter->loop_begin == nullptr || adapter->loop_quit == nullptr ||
100       adapter->add_fd == nullptr || adapter->remove_fd == nullptr) {
101     _E("Invalid argument. adapter callback is nullptr");
102     return -EINVAL;
103   }
104
105   callback_ = *callback;
106   adapter_callback_ = *adapter;
107   user_data_ = user_data;
108
109   if (!OnCreate()) {
110     _E("OnCreate() returns false");
111     return -1;
112   }
113
114   SchedPriority::Set(0);
115   OnAdapterLoopBegin();
116   return OnTerminate();
117 }
118
119 void LaunchpadLoader::Quit() {
120   OnAdapterLoopQuit();
121 }
122
123 const tizen_base::Bundle& LaunchpadLoader::GetBundle() const {
124   return app_info_.GetBundle();
125 }
126
127 void LaunchpadLoader::ResetArgs() {
128   memset(argv_[LoaderArg::Type], 0, strlen(argv_[LoaderArg::Type]));
129   memset(argv_[LoaderArg::Id], 0, strlen(argv_[LoaderArg::Id]));
130   memset(argv_[LoaderArg::Extra], 0, strlen(argv_[LoaderArg::Extra]));
131 }
132
133 void LaunchpadLoader::WaitForThreads(int threads) {
134   uint32_t retrying_count = 0;
135
136   if (threads <= 1)
137     return;
138
139   _W("Thread count = %u", threads);
140   do {
141     int thread_count = ThreadControl::GetInst().GetCount();
142     if (thread_count >= threads) {
143       _E("Threads(%u) are ready", thread_count);
144       return;
145     }
146
147     _D("Current thread count = %u", thread_count);
148     usleep(50 * 1000);
149     retrying_count++;
150   } while (retrying_count < kMaxRetryingCount);
151   _E("Maximum retyring count exceeded");
152 }
153
154 int LaunchpadLoader::ConnectToLaunchpad() {
155   std::string endpoint = "/run/aul/daemons/" + std::to_string(getuid()) +
156       "/" + std::string(kLaunchpadLoaderSocketName) +
157       std::to_string(loader_type_) + "-" + std::to_string(loader_id_);
158   ClientSocket client_socket;
159   client_socket.Connect(endpoint);
160   client_socket.SetReceiveBufferSize(Socket::kSocketMaxBufferSize);
161   client_socket.SetReceiveTimeout(5000);
162
163   pid_t pid = getpid();
164   int ret = client_socket.Send(static_cast<void*>(&pid), sizeof(pid));
165   if (ret != 0) {
166     _E("Send() is failed. error: %d", ret);
167     return ret;
168   }
169
170   return client_socket.RemoveFd();
171 }
172
173 bool LaunchpadLoader::OnCreate() {
174   setsid();
175   tizen_base::Bundle extra(argv_[LoaderArg::Extra]);
176   ResetArgs();
177
178   if (callback_.create == nullptr) {
179     _E("create callback is nullptr");
180     return false;
181   }
182
183   int threads = 0;
184   std::string threads_str = extra.GetString("threads");
185   if (!threads_str.empty() && std::isdigit(threads_str[0])) {
186     _W("threads: %s", threads_str.c_str());
187     threads = std::stoi(threads_str);
188   }
189
190   callback_.create(extra.GetHandle(), loader_type_, user_data_);
191   aul_launch_worker_init();
192   WaitForThreads(threads);
193   malloc_trim(0);
194
195   try {
196     int fd = ConnectToLaunchpad();
197     if (fd < 0) {
198       _E("Connection to launchpad was failed. error: %d", fd);
199       return false;
200     }
201
202     OnAdapterAddFd(fd);
203   } catch (const Exception& e) {
204     _E("Exception occurs. error: %s", e.what());
205     return false;
206   }
207
208   language_config_.reset(new LanguageConfig());
209   region_format_config_.reset(new RegionFormatConfig());
210   return true;
211 }
212
213 void LaunchpadLoader::OnPrelaunch(int argc, char** argv, AppInfo* app_info) {
214   if (callback_.prelaunch) {
215     int ret = callback_.prelaunch(argc, argv, app_info->GetAppPath().c_str(),
216         app_info->GetAppId().c_str(), app_info->GetPkgId().c_str(),
217         app_info->GetPkgType().c_str(), user_data_);
218     if (ret < 0) {
219       _E("prelaunch returns an error: %d", ret);
220       exit(-1);
221     }
222   }
223 }
224
225 void LaunchpadLoader::DefaultLaunch(AppInfo* app_info) {
226   StepPrepareExecution step_prepare_execution;
227   if (step_prepare_execution.Prepare(app_info) != 0) {
228     _E("Failed to prepare execution");
229     exit(-1);
230   }
231 }
232
233 void LaunchpadLoader::ChangeArgs(int argc, char** argv) {
234   auto* env = getenv("TIZEN_LOADER_ARGS");
235   if (env != nullptr) {
236     auto* loader_args = reinterpret_cast<char*>(strtoul(env, nullptr, 10));
237     if (loader_args != nullptr)
238       argv_[0] = loader_args;
239   }
240
241   memset(argv_[0], '\0', strlen(argv_[0]));
242   snprintf(argv_[0], kLoaderArgLength, "%s", argv[0]);
243 }
244
245 int LaunchpadLoader::OnLaunch(int argc, char** argv, AppInfo* app_info) {
246   if (callback_.launch == nullptr)
247     return -1;
248
249   return callback_.launch(argc, argv, app_info->GetAppPath().c_str(),
250       app_info->GetAppId().c_str(), app_info->GetPkgId().c_str(),
251       app_info->GetPkgType().c_str(), user_data_);
252 }
253
254 int LaunchpadLoader::OnTerminate() {
255   _D("Terminating...");
256   region_format_config_.reset();
257   language_config_.reset();
258
259   if (app_argc_ == 0 || app_argv_ == nullptr)
260     return -1;
261
262   if (callback_.terminate != nullptr)
263     return callback_.terminate(app_argc_, app_argv_, user_data_);
264
265   return -1;
266 }
267
268 void LaunchpadLoader::OnAdapterLoopBegin() {
269   _W("Loop begin");
270   adapter_callback_.loop_begin(user_data_);
271 }
272
273 void LaunchpadLoader::OnAdapterLoopQuit() {
274   _W("Loop quit");
275   adapter_callback_.loop_quit(user_data_);
276 }
277
278 void LaunchpadLoader::OnAdapterAddFd(int fd) {
279   _W("Add fd: %d", fd);
280   adapter_callback_.add_fd(user_data_, fd, ReceiverCb);
281 }
282
283 void LaunchpadLoader::OnAdapterRemovefd(int fd) {
284   _W("Remove fd: %d", fd);
285   adapter_callback_.remove_fd(user_data_, fd);
286 }
287
288 void LaunchpadLoader::HandleReceiverEvent(int fd) {
289   _D("fd: %d", fd);
290   ClientSocket socket(fd);
291   size_t data_size = 0;
292   int ret = socket.Receive(static_cast<void*>(&data_size), sizeof(data_size));
293   if (ret != 0) {
294     _E("Failed to receive the data from socket. error(%d)", ret);
295     return;
296   }
297
298   std::vector<uint8_t> data(data_size);
299   ret = socket.Receive(data.data(), data.size());
300   if (ret != 0) {
301     _E("Failed to receive the data from socket. error(%d)", ret);
302     return;
303   }
304
305   tizen_base::Parcel parcel(data.data(), data.size());
306   OnAdapterRemovefd(fd);
307
308   ProcessLaunchRequest(&parcel);
309   if (ret >= 0)
310     Quit();
311 }
312
313 void LaunchpadLoader::ReceiverCb(int fd) {
314   context->HandleReceiverEvent(fd);
315 }
316
317 void LaunchpadLoader::ProcessLaunchRequest(tizen_base::Parcel* parcel) {
318   parcel->ReadParcelable(&app_info_);
319   SECURE_LOGD("App Id: %s, Package Id: %s, Loader Type: %d",
320       app_info_.GetAppId().c_str(), app_info_.GetPkgId().c_str(), loader_type_);
321   if (app_info_.GetAppPath().empty() || app_info_.GetAppPath()[0] != '/') {
322     _E("AppPath is not absolute path");
323     exit(-1);
324   }
325
326   Util::SetEnvironments(&app_info_);
327   auto exported_args = app_info_.GetBundle().Export();
328   exported_args[0] = app_info_.GetAppPath();
329   app_argc_ = exported_args.size();
330   app_argv_ = static_cast<char**>(calloc(app_argc_, sizeof(char*)));
331   if (app_argv_ == nullptr) {
332     _E("calloc() is failed");
333     exit(-ENOMEM);
334   }
335
336   for (int i = 0; i < app_argc_; ++i) {
337     app_argv_[i] = strdup(exported_args[i].c_str());
338     if (app_argv_[i] == nullptr) {
339       _E("strdup() is failed. [%d] %s", i, exported_args[i].c_str());
340       exit(-ENOMEM);
341     }
342
343     SECURE_LOGD("Input argument %d : %s##", i, app_argv_[i]);
344   }
345
346   OnPrelaunch(app_argc_, app_argv_, &app_info_);
347   DefaultLaunch(&app_info_);
348   OnLaunch(app_argc_, app_argv_, &app_info_);
349   ChangeArgs(app_argc_, app_argv_);
350 }
351
352 }  // namespace launchpad
353
354 using namespace launchpad;
355
356 extern "C" EXPORT_API bundle* launchpad_loader_get_bundle(void) {
357   if (!::context)
358     return nullptr;
359
360   return ::context->GetBundle().GetHandle();
361 }
362
363 extern "C" EXPORT_API int launchpad_loader_main(int argc, char** argv,
364     loader_lifecycle_callback_s* callback, loader_adapter_s* adapter,
365     void* user_data) {
366   int ret = -1;
367   try {
368     LaunchpadLoader loader(argc, argv);
369     ret = loader.Run(callback, adapter, user_data);
370   } catch (const Exception& e) {
371     _E("Exception occurs. error: %s", e.what());
372     return e.GetErrorCode();
373   }
374
375   return ret;
376 }
377
378 extern "C" EXPORT_API int launchpad_loader_set_priority(int prio) {
379   return SchedPriority::Set(prio);
380 }
381
382 extern "C" EXPORT_API int launchpad_loader_block_threads(void) {
383   return ThreadControl::GetInst().BlockThreads();
384 }
385
386 extern "C" EXPORT_API int launchpad_loader_unblock_threads(void) {
387   return ThreadControl::GetInst().UnblockThreads();
388 }