1 /*
2  * Copyright (c) 2009-2022, Google LLC
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are met:
7  *     * Redistributions of source code must retain the above copyright
8  *       notice, this list of conditions and the following disclaimer.
9  *     * Redistributions in binary form must reproduce the above copyright
10  *       notice, this list of conditions and the following disclaimer in the
11  *       documentation and/or other materials provided with the distribution.
12  *     * Neither the name of Google LLC nor the
13  *       names of its contributors may be used to endorse or promote products
14  *       derived from this software without specific prior written permission.
15  *
16  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19  * ARE DISCLAIMED. IN NO EVENT SHALL Google LLC BE LIABLE FOR ANY DIRECT,
20  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23  * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26  */
27 
28 // Shamelessly copied from the protobuf compiler's subprocess.cc
29 // except this version passes strings instead of Messages.
30 
31 #include "upbc/subprocess.h"
32 
33 #include <algorithm>
34 #include <cstring>
35 #include <iostream>
36 
37 #ifndef _MSVC_LANG
38 #include <errno.h>
39 #include <signal.h>
40 #include <sys/select.h>
41 #include <sys/wait.h>
42 #endif
43 
44 #include "absl/log/absl_log.h"
45 #include "absl/strings/substitute.h"
46 
47 // Must be last.
48 #include "upb/port/def.inc"
49 
50 namespace upbc {
51 
52 namespace {
portable_strdup(const char * s)53 char* portable_strdup(const char* s) {
54   char* ns = (char*)malloc(strlen(s) + 1);
55   if (ns != nullptr) {
56     strcpy(ns, s);
57   }
58   return ns;
59 }
60 }  // namespace
61 
62 #ifdef _WIN32
63 
CloseHandleOrDie(HANDLE handle)64 static void CloseHandleOrDie(HANDLE handle) {
65   if (!CloseHandle(handle)) {
66     ABSL_LOG(FATAL) << "CloseHandle: "
67                     << Subprocess::Win32ErrorMessage(GetLastError());
68   }
69 }
70 
Subprocess()71 Subprocess::Subprocess()
72     : process_start_error_(ERROR_SUCCESS),
73       child_handle_(nullptr),
74       child_stdin_(nullptr),
75       child_stdout_(nullptr) {}
76 
~Subprocess()77 Subprocess::~Subprocess() {
78   if (child_stdin_ != nullptr) {
79     CloseHandleOrDie(child_stdin_);
80   }
81   if (child_stdout_ != nullptr) {
82     CloseHandleOrDie(child_stdout_);
83   }
84 }
85 
Start(const std::string & program,SearchMode search_mode)86 void Subprocess::Start(const std::string& program, SearchMode search_mode) {
87   // Create the pipes.
88   HANDLE stdin_pipe_read;
89   HANDLE stdin_pipe_write;
90   HANDLE stdout_pipe_read;
91   HANDLE stdout_pipe_write;
92 
93   if (!CreatePipe(&stdin_pipe_read, &stdin_pipe_write, nullptr, 0)) {
94     ABSL_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
95   }
96   if (!CreatePipe(&stdout_pipe_read, &stdout_pipe_write, nullptr, 0)) {
97     ABSL_LOG(FATAL) << "CreatePipe: " << Win32ErrorMessage(GetLastError());
98   }
99 
100   // Make child side of the pipes inheritable.
101   if (!SetHandleInformation(stdin_pipe_read, HANDLE_FLAG_INHERIT,
102                             HANDLE_FLAG_INHERIT)) {
103     ABSL_LOG(FATAL) << "SetHandleInformation: "
104                     << Win32ErrorMessage(GetLastError());
105   }
106   if (!SetHandleInformation(stdout_pipe_write, HANDLE_FLAG_INHERIT,
107                             HANDLE_FLAG_INHERIT)) {
108     ABSL_LOG(FATAL) << "SetHandleInformation: "
109                     << Win32ErrorMessage(GetLastError());
110   }
111 
112   // Setup STARTUPINFO to redirect handles.
113   STARTUPINFOA startup_info;
114   ZeroMemory(&startup_info, sizeof(startup_info));
115   startup_info.cb = sizeof(startup_info);
116   startup_info.dwFlags = STARTF_USESTDHANDLES;
117   startup_info.hStdInput = stdin_pipe_read;
118   startup_info.hStdOutput = stdout_pipe_write;
119   startup_info.hStdError = GetStdHandle(STD_ERROR_HANDLE);
120 
121   if (startup_info.hStdError == INVALID_HANDLE_VALUE) {
122     ABSL_LOG(FATAL) << "GetStdHandle: " << Win32ErrorMessage(GetLastError());
123   }
124 
125   // Invoking cmd.exe allows for '.bat' files from the path as well as '.exe'.
126   // Using a malloc'ed string because CreateProcess() can mutate its second
127   // parameter.
128   char* command_line =
129       portable_strdup(("cmd.exe /c \"" + program + "\"").c_str());
130 
131   // Create the process.
132   PROCESS_INFORMATION process_info;
133 
134   if (CreateProcessA((search_mode == SEARCH_PATH) ? nullptr : program.c_str(),
135                      (search_mode == SEARCH_PATH) ? command_line : nullptr,
136                      nullptr,  // process security attributes
137                      nullptr,  // thread security attributes
138                      TRUE,     // inherit handles?
139                      0,        // obscure creation flags
140                      nullptr,  // environment (inherit from parent)
141                      nullptr,  // current directory (inherit from parent)
142                      &startup_info, &process_info)) {
143     child_handle_ = process_info.hProcess;
144     CloseHandleOrDie(process_info.hThread);
145     child_stdin_ = stdin_pipe_write;
146     child_stdout_ = stdout_pipe_read;
147   } else {
148     process_start_error_ = GetLastError();
149     CloseHandleOrDie(stdin_pipe_write);
150     CloseHandleOrDie(stdout_pipe_read);
151   }
152 
153   CloseHandleOrDie(stdin_pipe_read);
154   CloseHandleOrDie(stdout_pipe_write);
155   free(command_line);
156 }
157 
Communicate(const std::string & input_data,std::string * output_data,std::string * error)158 bool Subprocess::Communicate(const std::string& input_data,
159                              std::string* output_data, std::string* error) {
160   if (process_start_error_ != ERROR_SUCCESS) {
161     *error = Win32ErrorMessage(process_start_error_);
162     return false;
163   }
164 
165   GOOGLE_CHECK(child_handle_ != nullptr) << "Must call Start() first.";
166 
167   int input_pos = 0;
168 
169   while (child_stdout_ != nullptr) {
170     HANDLE handles[2];
171     int handle_count = 0;
172 
173     if (child_stdin_ != nullptr) {
174       handles[handle_count++] = child_stdin_;
175     }
176     if (child_stdout_ != nullptr) {
177       handles[handle_count++] = child_stdout_;
178     }
179 
180     DWORD wait_result =
181         WaitForMultipleObjects(handle_count, handles, FALSE, INFINITE);
182 
183     HANDLE signaled_handle = nullptr;
184     if (wait_result >= WAIT_OBJECT_0 &&
185         wait_result < WAIT_OBJECT_0 + handle_count) {
186       signaled_handle = handles[wait_result - WAIT_OBJECT_0];
187     } else if (wait_result == WAIT_FAILED) {
188       ABSL_LOG(FATAL) << "WaitForMultipleObjects: "
189                       << Win32ErrorMessage(GetLastError());
190     } else {
191       ABSL_LOG(FATAL) << "WaitForMultipleObjects: Unexpected return code: "
192                       << wait_result;
193     }
194 
195     if (signaled_handle == child_stdin_) {
196       DWORD n;
197       if (!WriteFile(child_stdin_, input_data.data() + input_pos,
198                      input_data.size() - input_pos, &n, nullptr)) {
199         // Child closed pipe.  Presumably it will report an error later.
200         // Pretend we're done for now.
201         input_pos = input_data.size();
202       } else {
203         input_pos += n;
204       }
205 
206       if (input_pos == input_data.size()) {
207         // We're done writing.  Close.
208         CloseHandleOrDie(child_stdin_);
209         child_stdin_ = nullptr;
210       }
211     } else if (signaled_handle == child_stdout_) {
212       char buffer[4096];
213       DWORD n;
214 
215       if (!ReadFile(child_stdout_, buffer, sizeof(buffer), &n, nullptr)) {
216         // We're done reading.  Close.
217         CloseHandleOrDie(child_stdout_);
218         child_stdout_ = nullptr;
219       } else {
220         output_data->append(buffer, n);
221       }
222     }
223   }
224 
225   if (child_stdin_ != nullptr) {
226     // Child did not finish reading input before it closed the output.
227     // Presumably it exited with an error.
228     CloseHandleOrDie(child_stdin_);
229     child_stdin_ = nullptr;
230   }
231 
232   DWORD wait_result = WaitForSingleObject(child_handle_, INFINITE);
233 
234   if (wait_result == WAIT_FAILED) {
235     ABSL_LOG(FATAL) << "WaitForSingleObject: "
236                     << Win32ErrorMessage(GetLastError());
237   } else if (wait_result != WAIT_OBJECT_0) {
238     ABSL_LOG(FATAL) << "WaitForSingleObject: Unexpected return code: "
239                     << wait_result;
240   }
241 
242   DWORD exit_code;
243   if (!GetExitCodeProcess(child_handle_, &exit_code)) {
244     ABSL_LOG(FATAL) << "GetExitCodeProcess: "
245                     << Win32ErrorMessage(GetLastError());
246   }
247 
248   CloseHandleOrDie(child_handle_);
249   child_handle_ = nullptr;
250 
251   if (exit_code != 0) {
252     *error = absl::Substitute("Plugin failed with status code $0.", exit_code);
253     return false;
254   }
255 
256   return true;
257 }
258 
Win32ErrorMessage(DWORD error_code)259 std::string Subprocess::Win32ErrorMessage(DWORD error_code) {
260   char* message;
261 
262   // WTF?
263   FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
264                      FORMAT_MESSAGE_IGNORE_INSERTS,
265                  nullptr, error_code,
266                  MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
267                  (LPSTR)&message,  // NOT A BUG!
268                  0, nullptr);
269 
270   std::string result = message;
271   LocalFree(message);
272   return result;
273 }
274 
275 // ===================================================================
276 
277 #else  // _WIN32
278 
Subprocess()279 Subprocess::Subprocess()
280     : child_pid_(-1), child_stdin_(-1), child_stdout_(-1) {}
281 
~Subprocess()282 Subprocess::~Subprocess() {
283   if (child_stdin_ != -1) {
284     close(child_stdin_);
285   }
286   if (child_stdout_ != -1) {
287     close(child_stdout_);
288   }
289 }
290 
Start(const std::string & program,SearchMode search_mode)291 void Subprocess::Start(const std::string& program, SearchMode search_mode) {
292   // Note that we assume that there are no other threads, thus we don't have to
293   // do crazy stuff like using socket pairs or avoiding libc locks.
294 
295   // [0] is read end, [1] is write end.
296   int stdin_pipe[2];
297   int stdout_pipe[2];
298 
299   int p0 = pipe(stdin_pipe);
300   int p1 = pipe(stdout_pipe);
301   UPB_ASSERT(p0 != -1);
302   UPB_ASSERT(p1 != -1);
303 
304   char* argv[2] = {portable_strdup(program.c_str()), nullptr};
305 
306   child_pid_ = fork();
307   if (child_pid_ == -1) {
308     std::cerr << "fork: " << strerror(errno);
309   } else if (child_pid_ == 0) {
310     // We are the child.
311     dup2(stdin_pipe[0], STDIN_FILENO);
312     dup2(stdout_pipe[1], STDOUT_FILENO);
313 
314     close(stdin_pipe[0]);
315     close(stdin_pipe[1]);
316     close(stdout_pipe[0]);
317     close(stdout_pipe[1]);
318 
319     switch (search_mode) {
320       case SEARCH_PATH:
321         execvp(argv[0], argv);
322         break;
323       case EXACT_NAME:
324         execv(argv[0], argv);
325         break;
326     }
327 
328     // Write directly to STDERR_FILENO to avoid stdio code paths that may do
329     // stuff that is unsafe here.
330     int ignored;
331     ignored = write(STDERR_FILENO, argv[0], strlen(argv[0]));
332     const char* message =
333         ": program not found or is not executable\n"
334         "Please specify a program using absolute path or make sure "
335         "the program is available in your PATH system variable\n";
336     ignored = write(STDERR_FILENO, message, strlen(message));
337     (void)ignored;
338 
339     // Must use _exit() rather than exit() to avoid flushing output buffers
340     // that will also be flushed by the parent.
341     _exit(1);
342   } else {
343     free(argv[0]);
344 
345     close(stdin_pipe[0]);
346     close(stdout_pipe[1]);
347 
348     child_stdin_ = stdin_pipe[1];
349     child_stdout_ = stdout_pipe[0];
350   }
351 }
352 
Communicate(const std::string & input_data,std::string * output_data,std::string * error)353 bool Subprocess::Communicate(const std::string& input_data,
354                              std::string* output_data, std::string* error) {
355   if (child_stdin_ == -1) {
356     std::cerr << "Must call Start() first." << std::endl;
357     UPB_ASSERT(child_stdin_ != -1);
358   }
359 
360   // The "sighandler_t" typedef is GNU-specific, so define our own.
361   typedef void SignalHandler(int);
362 
363   // Make sure SIGPIPE is disabled so that if the child dies it doesn't kill us.
364   SignalHandler* old_pipe_handler = signal(SIGPIPE, SIG_IGN);
365 
366   int input_pos = 0;
367   int max_fd = std::max(child_stdin_, child_stdout_);
368 
369   while (child_stdout_ != -1) {
370     fd_set read_fds;
371     fd_set write_fds;
372     FD_ZERO(&read_fds);
373     FD_ZERO(&write_fds);
374     if (child_stdout_ != -1) {
375       FD_SET(child_stdout_, &read_fds);
376     }
377     if (child_stdin_ != -1) {
378       FD_SET(child_stdin_, &write_fds);
379     }
380 
381     if (select(max_fd + 1, &read_fds, &write_fds, nullptr, nullptr) < 0) {
382       if (errno == EINTR) {
383         // Interrupted by signal.  Try again.
384         continue;
385       } else {
386         std::cerr << "select: " << strerror(errno) << std::endl;
387         UPB_ASSERT(0);
388       }
389     }
390 
391     if (child_stdin_ != -1 && FD_ISSET(child_stdin_, &write_fds)) {
392       int n = write(child_stdin_, input_data.data() + input_pos,
393                     input_data.size() - input_pos);
394       if (n < 0) {
395         // Child closed pipe.  Presumably it will report an error later.
396         // Pretend we're done for now.
397         input_pos = input_data.size();
398       } else {
399         input_pos += n;
400       }
401 
402       if (input_pos == (int)input_data.size()) {
403         // We're done writing.  Close.
404         close(child_stdin_);
405         child_stdin_ = -1;
406       }
407     }
408 
409     if (child_stdout_ != -1 && FD_ISSET(child_stdout_, &read_fds)) {
410       char buffer[4096];
411       int n = read(child_stdout_, buffer, sizeof(buffer));
412 
413       if (n > 0) {
414         output_data->append(buffer, (size_t)n);
415       } else {
416         // We're done reading.  Close.
417         close(child_stdout_);
418         child_stdout_ = -1;
419       }
420     }
421   }
422 
423   if (child_stdin_ != -1) {
424     // Child did not finish reading input before it closed the output.
425     // Presumably it exited with an error.
426     close(child_stdin_);
427     child_stdin_ = -1;
428   }
429 
430   int status;
431   while (waitpid(child_pid_, &status, 0) == -1) {
432     if (errno != EINTR) {
433       std::cerr << "waitpid: " << strerror(errno) << std::endl;
434       UPB_ASSERT(0);
435     }
436   }
437 
438   // Restore SIGPIPE handling.
439   signal(SIGPIPE, old_pipe_handler);
440 
441   if (WIFEXITED(status)) {
442     if (WEXITSTATUS(status) != 0) {
443       int error_code = WEXITSTATUS(status);
444       *error =
445           absl::Substitute("Plugin failed with status code $0.", error_code);
446       return false;
447     }
448   } else if (WIFSIGNALED(status)) {
449     int signal = WTERMSIG(status);
450     *error = absl::Substitute("Plugin killed by signal $0.", signal);
451     return false;
452   } else {
453     *error = "Neither WEXITSTATUS nor WTERMSIG is true?";
454     return false;
455   }
456 
457   return true;
458 }
459 
460 #endif  // !_WIN32
461 
462 }  // namespace upbc
463