d91bc58e403694b6ca73ec9b2791cf32509d7e02
[platform/core/appfw/launchpad.git] / src / launchpad-process-pool / process_pool.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/process_pool.hh"
18
19 #include <dlog-redirect-stdout.h>
20 #include <errno.h>
21 #include <fcntl.h>
22 #include <string.h>
23 #include <sys/stat.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26
27 #include <filesystem>
28 #include <utility>
29 #include <vector>
30
31 #include <util.hh>
32
33 #include "launchpad-process-pool/launchpad_args.hh"
34 #include "launchpad-process-pool/log_private.hh"
35
36 namespace fs = std::filesystem;
37
38 namespace launchpad {
39 namespace {
40
41 constexpr const char kProcessPool[] = "process-pool";
42
43 std::vector<int> GetDlogFds() {
44   std::vector<int> fds;
45   try {
46     fs::path proc_path("/proc/self/fd");
47     for (const auto& entry : fs::directory_iterator(proc_path)) {
48       if (!isdigit(entry.path().filename().string()[0]))
49         continue;
50
51       int fd = std::stoi(entry.path().filename().string());
52       if (dlog_is_log_fd(fd))
53         fds.push_back(fd);
54     }
55   } catch (const fs::filesystem_error& e) {
56     _E("Exception occurs. error(%s)", e.what());
57   }
58
59   return fds;
60 }
61
62 }  // namespace
63
64 ProcessPool::ProcessPool(std::string name, int num_processes,
65     IEvent* event_listener = nullptr)
66     : Executor(this),
67       name_(std::move(name)),
68       num_processes_(num_processes),
69       event_listener_(event_listener) {
70   PrepareProcess();
71 }
72
73 ProcessPool::~ProcessPool() {
74   Dispose();
75 }
76
77 bool ProcessPool::IsPrepared() const {
78   return !queue_.empty();
79 }
80
81 pid_t ProcessPool::Execute(const tizen_base::Parcel& parcel) {
82   SetTimer();
83   if (!IsPrepared())
84     return -1;
85
86   auto process = std::move(queue_.front());
87   queue_.pop();
88   process->Send(parcel);
89   return process->GetPid();
90 }
91
92 void ProcessPool::Dispose() {
93   while (!queue_.empty()) {
94     auto process = std::move(queue_.front());
95     queue_.pop();
96     process->Kill();
97     _D("Kill process(%d)", process->GetPid());
98   }
99
100   UnsetTimer();
101 }
102
103 ProcessPool::Process::Process(pid_t pid, int fd)
104     : pid_(pid), socket_(new Socket(fd)) {
105 }
106
107 pid_t ProcessPool::Process::GetPid() const {
108   return pid_;
109 }
110
111 int ProcessPool::Process::Send(const tizen_base::Parcel& parcel) {
112   _W("Send execution request. process ID: %d", pid_);
113   size_t data_size = parcel.GetDataSize();
114   int ret = socket_->Write(static_cast<void*>(&data_size), sizeof(data_size));
115   if (ret != 0) {
116     _E("Write() is failed. error(%d)", ret);
117     return ret;
118   }
119
120   return socket_->Write(parcel.GetData(), parcel.GetDataSize());
121 }
122
123 void ProcessPool::Process::Kill() {
124   socket_->Close();
125   if (kill(pid_, SIGKILL) == -1) {
126     _E("Failed to send kill signal to the process. pid(%d), errno(%d)",
127         pid_, errno);
128   }
129 }
130
131 void ProcessPool::OnExecution() {
132   _D("Candidate Process");
133   char** args = LaunchpadArgs::GetInst().GetArgs();
134   size_t length = strlen(args[0]);
135   memset(args[0], '\0', length);
136   snprintf(args[0], length, "/usr/bin/%s <%s>", kProcessPool, name_.c_str());
137
138   close(pipe_fd_[1]);
139   std::vector<int> except_fds = GetDlogFds();
140   except_fds.push_back(pipe_fd_[0]);
141   Util::CloseAllFds(except_fds);
142   int ret = WaitForRequest(std::make_unique<Socket>(pipe_fd_[0]));
143   exit(ret);
144 }
145
146 void ProcessPool::PrepareProcess() {
147   int current_process_count = static_cast<int>(queue_.size());
148   for (int i = current_process_count; i < num_processes_; ++i) {
149     pipe_fd_[0] = -1;
150     pipe_fd_[1] = -1;
151     if (pipe(pipe_fd_) == -1) {
152       _E("Failed to create pipe. errno(%d)", errno);
153       return;
154     }
155
156     if (fcntl(pipe_fd_[0], F_SETPIPE_SZ, Socket::kSocketMaxBufferSize) == -1)
157       _E("Failed to set pipe size. errno(%d)", errno);
158
159     if (fcntl(pipe_fd_[1], F_SETPIPE_SZ, Socket::kSocketMaxBufferSize) == -1)
160       _E("Failed to set pipe size. errno(%d)", errno);
161
162     pid_t pid = Executor::Execute();
163     if (pid == -1) {
164       _E("Failed to fork process. errno(%d)", errno);
165       close(pipe_fd_[0]);
166       close(pipe_fd_[1]);
167       return;
168     }
169
170     close(pipe_fd_[0]);
171     queue_.push(std::make_shared<Process>(pid, pipe_fd_[1]));
172   }
173 }
174
175 int ProcessPool::WaitForRequest(std::unique_ptr<Socket> socket) {
176   tizen_base::Parcel parcel;
177   int ret = 0;
178   do {
179     size_t data_size = 0;
180     ret = socket->Read(static_cast<void*>(&data_size), sizeof(data_size));
181     if (ret != 0) {
182       _E("Failed to read from socket. error(%d)", ret);
183       return -1;
184     }
185
186     std::vector<uint8_t> data(data_size);
187     ret = socket->Read(data.data(), data.size());
188     if (ret != 0) {
189       _E("Failed to read from socket. error(%d)", ret);
190       return -1;
191     }
192
193     parcel.Write(data.data(), data.size());
194   } while (ret != 0);
195
196   if (event_listener_ != nullptr)
197     event_listener_->OnRequestReceived(&parcel);
198
199   return 0;
200 }
201
202 void ProcessPool::SetTimer() {
203   if (timer_ != 0)
204     return;
205
206   timer_ = g_timeout_add(1000, OnTimeout, this);
207 }
208
209 void ProcessPool::UnsetTimer() {
210   if (timer_ != 0) {
211     g_source_remove(timer_);
212     timer_ = 0;
213   }
214 }
215
216 gboolean ProcessPool::OnTimeout(gpointer user_data) {
217   auto* process_pool = static_cast<ProcessPool*>(user_data);
218   process_pool->PrepareProcess();
219   process_pool->timer_ = 0;
220   return G_SOURCE_REMOVE;
221 }
222
223 }  // namespace launchpad
224