Add to mount lib directory for rpk
[platform/core/appfw/launchpad.git] / src / lib / launchpad-glib / util.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-glib/util.hh"
18
19 #include <bundle_internal.h>
20 #include <dbus/dbus.h>
21 #include <stdlib.h>
22 #include <sys/mount.h>
23 #include <sys/personality.h>
24 #include <sys/resource.h>
25 #include <sys/time.h>
26 #include <sys/types.h>
27 #include <tzplatform_config.h>
28 #include <unistd.h>
29
30 #include <algorithm>
31 #include <filesystem>
32 #include <fstream>
33 #include <memory>
34 #include <string>
35 #include <utility>
36 #include <vector>
37
38 #include <aul_keys.hh>
39 #include <exception.hh>
40 #include <parcel.hh>
41 #include <parcelable.hh>
42 #include <server_socket.hh>
43 #include <socket.hh>
44
45 #include "launchpad-glib/log_private.hh"
46
47 #define GLOBAL_USER tzplatform_getuid(TZ_SYS_GLOBALAPP_USER)
48
49 namespace fs = std::filesystem;
50
51 namespace launchpad {
52 namespace {
53
54 constexpr const char kDefaultLocale[] = "en_US.UTF-8";
55 constexpr const char kTepBusName[] = "org.tizen.system.deviced";
56 constexpr const char kTepObjectPath[] = "/Org/Tizen/System/DeviceD/Tzip";
57 constexpr const char kTepInterfaceName[] = "org.tizen.system.deviced.Tzip";
58 constexpr const char kTepIsMountedMethod[] = "IsMounted";
59 const int kMaxTepIsMountRetryCount = 100;
60 constexpr const char kApp2sdBusName[] = "org.tizen.app2sd";
61 constexpr const char kApp2sdObjectPath[] = "/org/tizen/app2sd";
62 constexpr const char kApp2sdInterfaceName[] = "org.tizen.app2sd";
63 constexpr const char kApp2sdOndemandSetupInitMethod[] = "OndemandSetupInit";
64 const int kApp2sdRetryMax = 5;
65 const int kApp2sdWaitUsec = 1000000 / 2;  // 0.5 sec
66
67 void SetLanguageEnvironments() {
68   const char* lang = getenv("LANG");
69   if (lang == nullptr) {
70     lang = kDefaultLocale;
71     setenv("LANG", lang, 1);
72   }
73
74   setenv("LANGUAGE", lang, 1);
75   setenv("LC_MESSAGES", lang, 1);
76   setenv("LC_ALL", lang, 1);
77 }
78
79 void SetRegionFormatEnvironments() {
80   const char* region = getenv("LC_CTYPE");
81   if (region == nullptr) {
82     region = kDefaultLocale;
83     setenv("LC_CTYPE", region, 1);
84   }
85
86   setenv("LC_NUMERIC", region, 1);
87   setenv("LC_TIME", region, 1);
88   setenv("LC_COLLATE", region, 1);
89   setenv("LC_MONETARY", region, 1);
90   setenv("LC_PAPER", region, 1);
91   setenv("LC_NAME", region, 1);
92   setenv("LC_ADDRESS", region, 1);
93   setenv("LC_TELEPHONE", region, 1);
94   setenv("LC_MEASUREMENT", region, 1);
95   setenv("LC_IDENTIFICATION", region, 1);
96 }
97
98 void SetGadgetPkgIdsEnvironments(const tizen_base::Bundle& b) {
99   auto gadget_pkgids = b.GetStringArray(kAulMountGadgetPkgIds);
100   if (gadget_pkgids.empty()) return;
101
102   std::string pkgids;
103   for (auto& pkgid : gadget_pkgids) {
104     if (!pkgids.empty()) pkgids += ":";
105
106     pkgids += pkgid;
107   }
108
109   setenv("GADGET_PKGIDS", pkgids.c_str(), 1);
110 }
111
112 #ifdef TIZEN_FEATURE_SET_PERSONALITY_32
113 static void SetExecutionDomain() {
114   int ret = personality(PER_LINUX32);
115   if (ret < 0) {
116     char err_buf[1024];
117     _E("persionality() is failed. errno: %d(%s)",
118         errno, strerror_r(errno, err_buf, sizeof(err_buf)));
119   }
120 }
121 #endif  // TIZEN_FEATURE_SET_PERSONALITY_32
122
123 class DBus {
124  public:
125   DBus() {
126     DBusError error;
127     dbus_error_init(&error);
128     conn_ = dbus_bus_get_private(DBUS_BUS_SYSTEM, &error);
129     if (conn_ == nullptr) {
130       _E("Failed to connect to D-Bus Daemon");
131       if (dbus_error_is_set(&error)) {
132         _E("D-Bus error: %s", error.message);
133         dbus_error_free(&error);
134       }
135       THROW(-ENOTCONN);
136     }
137   }
138
139   virtual ~DBus() {
140     if (conn_ != nullptr)
141       dbus_connection_close(conn_);
142   }
143
144   void SendMessage(DBusMessage* message, int timeout, int* result) {
145     if (message == nullptr) {
146       _E("Invalid parameter");
147       THROW(-EINVAL);
148     }
149
150     DBusPendingCall* pending = nullptr;
151     dbus_bool_t ret = dbus_connection_send_with_reply(conn_, message, &pending,
152         timeout);
153     if (!ret || pending == nullptr) {
154       _E("Failed to send message");
155       THROW(-EIO);
156     }
157
158     dbus_connection_flush(conn_);
159     dbus_pending_call_block(pending);
160     auto* reply = dbus_pending_call_steal_reply(pending);
161     dbus_pending_call_unref(pending);
162     if (reply == nullptr) {
163       _E("Failed to get reply message");
164       THROW(-EIO);
165     }
166
167     auto reply_auto =
168         std::unique_ptr<DBusMessage, decltype(dbus_message_unref)*>(
169             reply, dbus_message_unref);
170     DBusMessageIter iter;
171     if (!dbus_message_iter_init(reply, &iter)) {
172       _E("Message ha no argument");
173       THROW(-EIO);
174     }
175
176     if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INT32) {
177       _E("Argument is not interger. type(%d)",
178           dbus_message_iter_get_arg_type(&iter));
179       THROW(-EIO);
180     }
181
182     dbus_int32_t res;
183     dbus_message_iter_get_basic(&iter, &res);
184     *result = static_cast<int>(res);
185     _D("Result: %d", *result);
186   }
187
188  private:
189   DBusConnection* conn_ = nullptr;
190 };
191
192 class TepMountChecker : public DBus {
193  public:
194   explicit TepMountChecker(std::vector<std::string> paths)
195       : paths_(std::move(paths)) {}
196
197   bool IsMounted() {
198     for (auto& path : paths_) {
199       if (CheckTepMount(path) != 0)
200         return false;
201     }
202
203     return true;
204   }
205
206  private:
207   DBusMessage* CreateTepMountMessage(const std::string& path) {
208     DBusMessage* message = dbus_message_new_method_call(kTepBusName,
209         kTepObjectPath, kTepInterfaceName, kTepIsMountedMethod);
210     if (message == nullptr) {
211       _E("dbus_message_new_method_call() is failed");
212       return nullptr;
213     }
214
215     DBusMessageIter iter;
216     dbus_message_iter_init_append(message, &iter);
217     auto* tep_path = path.c_str();
218     auto ret = dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
219         &tep_path);
220     if (!ret) {
221       _E("dbus_message_iter_append_basic() is failed");
222       dbus_message_unref(message);
223       return nullptr;
224     }
225
226     return message;
227   }
228
229   int IsTepMountDone(const std::string& path) {
230     int result = -1;
231     auto message = std::unique_ptr<DBusMessage, decltype(dbus_message_unref)*>(
232         CreateTepMountMessage(path), dbus_message_unref);
233     SendMessage(message.get(), 500, &result);
234     return result;
235   }
236
237   int CheckTepMount(const std::string& path) {
238     if (path.empty())
239       return 0;
240
241     int count = 0;
242     while (count < kMaxTepIsMountRetryCount) {
243       if (IsTepMountDone(path) == 1)
244         return 0;
245
246       usleep(50 * 1000);
247       count++;
248     }
249
250     _E("Not able to mount within 5 seconds. path(%s", path.c_str());
251     return -1;
252   }
253
254  private:
255   std::vector<std::string> paths_;
256 };
257
258 int MountDirectories(const std::vector<std::string>& srcs,
259     const std::string& dest) {
260   std::string opt = "lowerdir=" + dest;
261   for (auto& src : srcs)
262     opt += ":" + src;
263
264   _D("mount opt: %s", opt.c_str());
265   int ret = mount(nullptr, dest.c_str(), "overlay", MS_RDONLY, opt.c_str());
266   if (ret != 0)
267     _E("mount() is failed. dest(%s), errno(%d)", dest.c_str(), errno);
268
269   return ret;
270 }
271
272 class ExternalPackage : public DBus {
273  public:
274   ExternalPackage(std::string package, uid_t uid)
275       : package_(std::move(package)), uid_(uid) {
276     _D("package(%s), uid(%u)", package_.c_str(), uid_);
277   }
278
279   void Enable() {
280     int result = -1;
281     int retry_count = 0;
282     auto message = std::unique_ptr<DBusMessage, decltype(dbus_message_unref)*>(
283         CreateApp2sdMessage(), dbus_message_unref);
284     while (retry_count <= kApp2sdRetryMax) {
285       try {
286         SendMessage(message.get(), 500, &result);
287       } catch (const Exception& e) {
288         _E("Exception occurs. error(%s)", e.what());
289         retry_count++;
290         continue;
291       }
292
293       break;
294     }
295
296     _D("Result: %d", result);
297     if (result < 0)
298       THROW(-EIO);
299   }
300
301  private:
302   DBusMessage* CreateApp2sdMessage() {
303     DBusMessage* message = dbus_message_new_method_call(kApp2sdBusName,
304         kApp2sdObjectPath, kApp2sdInterfaceName,
305         kApp2sdOndemandSetupInitMethod);
306     if (message == nullptr) {
307       _E("dbus_message_new_method_call() is failed");
308       return nullptr;
309     }
310
311     DBusMessageIter iter;
312     dbus_message_iter_init_append(message, &iter);
313     const char* package = package_.c_str();
314     if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &package)) {
315       _E("dbus_message_iter_append_basic() is failed");
316       dbus_message_unref(message);
317       return nullptr;
318     }
319
320     if (!dbus_message_iter_append_basic(&iter, DBUS_TYPE_INT32, &uid_)) {
321       _E("dbus_message_iter_append_basic() is failed");
322       dbus_message_unref(message);
323       return nullptr;
324     }
325
326     return message;
327   }
328
329  private:
330   std::string package_;
331   uid_t uid_;
332 };
333
334 class AmdPacket : public tizen_base::Parcelable {
335  public:
336   explicit AmdPacket(int cmd, bundle* request, int opt)
337       : cmd_(cmd), request_(request), opt_(opt) {}
338
339   void WriteToParcel(tizen_base::Parcel* parcel) const {
340     parcel->WriteInt32(cmd_);
341     if (request_ == nullptr) {
342       parcel->WriteInt32(0);
343       parcel->WriteInt32(opt_);
344     } else {
345       bundle_raw* raw = nullptr;
346       int len = 0;
347       bundle_encode(request_, &raw, &len);
348       parcel->WriteInt32(len);
349       parcel->WriteInt32(opt_);
350       parcel->Write(reinterpret_cast<unsigned char*>(raw), len);
351       bundle_free_encoded_rawdata(&raw);
352     }
353   }
354
355   void ReadFromParcel(tizen_base::Parcel* parcel) {
356     parcel->ReadInt32(&cmd_);
357   }
358
359  private:
360   int cmd_ = -1;
361   bundle* request_ = nullptr;
362   int opt_ = 0;
363 };
364
365 }  // namespace
366
367 void Util::SetEnvironments(const AppInfo* app_info) {
368   auto& b = app_info->GetBundle();
369   auto value = b.GetString(kAulStarttime);
370   if (!value.empty())
371     setenv("APP_START_TIME", value.c_str(), 1);
372
373   if (!app_info->GetHwacc().empty())
374     setenv("HWACC", app_info->GetHwacc().c_str(), 1);
375
376   if (!app_info->GetTaskmanage().empty())
377     setenv("TASKMANAGE", app_info->GetTaskmanage().c_str(), 1);
378
379   if (!app_info->GetRootPath().empty())
380     setenv("AUL_ROOT_PATH", app_info->GetRootPath().c_str(), 1);
381
382   if (!app_info->GetAppId().empty()) {
383     auto multiple_instance_app_id = b.GetString(kAulMultipleInstanceAppId);
384     if (!multiple_instance_app_id.empty())
385       setenv("AUL_APPID", multiple_instance_app_id.c_str(), 1);
386     else
387       setenv("AUL_APPID", app_info->GetAppId().c_str(), 1);
388   }
389
390   if (!app_info->GetPkgId().empty())
391     setenv("AUL_PKGID", app_info->GetPkgId().c_str(), 1);
392
393   if (!app_info->GetAppType().empty())
394     setenv("RUNTIME_TYPE", app_info->GetAppType().c_str(), 1);
395
396   value = b.GetString(kAulWaylandDisplay);
397   if (!value.empty())
398     setenv("WAYLAND_DISPLAY", value.c_str(), 1);
399
400   value = b.GetString(kAulWaylandWorkingDir);
401   if (!value.empty())
402     setenv("XDG_RUNTIME_DIR", value.c_str(), 1);
403
404   value = b.GetString(kAulApiVersion);
405   if (!value.empty())
406     setenv("TIZEN_API_VERSION", value.c_str(), 1);
407
408   setenv("AUL_PID", std::to_string(getpid()).c_str(), 1);
409
410   if (getenv("TIZEN_GLIB_CONTEXT") == nullptr)
411     setenv("TIZEN_GLIB_CONTEXT", "0", 1);
412
413   value = b.GetString(kAulFastLaunch);
414   if (!value.empty())
415     setenv("AUL_FAST_LAUNCH", value.c_str(), 1);
416
417   SetLanguageEnvironments();
418   SetRegionFormatEnvironments();
419
420 #ifdef TIZEN_FEATURE_SET_PERSONALITY_32
421   SetExecutionDomain();
422 #endif  // TIZEN_FEATURE_SET_PERSONALITY_32
423
424   setenv("GCOV_PREFIX", "/tmp", 1);
425   setenv("DALI_DISABLE_PARTIAL_UPDATE", "0", 1);
426   SetGadgetPkgIdsEnvironments(b);
427 }
428
429 void Util::DeleteSocketPath(pid_t pid, uid_t uid) {
430   std::string path ="/run/aul/apps/" + std::to_string(uid) + "/" +
431       std::to_string(pid);
432   if (!fs::exists(path))
433     return;
434
435   try {
436     fs::remove_all(path);
437   } catch (const std::exception& e) {
438     _E("Exception occurs. error: %s", e.what());
439   }
440 }
441
442 int Util::EnableExternalPackage(const AppInfo* app_info) {
443   auto installed_storage = app_info->GetBundle().GetString(
444       kAulInstalledStorage);
445   if (installed_storage != "external")
446     return 0;
447
448   try {
449     ExternalPackage external_package(app_info->GetPkgId(),
450         app_info->IsGlobal() ? GLOBAL_USER : getuid());
451     external_package.Enable();
452   } catch (const Exception& e) {
453     _E("Exception occurs. error(%s)", e.what());
454     return -1;
455   }
456
457   return 0;
458 }
459
460 int Util::MountResourceDirectories(const AppInfo* app_info) {
461   auto& root_path = app_info->GetRootPath();
462   auto& b = app_info->GetBundle();
463   auto global_res_dir = b.GetStringArray(kAulMountGlobalResDir);
464   if (!global_res_dir.empty())
465     MountDirectories(global_res_dir, root_path + "/res/mount/global");
466
467   auto allowed_res_dir = b.GetStringArray(kAulMountAllowedResDir);
468   if (!allowed_res_dir.empty())
469     MountDirectories(allowed_res_dir, root_path + "/res/mount/allowed");
470
471   return 0;
472 }
473
474 int Util::MountLibraryDirectories(const tizen_base::Bundle& b) {
475   auto lib_dir = b.GetStringArray(kAulMountLibDir);
476   if (!lib_dir.empty()) {
477     auto root_path = b.GetString(kAulRootPath);
478     MountDirectories(lib_dir, root_path + "/lib/");
479   }
480
481   return 0;
482 }
483
484 int Util::MountGadgetDirectories(const tizen_base::Bundle& b) {
485   auto gadget_paths = b.GetStringArray(kAulMountGadgetPaths);
486   if (!gadget_paths.empty()) {
487     auto root_path = b.GetString(kAulRootPath);
488     return MountDirectories(gadget_paths, root_path + "/bin");
489   }
490
491   return 0;
492 }
493
494 int Util::WaitTepMount(const AppInfo* app_info) {
495   if (app_info->GetBundle().GetType(kAulTepPath) == BUNDLE_TYPE_NONE)
496     return 0;
497
498   try {
499     auto paths = app_info->GetBundle().GetStringArray(kAulTepPath);
500     TepMountChecker checker(std::move(paths));
501     if (!checker.IsMounted())
502       return -1;
503   } catch (const Exception& e) {
504     _E("Exception occurs. error(%s)", e.what());
505     return e.GetErrorCode();
506   }
507
508   _I("TEP Mount has been done");
509   return 0;
510 }
511
512 std::string Util::GetLibDirectory(const std::string& app_path) {
513   std::filesystem::path path(app_path);
514   auto lib_dir = path.parent_path().string() + "/../lib/";
515   if (std::filesystem::exists(lib_dir))
516     return lib_dir;
517
518   return "";
519 }
520
521 void Util::CloseAllFds(const std::vector<int>& except_fds) {
522   int aul_fd = -1;
523   const char* aul_listen_fd = getenv("AUL_LISTEN_FD");
524   if (aul_listen_fd != nullptr)
525     aul_fd = atoi(aul_listen_fd);
526
527   std::vector<int> fds;
528   try {
529     fs::path proc_path("/proc/self/fd");
530     for (const auto& entry : fs::directory_iterator(proc_path)) {
531       if (!isdigit(entry.path().filename().string()[0]))
532         continue;
533
534       int fd = std::stoi(entry.path().filename().string());
535       if (fd < 3 || fd == aul_fd)
536         continue;
537
538       auto found = std::find_if(except_fds.begin(), except_fds.end(),
539           [&](int except_fd) {
540             return except_fd == fd;
541           });
542       if (found != except_fds.end())
543         continue;
544
545       fds.push_back(fd);
546     }
547   } catch (const fs::filesystem_error& e) {
548     _E("Execption occurs. error(%s)", e.what());
549   }
550
551   _W("size: %zd", fds.size());
552   for (auto fd : fds)
553     close(fd);
554 }
555
556 int Util::PrepareAppSocket() {
557   try {
558     std::string path = "/run/aul/apps/" + std::to_string(getuid()) + "/" +
559         std::to_string(getpid());
560     if (access(path.c_str(), F_OK) != 0)
561       fs::create_directory(path);
562
563     path += "/.app-sock";
564     ServerSocket socket;
565     socket.Bind(path);
566     socket.Listen(128);
567     socket.SetReceiveBufferSize(Socket::kSocketMaxBufferSize);
568     socket.SetSendBufferSize(Socket::kSocketMaxBufferSize);
569     socket.SetCloseOnExec(false);
570     int fd = socket.RemoveFd();
571     setenv("AUL_LISTEN_FD", std::to_string(fd).c_str(), 1);
572   } catch (const Exception& e) {
573     _E("Exception occurs. error(%s)", e.what());
574     return e.GetErrorCode();
575   }
576
577   return 0;
578 }
579
580 int Util::PrepareAppIdFile(const AppInfo* app_info) {
581   std::string path = "/run/aul/apps/" + std::to_string(getuid()) + "/" +
582       std::to_string(getpid()) + "/" + app_info->GetAppId();
583   std::ofstream stream(path);
584   stream.close();
585   return 0;
586 }
587
588 int Util::SendCmdToAmd(enum AmdCmd cmd) {
589   return SendCmdToAmd(cmd, nullptr, static_cast<int>(AmdSocketOption::None));
590 }
591
592 int Util::SendCmdToAmd(enum AmdCmd cmd, bundle* request, int opt) {
593   try {
594     std::string endpoint = "/run/aul/daemons/.amd-sock";
595     ClientSocket socket;
596     socket.Connect(endpoint);
597     socket.SetReceiveBufferSize(Socket::kSocketMaxBufferSize);
598     socket.SetReceiveTimeout(5000);
599
600     AmdPacket packet(static_cast<int>(cmd), request, opt);
601     tizen_base::Parcel parcel;
602     parcel.WriteParcelable(packet);
603
604     int ret = socket.Send(parcel.GetData(), parcel.GetDataSize());
605     if (ret != 0) {
606       _E("Failed to send cmd(%d), error(%d)", static_cast<int>(cmd), ret);
607       return -ECOMM;
608     }
609   } catch (const Exception& e) {
610     _E("Exception occrus. error(%s)", e.what());
611     return e.GetErrorCode();
612   }
613
614   return 0;
615 }
616
617 }  // namespace launchpad