• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <grpc/support/alloc.h>
25 #include <signal.h>
26 #include <string.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29 
30 #include <iostream>
31 
32 #include "absl/log/check.h"
33 #include "absl/log/log.h"
34 #include "absl/strings/substitute.h"
35 #include "src/core/util/memory.h"
36 #include "src/core/util/strerror.h"
37 #include "src/core/util/subprocess.h"
38 
39 struct gpr_subprocess {
40   int pid;
41   bool joined;
42   int child_stdin_;
43   int child_stdout_;
44 };
45 
gpr_subprocess_binary_extension()46 const char* gpr_subprocess_binary_extension() { return ""; }
47 
gpr_subprocess_create(int argc,const char ** argv)48 gpr_subprocess* gpr_subprocess_create(int argc, const char** argv) {
49   gpr_subprocess* r;
50   int pid;
51   char** exec_args;
52   pid = fork();
53   if (pid == -1) {
54     return nullptr;
55   } else if (pid == 0) {
56     exec_args = static_cast<char**>(
57         gpr_malloc((static_cast<size_t>(argc) + 1) * sizeof(char*)));
58     memcpy(exec_args, argv, static_cast<size_t>(argc) * sizeof(char*));
59     exec_args[argc] = nullptr;
60     execv(exec_args[0], exec_args);
61     // if we reach here, an error has occurred
62     LOG(ERROR) << "execv '" << exec_args[0]
63                << "' failed: " << grpc_core::StrError(errno);
64     _exit(1);
65   } else {
66     r = grpc_core::Zalloc<gpr_subprocess>();
67     r->pid = pid;
68     r->child_stdin_ = -1;
69     r->child_stdout_ = -1;
70     return r;
71   }
72 }
73 
gpr_subprocess_create_with_envp(int argc,const char ** argv,int envc,const char ** envp)74 gpr_subprocess* gpr_subprocess_create_with_envp(int argc, const char** argv,
75                                                 int envc, const char** envp) {
76   gpr_subprocess* r;
77   int pid;
78   char **exec_args, **envp_args;
79   int stdin_pipe[2];
80   int stdout_pipe[2];
81   int p0 = pipe(stdin_pipe);
82   int p1 = pipe(stdout_pipe);
83   CHECK_NE(p0, -1);
84   CHECK_NE(p1, -1);
85   pid = fork();
86   if (pid == -1) {
87     return nullptr;
88   } else if (pid == 0) {
89     dup2(stdin_pipe[0], STDIN_FILENO);
90     dup2(stdout_pipe[1], STDOUT_FILENO);
91     close(stdin_pipe[0]);
92     close(stdin_pipe[1]);
93     close(stdout_pipe[0]);
94     close(stdout_pipe[1]);
95     exec_args = static_cast<char**>(
96         gpr_malloc((static_cast<size_t>(argc) + 1) * sizeof(char*)));
97     memcpy(exec_args, argv, static_cast<size_t>(argc) * sizeof(char*));
98     exec_args[argc] = nullptr;
99     envp_args = static_cast<char**>(
100         gpr_malloc((static_cast<size_t>(envc) + 1) * sizeof(char*)));
101     memcpy(envp_args, envp, static_cast<size_t>(envc) * sizeof(char*));
102     envp_args[envc] = nullptr;
103     execve(exec_args[0], exec_args, envp_args);
104     // if we reach here, an error has occurred
105     LOG(ERROR) << "execvpe '" << exec_args[0]
106                << "' failed: " << grpc_core::StrError(errno);
107     _exit(1);
108   } else {
109     r = grpc_core::Zalloc<gpr_subprocess>();
110     r->pid = pid;
111     close(stdin_pipe[0]);
112     close(stdout_pipe[1]);
113     r->child_stdin_ = stdin_pipe[1];
114     r->child_stdout_ = stdout_pipe[0];
115     return r;
116   }
117 }
118 
gpr_subprocess_communicate(gpr_subprocess * p,std::string & input_data,std::string * output_data,std::string * error)119 bool gpr_subprocess_communicate(gpr_subprocess* p, std::string& input_data,
120                                 std::string* output_data, std::string* error) {
121   typedef void SignalHandler(int);
122 
123   // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
124   SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN);
125 
126   int input_pos = 0;
127   int max_fd = std::max(p->child_stdin_, p->child_stdout_);
128 
129   while (p->child_stdout_ != -1) {
130     fd_set read_fds;
131     fd_set write_fds;
132     FD_ZERO(&read_fds);
133     FD_ZERO(&write_fds);
134     if (p->child_stdout_ != -1) {
135       FD_SET(p->child_stdout_, &read_fds);
136     }
137     if (p->child_stdin_ != -1) {
138       FD_SET(p->child_stdin_, &write_fds);
139     }
140 
141     if (select(max_fd + 1, &read_fds, &write_fds, nullptr, nullptr) < 0) {
142       if (errno == EINTR) {
143         // Interrupted by signal.  Try again.
144         continue;
145       } else {
146         std::cerr << "select: " << strerror(errno) << std::endl;
147         CHECK(0);
148       }
149     }
150 
151     if (p->child_stdin_ != -1 && FD_ISSET(p->child_stdin_, &write_fds)) {
152       int n = write(p->child_stdin_, input_data.data() + input_pos,
153                     input_data.size() - input_pos);
154       if (n < 0) {
155         // Child closed pipe.  Presumably it will report an error later.
156         // Pretend we're done for now.
157         input_pos = input_data.size();
158       } else {
159         input_pos += n;
160       }
161 
162       if (input_pos == static_cast<int>(input_data.size())) {
163         // We're done writing.  Close.
164         close(p->child_stdin_);
165         p->child_stdin_ = -1;
166       }
167     }
168 
169     if (p->child_stdout_ != -1 && FD_ISSET(p->child_stdout_, &read_fds)) {
170       char buffer[4096];
171       int n = read(p->child_stdout_, buffer, sizeof(buffer));
172 
173       if (n > 0) {
174         output_data->append(buffer, static_cast<size_t>(n));
175       } else {
176         // We're done reading.  Close.
177         close(p->child_stdout_);
178         p->child_stdout_ = -1;
179       }
180     }
181   }
182 
183   if (p->child_stdin_ != -1) {
184     // Child did not finish reading input before it closed the output.
185     // Presumably it exited with an error.
186     close(p->child_stdin_);
187     p->child_stdin_ = -1;
188   }
189 
190   int status;
191   while (waitpid(p->pid, &status, 0) == -1) {
192     if (errno != EINTR) {
193       std::cerr << "waitpid: " << strerror(errno) << std::endl;
194       CHECK(0);
195     }
196   }
197 
198   // Restore SIGPIPE handling.
199   signal(SIGPIPE, old_pipe_handler);
200 
201   if (WIFEXITED(status)) {
202     if (WEXITSTATUS(status) != 0) {
203       int error_code = WEXITSTATUS(status);
204       *error =
205           absl::Substitute("Plugin failed with status code $0.", error_code);
206       return false;
207     }
208   } else if (WIFSIGNALED(status)) {
209     int signal = WTERMSIG(status);
210     *error = absl::Substitute("Plugin killed by signal $0.", signal);
211     return false;
212   } else {
213     *error = "Neither WEXITSTATUS nor WTERMSIG is true?";
214     return false;
215   }
216 
217   return true;
218 }
219 
gpr_subprocess_destroy(gpr_subprocess * p)220 void gpr_subprocess_destroy(gpr_subprocess* p) {
221   if (!p->joined) {
222     kill(p->pid, SIGKILL);
223     gpr_subprocess_join(p);
224   }
225   gpr_free(p);
226 }
227 
gpr_subprocess_join(gpr_subprocess * p)228 int gpr_subprocess_join(gpr_subprocess* p) {
229   int status;
230 retry:
231   if (waitpid(p->pid, &status, 0) == -1) {
232     if (errno == EINTR) {
233       goto retry;
234     }
235     LOG(ERROR) << "waitpid failed for pid " << p->pid << ": "
236                << grpc_core::StrError(errno);
237     return -1;
238   }
239   p->joined = true;
240   return status;
241 }
242 
gpr_subprocess_interrupt(gpr_subprocess * p)243 void gpr_subprocess_interrupt(gpr_subprocess* p) {
244   if (!p->joined) {
245     kill(p->pid, SIGINT);
246   }
247 }
248 
gpr_subprocess_get_process_id(gpr_subprocess * p)249 int gpr_subprocess_get_process_id(gpr_subprocess* p) { return p->pid; }
250 
251 #endif  // GPR_POSIX_SUBPROCESS
252