PageRenderTime 48ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/3rdparty/libprocess/src/subprocess.cpp

https://gitlab.com/huayuting/mesos
C++ | 348 lines | 252 code | 41 blank | 55 comment | 93 complexity | d605373ee2832429dbc960bdb85d6278 MD5 | raw file
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <string>
  6. #include <glog/logging.h>
  7. #include <process/future.hpp>
  8. #include <process/reap.hpp>
  9. #include <process/subprocess.hpp>
  10. #include <stout/error.hpp>
  11. #include <stout/lambda.hpp>
  12. #include <stout/foreach.hpp>
  13. #include <stout/option.hpp>
  14. #include <stout/os.hpp>
  15. #include <stout/strings.hpp>
  16. #include <stout/try.hpp>
  17. #include <stout/unreachable.hpp>
  18. #include <stout/os/execenv.hpp>
  19. using std::map;
  20. using std::string;
  21. namespace process {
  22. namespace internal {
  23. // See the comment below as to why subprocess is passed to cleanup.
  24. static void cleanup(
  25. const Future<Option<int> >& result,
  26. Promise<Option<int> >* promise,
  27. const Subprocess& subprocess)
  28. {
  29. CHECK(!result.isPending());
  30. CHECK(!result.isDiscarded());
  31. if (result.isFailed()) {
  32. promise->fail(result.failure());
  33. } else {
  34. promise->set(result.get());
  35. }
  36. delete promise;
  37. }
  38. static void close(int stdinFd[2], int stdoutFd[2], int stderrFd[2])
  39. {
  40. os::close(stdinFd[0]);
  41. os::close(stdinFd[1]);
  42. os::close(stdoutFd[0]);
  43. os::close(stdoutFd[1]);
  44. os::close(stderrFd[0]);
  45. os::close(stderrFd[1]);
  46. }
  47. // This function will invoke os::cloexec on all file descriptors in
  48. // these pairs that are valid (i.e., >= 0).
  49. static Try<Nothing> cloexec(int stdinFd[2], int stdoutFd[2], int stderrFd[2])
  50. {
  51. int fd[6] = {
  52. stdinFd[0],
  53. stdinFd[1],
  54. stdoutFd[0],
  55. stdoutFd[1],
  56. stderrFd[0],
  57. stderrFd[1]
  58. };
  59. for (int i = 0; i < 6; i++) {
  60. if (fd[i] >= 0) {
  61. Try<Nothing> cloexec = os::cloexec(fd[i]);
  62. if (cloexec.isError()) {
  63. return Error(cloexec.error());
  64. }
  65. }
  66. }
  67. return Nothing();
  68. }
  69. } // namespace internal {
  70. // Runs the provided command in a subprocess.
  71. Try<Subprocess> subprocess(
  72. const string& _command,
  73. const Subprocess::IO& in,
  74. const Subprocess::IO& out,
  75. const Subprocess::IO& err,
  76. const Option<flags::FlagsBase>& flags,
  77. const Option<map<string, string> >& environment,
  78. const Option<lambda::function<int()> >& setup)
  79. {
  80. // File descriptors for redirecting stdin/stdout/stderr. These file
  81. // descriptors are used for different purposes depending on the
  82. // specified I/O modes. If the mode is PIPE, the two file
  83. // descriptors represent two ends of a pipe. If the mode is PATH or
  84. // FD, only one of the two file descriptors is used. Our protocol
  85. // here is that index 0 is always for reading, and index 1 is always
  86. // for writing (similar to the pipe semantics).
  87. int stdinFd[2] = { -1, -1 };
  88. int stdoutFd[2] = { -1, -1 };
  89. int stderrFd[2] = { -1, -1 };
  90. // Prepare the file descriptor(s) for stdin.
  91. switch (in.mode) {
  92. case Subprocess::IO::FD: {
  93. stdinFd[0] = ::dup(in.fd.get());
  94. if (stdinFd[0] == -1) {
  95. return ErrnoError("Failed to dup");
  96. }
  97. break;
  98. }
  99. case Subprocess::IO::PIPE: {
  100. if (pipe(stdinFd) == -1) {
  101. return ErrnoError("Failed to create pipe");
  102. }
  103. break;
  104. }
  105. case Subprocess::IO::PATH: {
  106. Try<int> open = os::open(in.path.get(), O_RDONLY);
  107. if (open.isError()) {
  108. return Error(
  109. "Failed to open '" + in.path.get() + "': " + open.error());
  110. }
  111. stdinFd[0] = open.get();
  112. break;
  113. }
  114. default:
  115. return Try<Subprocess>(UNREACHABLE());
  116. }
  117. // Prepare the file descriptor(s) for stdout.
  118. switch (out.mode) {
  119. case Subprocess::IO::FD: {
  120. stdoutFd[1] = ::dup(out.fd.get());
  121. if (stdoutFd[1] == -1) {
  122. // Save the errno as 'close' below might overwrite it.
  123. ErrnoError error("Failed to dup");
  124. internal::close(stdinFd, stdoutFd, stderrFd);
  125. return error;
  126. }
  127. break;
  128. }
  129. case Subprocess::IO::PIPE: {
  130. if (pipe(stdoutFd) == -1) {
  131. // Save the errno as 'close' below might overwrite it.
  132. ErrnoError error("Failed to create pipe");
  133. internal::close(stdinFd, stdoutFd, stderrFd);
  134. return error;
  135. }
  136. break;
  137. }
  138. case Subprocess::IO::PATH: {
  139. Try<int> open = os::open(
  140. out.path.get(),
  141. O_WRONLY | O_CREAT | O_APPEND,
  142. S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
  143. if (open.isError()) {
  144. internal::close(stdinFd, stdoutFd, stderrFd);
  145. return Error(
  146. "Failed to open '" + out.path.get() + "': " + open.error());
  147. }
  148. stdoutFd[1] = open.get();
  149. break;
  150. }
  151. default:
  152. return Try<Subprocess>(UNREACHABLE());
  153. }
  154. // Prepare the file descriptor(s) for stderr.
  155. switch (err.mode) {
  156. case Subprocess::IO::FD: {
  157. stderrFd[1] = ::dup(err.fd.get());
  158. if (stderrFd[1] == -1) {
  159. // Save the errno as 'close' below might overwrite it.
  160. ErrnoError error("Failed to dup");
  161. internal::close(stdinFd, stdoutFd, stderrFd);
  162. return error;
  163. }
  164. break;
  165. }
  166. case Subprocess::IO::PIPE: {
  167. if (pipe(stderrFd) == -1) {
  168. // Save the errno as 'close' below might overwrite it.
  169. ErrnoError error("Failed to create pipe");
  170. internal::close(stdinFd, stdoutFd, stderrFd);
  171. return error;
  172. }
  173. break;
  174. }
  175. case Subprocess::IO::PATH: {
  176. Try<int> open = os::open(
  177. err.path.get(),
  178. O_WRONLY | O_CREAT | O_APPEND,
  179. S_IRUSR | S_IWUSR | S_IRGRP | S_IRWXO);
  180. if (open.isError()) {
  181. internal::close(stdinFd, stdoutFd, stderrFd);
  182. return Error(
  183. "Failed to open '" + err.path.get() + "': " + open.error());
  184. }
  185. stderrFd[1] = open.get();
  186. break;
  187. }
  188. default:
  189. return Try<Subprocess>(UNREACHABLE());
  190. }
  191. // TODO(jieyu): Consider using O_CLOEXEC for atomic close-on-exec.
  192. Try<Nothing> cloexec = internal::cloexec(stdinFd, stdoutFd, stderrFd);
  193. if (cloexec.isError()) {
  194. internal::close(stdinFd, stdoutFd, stderrFd);
  195. return Error("Failed to cloexec: " + cloexec.error());
  196. }
  197. // Prepare the command to execute. If the user specifies the
  198. // 'flags', we will stringify it and append it to the command.
  199. string command = _command;
  200. if (flags.isSome()) {
  201. foreachpair (const string& name, const flags::Flag& flag, flags.get()) {
  202. Option<string> value = flag.stringify(flags.get());
  203. if (value.isSome()) {
  204. // TODO(jieyu): Need a better way to escape quotes. For
  205. // example, what if 'value.get()' contains a single quote?
  206. string argument = "--" + name + "='" + value.get() + "'";
  207. command = strings::join(" ", command, argument);
  208. }
  209. }
  210. }
  211. // We need to do this construction before doing the fork as it
  212. // might not be async-safe.
  213. // TODO(tillt): Consider optimizing this to not pass an empty map
  214. // into the constructor or even further to use execl instead of
  215. // execle once we have no user supplied environment.
  216. os::ExecEnv envp(environment.get(map<string, string>()));
  217. pid_t pid;
  218. if ((pid = fork()) == -1) {
  219. // Save the errno as 'close' below might overwrite it.
  220. ErrnoError error("Failed to fork");
  221. internal::close(stdinFd, stdoutFd, stderrFd);
  222. return error;
  223. }
  224. Subprocess process;
  225. process.data->pid = pid;
  226. if (process.data->pid == 0) {
  227. // Child.
  228. // Close parent's end of the pipes.
  229. if (in.mode == Subprocess::IO::PIPE) {
  230. while (::close(stdinFd[1]) == -1 && errno == EINTR);
  231. }
  232. if (out.mode == Subprocess::IO::PIPE) {
  233. while (::close(stdoutFd[0]) == -1 && errno == EINTR);
  234. }
  235. if (err.mode == Subprocess::IO::PIPE) {
  236. while (::close(stderrFd[0]) == -1 && errno == EINTR);
  237. }
  238. // Redirect I/O for stdin/stdout/stderr.
  239. while (::dup2(stdinFd[0], STDIN_FILENO) == -1 && errno == EINTR);
  240. while (::dup2(stdoutFd[1], STDOUT_FILENO) == -1 && errno == EINTR);
  241. while (::dup2(stderrFd[1], STDERR_FILENO) == -1 && errno == EINTR);
  242. // Close the copies. We need to make sure that we do not close the
  243. // file descriptor assigned to stdin/stdout/stderr in case the
  244. // parent has closed stdin/stdout/stderr when calling this
  245. // function (in that case, a dup'ed file descriptor may have the
  246. // same file descriptor number as stdin/stdout/stderr).
  247. if (stdinFd[0] != STDIN_FILENO &&
  248. stdinFd[0] != STDOUT_FILENO &&
  249. stdinFd[0] != STDERR_FILENO) {
  250. while (::close(stdinFd[0]) == -1 && errno == EINTR);
  251. }
  252. if (stdoutFd[1] != STDIN_FILENO &&
  253. stdoutFd[1] != STDOUT_FILENO &&
  254. stdoutFd[1] != STDERR_FILENO) {
  255. while (::close(stdoutFd[1]) == -1 && errno == EINTR);
  256. }
  257. if (stderrFd[1] != STDIN_FILENO &&
  258. stderrFd[1] != STDOUT_FILENO &&
  259. stderrFd[1] != STDERR_FILENO) {
  260. while (::close(stderrFd[1]) == -1 && errno == EINTR);
  261. }
  262. if (setup.isSome()) {
  263. int status = setup.get()();
  264. if (status != 0) {
  265. _exit(status);
  266. }
  267. }
  268. // TODO(jieyu): Consider providing an optional way to launch the
  269. // subprocess without using the shell (similar to 'shell=False'
  270. // used in python subprocess.Popen).
  271. execle("/bin/sh", "sh", "-c", command.c_str(), (char*) NULL, envp());
  272. ABORT("Failed to execle '/bin/sh -c ", command.c_str(), "'\n");
  273. }
  274. // Parent.
  275. // Close the file descriptors that are created by this function. For
  276. // pipes, we close the child ends and store the parent ends (see the
  277. // code below).
  278. os::close(stdinFd[0]);
  279. os::close(stdoutFd[1]);
  280. os::close(stderrFd[1]);
  281. // If the mode is PIPE, store the parent side of the pipe so that
  282. // the user can communicate with the subprocess.
  283. if (in.mode == Subprocess::IO::PIPE) {
  284. process.data->in = stdinFd[1];
  285. }
  286. if (out.mode == Subprocess::IO::PIPE) {
  287. process.data->out = stdoutFd[0];
  288. }
  289. if (err.mode == Subprocess::IO::PIPE) {
  290. process.data->err = stderrFd[0];
  291. }
  292. // Rather than directly exposing the future from process::reap, we
  293. // must use an explicit promise so that we can ensure we can receive
  294. // the termination signal. Otherwise, the caller can discard the
  295. // reap future, and we will not know when it is safe to close the
  296. // file descriptors.
  297. Promise<Option<int> >* promise = new Promise<Option<int> >();
  298. process.data->status = promise->future();
  299. // We need to bind a copy of this Subprocess into the onAny callback
  300. // below to ensure that we don't close the file descriptors before
  301. // the subprocess has terminated (i.e., because the caller doesn't
  302. // keep a copy of this Subprocess around themselves).
  303. process::reap(process.data->pid)
  304. .onAny(lambda::bind(internal::cleanup, lambda::_1, promise, process));
  305. return process;
  306. }
  307. } // namespace process {