source: launchers/macosx/include/subprocess.hpp @ b40b987

Last change on this file since b40b987 was b40b987, checked in by meeh <meeh@…>, 2 years ago

Mac OSX Launcher:

  • Fixed issue with >> and compilers in C++ in subprocess.hpp
  • Added queue for logger
  • Misc
  • Property mode set to 100644
File size: 43.2 KB
Line 
1/*!
2
3Documentation 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 &copy; 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#ifdef __cplusplus
38
39#include <map>
40#include <algorithm>
41#include <iostream>
42#include <string>
43#include <cstdlib>
44#include <cassert>
45#include <cstring>
46#include <cstdio>
47#include <csignal>
48#include <future>
49#include <vector>
50#include <sstream>
51#include <memory>
52#include <initializer_list>
53#include <exception>
54
55extern "C" {
56  #include <unistd.h>
57  #include <fcntl.h>
58  #include <sys/types.h>
59  #include <sys/wait.h>
60  #include <signal.h>
61}
62
63/*!
64 * Getting started with reading this source code.
65 * The source is mainly divided into four parts:
66 * 1. Exception Classes:
67 *    These are very basic exception classes derived from
68 *    runtime_error exception.
69 *    There are two types of exception thrown from subprocess
70 *    library: OSError and CalledProcessError
71 *
72 * 2. Popen Class
73 *    This is the main class the users will deal with. It
74 *    provides with all the API's to deal with processes.
75 *
76 * 3. Util namespace
77 *    It includes some helper functions to split/join a string,
78 *    reading from file descriptors, waiting on a process, fcntl
79 *    options on file descriptors etc.
80 *
81 * 4. Detail namespace
82 *    This includes some metaprogramming and helper classes.
83 */
84
85
86namespace subprocess {
87
88// Max buffer size allocated on stack for read error
89// from pipe
90static const size_t SP_MAX_ERR_BUF_SIZ = 1024;
91
92// Default buffer capcity for OutBuffer and ErrBuffer.
93// If the data exceeds this capacity, the buffer size is grown
94// by 1.5 times its previous capacity
95static const size_t DEFAULT_BUF_CAP_BYTES = 8192;
96
97
98/*-----------------------------------------------
99 *    EXCEPTION CLASSES
100 *-----------------------------------------------
101 */
102
103/*!
104 * class: CalledProcessError
105 * Thrown when there was error executing the command.
106 * Check Popen class API's to know when this exception
107 * can be thrown.
108 *
109 */
110class CalledProcessError: public std::runtime_error
111{
112public:
113  CalledProcessError(const std::string& error_msg):
114    std::runtime_error(error_msg)
115  {}
116};
117
118
119/*!
120 * class: OSError
121 * Thrown when some system call fails to execute or give result.
122 * The exception message contains the name of the failed system call
123 * with the stringisized errno code.
124 * Check Popen class API's to know when this exception would be
125 * thrown.
126 * Its usual that the API exception specification would have
127 * this exception together with CalledProcessError.
128 */
129class OSError: public std::runtime_error
130{
131public:
132  OSError(const std::string& err_msg, int err_code):
133    std::runtime_error( err_msg + " : " + std::strerror(err_code) )
134  {}
135};
136
137//--------------------------------------------------------------------
138
139namespace util
140{
141
142  /*!
143   * Function: split
144   * Parameters:
145   * [in] str : Input string which needs to be split based upon the
146   *            delimiters provided.
147   * [in] deleims : Delimiter characters based upon which the string needs
148   *                to be split. Default constructed to ' '(space) and '\t'(tab)
149   * [out] vector<string> : Vector of strings split at deleimiter.
150   */
151  static inline std::vector<std::string>
152  split(const std::string& str, const std::string& delims=" \t")
153  {
154    std::vector<std::string> res;
155    size_t init = 0;
156
157    while (true) {
158      auto pos = str.find_first_of(delims, init);
159      if (pos == std::string::npos) {
160        res.emplace_back(str.substr(init, str.length()));
161        break;
162      }
163      res.emplace_back(str.substr(init, pos - init));
164      pos++;
165      init = pos;
166    }
167
168    return res;
169  }
170
171
172  /*!
173   * Function: join
174   * Parameters:
175   * [in] vec : Vector of strings which needs to be joined to form
176   *            a single string with words seperated by a seperator char.
177   *  [in] sep : String used to seperate 2 words in the joined string.
178   *             Default constructed to ' ' (space).
179   *  [out] string: Joined string.
180   */
181  static inline
182  std::string join(const std::vector<std::string>& vec,
183                   const std::string& sep = " ")
184  {
185    std::string res;
186    for (auto& elem : vec) res.append(elem + sep);
187    res.erase(--res.end());
188    return res;
189  }
190
191
192  /*!
193   * Function: set_clo_on_exec
194   * Sets/Resets the FD_CLOEXEC flag on the provided file descriptor
195   * based upon the `set` parameter.
196   * Parameters:
197   * [in] fd : The descriptor on which FD_CLOEXEC needs to be set/reset.
198   * [in] set : If 'true', set FD_CLOEXEC.
199   *            If 'false' unset FD_CLOEXEC.
200   */
201  static inline
202  void set_clo_on_exec(int fd, bool set = true)
203  {
204    int flags = fcntl(fd, F_GETFD, 0);
205    if (set) flags |= FD_CLOEXEC;
206    else flags &= ~FD_CLOEXEC;
207    //TODO: should check for errors
208    fcntl(fd, F_SETFD, flags);
209  }
210
211
212  /*!
213   * Function: pipe_cloexec
214   * Creates a pipe and sets FD_CLOEXEC flag on both
215   * read and write descriptors of the pipe.
216   * Parameters:
217   * [out] : A pair of file descriptors.
218   *         First element of pair is the read descriptor of pipe.
219   *         Second element is the write descriptor of pipe.
220   */
221  static inline
222  std::pair<int, int> pipe_cloexec() throw (OSError)
223  {
224    int pipe_fds[2];
225    int res = pipe(pipe_fds);
226    if (res) {
227      throw OSError("pipe failure", errno);
228    }
229
230    set_clo_on_exec(pipe_fds[0]);
231    set_clo_on_exec(pipe_fds[1]);
232
233    return std::make_pair(pipe_fds[0], pipe_fds[1]);
234  }
235
236
237  /*!
238   * Function: write_n
239   * Writes `length` bytes to the file descriptor `fd`
240   * from the buffer `buf`.
241   * Parameters:
242   * [in] fd : The file descriptotr to write to.
243   * [in] buf: Buffer from which data needs to be written to fd.
244   * [in] length: The number of bytes that needs to be written from
245   *              `buf` to `fd`.
246   * [out] int : Number of bytes written or -1 in case of failure.
247   */
248  static inline
249  int write_n(int fd, const char* buf, size_t length)
250  {
251    size_t nwritten = 0;
252    while (nwritten < length) {
253      int written = write(fd, buf + nwritten, length - nwritten);
254      if (written == -1) return -1;
255      nwritten += written;
256    }
257    return nwritten;
258  }
259
260
261  /*!
262   * Function: read_atmost_n
263   * Reads at the most `read_upto` bytes from the
264   * file descriptor `fd` before returning.
265   * Parameters:
266   * [in] fd : The file descriptor from which it needs to read.
267   * [in] buf : The buffer into which it needs to write the data.
268   * [in] read_upto: Max number of bytes which must be read from `fd`.
269   * [out] int : Number of bytes written to `buf` or read from `fd`
270   *             OR -1 in case of error.
271   *  NOTE: In case of EINTR while reading from socket, this API
272   *  will retry to read from `fd`, but only till the EINTR counter
273   *  reaches 50 after which it will return with whatever data it read.
274   */
275  static inline
276  int read_atmost_n(int fd, char* buf, size_t read_upto)
277  {
278    int rbytes = 0;
279    int eintr_cnter = 0;
280
281    while (1) {
282      int read_bytes = read(fd, buf + rbytes, read_upto - rbytes);
283      if (read_bytes == -1) {
284        if (errno == EINTR) {
285          if (eintr_cnter >= 50) return -1;
286          eintr_cnter++;
287          continue;
288        }
289        return -1;
290      }
291      if (read_bytes == 0) return rbytes;
292
293      rbytes += read_bytes;
294    }
295    return rbytes;
296  }
297
298
299  /*!
300   * Function: read_all
301   * Reads all the available data from `fd` into
302   * `buf`. Internally calls read_atmost_n.
303   * Parameters:
304   * [in] fd : The file descriptor from which to read from.
305   * [in] buf : The buffer of type `class Buffer` into which
306   *            the read data is written to.
307   * [out] int: Number of bytes read OR -1 in case of failure.
308   *
309   * NOTE: `class Buffer` is a exposed public class. See below.
310   */
311  template <typename Buffer>
312  // Requires Buffer to be of type class Buffer
313  static inline int read_all(int fd, Buffer& buf)
314  {
315    size_t orig_size = buf.size();
316    int increment = orig_size;
317    auto buffer = buf.data();
318    int total_bytes_read = 0;
319
320    while (1) {
321      int rd_bytes = read_atmost_n(fd, buffer, buf.size());
322      if (rd_bytes == increment) {
323        // Resize the buffer to accomodate more
324        orig_size = orig_size * 1.5;
325        increment = orig_size - buf.size();
326        buf.resize(orig_size);
327        //update the buffer pointer
328        buffer = buf.data();
329        buffer += rd_bytes;
330        total_bytes_read += rd_bytes;
331
332      } else if (rd_bytes != -1) {
333        total_bytes_read += rd_bytes;
334        break;
335
336      } else {
337        if (total_bytes_read == 0) return -1;
338        break;
339      }
340    }
341    return total_bytes_read;
342  }
343
344
345  /*!
346   * Function: wait_for_child_exit
347   * Waits for the process with pid `pid` to exit
348   * and returns its status.
349   * Parameters:
350   * [in] pid : The pid of the process.
351   * [out] pair<int, int>:
352   *    pair.first : Return code of the waitpid call.
353   *    pair.second : Exit status of the process.
354   *
355   *  NOTE: This is a blocking call as in, it will loop
356   *  till the child is exited.
357   */
358  static inline
359  std::pair<int, int> wait_for_child_exit(int pid)
360  {
361    int status = 0;
362    int ret = -1;
363    while (1) {
364      ret = waitpid(pid, &status, WNOHANG);
365      if (ret == -1) break;
366      if (ret == 0) continue;
367      return std::make_pair(ret, status);
368    }
369
370    return std::make_pair(ret, status);
371  }
372
373
374}; // end namespace util
375
376
377
378/* -------------------------------
379 *     Popen Arguments
380 * -------------------------------
381 */
382
383/*!
384 * The buffer size of the stdin/stdout/stderr
385 * streams of the child process.
386 * Default value is 0.
387 */
388struct bufsize {
389  bufsize(int siz): bufsiz(siz) {}
390  int  bufsiz = 0;
391};
392
393/*!
394 * Option to defer spawning of the child process
395 * till `Popen::start_process` API is called.
396 * Default value is false.
397 */
398struct defer_spawn {
399  defer_spawn(bool d): defer(d) {}
400  bool defer  = false;
401};
402
403/*!
404 * Option to close all file descriptors
405 * when the child process is spawned.
406 * The close fd list does not include
407 * input/output/error if they are explicitly
408 * set as part of the Popen arguments.
409 *
410 * Default value is false.
411 */
412struct close_fds {
413  close_fds(bool c): close_all(c) {}
414  bool close_all = false;
415};
416
417/*!
418 * Option to make the child process as the
419 * session leader and thus the process
420 * group leader.
421 * Default value is false.
422 */
423struct session_leader {
424  session_leader(bool sl): leader_(sl) {}
425  bool leader_ = false;
426};
427
428struct shell {
429  shell(bool s): shell_(s) {}
430  bool shell_ = false;
431};
432
433/*!
434 * Base class for all arguments involving string value.
435 */
436struct string_arg
437{
438  string_arg(const char* arg): arg_value(arg) {}
439  string_arg(std::string&& arg): arg_value(std::move(arg)) {}
440  string_arg(std::string arg): arg_value(std::move(arg)) {}
441  std::string arg_value;
442};
443
444/*!
445 * Option to specify the executable name seperately
446 * from the args sequence.
447 * In this case the cmd args must only contain the
448 * options required for this executable.
449 *
450 * Eg: executable{"ls"}
451 */
452struct executable: string_arg
453{
454  template <typename T>
455  executable(T&& arg): string_arg(std::forward<T>(arg)) {}
456};
457
458/*!
459 * Option to set the current working directory
460 * of the spawned process.
461 *
462 * Eg: cwd{"/som/path/x"}
463 */
464struct cwd: string_arg
465{
466  template <typename T>
467  cwd(T&& arg): string_arg(std::forward<T>(arg)) {}
468};
469
470/*!
471 * Option to specify environment variables required by
472 * the spawned process.
473 *
474 * Eg: environment{{ {"K1", "V1"}, {"K2", "V2"},... }}
475 */
476struct environment
477{
478  environment(std::map<std::string, std::string>&& env):
479    env_(std::move(env)) {}
480  environment(const std::map<std::string, std::string>& env):
481    env_(env) {}
482  std::map<std::string, std::string> env_;
483};
484
485
486/*!
487 * Used for redirecting input/output/error
488 */
489enum IOTYPE {
490  STDOUT = 1,
491  STDERR,
492  PIPE,
493};
494
495//TODO: A common base/interface for below stream structures ??
496
497/*!
498 * Option to specify the input channel for the child
499 * process. It can be:
500 * 1. An already open file descriptor.
501 * 2. A file name.
502 * 3. IOTYPE. Usuall a PIPE
503 *
504 * Eg: input{PIPE}
505 * OR in case of redirection, output of another Popen
506 * input{popen.output()}
507 */
508struct input
509{
510  // For an already existing file descriptor.
511  input(int fd): rd_ch_(fd) {}
512
513  // FILE pointer.
514  input (FILE* fp):input(fileno(fp)) { assert(fp); }
515
516  input(const char* filename) {
517    int fd = open(filename, O_RDONLY);
518    if (fd == -1) throw OSError("File not found: ", errno);
519    rd_ch_ = fd;
520  }
521  input(IOTYPE typ) {
522    assert (typ == PIPE && "STDOUT/STDERR not allowed");
523    std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
524  }
525
526  int rd_ch_ = -1;
527  int wr_ch_ = -1;
528};
529
530
531/*!
532 * Option to specify the output channel for the child
533 * process. It can be:
534 * 1. An already open file descriptor.
535 * 2. A file name.
536 * 3. IOTYPE. Usually a PIPE.
537 *
538 * Eg: output{PIPE}
539 * OR output{"output.txt"}
540 */
541struct output
542{
543  output(int fd): wr_ch_(fd) {}
544
545  output (FILE* fp):output(fileno(fp)) { assert(fp); }
546
547  output(const char* filename) {
548    int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
549    if (fd == -1) throw OSError("File not found: ", errno);
550    wr_ch_ = fd;
551  }
552  output(IOTYPE typ) {
553    assert (typ == PIPE && "STDOUT/STDERR not allowed");
554    std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
555  }
556
557  int rd_ch_ = -1;
558  int wr_ch_ = -1;
559};
560
561
562/*!
563 * Option to specify the error channel for the child
564 * process. It can be:
565 * 1. An already open file descriptor.
566 * 2. A file name.
567 * 3. IOTYPE. Usually a PIPE or STDOUT
568 *
569 */
570struct error
571{
572  error(int fd): wr_ch_(fd) {}
573
574  error(FILE* fp):error(fileno(fp)) { assert(fp); }
575
576  error(const char* filename) {
577    int fd = open(filename, O_APPEND | O_CREAT | O_RDWR, 0640);
578    if (fd == -1) throw OSError("File not found: ", errno);
579    wr_ch_ = fd;
580  }
581  error(IOTYPE typ) {
582    assert ((typ == PIPE || typ == STDOUT) && "STDERR not aloowed");
583    if (typ == PIPE) {
584      std::tie(rd_ch_, wr_ch_) = util::pipe_cloexec();
585    } else {
586      // Need to defer it till we have checked all arguments
587      deferred_ = true;
588    }
589  }
590
591  bool deferred_ = false;
592  int rd_ch_ = -1;
593  int wr_ch_ = -1;
594};
595
596// Impoverished, meager, needy, truly needy
597// version of type erasure to store function pointers
598// needed to provide the functionality of preexec_func
599// ATTN: Can be used only to execute functions with no
600// arguments and returning void.
601// Could have used more efficient methods, ofcourse, but
602// that wont yield me the consistent syntax which I am
603// aiming for. If you know, then please do let me know.
604
605class preexec_func
606{
607public:
608  preexec_func() {}
609
610  template <typename Func>
611  preexec_func(Func f): holder_(new FuncHolder<Func>(f))
612  {}
613
614  void operator()() {
615    (*holder_)();
616  }
617
618private:
619  struct HolderBase {
620    virtual void operator()() const = 0;
621  };
622  template <typename T>
623  struct FuncHolder: HolderBase {
624    FuncHolder(T func): func_(func) {}
625    void operator()() const override {}
626    // The function pointer/reference
627    T func_;
628  };
629
630  std::unique_ptr<HolderBase> holder_ = nullptr;
631};
632
633// ~~~~ End Popen Args ~~~~
634
635
636/*!
637 * class: Buffer
638 * This class is a very thin wrapper around std::vector<char>
639 * This is basically used to determine the length of the actual
640 * data stored inside the dynamically resized vector.
641 *
642 * This is what is returned as the output to communicate and check_output
643 * functions, so, users must know about this class.
644 *
645 * OutBuffer and ErrBuffer are just different typedefs to this class.
646 */
647class Buffer
648{
649public:
650  Buffer() {}
651  Buffer(size_t cap) { buf.resize(cap); }
652  void add_cap(size_t cap) { buf.resize(cap); }
653
654#if 0
655  Buffer(const Buffer& other):
656    buf(other.buf),
657    length(other.length)
658  {
659    std::cout << "COPY" << std::endl;
660  }
661
662  Buffer(Buffer&& other):
663    buf(std::move(other.buf)),
664    length(other.length)
665  {
666    std::cout << "MOVE" << std::endl;
667  }
668#endif
669
670public:
671  std::vector<char> buf;
672  size_t length = 0;
673};
674
675// Buffer for storing output written to output fd
676using OutBuffer = Buffer;
677// Buffer for storing output written to error fd
678using ErrBuffer = Buffer;
679
680
681// Fwd Decl.
682class Popen;
683
684/*---------------------------------------------------
685 *      DETAIL NAMESPACE
686 *---------------------------------------------------
687 */
688
689namespace detail {
690
691// Metaprogram for searching a type within
692// a variadic parameter pack
693// This is particularly required to do a compile time
694// checking of the arguments provided to 'check_ouput' function
695// wherein the user is not expected to provide an 'ouput' option.
696
697template <typename... T> struct param_pack{};
698
699template <typename F, typename T> struct has_type;
700
701template <typename F>
702struct has_type<F, param_pack<> > {
703  static constexpr bool value = false;
704};
705
706template <typename F, typename... T>
707struct has_type<F, param_pack<F, T...> > {
708  static constexpr bool value = true;
709};
710
711template <typename F, typename H, typename... T>
712struct has_type<F, param_pack<H,T...> > {
713  static constexpr bool value =
714    std::is_same<F, typename std::decay<H>::type>::value ? true : has_type<F, param_pack<T...> >::value;
715};
716
717//----
718
719/*!
720 * A helper class to Popen class for setting
721 * options as provided in the Popen constructor
722 * or in check_ouput arguments.
723 * This design allows us to _not_ have any fixed position
724 * to any arguments and specify them in a way similar to what
725 * can be done in python.
726 */
727struct ArgumentDeducer
728{
729  ArgumentDeducer(Popen* p): popen_(p) {}
730
731  void set_option(executable&& exe);
732  void set_option(cwd&& cwdir);
733  void set_option(bufsize&& bsiz);
734  void set_option(environment&& env);
735  void set_option(defer_spawn&& defer);
736  void set_option(shell&& sh);
737  void set_option(input&& inp);
738  void set_option(output&& out);
739  void set_option(error&& err);
740  void set_option(close_fds&& cfds);
741  void set_option(preexec_func&& prefunc);
742  void set_option(session_leader&& sleader);
743
744private:
745  Popen* popen_ = nullptr;
746};
747
748/*!
749 * A helper class to Popen.
750 * This takes care of all the fork-exec logic
751 * in the execute_child API.
752 */
753class Child
754{
755public:
756  Child(Popen* p, int err_wr_pipe):
757    parent_(p),
758    err_wr_pipe_(err_wr_pipe)
759  {}
760
761  void execute_child();
762
763private:
764  // Lets call it parent even though
765  // technically a bit incorrect
766  Popen* parent_ = nullptr;
767  int err_wr_pipe_ = -1;
768};
769
770// Fwd Decl.
771class Streams;
772
773/*!
774 * A helper class to Streams.
775 * This takes care of management of communicating
776 * with the child process with the means of the correct
777 * file descriptor.
778 */
779class Communication
780{
781public:
782  Communication(Streams* stream): stream_(stream)
783  {}
784  void operator=(const Communication&) = delete;
785public:
786  int send(const char* msg, size_t length);
787  int send(const std::vector<char>& msg);
788
789  std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length);
790  std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
791  { return communicate(msg.data(), msg.size()); }
792
793  void set_out_buf_cap(size_t cap) { out_buf_cap_ = cap; }
794  void set_err_buf_cap(size_t cap) { err_buf_cap_ = cap; }
795
796private:
797  std::pair<OutBuffer, ErrBuffer> communicate_threaded(
798      const char* msg, size_t length);
799
800private:
801  Streams* stream_;
802  size_t out_buf_cap_ = DEFAULT_BUF_CAP_BYTES;
803  size_t err_buf_cap_ = DEFAULT_BUF_CAP_BYTES;
804};
805
806
807
808/*!
809 * This is a helper class to Popen.
810 * It takes care of management of all the file descriptors
811 * and file pointers.
812 * It dispatches of the communication aspects to the
813 * Communication class.
814 * Read through the data members to understand about the
815 * various file descriptors used.
816 */
817class Streams
818{
819public:
820  Streams():comm_(this) {}
821  void operator=(const Streams&) = delete;
822
823public:
824  void setup_comm_channels();
825
826  void cleanup_fds()
827  {
828    if (write_to_child_ != -1 && read_from_parent_ != -1) {
829      close(write_to_child_);
830    }
831    if (write_to_parent_ != -1 && read_from_child_ != -1) {
832      close(read_from_child_);
833    }
834    if (err_write_ != -1 && err_read_ != -1) {
835      close(err_read_);
836    }
837  }
838
839  void close_parent_fds()
840  {
841    if (write_to_child_ != -1)  close(write_to_child_);
842    if (read_from_child_ != -1) close(read_from_child_);
843    if (err_read_ != -1)        close(err_read_);
844  }
845
846  void close_child_fds()
847  {
848    if (write_to_parent_ != -1)  close(write_to_parent_);
849    if (read_from_parent_ != -1) close(read_from_parent_);
850    if (err_write_ != -1)        close(err_write_);
851  }
852
853  FILE* input()  { return input_.get(); }
854  FILE* output() { return output_.get(); }
855  FILE* error()  { return error_.get(); }
856
857  void input(FILE* fp)  { input_.reset(fp, fclose); }
858  void output(FILE* fp) { output_.reset(fp, fclose); }
859  void error(FILE* fp)  { error_.reset(fp, fclose); }
860
861  void set_out_buf_cap(size_t cap) { comm_.set_out_buf_cap(cap); }
862  void set_err_buf_cap(size_t cap) { comm_.set_err_buf_cap(cap); }
863
864public: /* Communication forwarding API's */
865  int send(const char* msg, size_t length)
866  { return comm_.send(msg, length); }
867
868  int send(const std::vector<char>& msg)
869  { return comm_.send(msg); }
870
871  std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
872  { return comm_.communicate(msg, length); }
873
874  std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
875  { return comm_.communicate(msg); }
876
877
878public:// Yes they are public
879
880  std::shared_ptr<FILE> input_  = nullptr;
881  std::shared_ptr<FILE> output_ = nullptr;
882  std::shared_ptr<FILE> error_  = nullptr;
883
884  // Buffer size for the IO streams
885  int bufsiz_ = 0;
886
887  // Pipes for communicating with child
888
889  // Emulates stdin
890  int write_to_child_   = -1; // Parent owned descriptor
891  int read_from_parent_ = -1; // Child owned descriptor
892
893  // Emulates stdout
894  int write_to_parent_ = -1; // Child owned descriptor
895  int read_from_child_ = -1; // Parent owned descriptor
896
897  // Emulates stderr
898  int err_write_ = -1; // Write error to parent (Child owned)
899  int err_read_  = -1; // Read error from child (Parent owned)
900
901private:
902  Communication comm_;
903};
904
905}; // end namespace detail
906
907
908
909/*!
910 * class: Popen
911 * This is the single most important class in the whole library
912 * and glues together all the helper classes to provide a common
913 * interface to the client.
914 *
915 * API's provided by the class:
916 * 1. Popen({"cmd"}, output{..}, error{..}, cwd{..}, ....)
917 *    Command provided as a sequence.
918 * 2. Popen("cmd arg1"m output{..}, error{..}, cwd{..}, ....)
919 *    Command provided in a single string.
920 * 3. wait()             - Wait for the child to exit.
921 * 4. retcode()          - The return code of the exited child.
922 * 5. pid()              - PID of the spawned child.
923 * 6. poll()             - Check the status of the running child.
924 * 7. kill(sig_num)      - Kill the child. SIGTERM used by default.
925 * 8. send(...)          - Send input to the input channel of the child.
926 * 9. communicate(...)   - Get the output/error from the child and close the channels
927 *                         from the parent side.
928 *10. input()            - Get the input channel/File pointer. Can be used for
929 *                         cutomizing the way of sending input to child.
930 *11. output()           - Get the output channel/File pointer. Usually used
931                           in case of redirection. See piping examples.
932 *12. error()            - Get the error channel/File poiner. Usually used
933                           in case of redirection.
934 *13. start_process()    - Start the child process. Only to be used when
935 *                         `defer_spawn` option was provided in Popen constructor.
936 */
937class Popen
938{
939public:
940  friend struct detail::ArgumentDeducer;
941  friend class detail::Child;
942
943  template <typename... Args>
944  Popen(const std::string& cmd_args, Args&& ...args):
945    args_(cmd_args)
946  {
947    vargs_ = util::split(cmd_args);
948    init_args(std::forward<Args>(args)...);
949
950    // Setup the communication channels of the Popen class
951    stream_.setup_comm_channels();
952
953    if (!defer_process_start_) execute_process();
954  }
955
956  template <typename... Args>
957  Popen(std::initializer_list<const char*> cmd_args, Args&& ...args)
958  {
959    vargs_.insert(vargs_.end(), cmd_args.begin(), cmd_args.end());
960    init_args(std::forward<Args>(args)...);
961
962    // Setup the communication channels of the Popen class
963    stream_.setup_comm_channels();
964
965    if (!defer_process_start_) execute_process();
966  }
967
968  void start_process() throw (CalledProcessError, OSError);
969
970  int pid() const noexcept { return child_pid_; }
971
972  int retcode() const noexcept { return retcode_; }
973
974  int wait() throw(OSError);
975
976  int poll() throw(OSError);
977
978  // Does not fail, Caller is expected to recheck the
979  // status with a call to poll()
980  void kill(int sig_num = 9);
981
982  void set_out_buf_cap(size_t cap) { stream_.set_out_buf_cap(cap); }
983
984  void set_err_buf_cap(size_t cap) { stream_.set_err_buf_cap(cap); }
985
986  int send(const char* msg, size_t length)
987  { return stream_.send(msg, length); }
988
989  int send(const std::vector<char>& msg)
990  { return stream_.send(msg); }
991
992  std::pair<OutBuffer, ErrBuffer> communicate(const char* msg, size_t length)
993  {
994    auto res = stream_.communicate(msg, length);
995    retcode_ = wait();
996    return res;
997  }
998
999  std::pair<OutBuffer, ErrBuffer> communicate(const std::vector<char>& msg)
1000  {
1001    auto res = stream_.communicate(msg);
1002    retcode_ = wait();
1003    return res;
1004  }
1005
1006  std::pair<OutBuffer, ErrBuffer> communicate()
1007  {
1008    return communicate(nullptr, 0);
1009  }
1010
1011  FILE* input()  { return stream_.input(); }
1012  FILE* output() { return stream_.output();}
1013  FILE* error()  { return stream_.error(); }
1014
1015private:
1016  template <typename F, typename... Args>
1017  void init_args(F&& farg, Args&&... args);
1018  void init_args();
1019  void populate_c_argv();
1020  void execute_process() throw (CalledProcessError, OSError);
1021
1022private:
1023  detail::Streams stream_;
1024
1025  bool defer_process_start_ = false;
1026  bool close_fds_ = false;
1027  bool has_preexec_fn_ = false;
1028  bool shell_ = false;
1029  bool session_leader_ = false;
1030
1031  std::string exe_name_;
1032  std::string cwd_;
1033  std::map<std::string, std::string> env_;
1034  preexec_func preexec_fn_;
1035
1036  // Command in string format
1037  std::string args_;
1038  // Comamnd provided as sequence
1039  std::vector<std::string> vargs_;
1040  std::vector<char*> cargv_;
1041
1042  bool child_created_ = false;
1043  // Pid of the child process
1044  int child_pid_ = -1;
1045
1046  int retcode_ = -1;
1047};
1048
1049inline void Popen::init_args() {
1050  populate_c_argv();
1051}
1052
1053template <typename F, typename... Args>
1054inline void Popen::init_args(F&& farg, Args&&... args)
1055{
1056  detail::ArgumentDeducer argd(this);
1057  argd.set_option(std::forward<F>(farg));
1058  init_args(std::forward<Args>(args)...);
1059}
1060
1061inline void Popen::populate_c_argv()
1062{
1063  cargv_.clear();
1064  cargv_.reserve(vargs_.size() + 1);
1065  for (auto& arg : vargs_) cargv_.push_back(&arg[0]);
1066  cargv_.push_back(nullptr);
1067}
1068
1069inline void Popen::start_process() throw (CalledProcessError, OSError)
1070{
1071  // The process was started/tried to be started
1072  // in the constructor itself.
1073  // For explicitly calling this API to start the
1074  // process, 'defer_spawn' argument must be set to
1075  // true in the constructor.
1076  if (!defer_process_start_) {
1077    assert (0);
1078    return;
1079  }
1080  execute_process();
1081}
1082
1083inline int Popen::wait() throw (OSError)
1084{
1085  int ret, status;
1086  std::tie(ret, status) = util::wait_for_child_exit(pid());
1087  if (ret == -1) {
1088    if (errno != ECHILD) throw OSError("waitpid failed", errno);
1089    return 0;
1090  }
1091  if (WIFEXITED(status)) return WEXITSTATUS(status);
1092  if (WIFSIGNALED(status)) return WTERMSIG(status);
1093  else return 255;
1094
1095  return 0;
1096}
1097
1098inline int Popen::poll() throw (OSError)
1099{
1100  int status;
1101  if (!child_created_) return -1; // TODO: ??
1102
1103  // Returns zero if child is still running
1104  int ret = waitpid(child_pid_, &status, WNOHANG);
1105  if (ret == 0) return -1;
1106
1107  if (ret == child_pid_) {
1108    if (WIFSIGNALED(status)) {
1109      retcode_ = WTERMSIG(status);
1110    } else if (WIFEXITED(status)) {
1111      retcode_ = WEXITSTATUS(status);
1112    } else {
1113      retcode_ = 255;
1114    }
1115    return retcode_;
1116  }
1117
1118  if (ret == -1) {
1119    // From subprocess.py
1120    // This happens if SIGCHLD is set to be ignored
1121    // or waiting for child process has otherwise been disabled
1122    // for our process. This child is dead, we cannot get the
1123    // status.
1124    if (errno == ECHILD) retcode_ = 0;
1125    else throw OSError("waitpid failed", errno);
1126  } else {
1127    retcode_ = ret;
1128  }
1129
1130  return retcode_;
1131}
1132
1133inline void Popen::kill(int sig_num)
1134{
1135  if (session_leader_) killpg(child_pid_, sig_num);
1136  else ::kill(child_pid_, sig_num);
1137}
1138
1139
1140inline void Popen::execute_process() throw (CalledProcessError, OSError)
1141{
1142  int err_rd_pipe, err_wr_pipe;
1143  std::tie(err_rd_pipe, err_wr_pipe) = util::pipe_cloexec();
1144
1145  if (shell_) {
1146    auto new_cmd = util::join(vargs_);
1147    vargs_.clear();
1148    vargs_.insert(vargs_.begin(), {"/bin/sh", "-c"});
1149    vargs_.push_back(new_cmd);
1150    populate_c_argv();
1151  }
1152
1153  if (exe_name_.length()) {
1154    vargs_.insert(vargs_.begin(), exe_name_);
1155    populate_c_argv();
1156  }
1157  exe_name_ = vargs_[0];
1158
1159  child_pid_ = fork();
1160
1161  if (child_pid_ < 0) {
1162    close(err_rd_pipe);
1163    close(err_wr_pipe);
1164    throw OSError("fork failed", errno);
1165  }
1166
1167  child_created_ = true;
1168
1169  if (child_pid_ == 0)
1170  {
1171    // Close descriptors belonging to parent
1172    stream_.close_parent_fds();
1173
1174    //Close the read end of the error pipe
1175    close(err_rd_pipe);
1176
1177    detail::Child chld(this, err_wr_pipe);
1178    chld.execute_child();
1179  }
1180  else
1181  {
1182    close (err_wr_pipe);// close child side of pipe, else get stuck in read below
1183
1184    stream_.close_child_fds();
1185
1186    try {
1187      char err_buf[SP_MAX_ERR_BUF_SIZ] = {0,};
1188
1189      int read_bytes = util::read_atmost_n(
1190                                  err_rd_pipe,
1191                                  err_buf,
1192                                  SP_MAX_ERR_BUF_SIZ);
1193      close(err_rd_pipe);
1194
1195      if (read_bytes || strlen(err_buf)) {
1196        // Call waitpid to reap the child process
1197        // waitpid suspends the calling process until the
1198        // child terminates.
1199        wait();
1200
1201        // Throw whatever information we have about child failure
1202        throw CalledProcessError(err_buf);
1203      }
1204    } catch (std::exception& exp) {
1205      stream_.cleanup_fds();
1206      throw exp;
1207    }
1208
1209  }
1210}
1211
1212namespace detail {
1213
1214  inline void ArgumentDeducer::set_option(executable&& exe) {
1215    popen_->exe_name_ = std::move(exe.arg_value);
1216  }
1217
1218  inline void ArgumentDeducer::set_option(cwd&& cwdir) {
1219    popen_->cwd_ = std::move(cwdir.arg_value);
1220  }
1221
1222  inline void ArgumentDeducer::set_option(bufsize&& bsiz) {
1223    popen_->stream_.bufsiz_ = bsiz.bufsiz;
1224  }
1225
1226  inline void ArgumentDeducer::set_option(environment&& env) {
1227    popen_->env_ = std::move(env.env_);
1228  }
1229
1230  inline void ArgumentDeducer::set_option(defer_spawn&& defer) {
1231    popen_->defer_process_start_ = defer.defer;
1232  }
1233
1234  inline void ArgumentDeducer::set_option(shell&& sh) {
1235    popen_->shell_ = sh.shell_;
1236  }
1237
1238  inline void ArgumentDeducer::set_option(session_leader&& sleader) {
1239    popen_->session_leader_ = sleader.leader_;
1240  }
1241
1242  inline void ArgumentDeducer::set_option(input&& inp) {
1243    if (inp.rd_ch_ != -1) popen_->stream_.read_from_parent_ = inp.rd_ch_;
1244    if (inp.wr_ch_ != -1) popen_->stream_.write_to_child_ = inp.wr_ch_;
1245  }
1246
1247  inline void ArgumentDeducer::set_option(output&& out) {
1248    if (out.wr_ch_ != -1) popen_->stream_.write_to_parent_ = out.wr_ch_;
1249    if (out.rd_ch_ != -1) popen_->stream_.read_from_child_ = out.rd_ch_;
1250  }
1251
1252  inline void ArgumentDeducer::set_option(error&& err) {
1253    if (err.deferred_) {
1254      if (popen_->stream_.write_to_parent_) {
1255        popen_->stream_.err_write_ = popen_->stream_.write_to_parent_;
1256      } else {
1257        throw std::runtime_error("Set output before redirecting error to output");
1258      }
1259    }
1260    if (err.wr_ch_ != -1) popen_->stream_.err_write_ = err.wr_ch_;
1261    if (err.rd_ch_ != -1) popen_->stream_.err_read_ = err.rd_ch_;
1262  }
1263
1264  inline void ArgumentDeducer::set_option(close_fds&& cfds) {
1265    popen_->close_fds_ = cfds.close_all;
1266  }
1267
1268  inline void ArgumentDeducer::set_option(preexec_func&& prefunc) {
1269    popen_->preexec_fn_ = std::move(prefunc);
1270    popen_->has_preexec_fn_ = true;
1271  }
1272
1273
1274  inline void Child::execute_child() {
1275    int sys_ret = -1;
1276    auto& stream = parent_->stream_;
1277
1278    try {
1279      if (stream.write_to_parent_ == 0)
1280        stream.write_to_parent_ = dup(stream.write_to_parent_);
1281
1282      if (stream.err_write_ == 0 || stream.err_write_ == 1)
1283        stream.err_write_ = dup(stream.err_write_);
1284
1285      // Make the child owned descriptors as the
1286      // stdin, stdout and stderr for the child process
1287      auto _dup2_ = [](int fd, int to_fd) {
1288        if (fd == to_fd) {
1289          // dup2 syscall does not reset the
1290          // CLOEXEC flag if the descriptors
1291          // provided to it are same.
1292          // But, we need to reset the CLOEXEC
1293          // flag as the provided descriptors
1294          // are now going to be the standard
1295          // input, output and error
1296          util::set_clo_on_exec(fd, false);
1297        } else if(fd != -1) {
1298          int res = dup2(fd, to_fd);
1299          if (res == -1) throw OSError("dup2 failed", errno);
1300        }
1301      };
1302
1303      // Create the standard streams
1304      _dup2_(stream.read_from_parent_, 0); // Input stream
1305      _dup2_(stream.write_to_parent_,  1); // Output stream
1306      _dup2_(stream.err_write_,        2); // Error stream
1307
1308      // Close the duped descriptors
1309      if (stream.read_from_parent_ != -1 && stream.read_from_parent_ > 2)
1310        close(stream.read_from_parent_);
1311
1312      if (stream.write_to_parent_ != -1 && stream.write_to_parent_ > 2)
1313        close(stream.write_to_parent_);
1314
1315      if (stream.err_write_ != -1 && stream.err_write_ > 2)
1316        close(stream.err_write_);
1317
1318      // Close all the inherited fd's except the error write pipe
1319      if (parent_->close_fds_) {
1320        int max_fd = sysconf(_SC_OPEN_MAX);
1321        if (max_fd == -1) throw OSError("sysconf failed", errno);
1322
1323        for (int i = 3; i < max_fd; i++) {
1324          if (i == err_wr_pipe_) continue;
1325          close(i);
1326        }
1327      }
1328
1329      // Change the working directory if provided
1330      if (parent_->cwd_.length()) {
1331        sys_ret = chdir(parent_->cwd_.c_str());
1332        if (sys_ret == -1) throw OSError("chdir failed", errno);
1333      }
1334
1335      if (parent_->has_preexec_fn_) {
1336        parent_->preexec_fn_();
1337      }
1338
1339      if (parent_->session_leader_) {
1340        sys_ret = setsid();
1341        if (sys_ret == -1) throw OSError("setsid failed", errno);
1342      }
1343
1344      // Replace the current image with the executable
1345      if (parent_->env_.size()) {
1346        for (auto& kv : parent_->env_) {
1347          setenv(kv.first.c_str(), kv.second.c_str(), 1);
1348        }
1349        sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
1350      } else {
1351        sys_ret = execvp(parent_->exe_name_.c_str(), parent_->cargv_.data());
1352      }
1353
1354      if (sys_ret == -1) throw OSError("execve failed", errno);
1355
1356    } catch (const OSError& exp) {
1357      // Just write the exception message
1358      // TODO: Give back stack trace ?
1359      std::string err_msg(exp.what());
1360      //ATTN: Can we do something on error here ?
1361      util::write_n(err_wr_pipe_, err_msg.c_str(), err_msg.length());
1362      throw exp;
1363    }
1364
1365    // Calling application would not get this
1366    // exit failure
1367    exit (EXIT_FAILURE);
1368  }
1369
1370
1371  inline void Streams::setup_comm_channels()
1372  {
1373    if (write_to_child_ != -1)  input(fdopen(write_to_child_, "wb"));
1374    if (read_from_child_ != -1) output(fdopen(read_from_child_, "rb"));
1375    if (err_read_ != -1)        error(fdopen(err_read_, "rb"));
1376
1377    auto handles = {input(), output(), error()};
1378
1379    for (auto& h : handles) {
1380      if (h == nullptr) continue;
1381      switch (bufsiz_) {
1382      case 0:
1383        setvbuf(h, nullptr, _IONBF, BUFSIZ);
1384        break;
1385      case 1:
1386        setvbuf(h, nullptr, _IONBF, BUFSIZ);
1387        break;
1388      default:
1389        setvbuf(h, nullptr, _IOFBF, bufsiz_);
1390      };
1391    }
1392  }
1393
1394  inline int Communication::send(const char* msg, size_t length)
1395  {
1396    if (stream_->input() == nullptr) return -1;
1397    return std::fwrite(msg, sizeof(char), length, stream_->input());
1398  }
1399
1400  inline int Communication::send(const std::vector<char>& msg)
1401  {
1402    return send(msg.data(), msg.size());
1403  }
1404
1405  inline std::pair<OutBuffer, ErrBuffer>
1406  Communication::communicate(const char* msg, size_t length)
1407  {
1408    // Optimization from subprocess.py
1409    // If we are using one pipe, or no pipe
1410    // at all, using select() or threads is unnecessary.
1411    auto hndls = {stream_->input(), stream_->output(), stream_->error()};
1412    int count = std::count(std::begin(hndls), std::end(hndls), nullptr);
1413    const int len_conv = length;
1414
1415    if (count >= 2) {
1416      OutBuffer obuf;
1417      ErrBuffer ebuf;
1418      if (stream_->input()) {
1419        if (msg) {
1420          int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
1421          if (wbytes < len_conv) {
1422            if (errno != EPIPE && errno != EINVAL) {
1423              throw OSError("fwrite error", errno);
1424            }
1425          }
1426        }
1427        // Close the input stream
1428        stream_->input_.reset();
1429      } else if (stream_->output()) {
1430        // Read till EOF
1431        // ATTN: This could be blocking, if the process
1432        // at the other end screws up, we get screwed as well
1433        obuf.add_cap(out_buf_cap_);
1434
1435        int rbytes = util::read_all(
1436                            fileno(stream_->output()),
1437                            obuf.buf);
1438
1439        if (rbytes == -1) {
1440          throw OSError("read to obuf failed", errno);
1441        }
1442
1443        obuf.length = rbytes;
1444        // Close the output stream
1445        stream_->output_.reset();
1446
1447      } else if (stream_->error()) {
1448        // Same screwness applies here as well
1449        ebuf.add_cap(err_buf_cap_);
1450
1451        int rbytes = util::read_atmost_n(
1452                                  fileno(stream_->error()),
1453                                  ebuf.buf.data(),
1454                                  ebuf.buf.size());
1455
1456        if (rbytes == -1) {
1457          throw OSError("read to ebuf failed", errno);
1458        }
1459
1460        ebuf.length = rbytes;
1461        // Close the error stream
1462        stream_->error_.reset();
1463      }
1464      return std::make_pair(std::move(obuf), std::move(ebuf));
1465    }
1466
1467    return communicate_threaded(msg, length);
1468  }
1469
1470
1471  inline std::pair<OutBuffer, ErrBuffer>
1472  Communication::communicate_threaded(const char* msg, size_t length)
1473  {
1474    OutBuffer obuf;
1475    ErrBuffer ebuf;
1476    std::future<int> out_fut, err_fut;
1477    const int length_conv = length;
1478
1479    if (stream_->output()) {
1480      obuf.add_cap(out_buf_cap_);
1481
1482      out_fut = std::async(std::launch::async,
1483                          [&obuf, this] {
1484                            return util::read_all(fileno(this->stream_->output()), obuf.buf);
1485                          });
1486    }
1487    if (stream_->error()) {
1488      ebuf.add_cap(err_buf_cap_);
1489
1490      err_fut = std::async(std::launch::async,
1491                          [&ebuf, this] {
1492                            return util::read_all(fileno(this->stream_->error()), ebuf.buf);
1493                          });
1494    }
1495    if (stream_->input()) {
1496      if (msg) {
1497        int wbytes = std::fwrite(msg, sizeof(char), length, stream_->input());
1498        if (wbytes < length_conv) {
1499          if (errno != EPIPE && errno != EINVAL) {
1500            throw OSError("fwrite error", errno);
1501          }
1502        }
1503      }
1504      stream_->input_.reset();
1505    }
1506
1507    if (out_fut.valid()) {
1508      int res = out_fut.get();
1509      if (res != -1) obuf.length = res;
1510      else obuf.length = 0;
1511    }
1512    if (err_fut.valid()) {
1513      int res = err_fut.get();
1514      if (res != -1) ebuf.length = res;
1515      else ebuf.length = 0;
1516    }
1517
1518    return std::make_pair(std::move(obuf), std::move(ebuf));
1519  }
1520
1521}; // end namespace detail
1522
1523
1524
1525// Convenience Functions
1526//
1527//
1528namespace detail
1529{
1530  template<typename F, typename... Args>
1531  OutBuffer check_output_impl(F& farg, Args&&... args)
1532  {
1533    static_assert(!detail::has_type<output, detail::param_pack<Args...> >::value, "output not allowed in args");
1534    auto p = Popen(std::forward<F>(farg), std::forward<Args>(args)..., output{PIPE});
1535    auto res = p.communicate();
1536    auto retcode = p.poll();
1537    if (retcode > 0) {
1538      throw CalledProcessError("Command failed : Non zero retcode");
1539    }
1540    return std::move(res.first);
1541  }
1542
1543  template<typename F, typename... Args>
1544  int call_impl(F& farg, Args&&... args)
1545  {
1546    return Popen(std::forward<F>(farg), std::forward<Args>(args)...).wait();
1547  }
1548
1549  static inline void pipeline_impl(std::vector<Popen>& cmds)
1550  {
1551    /* EMPTY IMPL */
1552  }
1553
1554  template<typename... Args>
1555  static inline void pipeline_impl(std::vector<Popen>& cmds,
1556                                   const std::string& cmd,
1557                                   Args&&... args)
1558  {
1559    if (cmds.size() == 0) {
1560      cmds.emplace_back(cmd, output{PIPE}, defer_spawn{true});
1561    } else {
1562      cmds.emplace_back(cmd, input{cmds.back().output()}, output{PIPE}, defer_spawn{true});
1563    }
1564
1565    pipeline_impl(cmds, std::forward<Args>(args)...);
1566  }
1567
1568}
1569
1570/*-----------------------------------------------------------
1571 *        CONVIENIENCE FUNCTIONS
1572 *-----------------------------------------------------------
1573 */
1574
1575
1576/*!
1577 * Run the command with arguments and wait for it to complete.
1578 * The parameters passed to the argument are exactly the same
1579 * one would use for Popen constructors.
1580 */
1581template<typename... Args>
1582int call(std::initializer_list<const char*> plist, Args&&... args)
1583{
1584  return (detail::call_impl(plist, std::forward<Args>(args)...));
1585}
1586
1587template<typename... Args>
1588int call(const std::string& arg, Args&&... args)
1589{
1590  return (detail::call_impl(arg, std::forward<Args>(args)...));
1591}
1592
1593
1594/*!
1595 * Run the command with arguments and wait for it to complete.
1596 * If the exit code was non-zero it raises a CalledProcessError.
1597 * The arguments are the same as for the Popen constructor.
1598 */
1599template <typename... Args>
1600OutBuffer check_output(std::initializer_list<const char*> plist, Args&&... args)
1601{
1602  return (detail::check_output_impl(plist, std::forward<Args>(args)...));
1603}
1604
1605template <typename... Args>
1606OutBuffer check_output(const std::string& arg, Args&&... args)
1607{
1608  return (detail::check_output_impl(arg, std::forward<Args>(args)...));
1609}
1610
1611
1612/*!
1613 * An easy way to pipeline easy commands.
1614 * Provide the commands that needs to be pipelined in the order they
1615 * would appear in a regular command.
1616 * It would wait for the last command provided in the pipeline
1617 * to finish and then return the OutBuffer.
1618 */
1619template<typename... Args>
1620// Args expected to be flat commands using string instead of initializer_list
1621OutBuffer pipeline(Args&&... args)
1622{
1623  std::vector<Popen> pcmds;
1624  detail::pipeline_impl(pcmds, std::forward<Args>(args)...);
1625
1626  for (auto& p : pcmds) p.start_process();
1627  return (pcmds.back().communicate().first);
1628}
1629
1630};
1631
1632#endif // __cplusplus
1633
1634#endif // SUBPROCESS_HPP
Note: See TracBrowser for help on using the repository browser.