summaryrefslogtreecommitdiffstats
path: root/src/common/SubProcess.h
blob: ea81000d6f3a1af4d2453a8cdf61663fcc4bf3e7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
/*
 * Ceph distributed storage system
 *
 * Copyright (C) 2015 Mirantis Inc
 *
 * Author: Mykola Golub <mgolub@mirantis.com>
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 */

#ifndef SUB_PROCESS_H
#define SUB_PROCESS_H

#if defined(__FreeBSD__) || defined(__APPLE__)
#include <signal.h>
#endif

#ifndef _WIN32
#include <sys/wait.h>
#endif
#include <sstream>
#include <vector>

#include "include/compat.h"

/**
 * SubProcess:
 * A helper class to spawn a subprocess.
 *
 * Example:
 *
 *   SubProcess cat("cat", SubProcess::PIPE, SubProcess::PIPE);
 *   if (cat.spawn() != 0) {
 *     std::cerr << "cat failed: " << cat.err() << std::endl;
 *     return false;
 *   }
 *   write_to_fd(cat.get_stdout(), "hello world!\n");
 *   cat.close_stdout();
 *   read_from_fd(cat.get_stdin(), buf);
 *   if (cat.join() != 0) {
 *     std::cerr << cat.err() << std::endl;
 *     return false;
 *   }
 */

class SubProcess {
public:
  enum std_fd_op{
    KEEP,
    CLOSE,
    PIPE
  };
public:
  SubProcess(const char *cmd,
             std_fd_op stdin_op = CLOSE,
             std_fd_op stdout_op = CLOSE,
             std_fd_op stderr_op = CLOSE);
  virtual ~SubProcess();

  void add_cmd_args(const char *arg, ...);
  void add_cmd_arg(const char *arg);

  virtual int spawn(); // Returns 0 on success or -errno on failure.
  virtual int join();  // Returns exit code (0 on success).

  bool is_spawned() const { return pid > 0; }

  int get_stdin() const;
  int get_stdout() const;
  int get_stderr() const;

  void close_stdin();
  void close_stdout();
  void close_stderr();

  void kill(int signo = SIGTERM) const;

  const std::string err() const;

protected:
  bool is_child() const { return pid == 0; }
  virtual void exec();

  void close(int &fd);

#ifdef _WIN32
  void close_h(HANDLE &handle);
#endif

protected:
  std::string cmd;
  std::vector<std::string> cmd_args;
  std_fd_op stdin_op;
  std_fd_op stdout_op;
  std_fd_op stderr_op;
  int stdin_pipe_out_fd;
  int stdout_pipe_in_fd;
  int stderr_pipe_in_fd;
  int pid;
  std::ostringstream errstr;

#ifdef _WIN32
  HANDLE proc_handle = INVALID_HANDLE_VALUE;
#endif
};

class SubProcessTimed : public SubProcess {
public:
  SubProcessTimed(const char *cmd, std_fd_op stdin_op = CLOSE,
		  std_fd_op stdout_op = CLOSE, std_fd_op stderr_op = CLOSE,
		  int timeout = 0, int sigkill = SIGKILL);

#ifdef _WIN32
  int spawn() override;
  int join() override;
#endif

protected:
  void exec() override;

private:
  int timeout;
  int sigkill;

#ifdef _WIN32
  std::thread waiter;
#endif
};

void timeout_sighandler(int sig);

#endif