Add to mount lib directory for rpk
[platform/core/appfw/launchpad.git] / src / launchpad-process-pool / loader_mount.cc
1 /*
2  * Copyright (c) 2024 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_mount.hh"
18
19 #include <bundle_cpp.h>
20 #include <bundle_internal.h>
21 #include <dlog-redirect-stdout.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <linux/limits.h>
25 #include <signal.h>
26 #include <string.h>
27 #include <sys/stat.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30
31 #include <filesystem>
32 #include <memory>
33 #include <string>
34 #include <utility>
35 #include <vector>
36
37 #include <aul_keys.hh>
38 #include <exception.hh>
39 #include <parcelable.hh>
40 #include <util.hh>
41
42 #include "launchpad-process-pool/launchpad_args.hh"
43 #include "launchpad-process-pool/log_private.hh"
44
45 namespace fs = std::filesystem;
46 namespace {
47
48 bool IsExceptable(const std::string& path) {
49   static char buf[PATH_MAX];
50   ssize_t len = readlink(path.c_str(), buf, sizeof(buf));
51   if (len < 0) {
52     _E("readlink() is failed. errno: %d", errno);
53     return false;
54   }
55
56   buf[len] = '\0';
57   if (strstr(buf, "log") != nullptr ||
58       strstr(buf, "trace") != nullptr ||
59       strstr(buf, "dev") != nullptr)
60     return true;
61
62   return false;
63 }
64
65 std::vector<int> GetExceptableFds() {
66   std::vector<int> fds;
67   try {
68     fs::path proc_path("/proc/self/fd");
69     for (const auto& entry : fs::directory_iterator(proc_path)) {
70       if (!isdigit(entry.path().filename().string()[0]))
71         continue;
72
73       int fd = std::stoi(entry.path().filename().string());
74       if (dlog_is_log_fd(fd) || IsExceptable(entry.path().string()))
75         fds.push_back(fd);
76     }
77   } catch (const fs::filesystem_error& e) {
78     _E("Exception occurs. error(%s)", e.what());
79   }
80
81   return fds;
82 }
83
84 int ChangeMountNamespace(pid_t pid) {
85   std::string mnt_path = "/proc/" + std::to_string(pid) + "/ns/mnt";
86   int fd = open(mnt_path.c_str(), O_RDONLY);
87   if (fd < 0) {
88     _E("open() is failed. path(%s), errno(%d)", mnt_path.c_str(), errno);
89     return -1;
90   }
91
92   int ret = ::setns(fd, 0);
93   close(fd);
94   if (ret != 0) {
95     _E("setns() is failed. path(%s), errno(%d)", mnt_path.c_str(), errno);
96     return -1;
97   }
98
99   _D("setns() is successful. pid(%d)", pid);
100   return 0;
101 }
102
103 class Request : public tizen_base::Parcelable {
104  public:
105   explicit Request(tizen_base::Parcel* parcel) { ReadFromParcel(parcel); }
106
107   Request(pid_t pid, tizen_base::Bundle b) : pid_(pid), b_(std::move(b)) {}
108
109   pid_t GetPid() const { return pid_; }
110
111   const tizen_base::Bundle& GetBundle() const { return b_; }
112
113   void WriteToParcel(tizen_base::Parcel* parcel) const override {
114     parcel->WriteInt32(pid_);
115     bundle_raw* raw = nullptr;
116     int len = 0;
117     bundle_encode(b_.GetHandle(), &raw, &len);
118     parcel->WriteInt32(len);
119     parcel->Write(reinterpret_cast<unsigned char*>(raw), len);
120     bundle_free_encoded_rawdata(&raw);
121   }
122
123   void ReadFromParcel(tizen_base::Parcel* parcel) override {
124     parcel->ReadInt32(&pid_);
125     int len = 0;
126     parcel->ReadInt32(&len);
127     if (len > 0) {
128       std::vector<uint8_t> data(len);
129       parcel->Read(data.data(), data.size());
130       b_ = tizen_base::Bundle(
131           bundle_decode(reinterpret_cast<bundle_raw*>(data.data()), len), false,
132           true);
133     }
134   }
135
136  private:
137   pid_t pid_;
138   tizen_base::Bundle b_;
139 };
140
141 class Reply : public tizen_base::Parcelable {
142  public:
143   explicit Reply(tizen_base::Parcel* parcel) { ReadFromParcel(parcel); }
144
145   explicit Reply(int result) : result_(result) {}
146
147   int GetResult() const { return result_; }
148
149   void WriteToParcel(tizen_base::Parcel* parcel) const override {
150     parcel->WriteInt32(result_);
151   }
152
153   void ReadFromParcel(tizen_base::Parcel* parcel) override {
154     parcel->ReadInt32(&result_);
155   }
156
157  private:
158   int result_ = -1;
159 };
160
161 }  // namespace
162
163 namespace launchpad {
164
165 LoaderMount::LoaderMount() : Executor(this), launchpad_ppid_(getppid()) {
166   Prepare();
167 }
168
169 LoaderMount::~LoaderMount() { Dispose(); }
170
171 void LoaderMount::Prepare() {
172   if (pid_ > 0) return;
173
174   int pipe_fd[2];
175   if (CreatePipe(&pipe_fd) != 0) return;
176
177   read_socket_.reset(new Socket(pipe_fd[0]));
178   int write_fd = pipe_fd[1];
179
180   if (CreatePipe(&pipe_fd) != 0) return;
181
182   int read_fd = pipe_fd[0];
183   write_socket_.reset(new Socket(pipe_fd[1]));
184
185   _W("read_socket=%d, write_socket=%d",
186      read_socket_->GetFd(), write_socket_->GetFd());
187   pid_ = Executor::Execute();
188   if (pid_ == -1) {
189     _E("Failed to fork process. errno(%d)", errno);
190     close(read_fd);
191     close(write_fd);
192     return;
193   }
194
195   read_socket_.reset(new Socket(read_fd));
196   write_socket_.reset(new Socket(write_fd));
197 }
198
199 void LoaderMount::Dispose() {
200   read_socket_.reset();
201   write_socket_.reset();
202   if (pid_ > 0) {
203     if (kill(pid_, SIGKILL) == -1)
204       _E("Failed to send kill signal. pid(%d), errno(%d)", pid_, errno);
205
206     pid_ = -1;
207   }
208 }
209
210 void LoaderMount::HandleSigchld(pid_t pid) {
211   if (pid_ != pid) return;
212
213   pid_ = -1;
214   Dispose();
215   Prepare();
216 }
217
218 int LoaderMount::Mount(pid_t pid, const AppInfo* app_info) {
219   auto& b = app_info->GetBundle();
220   if (b.GetType(kAulMountGadgetPaths) == BUNDLE_TYPE_NONE &&
221       b.GetType(kAulMountLibDir) == BUNDLE_TYPE_NONE) return 0;
222
223   tizen_base::Parcel parcel;
224   Request request(pid, b);
225   request.WriteToParcel(&parcel);
226
227   _W("Send mount request");
228   int ret = Write(parcel);
229   if (ret != 0) return ret;
230
231   parcel.Clear();
232   ret = Read(&parcel);
233   if (ret!= 0) return ret;
234
235   _W("receive result");
236   Reply reply(&parcel);
237   return reply.GetResult();
238 }
239
240 void LoaderMount::OnExecution() {
241   _D("Mount Manager");
242   char** args = LaunchpadArgs::GetInst().GetArgs();
243   size_t length = strlen(args[0]);
244   memset(args[0], '\0', length);
245   snprintf(args[0], length, "/usr/bin/loader-mount");
246
247   std::vector<int> except_fds = GetExceptableFds();
248   except_fds.push_back(read_socket_->GetFd());
249   except_fds.push_back(write_socket_->GetFd());
250   Util::CloseAllFds(except_fds);
251   int ret = ProcessRequests();
252   exit(ret);
253 }
254
255
256 int LoaderMount::CreatePipe(int (*pipe_fd)[2]) {
257   (*pipe_fd)[0] = -1;
258   (*pipe_fd)[1] = -1;
259
260   if (pipe(*pipe_fd) == -1) {
261     _E("pipe() is failed. errno(%d)", errno);
262     return -1;
263   }
264
265   if (fcntl((*pipe_fd)[0], F_SETPIPE_SZ, Socket::kSocketMaxBufferSize) == -1)
266     _E("Failed to set pipe size. pipe_fd(%d), errno(%d)", (*pipe_fd)[0], errno);
267
268   if (fcntl((*pipe_fd)[1], F_SETPIPE_SZ, Socket::kSocketMaxBufferSize) == -1)
269     _E("Failed to set pipe size. pipe_fd(%d), errno(%d)", (*pipe_fd)[1], errno);
270
271   return 0;
272 }
273
274 int LoaderMount::Write(const tizen_base::Parcel& parcel) {
275   size_t data_size = parcel.GetDataSize();
276   int ret =
277       write_socket_->Write(static_cast<void*>(&data_size), sizeof(data_size));
278   if (ret != 0) {
279     _E("Write() is failed. error(%d)", ret);
280     return -1;
281   }
282
283   return write_socket_->Write(parcel.GetData(), parcel.GetDataSize());
284 }
285
286 int LoaderMount::Read(tizen_base::Parcel* parcel) {
287   size_t data_size = 0;
288   int ret =
289       read_socket_->Read(static_cast<void*>(&data_size), sizeof(data_size));
290   if (ret != 0) {
291     _E("Read() is failed. error(%d)", ret);
292     return -1;
293   }
294
295   std::vector<uint8_t> data(data_size);
296   ret = read_socket_->Read(data.data(), data.size());
297   if (ret != 0) {
298     _E("Read() is failed. error(%d)", ret);
299     return -1;
300   }
301
302   parcel->Write(data.data(), data.size());
303   return 0;
304 }
305
306 int LoaderMount::ProcessRequests() {
307   tizen_base::Parcel parcel;
308   while (true) {
309     parcel.Clear();
310     int ret = Read(&parcel);
311     if (ret != 0) continue;
312
313     _W("Request received");
314     Request request(&parcel);
315     ret = ChangeMountNamespace(request.GetPid());
316     if (ret == 0) {
317       auto& b = request.GetBundle();
318       if (b.GetType(kAulMountGadgetPaths) != BUNDLE_TYPE_NONE)
319         ret |= Util::MountGadgetDirectories(b);
320       if (b.GetType(kAulMountLibDir) != BUNDLE_TYPE_NONE)
321         ret |= Util::MountLibraryDirectories(b);
322
323       ChangeMountNamespace(launchpad_ppid_);
324     }
325
326     Reply reply(ret);
327     parcel.Clear();
328     reply.WriteToParcel(&parcel);
329     Write(parcel);
330   }
331
332   return 0;
333 }
334
335 }  // namespace launchpad