xref: /aosp_15_r20/external/grpc-grpc/src/core/lib/gpr/subprocess_posix.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 //     http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18 
19 #include <grpc/support/port_platform.h>
20 
21 #ifdef GPR_POSIX_SUBPROCESS
22 
23 #include <errno.h>
24 #include <signal.h>
25 #include <string.h>
26 #include <sys/wait.h>
27 #include <unistd.h>
28 
29 #include <iostream>
30 
31 #include "absl/strings/substitute.h"
32 
33 #include <grpc/support/alloc.h>
34 #include <grpc/support/log.h>
35 
36 #include "src/core/lib/gpr/subprocess.h"
37 #include "src/core/lib/gprpp/memory.h"
38 #include "src/core/lib/gprpp/strerror.h"
39 
40 struct gpr_subprocess {
41   int pid;
42   bool joined;
43   int child_stdin_;
44   int child_stdout_;
45 };
46 
gpr_subprocess_binary_extension()47 const char* gpr_subprocess_binary_extension() { return ""; }
48 
gpr_subprocess_create(int argc,const char ** argv)49 gpr_subprocess* gpr_subprocess_create(int argc, const char** argv) {
50   gpr_subprocess* r;
51   int pid;
52   char** exec_args;
53   pid = fork();
54   if (pid == -1) {
55     return nullptr;
56   } else if (pid == 0) {
57     exec_args = static_cast<char**>(
58         gpr_malloc((static_cast<size_t>(argc) + 1) * sizeof(char*)));
59     memcpy(exec_args, argv, static_cast<size_t>(argc) * sizeof(char*));
60     exec_args[argc] = nullptr;
61     execv(exec_args[0], exec_args);
62     // if we reach here, an error has occurred
63     gpr_log(GPR_ERROR, "execv '%s' failed: %s", exec_args[0],
64             grpc_core::StrError(errno).c_str());
65     _exit(1);
66   } else {
67     r = grpc_core::Zalloc<gpr_subprocess>();
68     r->pid = pid;
69     r->child_stdin_ = -1;
70     r->child_stdout_ = -1;
71     return r;
72   }
73 }
74 
gpr_subprocess_create_with_envp(int argc,const char ** argv,int envc,const char ** envp)75 gpr_subprocess* gpr_subprocess_create_with_envp(int argc, const char** argv,
76                                                 int envc, const char** envp) {
77   gpr_subprocess* r;
78   int pid;
79   char **exec_args, **envp_args;
80   int stdin_pipe[2];
81   int stdout_pipe[2];
82   int p0 = pipe(stdin_pipe);
83   int p1 = pipe(stdout_pipe);
84   GPR_ASSERT(p0 != -1);
85   GPR_ASSERT(p1 != -1);
86   pid = fork();
87   if (pid == -1) {
88     return nullptr;
89   } else if (pid == 0) {
90     dup2(stdin_pipe[0], STDIN_FILENO);
91     dup2(stdout_pipe[1], STDOUT_FILENO);
92     close(stdin_pipe[0]);
93     close(stdin_pipe[1]);
94     close(stdout_pipe[0]);
95     close(stdout_pipe[1]);
96     exec_args = static_cast<char**>(
97         gpr_malloc((static_cast<size_t>(argc) + 1) * sizeof(char*)));
98     memcpy(exec_args, argv, static_cast<size_t>(argc) * sizeof(char*));
99     exec_args[argc] = nullptr;
100     envp_args = static_cast<char**>(
101         gpr_malloc((static_cast<size_t>(envc) + 1) * sizeof(char*)));
102     memcpy(envp_args, envp, static_cast<size_t>(envc) * sizeof(char*));
103     envp_args[envc] = nullptr;
104     execve(exec_args[0], exec_args, envp_args);
105     // if we reach here, an error has occurred
106     gpr_log(GPR_ERROR, "execvpe '%s' failed: %s", exec_args[0],
107             grpc_core::StrError(errno).c_str());
108     _exit(1);
109   } else {
110     r = grpc_core::Zalloc<gpr_subprocess>();
111     r->pid = pid;
112     close(stdin_pipe[0]);
113     close(stdout_pipe[1]);
114     r->child_stdin_ = stdin_pipe[1];
115     r->child_stdout_ = stdout_pipe[0];
116     return r;
117   }
118 }
119 
gpr_subprocess_communicate(gpr_subprocess * p,std::string & input_data,std::string * output_data,std::string * error)120 bool gpr_subprocess_communicate(gpr_subprocess* p, std::string& input_data,
121                                 std::string* output_data, std::string* error) {
122   typedef void SignalHandler(int);
123 
124   // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
125   SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN);
126 
127   int input_pos = 0;
128   int max_fd = std::max(p->child_stdin_, p->child_stdout_);
129 
130   while (p->child_stdout_ != -1) {
131     fd_set read_fds;
132     fd_set write_fds;
133     FD_ZERO(&read_fds);
134     FD_ZERO(&write_fds);
135     if (p->child_stdout_ != -1) {
136       FD_SET(p->child_stdout_, &read_fds);
137     }
138     if (p->child_stdin_ != -1) {
139       FD_SET(p->child_stdin_, &write_fds);
140     }
141 
142     if (select(max_fd + 1, &read_fds, &write_fds, nullptr, nullptr) < 0) {
143       if (errno == EINTR) {
144         // Interrupted by signal.  Try again.
145         continue;
146       } else {
147         std::cerr << "select: " << strerror(errno) << std::endl;
148         GPR_ASSERT(0);
149       }
150     }
151 
152     if (p->child_stdin_ != -1 && FD_ISSET(p->child_stdin_, &write_fds)) {
153       int n = write(p->child_stdin_, input_data.data() + input_pos,
154                     input_data.size() - input_pos);
155       if (n < 0) {
156         // Child closed pipe.  Presumably it will report an error later.
157         // Pretend we're done for now.
158         input_pos = input_data.size();
159       } else {
160         input_pos += n;
161       }
162 
163       if (input_pos == static_cast<int>(input_data.size())) {
164         // We're done writing.  Close.
165         close(p->child_stdin_);
166         p->child_stdin_ = -1;
167       }
168     }
169 
170     if (p->child_stdout_ != -1 && FD_ISSET(p->child_stdout_, &read_fds)) {
171       char buffer[4096];
172       int n = read(p->child_stdout_, buffer, sizeof(buffer));
173 
174       if (n > 0) {
175         output_data->append(buffer, static_cast<size_t>(n));
176       } else {
177         // We're done reading.  Close.
178         close(p->child_stdout_);
179         p->child_stdout_ = -1;
180       }
181     }
182   }
183 
184   if (p->child_stdin_ != -1) {
185     // Child did not finish reading input before it closed the output.
186     // Presumably it exited with an error.
187     close(p->child_stdin_);
188     p->child_stdin_ = -1;
189   }
190 
191   int status;
192   while (waitpid(p->pid, &status, 0) == -1) {
193     if (errno != EINTR) {
194       std::cerr << "waitpid: " << strerror(errno) << std::endl;
195       GPR_ASSERT(0);
196     }
197   }
198 
199   // Restore SIGPIPE handling.
200   signal(SIGPIPE, old_pipe_handler);
201 
202   if (WIFEXITED(status)) {
203     if (WEXITSTATUS(status) != 0) {
204       int error_code = WEXITSTATUS(status);
205       *error =
206           absl::Substitute("Plugin failed with status code $0.", error_code);
207       return false;
208     }
209   } else if (WIFSIGNALED(status)) {
210     int signal = WTERMSIG(status);
211     *error = absl::Substitute("Plugin killed by signal $0.", signal);
212     return false;
213   } else {
214     *error = "Neither WEXITSTATUS nor WTERMSIG is true?";
215     return false;
216   }
217 
218   return true;
219 }
220 
gpr_subprocess_destroy(gpr_subprocess * p)221 void gpr_subprocess_destroy(gpr_subprocess* p) {
222   if (!p->joined) {
223     kill(p->pid, SIGKILL);
224     gpr_subprocess_join(p);
225   }
226   gpr_free(p);
227 }
228 
gpr_subprocess_join(gpr_subprocess * p)229 int gpr_subprocess_join(gpr_subprocess* p) {
230   int status;
231 retry:
232   if (waitpid(p->pid, &status, 0) == -1) {
233     if (errno == EINTR) {
234       goto retry;
235     }
236     gpr_log(GPR_ERROR, "waitpid failed for pid %d: %s", p->pid,
237             grpc_core::StrError(errno).c_str());
238     return -1;
239   }
240   p->joined = true;
241   return status;
242 }
243 
gpr_subprocess_interrupt(gpr_subprocess * p)244 void gpr_subprocess_interrupt(gpr_subprocess* p) {
245   if (!p->joined) {
246     kill(p->pid, SIGINT);
247   }
248 }
249 
gpr_subprocess_get_process_id(gpr_subprocess * p)250 int gpr_subprocess_get_process_id(gpr_subprocess* p) { return p->pid; }
251 
252 #endif  // GPR_POSIX_SUBPROCESS
253