blob: 15842751840c35d98e800172230bab3cedbeef61 [file] [log] [blame]
Damien Martin-Guillerezf88f4d82015-09-25 13:56:55 +00001// Copyright 2014 The Bazel Authors. All rights reserved.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +01002//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.devtools.build.lib.shell;
16
ulfjackf2d45952017-08-09 15:27:49 +020017import com.google.common.base.Preconditions;
Lukacs Berki8b074c02016-07-01 13:36:38 +000018import com.google.common.collect.ImmutableList;
ulfjackf2d45952017-08-09 15:27:49 +020019import com.google.common.io.ByteStreams;
20import com.google.devtools.build.lib.shell.Consumers.OutErrConsumers;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010021import java.io.File;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.OutputStream;
ulfjackf2d45952017-08-09 15:27:49 +020025import java.time.Duration;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010026import java.util.List;
27import java.util.Map;
28import java.util.logging.Level;
29import java.util.logging.Logger;
ulfjackf2d45952017-08-09 15:27:49 +020030import javax.annotation.Nullable;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010031
32/**
ulfjackf2d45952017-08-09 15:27:49 +020033 * An executable command, including its arguments and runtime environment (environment variables,
34 * working directory). It lets a caller execute a command, get its results, and optionally forward
35 * interrupts to the subprocess. This class creates threads to ensure timely reading of subprocess
36 * outputs.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010037 *
ulfjackf2d45952017-08-09 15:27:49 +020038 * <p>This class is immutable and thread-safe.
39 *
40 * <p>The use of "shell" in the package name of this class is a misnomer. In terms of the way its
41 * arguments are interpreted, this class is closer to {@code execve(2)} than to {@code system(3)}.
42 * No shell is executed.
43 *
44 * <h4>Examples</h4>
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010045 *
46 * <p>The most basic use-case for this class is as follows:
47 * <pre>
48 * String[] args = { "/bin/du", "-s", directory };
lberki4741aa82018-02-05 07:59:13 -080049 * BlazeCommandResult result = new Command(args).execute();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010050 * String output = new String(result.getStdout());
51 * </pre>
ulfjackf2d45952017-08-09 15:27:49 +020052 * which writes the output of the {@code du(1)} command into {@code output}. More complex cases
53 * might inspect the stderr stream, kill the subprocess asynchronously, feed input to its standard
54 * input, handle the exceptions thrown if the command fails, or print the termination status (exit
55 * code or signal name).
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010056 *
ulfjackf2d45952017-08-09 15:27:49 +020057 * <h4>Other Features</h4>
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010058 *
ulfjackf2d45952017-08-09 15:27:49 +020059 * <p>A caller can optionally specify bytes to be written to the process's "stdin". The returned
60 * {@link CommandResult} object gives the caller access to the exit status, as well as output from
61 * "stdout" and "stderr". To use this class with processes that generate very large amounts of
62 * input/output, consider {@link #execute(OutputStream, OutputStream)},
63 * {@link #executeAsync(OutputStream, OutputStream)}, or
64 * {@link #executeAsync(InputStream, OutputStream, OutputStream, boolean)}.
65 *
66 * <p>This class ensures that stdout and stderr streams are read promptly, avoiding potential
67 * deadlock if the output is large. See
68 * <a href="http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html"> when
69 * <code>Runtime.exec()</code> won't</a>.
70 *
71 * <h4>Caution: Invoking Shell Commands</h4>
72 *
73 * <p>Perhaps the most common command invoked programmatically is the UNIX shell, {@code /bin/sh}.
74 * Because the shell is a general-purpose programming language, care must be taken to ensure that
75 * variable parts of the shell command (e.g. strings entered by the user) do not contain shell
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010076 * metacharacters, as this poses a correctness and/or security risk.
77 *
78 * <p>To execute a shell command directly, use the following pattern:
79 * <pre>
80 * String[] args = { "/bin/sh", "-c", shellCommand };
lberki4741aa82018-02-05 07:59:13 -080081 * BlazeCommandResult result = new Command(args).execute();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010082 * </pre>
ulfjackf2d45952017-08-09 15:27:49 +020083 * {@code shellCommand} is a complete Bourne shell program, possibly containing all kinds of
84 * unescaped metacharacters. For example, here's a shell command that enumerates the working
85 * directories of all processes named "foo":
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010086 * <pre>ps auxx | grep foo | awk '{print $1}' |
87 * while read pid; do readlink /proc/$pid/cwd; done</pre>
ulfjackf2d45952017-08-09 15:27:49 +020088 * It is the responsibility of the caller to ensure that this string means what they intend.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010089 *
ulfjackf2d45952017-08-09 15:27:49 +020090 * <p>Consider the risk posed by allowing the "foo" part of the previous command to be some
91 * arbitrary (untrusted) string called {@code processName}:
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010092 * <pre>
93 * // WARNING: unsafe!
94 * String shellCommand = "ps auxx | grep " + processName + " | awk '{print $1}' | "
95 * + "while read pid; do readlink /proc/$pid/cwd; done";</pre>
96 * </pre>
ulfjackf2d45952017-08-09 15:27:49 +020097 * Passing this string to {@link Command} is unsafe because if the string {@processName} contains
98 * shell metacharacters, the meaning of the command can be arbitrarily changed; consider:
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +010099 * <pre>String processName = ". ; rm -fr $HOME & ";</pre>
100 *
ulfjackf2d45952017-08-09 15:27:49 +0200101 * <p>To defend against this possibility, it is essential to properly quote the variable portions of
102 * the shell command so that shell metacharacters are escaped. Use {@link ShellUtils#shellEscape}
103 * for this purpose:
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100104 * <pre>
105 * // Safe.
106 * String shellCommand = "ps auxx | grep " + ShellUtils.shellEscape(processName)
107 * + " | awk '{print $1}' | while read pid; do readlink /proc/$pid/cwd; done";
108 * </pre>
109 *
ulfjackf2d45952017-08-09 15:27:49 +0200110 * <p>Tip: if you are only invoking a single known command, and no shell features (e.g. $PATH
111 * lookup, output redirection, pipelines, etc) are needed, call it directly without using a shell,
112 * as in the {@code du(1)} example above.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100113 */
114public final class Command {
115
lberki97abb522017-09-04 18:51:57 +0200116 private static final Logger logger =
117 Logger.getLogger("com.google.devtools.build.lib.shell.Command");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100118
ulfjackf2d45952017-08-09 15:27:49 +0200119 /** Pass this value to {@link #execute} to indicate that no input should be written to stdin. */
120 public static final InputStream NO_INPUT = new NullInputStream();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100121
ulfjackf2d45952017-08-09 15:27:49 +0200122 public static final boolean KILL_SUBPROCESS_ON_INTERRUPT = true;
123 public static final boolean CONTINUE_SUBPROCESS_ON_INTERRUPT = false;
124
Lukacs Berki8b074c02016-07-01 13:36:38 +0000125 private final SubprocessBuilder subprocessBuilder;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100126
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100127 /**
ulfjackf2d45952017-08-09 15:27:49 +0200128 * Creates a new {@link Command} for the given command line. The environment is inherited from the
129 * current process, as is the working directory. No timeout is enforced. The command line is
130 * executed exactly as given, without a shell. Subsequent calls to {@link #execute()} will use the
131 * JVM's working directory and environment.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100132 *
133 * @param commandLineElements elements of raw command line to execute
134 * @throws IllegalArgumentException if commandLine is null or empty
135 */
ulfjackf2d45952017-08-09 15:27:49 +0200136 public Command(String[] commandLineElements) {
137 this(commandLineElements, null, null, Duration.ZERO);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100138 }
139
140 /**
ulfjackf2d45952017-08-09 15:27:49 +0200141 * Just like {@link #Command(String[], Map, File, Duration)}, but without a timeout.
Lukacs Berki3d97e222016-08-19 14:40:20 +0000142 */
143 public Command(
144 String[] commandLineElements,
ulfjackf2d45952017-08-09 15:27:49 +0200145 @Nullable Map<String, String> environmentVariables,
146 @Nullable File workingDirectory) {
147 this(commandLineElements, environmentVariables, workingDirectory, Duration.ZERO);
Lukacs Berki3d97e222016-08-19 14:40:20 +0000148 }
149
150 /**
ulfjackf2d45952017-08-09 15:27:49 +0200151 * Creates a new {@link Command} for the given command line elements. The command line is executed
152 * without a shell.
Dmitry Lomov8efc3ef2016-02-17 09:50:03 +0000153 *
ulfjackf2d45952017-08-09 15:27:49 +0200154 * <p>The given environment variables and working directory are used in subsequent calls to
155 * {@link #execute()}.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100156 *
ulfjackf2d45952017-08-09 15:27:49 +0200157 * <p>This command treats the 0-th element of {@code commandLineElement} (the name of an
158 * executable to run) specially.
Dmitry Lomov8efc3ef2016-02-17 09:50:03 +0000159 * <ul>
160 * <li>If it is an absolute path, it is used as it</li>
161 * <li>If it is a single file name, the PATH lookup is performed</li>
162 * <li>If it is a relative path that is not a single file name, the command will attempt to
163 * execute the the binary at that path relative to {@code workingDirectory}.</li>
164 * </ul>
165 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100166 * @param commandLineElements elements of raw command line to execute
ulfjackf2d45952017-08-09 15:27:49 +0200167 * @param environmentVariables environment variables to replace JVM's environment variables; may
168 * be null
169 * @param workingDirectory working directory for execution; if null, the VM's current working
170 * directory is used
171 * @param timeout timeout; a value less than or equal to 0 is treated as no timeout
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100172 * @throws IllegalArgumentException if commandLine is null or empty
173 */
ulfjackf2d45952017-08-09 15:27:49 +0200174 // TODO(ulfjack): Throw a special exception if there was a timeout.
Dmitry Lomov8efc3ef2016-02-17 09:50:03 +0000175 public Command(
176 String[] commandLineElements,
ulfjackf2d45952017-08-09 15:27:49 +0200177 @Nullable Map<String, String> environmentVariables,
178 @Nullable File workingDirectory,
179 Duration timeout) {
180 Preconditions.checkNotNull(commandLineElements);
181 Preconditions.checkArgument(
182 commandLineElements.length != 0, "cannot run an empty command line");
Dmitry Lomov8efc3ef2016-02-17 09:50:03 +0000183
184 File executable = new File(commandLineElements[0]);
185 if (!executable.isAbsolute() && executable.getParent() != null) {
186 commandLineElements = commandLineElements.clone();
187 commandLineElements[0] = new File(workingDirectory, commandLineElements[0]).getAbsolutePath();
188 }
189
Lukacs Berki8b074c02016-07-01 13:36:38 +0000190 this.subprocessBuilder = new SubprocessBuilder();
191 subprocessBuilder.setArgv(ImmutableList.copyOf(commandLineElements));
192 subprocessBuilder.setEnv(environmentVariables);
193 subprocessBuilder.setWorkingDirectory(workingDirectory);
ulfjackf2d45952017-08-09 15:27:49 +0200194 subprocessBuilder.setTimeoutMillis(timeout.toMillis());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100195 }
196
ulfjackf2d45952017-08-09 15:27:49 +0200197 /** Returns the raw command line elements to be executed */
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100198 public String[] getCommandLineElements() {
Lukacs Berki8b074c02016-07-01 13:36:38 +0000199 final List<String> elements = subprocessBuilder.getArgv();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100200 return elements.toArray(new String[elements.size()]);
201 }
202
ulfjackf2d45952017-08-09 15:27:49 +0200203 /** Returns an (unmodifiable) {@link Map} view of command's environment variables or null. */
204 @Nullable public Map<String, String> getEnvironmentVariables() {
Lukacs Berki8b074c02016-07-01 13:36:38 +0000205 return subprocessBuilder.getEnv();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100206 }
207
ulfjackf2d45952017-08-09 15:27:49 +0200208 /** Returns the working directory to be used for execution, or null. */
209 @Nullable public File getWorkingDirectory() {
Lukacs Berki8b074c02016-07-01 13:36:38 +0000210 return subprocessBuilder.getWorkingDirectory();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100211 }
212
213 /**
ulfjackf2d45952017-08-09 15:27:49 +0200214 * Execute this command with no input to stdin, and with the output captured in memory. If the
215 * current process is interrupted, then the subprocess is also interrupted. This call blocks until
216 * the subprocess completes or an error occurs.
217 *
218 * <p>This method is a convenience wrapper for <code>executeAsync().get()</code>.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100219 *
220 * @return {@link CommandResult} representing result of the execution
ulfjackf2d45952017-08-09 15:27:49 +0200221 * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any reason
222 * @throws AbnormalTerminationException if an {@link IOException} is encountered while reading
223 * from the process, or the process was terminated due to a signal
224 * @throws BadExitStatusException if the process exits with a non-zero status
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100225 */
226 public CommandResult execute() throws CommandException {
ulfjackf2d45952017-08-09 15:27:49 +0200227 return executeAsync().get();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100228 }
229
230 /**
ulfjackf2d45952017-08-09 15:27:49 +0200231 * Execute this command with no input to stdin, and with the output streamed to the given output
232 * streams, which must be thread-safe. If the current process is interrupted, then the subprocess
233 * is also interrupted. This call blocks until the subprocess completes or an error occurs.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100234 *
ulfjackf2d45952017-08-09 15:27:49 +0200235 * <p>Note that the given output streams are never closed by this class.
236 *
237 * <p>This method is a convenience wrapper for <code>executeAsync(stdOut, stdErr).get()</code>.
238 *
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100239 * @return {@link CommandResult} representing result of the execution
Philipp Wollermann1e37a532016-06-30 08:45:05 +0000240 * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any reason
ulfjackf2d45952017-08-09 15:27:49 +0200241 * @throws AbnormalTerminationException if an {@link IOException} is encountered while reading
242 * from the process, or the process was terminated due to a signal
Philipp Wollermann1e37a532016-06-30 08:45:05 +0000243 * @throws BadExitStatusException if the process exits with a non-zero status
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100244 */
ulfjackf2d45952017-08-09 15:27:49 +0200245 public CommandResult execute(OutputStream stdOut, OutputStream stdErr) throws CommandException {
246 return doExecute(
247 NO_INPUT, Consumers.createStreamingConsumers(stdOut, stdErr), KILL_SUBPROCESS_ON_INTERRUPT)
248 .get();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100249 }
250
251 /**
ulfjackf2d45952017-08-09 15:27:49 +0200252 * Execute this command with no input to stdin, and with the output captured in memory. If the
253 * current process is interrupted, then the subprocess is also interrupted. This call blocks until
254 * the subprocess is started or throws an error if that fails, but does not wait for the
255 * subprocess to exit.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100256 *
ulfjackf2d45952017-08-09 15:27:49 +0200257 * @return {@link CommandResult} representing result of the execution
Philipp Wollermann1e37a532016-06-30 08:45:05 +0000258 * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any reason
ulfjackf2d45952017-08-09 15:27:49 +0200259 * @throws AbnormalTerminationException if an {@link IOException} is encountered while reading
260 * from the process, or the process was terminated due to a signal
261 * @throws BadExitStatusException if the process exits with a non-zero status
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100262 */
ulfjackf2d45952017-08-09 15:27:49 +0200263 public FutureCommandResult executeAsync() throws CommandException {
264 return doExecute(
265 NO_INPUT, Consumers.createAccumulatingConsumers(), KILL_SUBPROCESS_ON_INTERRUPT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100266 }
267
268 /**
ulfjackf2d45952017-08-09 15:27:49 +0200269 * Execute this command with no input to stdin, and with the output streamed to the given output
270 * streams, which must be thread-safe. If the current process is interrupted, then the subprocess
271 * is also interrupted. This call blocks until the subprocess is started or throws an error if
272 * that fails, but does not wait for the subprocess to exit.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100273 *
ulfjackf2d45952017-08-09 15:27:49 +0200274 * <p>Note that the given output streams are never closed by this class.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100275 *
ulfjackf2d45952017-08-09 15:27:49 +0200276 * @return {@link CommandResult} representing result of the execution
277 * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any reason
278 * @throws AbnormalTerminationException if an {@link IOException} is encountered while reading
279 * from the process, or the process was terminated due to a signal
280 * @throws BadExitStatusException if the process exits with a non-zero status
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100281 */
ulfjackf2d45952017-08-09 15:27:49 +0200282 public FutureCommandResult executeAsync(OutputStream stdOut, OutputStream stdErr)
283 throws CommandException {
284 return doExecute(
285 NO_INPUT, Consumers.createStreamingConsumers(stdOut, stdErr), KILL_SUBPROCESS_ON_INTERRUPT);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100286 }
287
288 /**
ulfjackf2d45952017-08-09 15:27:49 +0200289 * Execute this command with no input to stdin, and with the output captured in memory. This call
290 * blocks until the subprocess is started or throws an error if that fails, but does not wait for
291 * the subprocess to exit.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100292 *
ulfjackf2d45952017-08-09 15:27:49 +0200293 * @param killSubprocessOnInterrupt whether the subprocess should be killed if the current process
294 * is interrupted
295 * @return {@link CommandResult} representing result of the execution
296 * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any reason
297 * @throws AbnormalTerminationException if an {@link IOException} is encountered while reading
298 * from the process, or the process was terminated due to a signal
299 * @throws BadExitStatusException if the process exits with a non-zero status
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100300 */
ulfjackf2d45952017-08-09 15:27:49 +0200301 public FutureCommandResult executeAsync(
302 InputStream stdinInput, boolean killSubprocessOnInterrupt) throws CommandException {
303 return doExecute(
304 stdinInput, Consumers.createAccumulatingConsumers(), killSubprocessOnInterrupt);
Eric Fellheimer9a6c2eb2016-02-19 15:48:42 +0000305 }
Michajlo Matijkiwc3386bf2016-08-29 19:10:53 +0000306
ulfjackf2d45952017-08-09 15:27:49 +0200307 /**
308 * Execute this command with no input to stdin, and with the output streamed to the given output
309 * streams, which must be thread-safe. This call blocks until the subprocess is started or throws
310 * an error if that fails, but does not wait for the subprocess to exit.
311 *
312 * <p>Note that the given output streams are never closed by this class.
313 *
314 * @param killSubprocessOnInterrupt whether the subprocess should be killed if the current process
315 * is interrupted
316 * @return {@link CommandResult} representing result of the execution
317 * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any reason
318 * @throws AbnormalTerminationException if an {@link IOException} is encountered while reading
319 * from the process, or the process was terminated due to a signal
320 * @throws BadExitStatusException if the process exits with a non-zero status
321 */
322 public FutureCommandResult executeAsync(
323 InputStream stdinInput,
324 OutputStream stdOut,
325 OutputStream stdErr,
326 boolean killSubprocessOnInterrupt)
327 throws CommandException {
328 return doExecute(
329 stdinInput, Consumers.createStreamingConsumers(stdOut, stdErr), killSubprocessOnInterrupt);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100330 }
331
ulfjackf2d45952017-08-09 15:27:49 +0200332 /**
333 * A string representation of this command object which includes the arguments, the environment,
334 * and the working directory. Avoid relying on the specifics of this format. Note that the size of
335 * the result string will reflect the size of the command.
336 */
337 public String toDebugString() {
338 StringBuilder message = new StringBuilder(128);
339 message.append("Executing (without brackets):");
340 for (String arg : subprocessBuilder.getArgv()) {
341 message.append(" [");
342 message.append(arg);
343 message.append(']');
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100344 }
ulfjackf2d45952017-08-09 15:27:49 +0200345 message.append("; environment: ");
346 message.append(subprocessBuilder.getEnv());
347 message.append("; working dir: ");
348 File workingDirectory = subprocessBuilder.getWorkingDirectory();
349 message.append(workingDirectory == null ?
350 "(current)" :
351 workingDirectory.toString());
352 return message.toString();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100353 }
354
ulfjackf2d45952017-08-09 15:27:49 +0200355 private FutureCommandResult doExecute(
356 InputStream stdinInput, OutErrConsumers outErrConsumers, boolean killSubprocessOnInterrupt)
357 throws ExecFailedException {
358 Preconditions.checkNotNull(stdinInput, "stdinInput");
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100359 logCommand();
360
ulfjack27758e42017-09-07 13:41:33 +0200361 Subprocess process = startProcess();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100362
Dmitry Lomov9bf3f6a2016-07-07 13:50:06 +0000363 outErrConsumers.logConsumptionStrategy();
Dmitry Lomov9bf3f6a2016-07-07 13:50:06 +0000364 outErrConsumers.registerInputs(
cushon03e70182017-09-15 09:33:27 +0200365 process.getInputStream(), process.getErrorStream(), /* closeStreams= */ false);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100366
ulfjackf2d45952017-08-09 15:27:49 +0200367 // TODO(ulfjack): This call blocks until all input is written. If stdinInput is large (or
368 // unbounded), then the async calls can block for a long time, and the timeout is not properly
369 // enforced.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100370 processInput(stdinInput, process);
371
ulfjack27758e42017-09-07 13:41:33 +0200372 return new FutureCommandResultImpl(this, process, outErrConsumers, killSubprocessOnInterrupt);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100373 }
374
ulfjackf2d45952017-08-09 15:27:49 +0200375 private Subprocess startProcess() throws ExecFailedException {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100376 try {
Lukacs Berki8b074c02016-07-01 13:36:38 +0000377 return subprocessBuilder.start();
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100378 } catch (IOException ioe) {
379 throw new ExecFailedException(this, ioe);
380 }
381 }
382
ulfjackf2d45952017-08-09 15:27:49 +0200383 private static class NullInputStream extends InputStream {
384 @Override
385 public int read() {
386 return -1;
387 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100388
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100389 @Override
ulfjackf2d45952017-08-09 15:27:49 +0200390 public int available() {
391 return 0;
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100392 }
393 }
394
ulfjackf2d45952017-08-09 15:27:49 +0200395 private static void processInput(InputStream stdinInput, Subprocess process) {
lberki97abb522017-09-04 18:51:57 +0200396 if (logger.isLoggable(Level.FINER)) {
397 logger.finer(stdinInput.toString());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100398 }
ulfjackf2d45952017-08-09 15:27:49 +0200399 try (OutputStream out = process.getOutputStream()) {
400 ByteStreams.copy(stdinInput, out);
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100401 } catch (IOException ioe) {
ulfjackf2d45952017-08-09 15:27:49 +0200402 // Note: this is not an error! Perhaps the command just isn't hungry for our input and exited
403 // with success. Process.waitFor (later) will tell us.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100404 //
405 // (Unlike out/err streams, which are read asynchronously, the input stream is written
ulfjackf2d45952017-08-09 15:27:49 +0200406 // synchronously, in its entirety, before processInput returns. If the input is infinite, and
407 // is passed through e.g. "cat" subprocess and back into the ByteArrayOutputStream, that will
408 // eventually run out of memory, causing the output stream to be closed, "cat" to terminate
409 // with SIGPIPE, and processInput to receive an IOException.
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100410 }
411 }
412
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100413 private void logCommand() {
lberki97abb522017-09-04 18:51:57 +0200414 if (!logger.isLoggable(Level.FINE)) {
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100415 return;
416 }
lberki97abb522017-09-04 18:51:57 +0200417 logger.fine(toDebugString());
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100418 }
Han-Wen Nienhuysd08b27f2015-02-25 16:45:20 +0100419}