Line data Source code
1 : /*!
2 :
3 : Documentation for C++ subprocessing libraray.
4 :
5 : @copyright The code is licensed under the [MIT
6 : License](http://opensource.org/licenses/MIT):
7 : <br>
8 : Copyright © 2016-2018 Arun Muralidharan.
9 : <br>
10 : Permission is hereby granted, free of charge, to any person obtaining a copy
11 : of this software and associated documentation files (the "Software"), to deal
12 : in the Software without restriction, including without limitation the rights
13 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 : copies of the Software, and to permit persons to whom the Software is
15 : furnished to do so, subject to the following conditions:
16 : <br>
17 : The above copyright notice and this permission notice shall be included in
18 : all copies or substantial portions of the Software.
19 : <br>
20 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 : SOFTWARE.
27 :
28 : @author [Arun Muralidharan]
29 : @see https://github.com/arun11299/cpp-subprocess to download the source code
30 :
31 : @version 1.0.0
32 : */
33 :
34 : #ifndef SUBPROCESS_HPP
35 : #define SUBPROCESS_HPP
36 :
37 : #include <algorithm>
38 : #include <cassert>
39 : #include <csignal>
40 : #include <cstdio>
41 : #include <cstdlib>
42 : #include <cstring>
43 : #include <exception>
44 : #include <future>
45 : #include <initializer_list>
46 : #include <iostream>
47 : #include <locale>
48 : #include <map>
49 : #include <memory>
50 : #include <sstream>
51 : #include <string>
52 : #include <vector>
53 :
54 : #if (defined _MSC_VER) || (defined __MINGW32__)
55 : #define __USING_WINDOWS__
56 : #endif
57 :
58 : #ifdef __USING_WINDOWS__
59 : #include <codecvt>
60 : #endif
61 :
62 : extern "C" {
63 : #ifdef __USING_WINDOWS__
64 : #include <Windows.h>
65 : #include <io.h>
66 : #include <cwchar>
67 :
68 : #define close _close
69 : #define open _open
70 : #define fileno _fileno
71 : #else
72 : #include <sys/wait.h>
73 : #include <unistd.h>
74 : #endif
75 : #include <csignal>
76 : #include <fcntl.h>
77 : #include <sys/types.h>
78 : }
79 :
80 : /*!
81 : * Getting started with reading this source code.
82 : * The source is mainly divided into four parts:
83 : * 1. Exception Classes:
84 : * These are very basic exception classes derived from
85 : * runtime_error exception.
86 : * There are two types of exception thrown from subprocess
87 : * library: OSError and CalledProcessError
88 : *
89 : * 2. Popen Class
90 : * This is the main class the users will deal with. It
91 : * provides with all the API's to deal with processes.
92 : *
93 : * 3. Util namespace
94 : * It includes some helper functions to split/join a string,
95 : * reading from file descriptors, waiting on a process, fcntl
96 : * options on file descriptors etc.
97 : *
98 : * 4. Detail namespace
99 : * This includes some metaprogramming and helper classes.
100 : */
101 :
102 :
103 : namespace subprocess {
104 :
105 : // Max buffer size allocated on stack for read error
106 : // from pipe
107 : static const size_t SP_MAX_ERR_BUF_SIZ = 1024;
108 :
109 : // Default buffer capcity for OutBuffer and ErrBuffer.
110 : // If the data exceeds this capacity, the buffer size is grown
111 : // by 1.5 times its previous capacity
112 : static const size_t DEFAULT_BUF_CAP_BYTES = 8192;
113 :
114 :
115 : /*-----------------------------------------------
116 : * EXCEPTION CLASSES
117 : *-----------------------------------------------
118 : */
119 :
120 : /*!
121 : * class: CalledProcessError
122 : * Thrown when there was error executing the command.
123 : * Check Popen class API's to know when this exception
124 : * can be thrown.
125 : *
126 : */
127 : class CalledProcessError: public std::runtime_error
128 : {
129 : public:
130 : int retcode;
131 6 : CalledProcessError(const std::string& error_msg, int retcode):
132 6 : std::runtime_error(error_msg), retcode(retcode)
133 9 : {}
134 : };
135 :
136 :
137 : /*!
138 : * class: OSError
139 : * Thrown when some system call fails to execute or give result.
140 : * The exception message contains the name of the failed system call
141 : * with the stringisized errno code.
142 : * Check Popen class API's to know when this exception would be
143 : * thrown.
144 : * Its usual that the API exception specification would have
145 : * this exception together with CalledProcessError.
146 : */
147 : class OSError: public std::runtime_error
148 : {
149 : public:
150 0 : OSError(const std::string& err_msg, int err_code):
151 0 : std::runtime_error( err_msg + ": " + std::strerror(err_code) )
152 0 : {}
153 : };
154 :
155 : //--------------------------------------------------------------------
156 :
157 : //Environment Variable types
158 : #ifndef _MSC_VER
159 : using env_string_t = std::string;
160 : using env_char_t = char;
161 : #else
162 : using env_string_t = std::wstring;
163 : using env_char_t = wchar_t;
164 : #endif
165 : using env_map_t = std::map<env_string_t, env_string_t>;
166 : using env_vector_t = std::vector<env_char_t>;
167 :
168 : //--------------------------------------------------------------------
169 : namespace util
170 : {
171 : template <typename R>
172 : inline bool is_ready(std::shared_future<R> const &f)
173 : {
174 : return f.wait_for(std::chrono::seconds(0)) == std::future_status::ready;
175 : }
176 :
177 : inline void quote_argument(const std::wstring &argument, std::wstring &command_line,
178 : bool force)
179 : {
180 : //
181 : // Unless we're told otherwise, don't quote unless we actually
182 : // need to do so --- hopefully avoid problems if programs won't
183 : // parse quotes properly
184 : //
185 :
186 : if (force == false && argument.empty() == false &&
187 : argument.find_first_of(L" \t\n\v\"") == argument.npos) {
188 : command_line.append(argument);
189 : }
190 : else {
191 : command_line.push_back(L'"');
192 :
193 : for (auto it = argument.begin();; ++it) {
194 : unsigned number_backslashes = 0;
195 :
196 : while (it != argument.end() && *it == L'\\') {
197 : ++it;
198 : ++number_backslashes;
199 : }
200 :
201 : if (it == argument.end()) {
202 :
203 : //
204 : // Escape all backslashes, but let the terminating
205 : // double quotation mark we add below be interpreted
206 : // as a metacharacter.
207 : //
208 :
209 : command_line.append(number_backslashes * 2, L'\\');
210 : break;
211 : }
212 : else if (*it == L'"') {
213 :
214 : //
215 : // Escape all backslashes and the following
216 : // double quotation mark.
217 : //
218 :
219 : command_line.append(number_backslashes * 2 + 1, L'\\');
220 : command_line.push_back(*it);
221 : }
222 : else {
223 :
224 : //
225 : // Backslashes aren't special here.
226 : //
227 :
228 : command_line.append(number_backslashes, L'\\');
229 : command_line.push_back(*it);
230 : }
231 : }
232 :
233 : command_line.push_back(L'"');
234 : }
235 : }
236 :
237 : #ifdef __USING_WINDOWS__
238 : inline std::string get_last_error(DWORD errorMessageID)
239 : {
240 : if (errorMessageID == 0)
241 : return std::string();
242 :
243 : LPSTR messageBuffer = nullptr;
244 : size_t size = FormatMessageA(
245 : FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
246 : FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_MAX_WIDTH_MASK,
247 : NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
248 : (LPSTR)&messageBuffer, 0, NULL);
249 :
250 : std::string message(messageBuffer, size);
251 :
252 : LocalFree(messageBuffer);
253 :
254 : return message;
255 : }
256 :
257 : inline FILE *file_from_handle(HANDLE h, const char *mode)
258 : {
259 : int md;
260 : if (!mode) {
261 : throw OSError("invalid_mode", 0);
262 : }
263 :
264 : if (mode[0] == 'w') {
265 : md = _O_WRONLY;
266 : }
267 : else if (mode[0] == 'r') {
268 : md = _O_RDONLY;
269 : }
270 : else {
271 : throw OSError("file_from_handle", 0);
272 : }
273 :
274 : int os_fhandle = _open_osfhandle((intptr_t)h, md);
275 : if (os_fhandle == -1) {
276 : CloseHandle(h);
277 : throw OSError("_open_osfhandle", 0);
278 : }
279 :
280 : FILE *fp = _fdopen(os_fhandle, mode);
281 : if (fp == 0) {
282 : _close(os_fhandle);
283 : throw OSError("_fdopen", 0);
284 : }
285 :
286 : return fp;
287 : }
288 :
289 : inline void configure_pipe(HANDLE* read_handle, HANDLE* write_handle, HANDLE* child_handle)
290 : {
291 : SECURITY_ATTRIBUTES saAttr;
292 :
293 : // Set the bInheritHandle flag so pipe handles are inherited.
294 : saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
295 : saAttr.bInheritHandle = TRUE;
296 : saAttr.lpSecurityDescriptor = NULL;
297 :
298 : // Create a pipe for the child process's STDIN.
299 : if (!CreatePipe(read_handle, write_handle, &saAttr,0))
300 : throw OSError("CreatePipe", 0);
301 :
302 : // Ensure the write handle to the pipe for STDIN is not inherited.
303 : if (!SetHandleInformation(*child_handle, HANDLE_FLAG_INHERIT, 0))
304 : throw OSError("SetHandleInformation", 0);
305 : }
306 :
307 : // env_map_t MapFromWindowsEnvironment()
308 : // * Imports current Environment in a C-string table
309 : // * Parses the strings by splitting on the first "=" per line
310 : // * Creates a map of the variables
311 : // * Returns the map
312 : inline env_map_t MapFromWindowsEnvironment(){
313 : wchar_t *variable_strings_ptr;
314 : wchar_t *environment_strings_ptr;
315 : std::wstring delimeter(L"=");
316 : int del_len = delimeter.length();
317 : env_map_t mapped_environment;
318 :
319 : // Get a pointer to the environment block.
320 : environment_strings_ptr = GetEnvironmentStringsW();
321 : // If the returned pointer is NULL, exit.
322 : if (environment_strings_ptr == NULL)
323 : {
324 : throw OSError("GetEnvironmentStringsW", 0);
325 : }
326 :
327 : // Variable strings are separated by NULL byte, and the block is
328 : // terminated by a NULL byte.
329 :
330 : variable_strings_ptr = (wchar_t *) environment_strings_ptr;
331 :
332 : //Since the environment map ends with a null, we can loop until we find it.
333 : while (*variable_strings_ptr)
334 : {
335 : // Create a string from Variable String
336 : env_string_t current_line(variable_strings_ptr);
337 : // Find the first "equals" sign.
338 : auto pos = current_line.find(delimeter);
339 : // Assuming it's not missing ...
340 : if(pos!=std::wstring::npos){
341 : // ... parse the key and value.
342 : env_string_t key = current_line.substr(0, pos);
343 : env_string_t value = current_line.substr(pos + del_len);
344 : // Map the entry.
345 : mapped_environment[key] = value;
346 : }
347 : // Jump to next line in the environment map.
348 : variable_strings_ptr += std::wcslen(variable_strings_ptr) + 1;
349 : }
350 : // We're done with the old environment map buffer.
351 : FreeEnvironmentStringsW(environment_strings_ptr);
352 :
353 : // Return the map.
354 : return mapped_environment;
355 : }
356 :
357 : // env_vector_t WindowsEnvironmentVectorFromMap(const env_map_t &source_map)
358 : // * Creates a vector buffer for the new environment string table
359 : // * Copies in the mapped variables
360 : // * Returns the vector
361 : inline env_vector_t WindowsEnvironmentVectorFromMap(const env_map_t &source_map)
362 : {
363 : // Make a new environment map buffer.
364 : env_vector_t environment_map_buffer;
365 : // Give it some space.
366 : environment_map_buffer.reserve(4096);
367 :
368 : // And fill'er up.
369 : for(auto kv: source_map){
370 : // Create the line
371 : env_string_t current_line(kv.first); current_line += L"="; current_line += kv.second;
372 : // Add the line to the buffer.
373 : std::copy(current_line.begin(), current_line.end(), std::back_inserter(environment_map_buffer));
374 : // Append a null
375 : environment_map_buffer.push_back(0);
376 : }
377 : // Append one last null because of how Windows does it's environment maps.
378 : environment_map_buffer.push_back(0);
379 :
380 : return environment_map_buffer;
381 : }
382 :
383 : // env_vector_t CreateUpdatedWindowsEnvironmentVector(const env_map_t &changes_map)
384 : // * Merges host environment with new mapped variables
385 : // * Creates and returns string vector based on map
386 : inline env_vector_t CreateUpdatedWindowsEnvironmentVector(const env_map_t &changes_map){
387 : // Import the environment map
388 : env_map_t environment_map = MapFromWindowsEnvironment();
389 : // Merge in the changes with overwrite
390 : for(auto& it: changes_map)
391 : {
392 : environment_map[it.first] = it.second;
393 : }
394 : // Create a Windows-usable Environment Map Buffer
395 : env_vector_t environment_map_strings_vector = WindowsEnvironmentVectorFromMap(environment_map);
396 :
397 : return environment_map_strings_vector;
398 : }
399 :
400 : #endif
401 :
402 : /*!
403 : * Function: split
404 : * Parameters:
405 : * [in] str : Input string which needs to be split based upon the
406 : * delimiters provided.
407 : * [in] deleims : Delimiter characters based upon which the string needs
408 : * to be split. Default constructed to ' '(space) and '\t'(tab)
409 : * [out] vector<string> : Vector of strings split at deleimiter.
410 : */
411 : static inline std::vector<std::string>
412 38 : split(const std::string& str, const std::string& delims=" \t")
413 : {
414 38 : std::vector<std::string> res;
415 38 : size_t init = 0;
416 :
417 126 : while (true) {
418 126 : auto pos = str.find_first_of(delims, init);
419 126 : if (pos == std::string::npos) {
420 38 : res.emplace_back(str.substr(init, str.length()));
421 38 : break;
422 : }
423 88 : res.emplace_back(str.substr(init, pos - init));
424 88 : pos++;
425 88 : init = pos;
426 : }
427 :
428 38 : return res;
429 38 : }
430 :
431 :
432 : /*!
433 : * Function: join
434 : * Parameters:
435 : * [in] vec : Vector of strings which needs to be joined to form
436 : * a single string with words seperated by a seperator char.
437 : * [in] sep : String used to seperate 2 words in the joined string.
438 : * Default constructed to ' ' (space).
439 : * [out] string: Joined string.
440 : */
441 : static inline
442 0 : std::string join(const std::vector<std::string>& vec,
443 : const std::string& sep = " ")
444 : {
445 0 : std::string res;
446 0 : for (auto& elem : vec) res.append(elem + sep);
447 0 : res.erase(--res.end());
448 0 : return res;
449 0 : }
450 :
451 :
452 : #ifndef __USING_WINDOWS__
453 : /*!
454 : * Function: set_clo_on_exec
455 : * Sets/Resets the FD_CLOEXEC flag on the provided file descriptor
456 : * based upon the `set` parameter.
457 : * Parameters:
458 : * [in] fd : The descriptor on which FD_CLOEXEC needs to be set/reset.
459 : * [in] set : If 'true', set FD_CLOEXEC.
460 : * If 'false' unset FD_CLOEXEC.
461 : */
462 : static inline
463 304 : void set_clo_on_exec(int fd, bool set = true)
464 : {
465 304 : int flags = fcntl(fd, F_GETFD, 0);
466 304 : if (set) flags |= FD_CLOEXEC;
467 0 : else flags &= ~FD_CLOEXEC;
468 : //TODO: should check for errors
469 304 : fcntl(fd, F_SETFD, flags);
470 304 : }
471 :
472 :
473 : /*!
474 : * Function: pipe_cloexec
475 : * Creates a pipe and sets FD_CLOEXEC flag on both
476 : * read and write descriptors of the pipe.
477 : * Parameters:
478 : * [out] : A pair of file descriptors.
479 : * First element of pair is the read descriptor of pipe.
480 : * Second element is the write descriptor of pipe.
481 : */
482 : static inline
483 152 : std::pair<int, int> pipe_cloexec() noexcept(false)
484 : {
485 : int pipe_fds[2];
486 152 : int res = pipe(pipe_fds);
487 152 : if (res) {
488 0 : throw OSError("pipe failure", errno);
489 : }
490 :
491 152 : set_clo_on_exec(pipe_fds[0]);
492 152 : set_clo_on_exec(pipe_fds[1]);
493 :
494 152 : return std::make_pair(pipe_fds[0], pipe_fds[1]);
495 0 : }
496 : #endif
497 :
498 :
499 : /*!
500 : * Function: write_n
501 : * Writes `length` bytes to the file descriptor `fd`
502 : * from the buffer `buf`.
503 : * Parameters:
504 : * [in] fd : The file descriptotr to write to.
505 : * [in] buf: Buffer from which data needs to be written to fd.
506 : * [in] length: The number of bytes that needs to be written from
507 : * `buf` to `fd`.
508 : * [out] int : Number of bytes written or -1 in case of failure.
509 : */
510 : static inline
511 0 : int write_n(int fd, const char* buf, size_t length)
512 : {
513 0 : size_t nwritten = 0;
514 0 : while (nwritten < length) {
515 0 : int written = write(fd, buf + nwritten, length - nwritten);
516 0 : if (written == -1) return -1;
517 0 : nwritten += written;
518 : }
519 0 : return nwritten;
520 0 : }
521 :
522 :
523 : /*!
524 : * Function: read_atmost_n
525 : * Reads at the most `read_upto` bytes from the
526 : * file object `fp` before returning.
527 : * Parameters:
528 : * [in] fp : The file object from which it needs to read.
529 : * [in] buf : The buffer into which it needs to write the data.
530 : * [in] read_upto: Max number of bytes which must be read from `fd`.
531 : * [out] int : Number of bytes written to `buf` or read from `fd`
532 : * OR -1 in case of error.
533 : * NOTE: In case of EINTR while reading from socket, this API
534 : * will retry to read from `fd`, but only till the EINTR counter
535 : * reaches 50 after which it will return with whatever data it read.
536 : */
537 : static inline
538 108 : int read_atmost_n(FILE* fp, char* buf, size_t read_upto)
539 : {
540 : #ifdef __USING_WINDOWS__
541 : return (int)fread(buf, 1, read_upto, fp);
542 : #else
543 108 : int fd = fileno(fp);
544 108 : int rbytes = 0;
545 108 : int eintr_cnter = 0;
546 :
547 141 : while (1) {
548 141 : int read_bytes = read(fd, buf + rbytes, read_upto - rbytes);
549 141 : if (read_bytes == -1) {
550 0 : if (errno == EINTR) {
551 0 : if (eintr_cnter >= 50) return -1;
552 0 : eintr_cnter++;
553 0 : continue;
554 : }
555 0 : return -1;
556 : }
557 126 : if (read_bytes == 0) return rbytes;
558 :
559 33 : rbytes += read_bytes;
560 : }
561 : return rbytes;
562 : #endif
563 93 : }
564 :
565 :
566 : /*!
567 : * Function: read_all
568 : * Reads all the available data from `fp` into
569 : * `buf`. Internally calls read_atmost_n.
570 : * Parameters:
571 : * [in] fp : The file object from which to read from.
572 : * [in] buf : The buffer of type `class Buffer` into which
573 : * the read data is written to.
574 : * [out] int: Number of bytes read OR -1 in case of failure.
575 : *
576 : * NOTE: `class Buffer` is a exposed public class. See below.
577 : */
578 :
579 70 : static inline int read_all(FILE* fp, std::vector<char>& buf)
580 : {
581 70 : auto buffer = buf.data();
582 70 : int total_bytes_read = 0;
583 70 : int fill_sz = buf.size();
584 :
585 70 : while (1) {
586 70 : const int rd_bytes = read_atmost_n(fp, buffer, fill_sz);
587 :
588 70 : if (rd_bytes == -1) { // Read finished
589 0 : if (total_bytes_read == 0) return -1;
590 0 : break;
591 :
592 65 : } else if (rd_bytes == fill_sz) { // Buffer full
593 0 : const auto orig_sz = buf.size();
594 0 : const auto new_sz = orig_sz * 2;
595 0 : buf.resize(new_sz);
596 0 : fill_sz = new_sz - orig_sz;
597 :
598 : //update the buffer pointer
599 0 : buffer = buf.data();
600 0 : total_bytes_read += rd_bytes;
601 0 : buffer += total_bytes_read;
602 :
603 0 : } else { // Partial data ? Continue reading
604 66 : total_bytes_read += rd_bytes;
605 66 : fill_sz -= rd_bytes;
606 66 : break;
607 : }
608 : }
609 59 : buf.erase(buf.begin()+total_bytes_read, buf.end()); // remove extra nulls
610 59 : return total_bytes_read;
611 59 : }
612 :
613 : #ifndef __USING_WINDOWS__
614 : /*!
615 : * Function: wait_for_child_exit
616 : * Waits for the process with pid `pid` to exit
617 : * and returns its status.
618 : * Parameters:
619 : * [in] pid : The pid of the process.
620 : * [out] pair<int, int>:
621 : * pair.first : Return code of the waitpid call.
622 : * pair.second : Exit status of the process.
623 : *
624 : * NOTE: This is a blocking call as in, it will loop
625 : * till the child is exited.
626 : */
627 : static inline
628 38 : std::pair<int, int> wait_for_child_exit(int pid)
629 : {
630 38 : int status = 0;
631 38 : int ret = -1;
632 38 : while (1) {
633 38 : ret = waitpid(pid, &status, 0);
634 38 : if (ret == -1) break;
635 38 : if (ret == 0) continue;
636 38 : return std::make_pair(ret, status);
637 : }
638 :
639 0 : return std::make_pair(ret, status);
640 38 : }
641 : #endif
642 :
643 : } // end namespace util
644 :
645 :
646 :
647 : /* -------------------------------
648 : * Popen Arguments
649 : * -------------------------------
650 : */
651 :
652 : /*!
653 : * The buffer size of the stdin/stdout/stderr
654 : * streams of the child process.
655 : * Default value is 0.
656 : */
657 : struct bufsize {
658 : explicit bufsize(int siz): bufsiz(siz) {}
659 : int bufsiz = 0;
660 : };
661 :
662 : /*!
663 : * Option to defer spawning of the child process
664 : * till `Popen::start_process` API is called.
665 : * Default value is false.
666 : */
667 : struct defer_spawn {
668 : explicit defer_spawn(bool d): defer(d) {}
669 : bool defer = false;
670 : };
671 :
672 : /*!
673 : * Option to close all file descriptors
674 : * when the child process is spawned.
675 : * The close fd list does not include
676 : * input/output/error if they are explicitly
677 : * set as part of the Popen arguments.
678 : *
679 : * Default value is false.
680 : */
681 : struct close_fds {
682 : explicit close_fds(bool c): close_all(c) {}
683 : bool close_all = false;
684 : };
685 :
686 : /*!
687 : * Option to make the child process as the
688 : * session leader and thus the process
689 : * group leader.
690 : * Default value is false.
691 : */
692 : struct session_leader {
693 : explicit session_leader(bool sl): leader_(sl) {}
694 : bool leader_ = false;
695 : };
696 :
697 : struct shell {
698 : explicit shell(bool s): shell_(s) {}
699 : bool shell_ = false;
700 : };
701 :
702 : /*!
703 : * Base class for all arguments involving string value.
704 : */
705 : struct string_arg
706 : {
707 : string_arg(const char* arg): arg_value(arg) {}
708 : string_arg(std::string&& arg): arg_value(std::move(arg)) {}
709 : string_arg(std::string arg): arg_value(std::move(arg)) {}
710 : std::string arg_value;
711 : };
712 :
713 : /*!
714 : * Option to specify the executable name seperately
715 : * from the args sequence.
716 : * In this case the cmd args must only contain the
717 : * options required for this executable.
718 : *
719 : * Eg: executable{"ls"}
720 : */
721 : struct executable: string_arg
722 : {
723 : template <typename T>
724 : executable(T&& arg): string_arg(std::forward<T>(arg)) {}
725 : };
726 :
727 : /*!
728 : * Option to set the current working directory
729 : * of the spawned process.
730 : *
731 : * Eg: cwd{"/som/path/x"}
732 : */
733 : struct cwd: string_arg
734 : {
735 : template <typename T>
736 : cwd(T&& arg): string_arg(std::forward<T>(arg)) {}
737 : };
738 :
739 : /*!
740 : * Option to specify environment variables required by
741 : * the spawned process.
742 : *
743 : * Eg: environment{{ {"K1", "V1"}, {"K2", "V2"},... }}
744 : */
745 : struct environment
746 : {
747 : environment(env_map_t&& env):
748 : env_(std::move(env)) {}
749 : explicit environment(const env_map_t& env):
750 : env_(env) {}
751 : env_map_t env_;
752 : };
753 :
754 :
755 : /*!
756 : * Used for redirecting input/output/error
757 : */
758 : enum IOTYPE {
759 : STDOUT = 1,
760 : STDERR,
761 : PIPE,
762 : };
763 :
764 : //TODO: A common base/interface for below stream structures ??
765 :
766 : /*!
767 : * Option to specify the input channel for the child
768 : * process. It can be:
769 : * 1. An already open file descriptor.
770 : * 2. A file name.
771 : * 3. IOTYPE. Usual a PIPE
772 : *
773 : * Eg: input{PIPE}
774 : * OR in case of redirection, output of another Popen
775 : * input{popen.output()}
776 : */
777 : struct input
778 : {
779 : // For an already existing file descriptor.
780 : explicit input(int fd): rd_ch_(fd) {}
781 :
782 : // FILE pointer.
783 : explicit input (FILE* fp):input(fileno(fp)) { assert(fp); }
784 :
785 : explicit input(const char* filename) {
786 : int fd = open(filename, O_RDONLY);
787 : if (fd == -1) throw OSError("File not found: ", errno);
788 : rd_ch_ = fd;
789 : }
790 76 : explicit input(IOTYPE typ) {
791 76 : assert (typ == PIPE && "STDOUT/STDERR not allowed");
792 : #ifndef __USING_WINDOWS__
793 38 : std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
794 : #endif
795 76 : }
796 :
797 38 : int rd_ch_ = -1;
798 38 : int wr_ch_ = -1;
799 : };
800 :
801 :
802 : /*!
803 : * Option to specify the output channel for the child
804 : * process. It can be:
805 : * 1. An already open file descriptor.
806 : * 2. A file name.
807 : * 3. IOTYPE. Usually a PIPE.
808 : *
809 : * Eg: output{PIPE}
810 : * OR output{"output.txt"}
811 : */
812 : struct output
813 : {
814 : explicit output(int fd): wr_ch_(fd) {}
815 :
816 : explicit output (FILE* fp):output(fileno(fp)) { assert(fp); }
817 :
818 : explicit output(const char* filename) {
819 : int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
820 : if (fd == -1) throw OSError("File not found: ", errno);
821 : wr_ch_ = fd;
822 : }
823 76 : explicit output(IOTYPE typ) {
824 76 : assert (typ == PIPE && "STDOUT/STDERR not allowed");
825 : #ifndef __USING_WINDOWS__
826 38 : std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
827 : #endif
828 76 : }
829 :
830 38 : int rd_ch_ = -1;
831 38 : int wr_ch_ = -1;
832 : };
833 :
834 :
835 : /*!
836 : * Option to specify the error channel for the child
837 : * process. It can be:
838 : * 1. An already open file descriptor.
839 : * 2. A file name.
840 : * 3. IOTYPE. Usually a PIPE or STDOUT
841 : *
842 : */
843 : struct error
844 : {
845 : explicit error(int fd): wr_ch_(fd) {}
846 :
847 : explicit error(FILE* fp):error(fileno(fp)) { assert(fp); }
848 :
849 : explicit error(const char* filename) {
850 : int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
851 : if (fd == -1) throw OSError("File not found: ", errno);
852 : wr_ch_ = fd;
853 : }
854 76 : explicit error(IOTYPE typ) {
855 76 : assert ((typ == PIPE || typ == STDOUT) && "STDERR not aloowed");
856 38 : if (typ == PIPE) {
857 : #ifndef __USING_WINDOWS__
858 38 : std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
859 : #endif
860 38 : } else {
861 : // Need to defer it till we have checked all arguments
862 0 : deferred_ = true;
863 : }
864 76 : }
865 :
866 38 : bool deferred_ = false;
867 38 : int rd_ch_ = -1;
868 38 : int wr_ch_ = -1;
869 : };
870 :
871 : // Impoverished, meager, needy, truly needy
872 : // version of type erasure to store function pointers
873 : // needed to provide the functionality of preexec_func
874 : // ATTN: Can be used only to execute functions with no
875 : // arguments and returning void.
876 : // Could have used more efficient methods, ofcourse, but
877 : // that wont yield me the consistent syntax which I am
878 : // aiming for. If you know, then please do let me know.
879 :
880 : class preexec_func
881 : {
882 : public:
883 114 : preexec_func() {}
884 :
885 : template <typename Func>
886 : explicit preexec_func(Func f): holder_(new FuncHolder<Func>(std::move(f)))
887 : {}
888 :
889 0 : void operator()() {
890 0 : (*holder_)();
891 0 : }
892 :
893 : private:
894 : struct HolderBase {
895 : virtual void operator()() const = 0;
896 : virtual ~HolderBase(){};
897 : };
898 : template <typename T>
899 : struct FuncHolder: HolderBase {
900 : FuncHolder(T func): func_(std::move(func)) {}
901 : void operator()() const override { func_(); }
902 : // The function pointer/reference
903 : T func_;
904 : };
905 :
906 38 : std::unique_ptr<HolderBase> holder_ = nullptr;
907 : };
908 :
909 : // ~~~~ End Popen Args ~~~~
910 :
911 :
912 : /*!
913 : * class: Buffer
914 : * This class is a very thin wrapper around std::vector<char>
915 : * This is basically used to determine the length of the actual
916 : * data stored inside the dynamically resized vector.
917 : *
918 : * This is what is returned as the output to communicate and check_output
919 : * functions, so, users must know about this class.
920 : *
921 : * OutBuffer and ErrBuffer are just different typedefs to this class.
922 : */
923 : class Buffer
924 : {
925 : public:
926 210 : Buffer() {}
927 : explicit Buffer(size_t cap) { buf.resize(cap); }
928 70 : void add_cap(size_t cap) { buf.resize(cap); }
929 :
930 : #if 0
931 : Buffer(const Buffer& other):
932 : buf(other.buf),
933 : length(other.length)
934 : {
935 : std::cout << "COPY" << std::endl;
936 : }
937 :
938 : Buffer(Buffer&& other):
939 : buf(std::move(other.buf)),
940 : length(other.length)
941 : {
942 : std::cout << "MOVE" << std::endl;
943 : }
944 : #endif
945 :
946 : public:
947 : std::vector<char> buf;
948 70 : size_t length = 0;
949 : };
950 :
951 : // Buffer for storing output written to output fd
952 : using OutBuffer = Buffer;
953 : // Buffer for storing output written to error fd
954 : using ErrBuffer = Buffer;
955 :
956 :
957 : // Fwd Decl.
958 : class Popen;
959 :
960 : /*---------------------------------------------------
961 : * DETAIL NAMESPACE
962 : *---------------------------------------------------
963 : */
964 :
965 : namespace detail {
966 :
967 : // Metaprogram for searching a type within
968 : // a variadic parameter pack
969 : // This is particularly required to do a compile time
970 : // checking of the arguments provided to 'check_ouput' function
971 : // wherein the user is not expected to provide an 'ouput' option.
972 :
973 : template <typename... T> struct param_pack{};
974 :
975 : template <typename F, typename T> struct has_type;
976 :
977 : template <typename F>
978 : struct has_type<F, param_pack<>> {
979 : static constexpr bool value = false;
980 : };
981 :
982 : template <typename F, typename... T>
983 : struct has_type<F, param_pack<F, T...>> {
984 : static constexpr bool value = true;
985 : };
986 :
987 : template <typename F, typename H, typename... T>
988 : struct has_type<F, param_pack<H,T...>> {
989 : static constexpr bool value =
990 : std::is_same<F, typename std::decay<H>::type>::value ? true : has_type<F, param_pack<T...>>::value;
991 : };
992 :
993 : //----
994 :
995 : /*!
996 : * A helper class to Popen class for setting
997 : * options as provided in the Popen constructor
998 : * or in check_ouput arguments.
999 : * This design allows us to _not_ have any fixed position
1000 : * to any arguments and specify them in a way similar to what
1001 : * can be done in python.
1002 : */
1003 : struct ArgumentDeducer
1004 : {
1005 228 : ArgumentDeducer(Popen* p): popen_(p) {}
1006 :
1007 : void set_option(executable&& exe);
1008 : void set_option(cwd&& cwdir);
1009 : void set_option(bufsize&& bsiz);
1010 : void set_option(environment&& env);
1011 : void set_option(defer_spawn&& defer);
1012 : void set_option(shell&& sh);
1013 : void set_option(input&& inp);
1014 : void set_option(output&& out);
1015 : void set_option(error&& err);
1016 : void set_option(close_fds&& cfds);
1017 : void set_option(preexec_func&& prefunc);
1018 : void set_option(session_leader&& sleader);
1019 :
1020 : private:
1021 : Popen* popen_ = nullptr;
1022 : };
1023 :
1024 : /*!
1025 : * A helper class to Popen.
1026 : * This takes care of all the fork-exec logic
1027 : * in the execute_child API.
1028 : */
1029 : class Child
1030 : {
1031 : public:
1032 0 : Child(Popen* p, int err_wr_pipe):
1033 0 : parent_(p),
1034 0 : err_wr_pipe_(err_wr_pipe)
1035 0 : {}
1036 :
1037 : void execute_child();
1038 :
1039 : private:
1040 : // Lets call it parent even though
1041 : // technically a bit incorrect
1042 : Popen* parent_ = nullptr;
1043 : int err_wr_pipe_ = -1;
1044 : };
1045 :
1046 : // Fwd Decl.
1047 : class Streams;
1048 :
1049 : /*!
1050 : * A helper class to Streams.
1051 : * This takes care of management of communicating
1052 : * with the child process with the means of the correct
1053 : * file descriptor.
1054 : */
1055 : class Communication
1056 : {
1057 : public:
1058 76 : Communication(Streams* stream): stream_(stream)
1059 76 : {}
1060 : void operator=(const Communication&) = delete;
1061 : public:
1062 : int send(const char* msg, size_t length);
1063 : int send(const std::vector<char>& msg);
1064 :
1065 : std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length);
1066 : std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
1067 : { return communicate(msg.data(), msg.size()); }
1068 :
1069 : void set_out_buf_cap(size_t cap) { out_buf_cap_ = cap; }
1070 : void set_err_buf_cap(size_t cap) { err_buf_cap_ = cap; }
1071 :
1072 : private:
1073 : std::pair<OutBuffer, ErrBuffer> communicate_threaded(
1074 : const char* msg, size_t length);
1075 :
1076 : private:
1077 : Streams* stream_;
1078 38 : size_t out_buf_cap_ = DEFAULT_BUF_CAP_BYTES;
1079 38 : size_t err_buf_cap_ = DEFAULT_BUF_CAP_BYTES;
1080 : };
1081 :
1082 :
1083 :
1084 : /*!
1085 : * This is a helper class to Popen.
1086 : * It takes care of management of all the file descriptors
1087 : * and file pointers.
1088 : * It dispatches of the communication aspects to the
1089 : * Communication class.
1090 : * Read through the data members to understand about the
1091 : * various file descriptors used.
1092 : */
1093 : class Streams
1094 : {
1095 : public:
1096 114 : Streams():comm_(this) {}
1097 : void operator=(const Streams&) = delete;
1098 :
1099 : public:
1100 : void setup_comm_channels();
1101 :
1102 3 : void cleanup_fds()
1103 : {
1104 3 : if (write_to_child_ != -1 && read_from_parent_ != -1) {
1105 3 : close(write_to_child_);
1106 3 : }
1107 3 : if (write_to_parent_ != -1 && read_from_child_ != -1) {
1108 3 : close(read_from_child_);
1109 3 : }
1110 3 : if (err_write_ != -1 && err_read_ != -1) {
1111 3 : close(err_read_);
1112 3 : }
1113 3 : }
1114 :
1115 0 : void close_parent_fds()
1116 : {
1117 0 : if (write_to_child_ != -1) close(write_to_child_);
1118 0 : if (read_from_child_ != -1) close(read_from_child_);
1119 0 : if (err_read_ != -1) close(err_read_);
1120 0 : }
1121 :
1122 38 : void close_child_fds()
1123 : {
1124 38 : if (write_to_parent_ != -1) close(write_to_parent_);
1125 38 : if (read_from_parent_ != -1) close(read_from_parent_);
1126 38 : if (err_write_ != -1) close(err_write_);
1127 38 : }
1128 :
1129 118 : FILE* input() { return input_.get(); }
1130 143 : FILE* output() { return output_.get(); }
1131 143 : FILE* error() { return error_.get(); }
1132 :
1133 38 : void input(FILE* fp) { input_.reset(fp, fclose); }
1134 38 : void output(FILE* fp) { output_.reset(fp, fclose); }
1135 38 : void error(FILE* fp) { error_.reset(fp, fclose); }
1136 :
1137 : void set_out_buf_cap(size_t cap) { comm_.set_out_buf_cap(cap); }
1138 : void set_err_buf_cap(size_t cap) { comm_.set_err_buf_cap(cap); }
1139 :
1140 : public: /* Communication forwarding API's */
1141 5 : int send(const char* msg, size_t length)
1142 5 : { return comm_.send(msg, length); }
1143 :
1144 : int send(const std::vector<char>& msg)
1145 : { return comm_.send(msg); }
1146 :
1147 35 : std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
1148 35 : { return comm_.communicate(msg, length); }
1149 :
1150 : std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
1151 : { return comm_.communicate(msg); }
1152 :
1153 :
1154 : public:// Yes they are public
1155 :
1156 38 : std::shared_ptr<FILE> input_ = nullptr;
1157 38 : std::shared_ptr<FILE> output_ = nullptr;
1158 38 : std::shared_ptr<FILE> error_ = nullptr;
1159 :
1160 : #ifdef __USING_WINDOWS__
1161 : HANDLE g_hChildStd_IN_Rd = nullptr;
1162 : HANDLE g_hChildStd_IN_Wr = nullptr;
1163 : HANDLE g_hChildStd_OUT_Rd = nullptr;
1164 : HANDLE g_hChildStd_OUT_Wr = nullptr;
1165 : HANDLE g_hChildStd_ERR_Rd = nullptr;
1166 : HANDLE g_hChildStd_ERR_Wr = nullptr;
1167 : #endif
1168 :
1169 : // Buffer size for the IO streams
1170 38 : int bufsiz_ = 0;
1171 :
1172 : // Pipes for communicating with child
1173 :
1174 : // Emulates stdin
1175 38 : int write_to_child_ = -1; // Parent owned descriptor
1176 38 : int read_from_parent_ = -1; // Child owned descriptor
1177 :
1178 : // Emulates stdout
1179 38 : int write_to_parent_ = -1; // Child owned descriptor
1180 38 : int read_from_child_ = -1; // Parent owned descriptor
1181 :
1182 : // Emulates stderr
1183 38 : int err_write_ = -1; // Write error to parent (Child owned)
1184 38 : int err_read_ = -1; // Read error from child (Parent owned)
1185 :
1186 : private:
1187 : Communication comm_;
1188 : };
1189 :
1190 : } // end namespace detail
1191 :
1192 :
1193 :
1194 : /*!
1195 : * class: Popen
1196 : * This is the single most important class in the whole library
1197 : * and glues together all the helper classes to provide a common
1198 : * interface to the client.
1199 : *
1200 : * API's provided by the class:
1201 : * 1. Popen({"cmd"}, output{..}, error{..}, cwd{..}, ....)
1202 : * Command provided as a sequence.
1203 : * 2. Popen("cmd arg1"m output{..}, error{..}, cwd{..}, ....)
1204 : * Command provided in a single string.
1205 : * 3. wait() - Wait for the child to exit.
1206 : * 4. retcode() - The return code of the exited child.
1207 : * 5. pid() - PID of the spawned child.
1208 : * 6. poll() - Check the status of the running child.
1209 : * 7. kill(sig_num) - Kill the child. SIGTERM used by default.
1210 : * 8. send(...) - Send input to the input channel of the child.
1211 : * 9. communicate(...) - Get the output/error from the child and close the channels
1212 : * from the parent side.
1213 : *10. input() - Get the input channel/File pointer. Can be used for
1214 : * cutomizing the way of sending input to child.
1215 : *11. output() - Get the output channel/File pointer. Usually used
1216 : in case of redirection. See piping examples.
1217 : *12. error() - Get the error channel/File poiner. Usually used
1218 : in case of redirection.
1219 : *13. start_process() - Start the child process. Only to be used when
1220 : * `defer_spawn` option was provided in Popen constructor.
1221 : */
1222 : class Popen
1223 : {
1224 : public:
1225 : friend struct detail::ArgumentDeducer;
1226 : friend class detail::Child;
1227 :
1228 : template <typename... Args>
1229 152 : Popen(const std::string& cmd_args, Args&& ...args):
1230 38 : args_(cmd_args)
1231 38 : {
1232 38 : vargs_ = util::split(cmd_args);
1233 38 : init_args(std::forward<Args>(args)...);
1234 :
1235 : // Setup the communication channels of the Popen class
1236 38 : stream_.setup_comm_channels();
1237 :
1238 38 : if (!defer_process_start_) execute_process();
1239 76 : }
1240 :
1241 : template <typename... Args>
1242 : Popen(std::initializer_list<const char*> cmd_args, Args&& ...args)
1243 : {
1244 : vargs_.insert(vargs_.end(), cmd_args.begin(), cmd_args.end());
1245 : init_args(std::forward<Args>(args)...);
1246 :
1247 : // Setup the communication channels of the Popen class
1248 : stream_.setup_comm_channels();
1249 :
1250 : if (!defer_process_start_) execute_process();
1251 : }
1252 :
1253 : template <typename... Args>
1254 : Popen(std::vector<std::string> vargs_, Args &&... args) : vargs_(vargs_)
1255 : {
1256 : init_args(std::forward<Args>(args)...);
1257 :
1258 : // Setup the communication channels of the Popen class
1259 : stream_.setup_comm_channels();
1260 :
1261 : if (!defer_process_start_) execute_process();
1262 : }
1263 :
1264 : /*
1265 : ~Popen()
1266 : {
1267 : #ifdef __USING_WINDOWS__
1268 : CloseHandle(this->process_handle_);
1269 : #endif
1270 : }
1271 : */
1272 :
1273 : void start_process() noexcept(false);
1274 :
1275 38 : int pid() const noexcept { return child_pid_; }
1276 :
1277 35 : int retcode() const noexcept { return retcode_; }
1278 :
1279 : int wait() noexcept(false);
1280 :
1281 : int poll() noexcept(false);
1282 :
1283 : // Does not fail, Caller is expected to recheck the
1284 : // status with a call to poll()
1285 : void kill(int sig_num = 9);
1286 :
1287 : void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
1288 :
1289 : void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
1290 :
1291 5 : int send(const char* msg, size_t length)
1292 5 : { return stream_.send(msg, length); }
1293 :
1294 5 : int send(const std::string& msg)
1295 5 : { return send(msg.c_str(), msg.size()); }
1296 :
1297 : int send(const std::vector<char>& msg)
1298 : { return stream_.send(msg); }
1299 :
1300 35 : std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
1301 : {
1302 35 : auto res = stream_.communicate(msg, length);
1303 35 : retcode_ = wait();
1304 35 : return res;
1305 35 : }
1306 :
1307 : std::pair<OutBuffer, ErrBuffer> communicate(const std::string& msg)
1308 : {
1309 : return communicate(msg.c_str(), msg.size());
1310 : }
1311 :
1312 : std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
1313 : {
1314 : auto res = stream_.communicate(msg);
1315 : retcode_ = wait();
1316 : return res;
1317 : }
1318 :
1319 35 : std::pair<OutBuffer, ErrBuffer> communicate()
1320 : {
1321 35 : return communicate(nullptr, 0);
1322 : }
1323 :
1324 : FILE* input() { return stream_.input(); }
1325 : FILE* output() { return stream_.output();}
1326 : FILE* error() { return stream_.error(); }
1327 :
1328 : /// Stream close APIs
1329 : void close_input() { stream_.input_.reset(); }
1330 : void close_output() { stream_.output_.reset(); }
1331 : void close_error() { stream_.error_.reset(); }
1332 :
1333 : private:
1334 : template <typename F, typename... Args>
1335 : void init_args(F&& farg, Args&&... args);
1336 : void init_args();
1337 : void populate_c_argv();
1338 : void execute_process() noexcept(false);
1339 :
1340 : private:
1341 : detail::Streams stream_;
1342 :
1343 : #ifdef __USING_WINDOWS__
1344 : HANDLE process_handle_;
1345 : std::future<void> cleanup_future_;
1346 : #endif
1347 :
1348 38 : bool defer_process_start_ = false;
1349 38 : bool close_fds_ = false;
1350 38 : bool has_preexec_fn_ = false;
1351 38 : bool shell_ = false;
1352 38 : bool session_leader_ = false;
1353 :
1354 : std::string exe_name_;
1355 : std::string cwd_;
1356 : env_map_t env_;
1357 : preexec_func preexec_fn_;
1358 :
1359 : // Command in string format
1360 : std::string args_;
1361 : // Comamnd provided as sequence
1362 : std::vector<std::string> vargs_;
1363 : std::vector<char*> cargv_;
1364 :
1365 38 : bool child_created_ = false;
1366 : // Pid of the child process
1367 38 : int child_pid_ = -1;
1368 :
1369 38 : int retcode_ = -1;
1370 : };
1371 :
1372 38 : inline void Popen::init_args() {
1373 38 : populate_c_argv();
1374 38 : }
1375 :
1376 : template <typename F, typename... Args>
1377 114 : inline void Popen::init_args(F&& farg, Args&&... args)
1378 : {
1379 114 : detail::ArgumentDeducer argd(this);
1380 114 : argd.set_option(std::forward<F>(farg));
1381 114 : init_args(std::forward<Args>(args)...);
1382 114 : }
1383 :
1384 38 : inline void Popen::populate_c_argv()
1385 : {
1386 38 : cargv_.clear();
1387 38 : cargv_.reserve(vargs_.size() + 1);
1388 164 : for (auto& arg : vargs_) cargv_.push_back(&arg[0]);
1389 38 : cargv_.push_back(nullptr);
1390 38 : }
1391 :
1392 : inline void Popen::start_process() noexcept(false)
1393 : {
1394 : // The process was started/tried to be started
1395 : // in the constructor itself.
1396 : // For explicitly calling this API to start the
1397 : // process, 'defer_spawn' argument must be set to
1398 : // true in the constructor.
1399 : if (!defer_process_start_) {
1400 : assert (0);
1401 : return;
1402 : }
1403 : execute_process();
1404 : }
1405 :
1406 38 : inline int Popen::wait() noexcept(false)
1407 : {
1408 : #ifdef __USING_WINDOWS__
1409 : int ret = WaitForSingleObject(process_handle_, INFINITE);
1410 :
1411 : return 0;
1412 : #else
1413 : int ret, status;
1414 38 : std::tie(ret, status) = util::wait_for_child_exit(pid());
1415 38 : if (ret == -1) {
1416 0 : if (errno != ECHILD) throw OSError("waitpid failed", errno);
1417 0 : return 0;
1418 : }
1419 38 : if (WIFEXITED(status)) return WEXITSTATUS(status);
1420 0 : if (WIFSIGNALED(status)) return WTERMSIG(status);
1421 0 : else return 255;
1422 :
1423 : return 0;
1424 : #endif
1425 38 : }
1426 :
1427 : inline int Popen::poll() noexcept(false)
1428 : {
1429 : #ifdef __USING_WINDOWS__
1430 : int ret = WaitForSingleObject(process_handle_, 0);
1431 : if (ret != WAIT_OBJECT_0) return -1;
1432 :
1433 : DWORD dretcode_;
1434 : if (FALSE == GetExitCodeProcess(process_handle_, &dretcode_))
1435 : throw OSError("GetExitCodeProcess", 0);
1436 :
1437 : retcode_ = (int)dretcode_;
1438 : CloseHandle(process_handle_);
1439 :
1440 : return retcode_;
1441 : #else
1442 : if (!child_created_) return -1; // TODO: ??
1443 :
1444 : int status;
1445 :
1446 : // Returns zero if child is still running
1447 : int ret = waitpid(child_pid_, &status, WNOHANG);
1448 : if (ret == 0) return -1;
1449 :
1450 : if (ret == child_pid_) {
1451 : if (WIFSIGNALED(status)) {
1452 : retcode_ = WTERMSIG(status);
1453 : } else if (WIFEXITED(status)) {
1454 : retcode_ = WEXITSTATUS(status);
1455 : } else {
1456 : retcode_ = 255;
1457 : }
1458 : return retcode_;
1459 : }
1460 :
1461 : if (ret == -1) {
1462 : // From subprocess.py
1463 : // This happens if SIGCHLD is set to be ignored
1464 : // or waiting for child process has otherwise been disabled
1465 : // for our process. This child is dead, we cannot get the
1466 : // status.
1467 : if (errno == ECHILD) retcode_ = 0;
1468 : else throw OSError("waitpid failed", errno);
1469 : } else {
1470 : retcode_ = ret;
1471 : }
1472 :
1473 : return retcode_;
1474 : #endif
1475 : }
1476 :
1477 : inline void Popen::kill(int sig_num)
1478 : {
1479 : #ifdef __USING_WINDOWS__
1480 : if (!TerminateProcess(this->process_handle_, (UINT)sig_num)) {
1481 : throw OSError("TerminateProcess", 0);
1482 : }
1483 : #else
1484 : if (session_leader_) killpg(child_pid_, sig_num);
1485 : else ::kill(child_pid_, sig_num);
1486 : #endif
1487 : }
1488 :
1489 :
1490 38 : inline void Popen::execute_process() noexcept(false)
1491 : {
1492 : #ifdef __USING_WINDOWS__
1493 : if (this->shell_) {
1494 : throw OSError("shell not currently supported on windows", 0);
1495 : }
1496 :
1497 : void* environment_string_table_ptr = nullptr;
1498 : env_vector_t environment_string_vector;
1499 : if(this->env_.size()){
1500 : environment_string_vector = util::CreateUpdatedWindowsEnvironmentVector(env_);
1501 : environment_string_table_ptr = (void*)environment_string_vector.data();
1502 : }
1503 :
1504 : if (exe_name_.length()) {
1505 : this->vargs_.insert(this->vargs_.begin(), this->exe_name_);
1506 : this->populate_c_argv();
1507 : }
1508 : this->exe_name_ = vargs_[0];
1509 :
1510 : std::wstring_convert<std::codecvt_utf8_utf16<wchar_t>> converter;
1511 : std::wstring argument;
1512 : std::wstring command_line;
1513 :
1514 : for (auto arg : this->vargs_) {
1515 : argument = converter.from_bytes(arg);
1516 : util::quote_argument(argument, command_line, false);
1517 : command_line += L" ";
1518 : }
1519 :
1520 : // CreateProcessW can modify szCmdLine so we allocate needed memory
1521 : wchar_t *szCmdline = new wchar_t[command_line.size() + 1];
1522 : wcscpy_s(szCmdline, command_line.size() + 1, command_line.c_str());
1523 : PROCESS_INFORMATION piProcInfo;
1524 : STARTUPINFOW siStartInfo;
1525 : BOOL bSuccess = FALSE;
1526 : DWORD creation_flags = CREATE_UNICODE_ENVIRONMENT | CREATE_NO_WINDOW;
1527 :
1528 : // Set up members of the PROCESS_INFORMATION structure.
1529 : ZeroMemory(&piProcInfo, sizeof(PROCESS_INFORMATION));
1530 :
1531 : // Set up members of the STARTUPINFOW structure.
1532 : // This structure specifies the STDIN and STDOUT handles for redirection.
1533 :
1534 : ZeroMemory(&siStartInfo, sizeof(STARTUPINFOW));
1535 : siStartInfo.cb = sizeof(STARTUPINFOW);
1536 :
1537 : siStartInfo.hStdError = this->stream_.g_hChildStd_ERR_Wr;
1538 : siStartInfo.hStdOutput = this->stream_.g_hChildStd_OUT_Wr;
1539 : siStartInfo.hStdInput = this->stream_.g_hChildStd_IN_Rd;
1540 :
1541 : siStartInfo.dwFlags |= STARTF_USESTDHANDLES;
1542 :
1543 : // Create the child process.
1544 : bSuccess = CreateProcessW(NULL,
1545 : szCmdline, // command line
1546 : NULL, // process security attributes
1547 : NULL, // primary thread security attributes
1548 : TRUE, // handles are inherited
1549 : creation_flags, // creation flags
1550 : environment_string_table_ptr, // use provided environment
1551 : NULL, // use parent's current directory
1552 : &siStartInfo, // STARTUPINFOW pointer
1553 : &piProcInfo); // receives PROCESS_INFORMATION
1554 :
1555 : // If an error occurs, exit the application.
1556 : if (!bSuccess) {
1557 : DWORD errorMessageID = ::GetLastError();
1558 : throw CalledProcessError("CreateProcess failed: " + util::get_last_error(errorMessageID), errorMessageID);
1559 : }
1560 :
1561 : CloseHandle(piProcInfo.hThread);
1562 :
1563 : /*
1564 : TODO: use common apis to close linux handles
1565 : */
1566 :
1567 : this->process_handle_ = piProcInfo.hProcess;
1568 :
1569 : this->cleanup_future_ = std::async(std::launch::async, [this] {
1570 : WaitForSingleObject(this->process_handle_, INFINITE);
1571 :
1572 : CloseHandle(this->stream_.g_hChildStd_ERR_Wr);
1573 : CloseHandle(this->stream_.g_hChildStd_OUT_Wr);
1574 : CloseHandle(this->stream_.g_hChildStd_IN_Rd);
1575 : });
1576 :
1577 : /*
1578 : NOTE: In the linux version, there is a check to make sure that the process
1579 : has been started. Here, we do nothing because CreateProcess will throw
1580 : if we fail to create the process.
1581 : */
1582 :
1583 :
1584 : #else
1585 :
1586 : int err_rd_pipe, err_wr_pipe;
1587 38 : std::tie(err_rd_pipe, err_wr_pipe) = util::pipe_cloexec();
1588 :
1589 38 : if (shell_) {
1590 3 : auto new_cmd = util::join(vargs_);
1591 0 : vargs_.clear();
1592 0 : vargs_.insert(vargs_.begin(), {"/bin/sh", "-c"});
1593 0 : vargs_.push_back(new_cmd);
1594 0 : populate_c_argv();
1595 0 : }
1596 :
1597 0 : if (exe_name_.length()) {
1598 0 : vargs_.insert(vargs_.begin(), exe_name_);
1599 0 : populate_c_argv();
1600 0 : }
1601 38 : exe_name_ = vargs_[0];
1602 :
1603 38 : child_pid_ = fork();
1604 :
1605 38 : if (child_pid_ < 0) {
1606 0 : close(err_rd_pipe);
1607 0 : close(err_wr_pipe);
1608 0 : throw OSError("fork failed", errno);
1609 : }
1610 :
1611 38 : child_created_ = true;
1612 :
1613 38 : if (child_pid_ == 0)
1614 : {
1615 : // Close descriptors belonging to parent
1616 0 : stream_.close_parent_fds();
1617 :
1618 : //Close the read end of the error pipe
1619 0 : close(err_rd_pipe);
1620 :
1621 0 : detail::Child chld(this, err_wr_pipe);
1622 0 : chld.execute_child();
1623 0 : }
1624 : else
1625 : {
1626 38 : close (err_wr_pipe);// close child side of pipe, else get stuck in read below
1627 :
1628 38 : stream_.close_child_fds();
1629 :
1630 : try {
1631 38 : char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
1632 :
1633 38 : int read_bytes = util::read_atmost_n(
1634 38 : fdopen(err_rd_pipe, "r"),
1635 38 : err_buf,
1636 : SP_MAX_ERR_BUF_SIZ);
1637 38 : close(err_rd_pipe);
1638 :
1639 38 : if (read_bytes || strlen(err_buf)) {
1640 : // Call waitpid to reap the child process
1641 : // waitpid suspends the calling process until the
1642 : // child terminates.
1643 3 : int retcode = wait();
1644 :
1645 : // Throw whatever information we have about child failure
1646 3 : throw CalledProcessError(err_buf, retcode);
1647 : }
1648 38 : } catch (std::exception& exp) {
1649 3 : stream_.cleanup_fds();
1650 3 : throw;
1651 0 : }
1652 :
1653 : }
1654 : #endif
1655 35 : }
1656 :
1657 : namespace detail {
1658 :
1659 : inline void ArgumentDeducer::set_option(executable&& exe) {
1660 : popen_->exe_name_ = std::move(exe.arg_value);
1661 : }
1662 :
1663 : inline void ArgumentDeducer::set_option(cwd&& cwdir) {
1664 : popen_->cwd_ = std::move(cwdir.arg_value);
1665 : }
1666 :
1667 : inline void ArgumentDeducer::set_option(bufsize&& bsiz) {
1668 : popen_->stream_.bufsiz_ = bsiz.bufsiz;
1669 : }
1670 :
1671 : inline void ArgumentDeducer::set_option(environment&& env) {
1672 : popen_->env_ = std::move(env.env_);
1673 : }
1674 :
1675 : inline void ArgumentDeducer::set_option(defer_spawn&& defer) {
1676 : popen_->defer_process_start_ = defer.defer;
1677 : }
1678 :
1679 : inline void ArgumentDeducer::set_option(shell&& sh) {
1680 : popen_->shell_ = sh.shell_;
1681 : }
1682 :
1683 : inline void ArgumentDeducer::set_option(session_leader&& sleader) {
1684 : popen_->session_leader_ = sleader.leader_;
1685 : }
1686 :
1687 38 : inline void ArgumentDeducer::set_option(input&& inp) {
1688 38 : if (inp.rd_ch_ != -1) popen_->stream_.read_from_parent_ = inp.rd_ch_;
1689 38 : if (inp.wr_ch_ != -1) popen_->stream_.write_to_child_ = inp.wr_ch_;
1690 38 : }
1691 :
1692 38 : inline void ArgumentDeducer::set_option(output&& out) {
1693 38 : if (out.wr_ch_ != -1) popen_->stream_.write_to_parent_ = out.wr_ch_;
1694 38 : if (out.rd_ch_ != -1) popen_->stream_.read_from_child_ = out.rd_ch_;
1695 38 : }
1696 :
1697 38 : inline void ArgumentDeducer::set_option(error&& err) {
1698 38 : if (err.deferred_) {
1699 0 : if (popen_->stream_.write_to_parent_) {
1700 0 : popen_->stream_.err_write_ = popen_->stream_.write_to_parent_;
1701 0 : } else {
1702 0 : throw std::runtime_error("Set output before redirecting error to output");
1703 : }
1704 0 : }
1705 38 : if (err.wr_ch_ != -1) popen_->stream_.err_write_ = err.wr_ch_;
1706 38 : if (err.rd_ch_ != -1) popen_->stream_.err_read_ = err.rd_ch_;
1707 38 : }
1708 :
1709 : inline void ArgumentDeducer::set_option(close_fds&& cfds) {
1710 : popen_->close_fds_ = cfds.close_all;
1711 : }
1712 :
1713 : inline void ArgumentDeducer::set_option(preexec_func&& prefunc) {
1714 : popen_->preexec_fn_ = std::move(prefunc);
1715 : popen_->has_preexec_fn_ = true;
1716 : }
1717 :
1718 :
1719 0 : inline void Child::execute_child() {
1720 : #ifndef __USING_WINDOWS__
1721 0 : int sys_ret = -1;
1722 0 : auto& stream = parent_->stream_;
1723 :
1724 : try {
1725 0 : if (stream.write_to_parent_ == 0)
1726 0 : stream.write_to_parent_ = dup(stream.write_to_parent_);
1727 :
1728 0 : if (stream.err_write_ == 0 || stream.err_write_ == 1)
1729 0 : stream.err_write_ = dup(stream.err_write_);
1730 :
1731 : // Make the child owned descriptors as the
1732 : // stdin, stdout and stderr for the child process
1733 0 : auto _dup2_ = [](int fd, int to_fd) {
1734 0 : if (fd == to_fd) {
1735 : // dup2 syscall does not reset the
1736 : // CLOEXEC flag if the descriptors
1737 : // provided to it are same.
1738 : // But, we need to reset the CLOEXEC
1739 : // flag as the provided descriptors
1740 : // are now going to be the standard
1741 : // input, output and error
1742 0 : util::set_clo_on_exec(fd, false);
1743 0 : } else if(fd != -1) {
1744 0 : int res = dup2(fd, to_fd);
1745 0 : if (res == -1) throw OSError("dup2 failed", errno);
1746 0 : }
1747 0 : };
1748 :
1749 : // Create the standard streams
1750 0 : _dup2_(stream.read_from_parent_, 0); // Input stream
1751 0 : _dup2_(stream.write_to_parent_, 1); // Output stream
1752 0 : _dup2_(stream.err_write_, 2); // Error stream
1753 :
1754 : // Close the duped descriptors
1755 0 : if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2)
1756 0 : close(stream.read_from_parent_);
1757 :
1758 0 : if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2)
1759 0 : close(stream.write_to_parent_);
1760 :
1761 0 : if (stream.err_write_ != -1 && stream.err_write_ > 2)
1762 0 : close(stream.err_write_);
1763 :
1764 : // Close all the inherited fd's except the error write pipe
1765 0 : if (parent_->close_fds_) {
1766 0 : int max_fd = sysconf(_SC_OPEN_MAX);
1767 0 : if (max_fd == -1) throw OSError("sysconf failed", errno);
1768 :
1769 0 : for (int i = 3; i < max_fd; i++) {
1770 0 : if (i == err_wr_pipe_) continue;
1771 0 : close(i);
1772 0 : }
1773 0 : }
1774 :
1775 : // Change the working directory if provided
1776 0 : if (parent_->cwd_.length()) {
1777 0 : sys_ret = chdir(parent_->cwd_.c_str());
1778 0 : if (sys_ret == -1) throw OSError("chdir failed", errno);
1779 0 : }
1780 :
1781 0 : if (parent_->has_preexec_fn_) {
1782 0 : parent_->preexec_fn_();
1783 0 : }
1784 :
1785 0 : if (parent_->session_leader_) {
1786 0 : sys_ret = setsid();
1787 0 : if (sys_ret == -1) throw OSError("setsid failed", errno);
1788 0 : }
1789 :
1790 : // Replace the current image with the executable
1791 0 : if (parent_->env_.size()) {
1792 0 : for (auto& kv : parent_->env_) {
1793 0 : setenv(kv.first.c_str(), kv.second.c_str(), 1);
1794 : }
1795 0 : sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
1796 0 : } else {
1797 0 : sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
1798 : }
1799 :
1800 0 : if (sys_ret == -1) throw OSError("execve failed", errno);
1801 :
1802 0 : } catch (const OSError& exp) {
1803 : // Just write the exception message
1804 : // TODO: Give back stack trace ?
1805 0 : std::string err_msg(exp.what());
1806 : //ATTN: Can we do something on error here ?
1807 0 : util::write_n(err_wr_pipe_, err_msg.c_str(), err_msg.length());
1808 0 : }
1809 :
1810 : // Calling application would not get this
1811 : // exit failure
1812 0 : _exit (EXIT_FAILURE);
1813 : #endif
1814 0 : }
1815 :
1816 :
1817 38 : inline void Streams::setup_comm_channels()
1818 : {
1819 : #ifdef __USING_WINDOWS__
1820 : util::configure_pipe(&this->g_hChildStd_IN_Rd, &this->g_hChildStd_IN_Wr, &this->g_hChildStd_IN_Wr);
1821 : this->input(util::file_from_handle(this->g_hChildStd_IN_Wr, "w"));
1822 : this->write_to_child_ = _fileno(this->input());
1823 :
1824 : util::configure_pipe(&this->g_hChildStd_OUT_Rd, &this->g_hChildStd_OUT_Wr, &this->g_hChildStd_OUT_Rd);
1825 : this->output(util::file_from_handle(this->g_hChildStd_OUT_Rd, "r"));
1826 : this->read_from_child_ = _fileno(this->output());
1827 :
1828 : util::configure_pipe(&this->g_hChildStd_ERR_Rd, &this->g_hChildStd_ERR_Wr, &this->g_hChildStd_ERR_Rd);
1829 : this->error(util::file_from_handle(this->g_hChildStd_ERR_Rd, "r"));
1830 : this->err_read_ = _fileno(this->error());
1831 : #else
1832 :
1833 38 : if (write_to_child_ != -1) input(fdopen(write_to_child_, "wb"));
1834 38 : if (read_from_child_ != -1) output(fdopen(read_from_child_, "rb"));
1835 38 : if (err_read_ != -1) error(fdopen(err_read_, "rb"));
1836 :
1837 38 : auto handles = {input(), output(), error()};
1838 :
1839 152 : for (auto& h : handles) {
1840 114 : if (h == nullptr) continue;
1841 114 : switch (bufsiz_) {
1842 : case 0:
1843 114 : setvbuf(h, nullptr, _IONBF, BUFSIZ);
1844 114 : break;
1845 : case 1:
1846 0 : setvbuf(h, nullptr, _IONBF, BUFSIZ);
1847 0 : break;
1848 : default:
1849 0 : setvbuf(h, nullptr, _IOFBF, bufsiz_);
1850 0 : };
1851 : }
1852 : #endif
1853 38 : }
1854 :
1855 5 : inline int Communication::send(const char* msg, size_t length)
1856 : {
1857 5 : if (stream_->input() == nullptr) return -1;
1858 5 : return std::fwrite(msg, sizeof(char), length, stream_->input());
1859 5 : }
1860 :
1861 : inline int Communication::send(const std::vector<char>& msg)
1862 : {
1863 : return send(msg.data(), msg.size());
1864 : }
1865 :
1866 : inline std::pair<OutBuffer, ErrBuffer>
1867 35 : Communication::communicate(const char* msg, size_t length)
1868 : {
1869 : // Optimization from subprocess.py
1870 : // If we are using one pipe, or no pipe
1871 : // at all, using select() or threads is unnecessary.
1872 35 : auto hndls = {stream_->input(), stream_->output(), stream_->error()};
1873 35 : int count = std::count(std::begin(hndls), std::end(hndls), nullptr);
1874 35 : const int len_conv = length;
1875 :
1876 35 : if (count >= 2) {
1877 0 : OutBuffer obuf;
1878 0 : ErrBuffer ebuf;
1879 0 : if (stream_->input()) {
1880 0 : if (msg) {
1881 0 : int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
1882 0 : if (wbytes < len_conv) {
1883 0 : if (errno != EPIPE && errno != EINVAL) {
1884 0 : throw OSError("fwrite error", errno);
1885 : }
1886 0 : }
1887 0 : }
1888 : // Close the input stream
1889 0 : stream_->input_.reset();
1890 0 : } else if (stream_->output()) {
1891 : // Read till EOF
1892 : // ATTN: This could be blocking, if the process
1893 : // at the other end screws up, we get screwed as well
1894 0 : obuf.add_cap(out_buf_cap_);
1895 :
1896 0 : int rbytes = util::read_all(
1897 0 : stream_->output(),
1898 0 : obuf.buf);
1899 :
1900 0 : if (rbytes == -1) {
1901 0 : throw OSError("read to obuf failed", errno);
1902 : }
1903 :
1904 0 : obuf.length = rbytes;
1905 : // Close the output stream
1906 0 : stream_->output_.reset();
1907 :
1908 0 : } else if (stream_->error()) {
1909 : // Same screwness applies here as well
1910 0 : ebuf.add_cap(err_buf_cap_);
1911 :
1912 0 : int rbytes = util::read_atmost_n(
1913 0 : stream_->error(),
1914 0 : ebuf.buf.data(),
1915 0 : ebuf.buf.size());
1916 :
1917 0 : if (rbytes == -1) {
1918 0 : throw OSError("read to ebuf failed", errno);
1919 : }
1920 :
1921 0 : ebuf.length = rbytes;
1922 : // Close the error stream
1923 0 : stream_->error_.reset();
1924 0 : }
1925 0 : return std::make_pair(std::move(obuf), std::move(ebuf));
1926 0 : }
1927 :
1928 35 : return communicate_threaded(msg, length);
1929 35 : }
1930 :
1931 :
1932 : inline std::pair<OutBuffer, ErrBuffer>
1933 35 : Communication::communicate_threaded(const char* msg, size_t length)
1934 : {
1935 35 : OutBuffer obuf;
1936 35 : ErrBuffer ebuf;
1937 35 : std::future<int> out_fut, err_fut;
1938 35 : const int length_conv = length;
1939 :
1940 35 : if (stream_->output()) {
1941 35 : obuf.add_cap(out_buf_cap_);
1942 :
1943 35 : out_fut = std::async(std::launch::async,
1944 70 : [&obuf, this] {
1945 35 : return util::read_all(this->stream_->output(), obuf.buf);
1946 : });
1947 35 : }
1948 35 : if (stream_->error()) {
1949 35 : ebuf.add_cap(err_buf_cap_);
1950 :
1951 35 : err_fut = std::async(std::launch::async,
1952 70 : [&ebuf, this] {
1953 35 : return util::read_all(this->stream_->error(), ebuf.buf);
1954 : });
1955 35 : }
1956 35 : if (stream_->input()) {
1957 35 : if (msg) {
1958 0 : int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
1959 0 : if (wbytes < length_conv) {
1960 0 : if (errno != EPIPE && errno != EINVAL) {
1961 0 : throw OSError("fwrite error", errno);
1962 : }
1963 0 : }
1964 0 : }
1965 35 : stream_->input_.reset();
1966 35 : }
1967 :
1968 35 : if (out_fut.valid()) {
1969 35 : int res = out_fut.get();
1970 35 : if (res != -1) obuf.length = res;
1971 0 : else obuf.length = 0;
1972 35 : }
1973 35 : if (err_fut.valid()) {
1974 35 : int res = err_fut.get();
1975 35 : if (res != -1) ebuf.length = res;
1976 0 : else ebuf.length = 0;
1977 35 : }
1978 :
1979 35 : return std::make_pair(std::move(obuf), std::move(ebuf));
1980 35 : }
1981 :
1982 : } // end namespace detail
1983 :
1984 : }
1985 :
1986 : #endif // SUBPROCESS_HPP
|