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