Dispose candidate process of process pool
[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 <errno.h>
20 #include <fcntl.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <sys/types.h>
24 #include <unistd.h>
25
26 #include <utility>
27
28 #include <parcel.hh>
29 #include <procfs.hh>
30
31 #include "launchpad-process-pool/log_private.hh"
32
33 namespace launchpad {
34
35 ProcessPool::ProcessPool(int num_processes, IEvent* event_listener = nullptr)
36     : Executor(this),
37       num_processes_(num_processes),
38       event_listener_(event_listener) {
39   PrepareProcess();
40 }
41
42 ProcessPool::~ProcessPool() {
43   Dispose();
44 }
45
46 bool ProcessPool::IsPrepared() const {
47   return !queue_.empty();
48 }
49
50 pid_t ProcessPool::Execute(std::shared_ptr<AppPacket> app_packet) {
51   SetTimer();
52   if (!IsPrepared())
53     return -1;
54
55   auto process = std::move(queue_.front());
56   queue_.pop();
57   process->Send(std::move(app_packet));
58   return process->GetPid();
59 }
60
61 void ProcessPool::Dispose() {
62   while (!queue_.empty()) {
63     auto process = std::move(queue_.front());
64     queue_.pop();
65     process->Kill();
66     _D("Kill process(%d)", process->GetPid());
67   }
68
69   UnsetTimer();
70 }
71
72 ProcessPool::Process::Process(pid_t pid, int fd)
73     : pid_(pid), socket_(new Socket(fd)) {
74 }
75
76 pid_t ProcessPool::Process::GetPid() const {
77   return pid_;
78 }
79
80 int ProcessPool::Process::Send(std::shared_ptr<AppPacket> app_packet) {
81   _W("Send execution request. process ID: %d", pid_);
82   tizen_base::Parcel parcel;
83   app_packet->WriteToParcel(&parcel);
84   return socket_->Write(parcel.GetData(), parcel.GetDataSize());
85 }
86
87 void ProcessPool::Process::Kill() {
88   socket_->Close();
89   if (kill(pid_, SIGKILL) == -1) {
90     _E("Failed to send kill signal to the process. pid(%d), errno(%d)",
91         pid_, errno);
92   }
93 }
94
95 void ProcessPool::OnExecution() {
96   _W("Candidate Process");
97   Procfs::SetComm(getpid(), "process-pool+");
98   close(pipe_fd_[1]);
99   int ret = WaitForRequest(std::make_unique<Socket>(pipe_fd_[0]));
100   exit(ret);
101 }
102
103 void ProcessPool::PrepareProcess() {
104   int current_process_count = static_cast<int>(queue_.size());
105   for (int i = current_process_count; i < num_processes_; ++i) {
106     pipe_fd_[0] = -1;
107     pipe_fd_[1] = -1;
108     if (pipe(pipe_fd_) == -1) {
109       _E("Failed to create pipe. errno(%d)", errno);
110       return;
111     }
112
113     if (fcntl(pipe_fd_[0], F_SETPIPE_SZ, AppPacket::kMaxPacketSize) == -1)
114       _E("Failed to set pipe size. errno(%d)", errno);
115
116     if (fcntl(pipe_fd_[1], F_SETPIPE_SZ, AppPacket::kMaxPacketSize) == -1)
117       _E("Failed to set pipe size. errno(%d)", errno);
118
119     pid_t pid = Executor::Execute();
120     if (pid == -1) {
121       _E("Failed to fork process. errno(%d)", errno);
122       close(pipe_fd_[0]);
123       close(pipe_fd_[1]);
124       return;
125     }
126
127     close(pipe_fd_[0]);
128     queue_.push(std::make_shared<Process>(pid, pipe_fd_[1]));
129   }
130 }
131
132 int ProcessPool::WaitForRequest(std::unique_ptr<Socket> socket) {
133   auto app_packet = std::make_shared<AppPacket>();
134   int ret = 0;
135   do {
136     char buf[AppPacket::GetHeaderSize()] = { 0, };
137     ret = socket->Read(buf, sizeof(buf));
138     if (ret != 0) {
139       _E("Failed to read from socket. error(%d)", ret);
140       return -1;
141     }
142
143     tizen_base::Parcel parcel(buf, sizeof(buf));
144     app_packet->ReadFromParcel(&parcel);
145
146     std::vector<uint8_t> data;
147     data.resize(app_packet->GetDataSize());
148     ret = socket->Read(data.data(), app_packet->GetDataSize());
149     if (ret != 0) {
150       _E("Failed to read from socket. error(%d)", ret);
151       return -1;
152     }
153
154     parcel.Write(data.data(), data.size());
155     parcel.ResetReader();
156     app_packet->ReadFromParcel(&parcel);
157   } while (ret != 0);
158
159   if (event_listener_ != nullptr)
160     event_listener_->OnRequestReceived(std::move(app_packet));
161
162   return 0;
163 }
164
165 void ProcessPool::SetTimer() {
166   if (timer_ != 0)
167     return;
168
169   timer_ = g_timeout_add(1000, OnTimeout, this);
170 }
171
172 void ProcessPool::UnsetTimer() {
173   if (timer_ != 0) {
174     g_source_remove(timer_);
175     timer_ = 0;
176   }
177 }
178
179 gboolean ProcessPool::OnTimeout(gpointer user_data) {
180   auto* process_pool = static_cast<ProcessPool*>(user_data);
181   process_pool->PrepareProcess();
182   process_pool->timer_ = 0;
183   return G_SOURCE_REMOVE;
184 }
185
186 }  // namespace launchpad
187