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