Damien Martin-Guillerez | f88f4d8 | 2015-09-25 13:56:55 +0000 | [diff] [blame] | 1 | // Copyright 2014 The Bazel Authors. All rights reserved. |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 2 | // |
| 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 | |
| 15 | package com.google.devtools.build.lib.shell; |
| 16 | |
| 17 | |
| 18 | import java.io.File; |
| 19 | import java.io.IOException; |
| 20 | import java.io.InputStream; |
| 21 | import java.io.OutputStream; |
| 22 | import java.util.Collections; |
| 23 | import java.util.List; |
| 24 | import java.util.Map; |
| 25 | import java.util.logging.Level; |
| 26 | import java.util.logging.Logger; |
| 27 | |
| 28 | /** |
| 29 | * <p>Represents an executable command, including its arguments and |
| 30 | * runtime environment (environment variables, working directory). This class |
| 31 | * lets a caller execute a command, get its results, and optionally try to kill |
| 32 | * the task during execution.</p> |
| 33 | * |
| 34 | * <p>The use of "shell" in the full name of this class is a misnomer. In |
| 35 | * terms of the way its arguments are interpreted, this class is closer to |
| 36 | * {@code execve(2)} than to {@code system(3)}. No Bourne shell is executed. |
| 37 | * |
| 38 | * <p>The most basic use-case for this class is as follows: |
| 39 | * <pre> |
| 40 | * String[] args = { "/bin/du", "-s", directory }; |
| 41 | * CommandResult result = new Command(args).execute(); |
| 42 | * String output = new String(result.getStdout()); |
| 43 | * </pre> |
| 44 | * which writes the output of the {@code du(1)} command into {@code output}. |
| 45 | * More complex cases might inspect the stderr stream, kill the subprocess |
| 46 | * asynchronously, feed input to its standard input, handle the exceptions |
| 47 | * thrown if the command fails, or print the termination status (exit code or |
| 48 | * signal name). |
| 49 | * |
| 50 | * <h4>Invoking the Bourne shell</h4> |
| 51 | * |
| 52 | * <p>Perhaps the most common command invoked programmatically is the UNIX |
| 53 | * shell, {@code /bin/sh}. Because the shell is a general-purpose programming |
| 54 | * language, care must be taken to ensure that variable parts of the shell |
| 55 | * command (e.g. strings entered by the user) do not contain shell |
| 56 | * metacharacters, as this poses a correctness and/or security risk. |
| 57 | * |
| 58 | * <p>To execute a shell command directly, use the following pattern: |
| 59 | * <pre> |
| 60 | * String[] args = { "/bin/sh", "-c", shellCommand }; |
| 61 | * CommandResult result = new Command(args).execute(); |
| 62 | * </pre> |
| 63 | * {@code shellCommand} is a complete Bourne shell program, possibly containing |
| 64 | * all kinds of unescaped metacharacters. For example, here's a shell command |
| 65 | * that enumerates the working directories of all processes named "foo": |
| 66 | * <pre>ps auxx | grep foo | awk '{print $1}' | |
| 67 | * while read pid; do readlink /proc/$pid/cwd; done</pre> |
| 68 | * It is the responsibility of the caller to ensure that this string means what |
| 69 | * they intend. |
| 70 | * |
| 71 | * <p>Consider the risk posed by allowing the "foo" part of the previous |
| 72 | * command to be some arbitrary (untrusted) string called {@code processName}: |
| 73 | * <pre> |
| 74 | * // WARNING: unsafe! |
| 75 | * String shellCommand = "ps auxx | grep " + processName + " | awk '{print $1}' | " |
| 76 | * + "while read pid; do readlink /proc/$pid/cwd; done";</pre> |
| 77 | * </pre> |
| 78 | * Passing this string to {@link Command} is unsafe because if the string |
| 79 | * {@processName} contains shell metacharacters, the meaning of the command can |
| 80 | * be arbitrarily changed; consider: |
| 81 | * <pre>String processName = ". ; rm -fr $HOME & ";</pre> |
| 82 | * |
| 83 | * <p>To defend against this possibility, it is essential to properly quote the |
| 84 | * variable portions of the shell command so that shell metacharacters are |
| 85 | * escaped. Use {@link ShellUtils#shellEscape} for this purpose: |
| 86 | * <pre> |
| 87 | * // Safe. |
| 88 | * String shellCommand = "ps auxx | grep " + ShellUtils.shellEscape(processName) |
| 89 | * + " | awk '{print $1}' | while read pid; do readlink /proc/$pid/cwd; done"; |
| 90 | * </pre> |
| 91 | * |
| 92 | * <p>Tip: if you are only invoking a single known command, and no shell |
| 93 | * features (e.g. $PATH lookup, output redirection, pipelines, etc) are needed, |
| 94 | * call it directly without using a shell, as in the {@code du(1)} example |
| 95 | * above. |
| 96 | * |
| 97 | * <h4>Other features</h4> |
| 98 | * |
| 99 | * <p>A caller can optionally specify bytes to be written to the process's |
| 100 | * "stdin". The returned {@link CommandResult} object gives the caller access to |
| 101 | * the exit status, as well as output from "stdout" and "stderr". To use |
| 102 | * this class with processes that generate very large amounts of input/output, |
| 103 | * consider |
| 104 | * {@link #execute(InputStream, KillableObserver, OutputStream, OutputStream)} |
| 105 | * and |
| 106 | * {@link #execute(byte[], KillableObserver, OutputStream, OutputStream)}. |
| 107 | * </p> |
| 108 | * |
| 109 | * <p>This class ensures that stdout and stderr streams are read promptly, |
| 110 | * avoiding potential deadlock if the output is large. See <a |
| 111 | * href="http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html"> When |
| 112 | * <code>Runtime.exec()</code> won't</a>.</p> |
| 113 | * |
| 114 | * <p>This class is immutable and therefore thread-safe.</p> |
| 115 | */ |
| 116 | public final class Command { |
| 117 | |
| 118 | private static final Logger log = |
| 119 | Logger.getLogger("com.google.devtools.build.lib.shell.Command"); |
| 120 | |
| 121 | /** |
| 122 | * Pass this value to {@link #execute(byte[])} to indicate that no input |
| 123 | * should be written to stdin. |
| 124 | */ |
| 125 | public static final byte[] NO_INPUT = new byte[0]; |
| 126 | |
| 127 | private static final String[] EMPTY_STRING_ARRAY = new String[0]; |
| 128 | |
| 129 | /** |
| 130 | * Pass this to {@link #execute(byte[], KillableObserver, boolean)} to |
| 131 | * indicate that you do not wish to observe / kill the underlying |
| 132 | * process. |
| 133 | */ |
| 134 | public static final KillableObserver NO_OBSERVER = new KillableObserver() { |
| 135 | @Override |
| 136 | public void startObserving(final Killable killable) { |
| 137 | // do nothing |
| 138 | } |
| 139 | @Override |
| 140 | public void stopObserving(final Killable killable) { |
| 141 | // do nothing |
| 142 | } |
| 143 | }; |
| 144 | |
| 145 | private final ProcessBuilder processBuilder; |
| 146 | |
| 147 | // Start of public API ----------------------------------------------------- |
| 148 | |
| 149 | /** |
| 150 | * Creates a new {@link Command} that will execute a command line that |
| 151 | * is described by a {@link ProcessBuilder}. Command line elements, |
| 152 | * environment, and working directory are taken from this object. The |
| 153 | * command line is executed exactly as given, without a shell. |
| 154 | * |
| 155 | * @param processBuilder {@link ProcessBuilder} describing command line |
| 156 | * to execute |
| 157 | */ |
| 158 | public Command(final ProcessBuilder processBuilder) { |
| 159 | this(processBuilder.command().toArray(EMPTY_STRING_ARRAY), |
| 160 | processBuilder.environment(), |
| 161 | processBuilder.directory()); |
| 162 | } |
| 163 | |
| 164 | /** |
| 165 | * Creates a new {@link Command} for the given command line elements. The |
| 166 | * command line is executed exactly as given, without a shell. |
| 167 | * Subsequent calls to {@link #execute()} will use the JVM's working |
| 168 | * directory and environment. |
| 169 | * |
| 170 | * @param commandLineElements elements of raw command line to execute |
| 171 | * @throws IllegalArgumentException if commandLine is null or empty |
| 172 | */ |
| 173 | /* TODO(bazel-team): Use varargs here |
| 174 | */ |
| 175 | public Command(final String[] commandLineElements) { |
| 176 | this(commandLineElements, null, null); |
| 177 | } |
| 178 | |
| 179 | /** |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 180 | * Creates a new {@link Command} for the given command line elements. The |
| 181 | * command line is executed exactly as given, without a shell. The given |
| 182 | * environment variables and working directory are used in subsequent |
| 183 | * calls to {@link #execute()}. |
| 184 | * |
| 185 | * @param commandLineElements elements of raw command line to execute |
| 186 | * @param environmentVariables environment variables to replace JVM's |
| 187 | * environment variables; may be null |
| 188 | * @param workingDirectory working directory for execution; if null, current |
| 189 | * working directory is used |
| 190 | * @throws IllegalArgumentException if commandLine is null or empty |
| 191 | */ |
| 192 | public Command(final String[] commandLineElements, |
| 193 | final Map<String, String> environmentVariables, |
| 194 | final File workingDirectory) { |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 195 | if (commandLineElements == null || commandLineElements.length == 0) { |
| 196 | throw new IllegalArgumentException("command line is null or empty"); |
| 197 | } |
| 198 | this.processBuilder = |
Han-Wen Nienhuys | efe7f66 | 2015-02-16 13:34:57 +0000 | [diff] [blame] | 199 | new ProcessBuilder(commandLineElements); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 200 | if (environmentVariables != null) { |
| 201 | // TODO(bazel-team) remove next line eventually; it is here to mimic old |
| 202 | // Runtime.exec() behavior |
| 203 | this.processBuilder.environment().clear(); |
| 204 | this.processBuilder.environment().putAll(environmentVariables); |
| 205 | } |
| 206 | this.processBuilder.directory(workingDirectory); |
| 207 | } |
| 208 | |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 209 | /** |
| 210 | * @return raw command line elements to be executed |
| 211 | */ |
| 212 | public String[] getCommandLineElements() { |
| 213 | final List<String> elements = processBuilder.command(); |
| 214 | return elements.toArray(new String[elements.size()]); |
| 215 | } |
| 216 | |
| 217 | /** |
| 218 | * @return (unmodifiable) {@link Map} view of command's environment variables |
| 219 | */ |
| 220 | public Map<String, String> getEnvironmentVariables() { |
| 221 | return Collections.unmodifiableMap(processBuilder.environment()); |
| 222 | } |
| 223 | |
| 224 | /** |
| 225 | * @return working directory used for execution, or null if the current |
| 226 | * working directory is used |
| 227 | */ |
| 228 | public File getWorkingDirectory() { |
| 229 | return processBuilder.directory(); |
| 230 | } |
| 231 | |
| 232 | /** |
| 233 | * Execute this command with no input to stdin. This call will block until the |
| 234 | * process completes or an error occurs. |
| 235 | * |
| 236 | * @return {@link CommandResult} representing result of the execution |
| 237 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 238 | * reason |
| 239 | * @throws AbnormalTerminationException if an {@link IOException} is |
| 240 | * encountered while reading from the process, or the process was terminated |
| 241 | * due to a signal. |
| 242 | * @throws BadExitStatusException if the process exits with a |
| 243 | * non-zero status |
| 244 | */ |
| 245 | public CommandResult execute() throws CommandException { |
| 246 | return execute(NO_INPUT); |
| 247 | } |
| 248 | |
| 249 | /** |
| 250 | * Execute this command with given input to stdin. This call will block until |
| 251 | * the process completes or an error occurs. |
| 252 | * |
| 253 | * @param stdinInput bytes to be written to process's stdin |
| 254 | * @return {@link CommandResult} representing result of the execution |
| 255 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 256 | * reason |
| 257 | * @throws AbnormalTerminationException if an {@link IOException} is |
| 258 | * encountered while reading from the process, or the process was terminated |
| 259 | * due to a signal. |
| 260 | * @throws BadExitStatusException if the process exits with a |
| 261 | * non-zero status |
| 262 | * @throws NullPointerException if stdin is null |
| 263 | */ |
| 264 | public CommandResult execute(final byte[] stdinInput) |
| 265 | throws CommandException { |
| 266 | nullCheck(stdinInput, "stdinInput"); |
| 267 | return doExecute(new ByteArrayInputSource(stdinInput), |
| 268 | NO_OBSERVER, |
| 269 | Consumers.createAccumulatingConsumers(), |
| 270 | /*killSubprocess=*/false, /*closeOutput=*/false).get(); |
| 271 | } |
| 272 | |
| 273 | /** |
| 274 | * <p>Execute this command with given input to stdin. This call will block |
| 275 | * until the process completes or an error occurs. Caller may specify |
| 276 | * whether the method should ignore stdout/stderr output. If the |
| 277 | * given number of milliseconds elapses before the command has |
| 278 | * completed, this method will attempt to kill the command.</p> |
| 279 | * |
| 280 | * @param stdinInput bytes to be written to process's stdin, or |
| 281 | * {@link #NO_INPUT} if no bytes should be written |
| 282 | * @param timeout number of milliseconds to wait for command completion |
| 283 | * before attempting to kill the command |
| 284 | * @param ignoreOutput if true, method will ignore stdout/stderr output |
| 285 | * and return value will not contain this data |
| 286 | * @return {@link CommandResult} representing result of the execution |
| 287 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 288 | * reason |
| 289 | * @throws AbnormalTerminationException if an {@link IOException} is |
| 290 | * encountered while reading from the process, or the process was terminated |
| 291 | * due to a signal. |
| 292 | * @throws BadExitStatusException if the process exits with a |
| 293 | * non-zero status |
| 294 | * @throws NullPointerException if stdin is null |
| 295 | */ |
| 296 | public CommandResult execute(final byte[] stdinInput, |
| 297 | final long timeout, |
| 298 | final boolean ignoreOutput) |
| 299 | throws CommandException { |
| 300 | return execute(stdinInput, |
| 301 | new TimeoutKillableObserver(timeout), |
| 302 | ignoreOutput); |
| 303 | } |
| 304 | |
| 305 | /** |
| 306 | * <p>Execute this command with given input to stdin. This call will block |
| 307 | * until the process completes or an error occurs. Caller may specify |
| 308 | * whether the method should ignore stdout/stderr output. The given {@link |
| 309 | * KillableObserver} may also terminate the process early while running.</p> |
| 310 | * |
| 311 | * @param stdinInput bytes to be written to process's stdin, or |
| 312 | * {@link #NO_INPUT} if no bytes should be written |
| 313 | * @param observer {@link KillableObserver} that should observe the running |
| 314 | * process, or {@link #NO_OBSERVER} if caller does not wish to kill |
| 315 | * the process |
| 316 | * @param ignoreOutput if true, method will ignore stdout/stderr output |
| 317 | * and return value will not contain this data |
| 318 | * @return {@link CommandResult} representing result of the execution |
| 319 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 320 | * reason |
| 321 | * @throws AbnormalTerminationException if the process is interrupted (or |
| 322 | * killed) before completion, if an {@link IOException} is encountered while |
| 323 | * reading from the process, or the process was terminated due to a signal. |
| 324 | * @throws BadExitStatusException if the process exits with a |
| 325 | * non-zero status |
| 326 | * @throws NullPointerException if stdin is null |
| 327 | */ |
| 328 | public CommandResult execute(final byte[] stdinInput, |
| 329 | final KillableObserver observer, |
| 330 | final boolean ignoreOutput) |
| 331 | throws CommandException { |
| 332 | // supporting "null" here for backwards compatibility |
| 333 | final KillableObserver theObserver = |
| 334 | observer == null ? NO_OBSERVER : observer; |
| 335 | return doExecute(new ByteArrayInputSource(stdinInput), |
| 336 | theObserver, |
| 337 | ignoreOutput ? Consumers.createDiscardingConsumers() |
| 338 | : Consumers.createAccumulatingConsumers(), |
| 339 | /*killSubprocess=*/false, /*closeOutput=*/false).get(); |
| 340 | } |
| 341 | |
| 342 | /** |
| 343 | * <p>Execute this command with given input to stdin. This call blocks |
| 344 | * until the process completes or an error occurs. The caller provides |
| 345 | * {@link OutputStream} instances into which the process writes its |
| 346 | * stdout/stderr output; these streams are <em>not</em> closed when the |
| 347 | * process terminates. The given {@link KillableObserver} may also |
| 348 | * terminate the process early while running.</p> |
| 349 | * |
| 350 | * <p>Note that stdout and stderr are written concurrently. If these are |
| 351 | * aliased to each other, it is the caller's duty to ensure thread safety. |
| 352 | * </p> |
| 353 | * |
| 354 | * @param stdinInput bytes to be written to process's stdin, or |
| 355 | * {@link #NO_INPUT} if no bytes should be written |
| 356 | * @param observer {@link KillableObserver} that should observe the running |
| 357 | * process, or {@link #NO_OBSERVER} if caller does not wish to kill the |
| 358 | * process |
| 359 | * @param stdOut the process will write its standard output into this stream. |
| 360 | * E.g., you could pass {@link System#out} as <code>stdOut</code>. |
| 361 | * @param stdErr the process will write its standard error into this stream. |
| 362 | * E.g., you could pass {@link System#err} as <code>stdErr</code>. |
| 363 | * @return {@link CommandResult} representing result of the execution. Note |
| 364 | * that {@link CommandResult#getStdout()} and |
| 365 | * {@link CommandResult#getStderr()} will yield {@link IllegalStateException} |
| 366 | * in this case, as the output is written to <code>stdOut/stdErr</code> |
| 367 | * instead. |
| 368 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 369 | * reason |
| 370 | * @throws AbnormalTerminationException if the process is interrupted (or |
| 371 | * killed) before completion, if an {@link IOException} is encountered while |
| 372 | * reading from the process, or the process was terminated due to a signal. |
| 373 | * @throws BadExitStatusException if the process exits with a |
| 374 | * non-zero status |
| 375 | * @throws NullPointerException if any argument is null. |
| 376 | */ |
| 377 | public CommandResult execute(final byte[] stdinInput, |
| 378 | final KillableObserver observer, |
| 379 | final OutputStream stdOut, |
| 380 | final OutputStream stdErr) |
| 381 | throws CommandException { |
| 382 | return execute(stdinInput, observer, stdOut, stdErr, false); |
| 383 | } |
| 384 | |
| 385 | /** |
| 386 | * Like {@link #execute(byte[], KillableObserver, OutputStream, OutputStream)} |
| 387 | * but enables setting of the killSubprocessOnInterrupt attribute. |
| 388 | * |
| 389 | * @param killSubprocessOnInterrupt if set to true, the execution of |
| 390 | * this command is <i>interruptible</i>: in other words, if this thread is |
| 391 | * interrupted during a call to execute, the subprocess will be terminated |
| 392 | * and the call will return in a timely manner. If false, the subprocess |
| 393 | * will run to completion; this is the default value use by all other |
| 394 | * constructors. The thread's interrupted status is preserved in all cases, |
| 395 | * however. |
| 396 | */ |
| 397 | public CommandResult execute(final byte[] stdinInput, |
| 398 | final KillableObserver observer, |
| 399 | final OutputStream stdOut, |
| 400 | final OutputStream stdErr, |
| 401 | final boolean killSubprocessOnInterrupt) |
| 402 | throws CommandException { |
| 403 | nullCheck(stdinInput, "stdinInput"); |
| 404 | nullCheck(observer, "observer"); |
| 405 | nullCheck(stdOut, "stdOut"); |
| 406 | nullCheck(stdErr, "stdErr"); |
| 407 | return doExecute(new ByteArrayInputSource(stdinInput), |
| 408 | observer, |
| 409 | Consumers.createStreamingConsumers(stdOut, stdErr), |
| 410 | killSubprocessOnInterrupt, false).get(); |
| 411 | } |
| 412 | |
| 413 | /** |
| 414 | * <p>Execute this command with given input to stdin; this stream is closed |
| 415 | * when the process terminates, and exceptions raised when closing this |
| 416 | * stream are ignored. This call blocks |
| 417 | * until the process completes or an error occurs. The caller provides |
| 418 | * {@link OutputStream} instances into which the process writes its |
| 419 | * stdout/stderr output; these streams are <em>not</em> closed when the |
| 420 | * process terminates. The given {@link KillableObserver} may also |
| 421 | * terminate the process early while running.</p> |
| 422 | * |
| 423 | * @param stdinInput The input to this process's stdin |
| 424 | * @param observer {@link KillableObserver} that should observe the running |
| 425 | * process, or {@link #NO_OBSERVER} if caller does not wish to kill the |
| 426 | * process |
| 427 | * @param stdOut the process will write its standard output into this stream. |
| 428 | * E.g., you could pass {@link System#out} as <code>stdOut</code>. |
| 429 | * @param stdErr the process will write its standard error into this stream. |
| 430 | * E.g., you could pass {@link System#err} as <code>stdErr</code>. |
| 431 | * @return {@link CommandResult} representing result of the execution. Note |
| 432 | * that {@link CommandResult#getStdout()} and |
| 433 | * {@link CommandResult#getStderr()} will yield {@link IllegalStateException} |
| 434 | * in this case, as the output is written to <code>stdOut/stdErr</code> |
| 435 | * instead. |
| 436 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 437 | * reason |
| 438 | * @throws AbnormalTerminationException if the process is interrupted (or |
| 439 | * killed) before completion, if an {@link IOException} is encountered while |
| 440 | * reading from the process, or the process was terminated due to a signal. |
| 441 | * @throws BadExitStatusException if the process exits with a |
| 442 | * non-zero status |
| 443 | * @throws NullPointerException if any argument is null. |
| 444 | */ |
| 445 | public CommandResult execute(final InputStream stdinInput, |
| 446 | final KillableObserver observer, |
| 447 | final OutputStream stdOut, |
| 448 | final OutputStream stdErr) |
| 449 | throws CommandException { |
| 450 | nullCheck(stdinInput, "stdinInput"); |
| 451 | nullCheck(observer, "observer"); |
| 452 | nullCheck(stdOut, "stdOut"); |
| 453 | nullCheck(stdErr, "stdErr"); |
| 454 | return doExecute(new InputStreamInputSource(stdinInput), |
| 455 | observer, |
| 456 | Consumers.createStreamingConsumers(stdOut, stdErr), |
| 457 | /*killSubprocess=*/false, /*closeOutput=*/false).get(); |
| 458 | } |
| 459 | |
| 460 | /** |
| 461 | * <p>Execute this command with given input to stdin; this stream is closed |
| 462 | * when the process terminates, and exceptions raised when closing this |
| 463 | * stream are ignored. This call blocks |
| 464 | * until the process completes or an error occurs. The caller provides |
| 465 | * {@link OutputStream} instances into which the process writes its |
| 466 | * stdout/stderr output; these streams are closed when the process terminates |
| 467 | * if closeOut is set. The given {@link KillableObserver} may also |
| 468 | * terminate the process early while running.</p> |
| 469 | * |
| 470 | * @param stdinInput The input to this process's stdin |
| 471 | * @param observer {@link KillableObserver} that should observe the running |
| 472 | * process, or {@link #NO_OBSERVER} if caller does not wish to kill the |
| 473 | * process |
| 474 | * @param stdOut the process will write its standard output into this stream. |
| 475 | * E.g., you could pass {@link System#out} as <code>stdOut</code>. |
| 476 | * @param stdErr the process will write its standard error into this stream. |
| 477 | * E.g., you could pass {@link System#err} as <code>stdErr</code>. |
| 478 | * @param closeOut whether to close the output streams when the subprocess |
| 479 | * terminates. |
| 480 | * @return {@link CommandResult} representing result of the execution. Note |
| 481 | * that {@link CommandResult#getStdout()} and |
| 482 | * {@link CommandResult#getStderr()} will yield {@link IllegalStateException} |
| 483 | * in this case, as the output is written to <code>stdOut/stdErr</code> |
| 484 | * instead. |
| 485 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 486 | * reason |
| 487 | * @throws AbnormalTerminationException if the process is interrupted (or |
| 488 | * killed) before completion, if an {@link IOException} is encountered while |
| 489 | * reading from the process, or the process was terminated due to a signal. |
| 490 | * @throws BadExitStatusException if the process exits with a |
| 491 | * non-zero status |
| 492 | * @throws NullPointerException if any argument is null. |
| 493 | */ |
| 494 | public CommandResult execute(final InputStream stdinInput, |
| 495 | final KillableObserver observer, |
| 496 | final OutputStream stdOut, |
| 497 | final OutputStream stdErr, |
| 498 | boolean closeOut) |
| 499 | throws CommandException { |
| 500 | nullCheck(stdinInput, "stdinInput"); |
| 501 | nullCheck(observer, "observer"); |
| 502 | nullCheck(stdOut, "stdOut"); |
| 503 | nullCheck(stdErr, "stdErr"); |
| 504 | return doExecute(new InputStreamInputSource(stdinInput), |
| 505 | observer, |
| 506 | Consumers.createStreamingConsumers(stdOut, stdErr), |
| 507 | false, closeOut).get(); |
| 508 | } |
| 509 | |
| 510 | /** |
| 511 | * <p>Executes this command with the given stdinInput, but does not |
| 512 | * wait for it to complete. The caller may choose to observe the status |
| 513 | * of the launched process by calling methods on the returned object. |
| 514 | * |
| 515 | * @param stdinInput bytes to be written to process's stdin, or |
| 516 | * {@link #NO_INPUT} if no bytes should be written |
| 517 | * @return An object that can be used to check if the process terminated and |
| 518 | * obtain the process results. |
| 519 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 520 | * reason |
| 521 | * @throws NullPointerException if stdin is null |
| 522 | */ |
| 523 | public FutureCommandResult executeAsynchronously(final byte[] stdinInput) |
| 524 | throws CommandException { |
| 525 | return executeAsynchronously(stdinInput, NO_OBSERVER); |
| 526 | } |
| 527 | |
| 528 | /** |
| 529 | * <p>Executes this command with the given input to stdin, but does |
| 530 | * not wait for it to complete. The caller may choose to observe the |
| 531 | * status of the launched process by calling methods on the returned |
| 532 | * object. This method performs the minimum cleanup after the |
| 533 | * process terminates: It closes the input stream, and it ignores |
| 534 | * exceptions that result from closing it. The given {@link |
| 535 | * KillableObserver} may also terminate the process early while |
| 536 | * running.</p> |
| 537 | * |
| 538 | * <p>Note that in this case the {@link KillableObserver} will be assigned |
| 539 | * to start observing the process via |
| 540 | * {@link KillableObserver#startObserving(Killable)} but will only be |
| 541 | * unassigned via {@link KillableObserver#stopObserving(Killable)}, if |
| 542 | * {@link FutureCommandResult#get()} is called. If the |
| 543 | * {@link KillableObserver} implementation used with this method will |
| 544 | * not work correctly without calls to |
| 545 | * {@link KillableObserver#stopObserving(Killable)} then a new instance |
| 546 | * should be used for each call to this method.</p> |
| 547 | * |
| 548 | * @param stdinInput bytes to be written to process's stdin, or |
| 549 | * {@link #NO_INPUT} if no bytes should be written |
| 550 | * @param observer {@link KillableObserver} that should observe the running |
| 551 | * process, or {@link #NO_OBSERVER} if caller does not wish to kill |
| 552 | * the process |
| 553 | * @return An object that can be used to check if the process terminated and |
| 554 | * obtain the process results. |
| 555 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 556 | * reason |
| 557 | * @throws NullPointerException if stdin is null |
| 558 | */ |
| 559 | public FutureCommandResult executeAsynchronously(final byte[] stdinInput, |
| 560 | final KillableObserver observer) |
| 561 | throws CommandException { |
| 562 | // supporting "null" here for backwards compatibility |
| 563 | final KillableObserver theObserver = |
| 564 | observer == null ? NO_OBSERVER : observer; |
| 565 | nullCheck(stdinInput, "stdinInput"); |
| 566 | return doExecute(new ByteArrayInputSource(stdinInput), |
| 567 | theObserver, |
| 568 | Consumers.createDiscardingConsumers(), |
| 569 | /*killSubprocess=*/false, /*closeOutput=*/false); |
| 570 | } |
| 571 | |
| 572 | /** |
| 573 | * <p>Executes this command with the given input to stdin, but does |
| 574 | * not wait for it to complete. The caller may choose to observe the |
| 575 | * status of the launched process by calling methods on the returned |
| 576 | * object. This method performs the minimum cleanup after the |
| 577 | * process terminates: It closes the input stream, and it ignores |
| 578 | * exceptions that result from closing it. The caller provides |
| 579 | * {@link OutputStream} instances into which the process writes its |
| 580 | * stdout/stderr output; these streams are <em>not</em> closed when |
| 581 | * the process terminates. The given {@link KillableObserver} may |
| 582 | * also terminate the process early while running.</p> |
| 583 | * |
| 584 | * <p>Note that stdout and stderr are written concurrently. If these are |
| 585 | * aliased to each other, or if the caller continues to write to these |
| 586 | * streams, it is the caller's duty to ensure thread safety. |
| 587 | * </p> |
| 588 | * |
| 589 | * <p>Note that in this case the {@link KillableObserver} will be assigned |
| 590 | * to start observing the process via |
| 591 | * {@link KillableObserver#startObserving(Killable)} but will only be |
| 592 | * unassigned via {@link KillableObserver#stopObserving(Killable)}, if |
| 593 | * {@link FutureCommandResult#get()} is called. If the |
| 594 | * {@link KillableObserver} implementation used with this method will |
| 595 | * not work correctly without calls to |
| 596 | * {@link KillableObserver#stopObserving(Killable)} then a new instance |
| 597 | * should be used for each call to this method.</p> |
| 598 | * |
| 599 | * @param stdinInput The input to this process's stdin |
| 600 | * @param observer {@link KillableObserver} that should observe the running |
| 601 | * process, or {@link #NO_OBSERVER} if caller does not wish to kill |
| 602 | * the process |
| 603 | * @param stdOut the process will write its standard output into this stream. |
| 604 | * E.g., you could pass {@link System#out} as <code>stdOut</code>. |
| 605 | * @param stdErr the process will write its standard error into this stream. |
| 606 | * E.g., you could pass {@link System#err} as <code>stdErr</code>. |
| 607 | * @return An object that can be used to check if the process terminated and |
| 608 | * obtain the process results. |
| 609 | * @throws ExecFailedException if {@link Runtime#exec(String[])} fails for any |
| 610 | * reason |
| 611 | * @throws NullPointerException if stdin is null |
| 612 | */ |
| 613 | public FutureCommandResult executeAsynchronously(final InputStream stdinInput, |
| 614 | final KillableObserver observer, |
| 615 | final OutputStream stdOut, |
| 616 | final OutputStream stdErr) |
| 617 | throws CommandException { |
| 618 | // supporting "null" here for backwards compatibility |
| 619 | final KillableObserver theObserver = |
| 620 | observer == null ? NO_OBSERVER : observer; |
| 621 | nullCheck(stdinInput, "stdinInput"); |
| 622 | return doExecute(new InputStreamInputSource(stdinInput), |
| 623 | theObserver, |
| 624 | Consumers.createStreamingConsumers(stdOut, stdErr), |
| 625 | /*killSubprocess=*/false, /*closeOutput=*/false); |
| 626 | } |
| 627 | |
| 628 | // End of public API ------------------------------------------------------- |
| 629 | |
| 630 | private void nullCheck(Object argument, String argumentName) { |
| 631 | if (argument == null) { |
| 632 | String message = argumentName + " argument must not be null."; |
| 633 | throw new NullPointerException(message); |
| 634 | } |
| 635 | } |
| 636 | |
| 637 | private FutureCommandResult doExecute(final InputSource stdinInput, |
| 638 | final KillableObserver observer, |
| 639 | final Consumers.OutErrConsumers outErrConsumers, |
| 640 | final boolean killSubprocessOnInterrupt, |
| 641 | final boolean closeOutputStreams) |
| 642 | throws CommandException { |
| 643 | |
| 644 | logCommand(); |
| 645 | |
| 646 | final Process process = startProcess(); |
| 647 | |
| 648 | outErrConsumers.logConsumptionStrategy(); |
| 649 | |
| 650 | outErrConsumers.registerInputs(process.getInputStream(), |
| 651 | process.getErrorStream(), |
| 652 | closeOutputStreams); |
| 653 | |
| 654 | processInput(stdinInput, process); |
| 655 | |
| 656 | // TODO(bazel-team): if the input stream is unbounded, observers will not get start |
| 657 | // notification in a timely manner! |
| 658 | final Killable processKillable = observeProcess(process, observer); |
| 659 | |
| 660 | return new FutureCommandResult() { |
| 661 | @Override |
| 662 | public CommandResult get() throws AbnormalTerminationException { |
| 663 | return waitForProcessToComplete(process, |
| 664 | observer, |
| 665 | processKillable, |
| 666 | outErrConsumers, |
| 667 | killSubprocessOnInterrupt); |
| 668 | } |
| 669 | |
| 670 | @Override |
| 671 | public boolean isDone() { |
| 672 | try { |
| 673 | // exitValue seems to be the only non-blocking call for |
| 674 | // checking process liveness. |
| 675 | process.exitValue(); |
| 676 | return true; |
| 677 | } catch (IllegalThreadStateException e) { |
| 678 | return false; |
| 679 | } |
| 680 | } |
| 681 | }; |
| 682 | } |
| 683 | |
| 684 | private Process startProcess() |
| 685 | throws ExecFailedException { |
| 686 | try { |
| 687 | return processBuilder.start(); |
| 688 | } catch (IOException ioe) { |
| 689 | throw new ExecFailedException(this, ioe); |
| 690 | } |
| 691 | } |
| 692 | |
| 693 | private static interface InputSource { |
| 694 | void copyTo(OutputStream out) throws IOException; |
| 695 | boolean isEmpty(); |
| 696 | String toLogString(String sourceName); |
| 697 | } |
| 698 | |
| 699 | private static class ByteArrayInputSource implements InputSource { |
| 700 | private byte[] bytes; |
| 701 | ByteArrayInputSource(byte[] bytes){ |
| 702 | this.bytes = bytes; |
| 703 | } |
| 704 | @Override |
| 705 | public void copyTo(OutputStream out) throws IOException { |
| 706 | out.write(bytes); |
| 707 | out.flush(); |
| 708 | } |
| 709 | @Override |
| 710 | public boolean isEmpty() { |
| 711 | return bytes.length == 0; |
| 712 | } |
| 713 | @Override |
| 714 | public String toLogString(String sourceName) { |
| 715 | if (isEmpty()) { |
| 716 | return "No input to " + sourceName; |
| 717 | } else { |
| 718 | return "Input to " + sourceName + ": " + |
| 719 | LogUtil.toTruncatedString(bytes); |
| 720 | } |
| 721 | } |
| 722 | } |
| 723 | |
| 724 | private static class InputStreamInputSource implements InputSource { |
| 725 | private InputStream inputStream; |
| 726 | InputStreamInputSource(InputStream inputStream){ |
| 727 | this.inputStream = inputStream; |
| 728 | } |
| 729 | @Override |
| 730 | public void copyTo(OutputStream out) throws IOException { |
| 731 | byte[] buf = new byte[4096]; |
| 732 | int r; |
| 733 | while ((r = inputStream.read(buf)) != -1) { |
| 734 | out.write(buf, 0, r); |
| 735 | out.flush(); |
| 736 | } |
| 737 | } |
| 738 | @Override |
| 739 | public boolean isEmpty() { |
| 740 | return false; |
| 741 | } |
| 742 | @Override |
| 743 | public String toLogString(String sourceName) { |
| 744 | return "Input to " + sourceName + " is a stream."; |
| 745 | } |
| 746 | } |
| 747 | |
| 748 | private static void processInput(final InputSource stdinInput, |
| 749 | final Process process) { |
| 750 | if (log.isLoggable(Level.FINER)) { |
| 751 | log.finer(stdinInput.toLogString("stdin")); |
| 752 | } |
| 753 | try { |
| 754 | if (stdinInput.isEmpty()) { |
| 755 | return; |
| 756 | } |
| 757 | stdinInput.copyTo(process.getOutputStream()); |
| 758 | } catch (IOException ioe) { |
| 759 | // Note: this is not an error! Perhaps the command just isn't hungry for |
| 760 | // our input and exited with success. Process.waitFor (later) will tell |
| 761 | // us. |
| 762 | // |
| 763 | // (Unlike out/err streams, which are read asynchronously, the input stream is written |
| 764 | // synchronously, in its entirety, before processInput returns. If the input is |
| 765 | // infinite, and is passed through e.g. "cat" subprocess and back into the |
| 766 | // ByteArrayOutputStream, that will eventually run out of memory, causing the output stream |
| 767 | // to be closed, "cat" to terminate with SIGPIPE, and processInput to receive an IOException. |
| 768 | } finally { |
| 769 | // if this statement is ever deleted, the process's outputStream |
| 770 | // must be closed elsewhere -- it is not closed automatically |
| 771 | Command.silentClose(process.getOutputStream()); |
| 772 | } |
| 773 | } |
| 774 | |
| 775 | private static Killable observeProcess(final Process process, |
| 776 | final KillableObserver observer) { |
| 777 | final Killable processKillable = new ProcessKillable(process); |
| 778 | observer.startObserving(processKillable); |
| 779 | return processKillable; |
| 780 | } |
| 781 | |
| 782 | private CommandResult waitForProcessToComplete( |
| 783 | final Process process, |
| 784 | final KillableObserver observer, |
| 785 | final Killable processKillable, |
| 786 | final Consumers.OutErrConsumers outErr, |
| 787 | final boolean killSubprocessOnInterrupt) |
| 788 | throws AbnormalTerminationException { |
| 789 | |
| 790 | log.finer("Waiting for process..."); |
| 791 | |
| 792 | TerminationStatus status = |
| 793 | waitForProcess(process, killSubprocessOnInterrupt); |
| 794 | |
| 795 | observer.stopObserving(processKillable); |
| 796 | |
| 797 | log.finer(status.toString()); |
| 798 | |
| 799 | try { |
| 800 | outErr.waitForCompletion(); |
| 801 | } catch (IOException ioe) { |
| 802 | CommandResult noOutputResult = |
| 803 | new CommandResult(CommandResult.EMPTY_OUTPUT, |
| 804 | CommandResult.EMPTY_OUTPUT, |
| 805 | status); |
| 806 | if (status.success()) { |
| 807 | // If command was otherwise successful, throw an exception about this |
| 808 | throw new AbnormalTerminationException(this, noOutputResult, ioe); |
| 809 | } else { |
| 810 | // Otherwise, throw the more important exception -- command |
| 811 | // was not successful |
| 812 | String message = status |
| 813 | + "; also encountered an error while attempting to retrieve output"; |
| 814 | throw status.exited() |
| 815 | ? new BadExitStatusException(this, noOutputResult, message, ioe) |
| 816 | : new AbnormalTerminationException(this, |
| 817 | noOutputResult, message, ioe); |
| 818 | } |
| 819 | } |
| 820 | |
| 821 | CommandResult result = new CommandResult(outErr.getAccumulatedOut(), |
| 822 | outErr.getAccumulatedErr(), |
| 823 | status); |
| 824 | result.logThis(); |
| 825 | if (status.success()) { |
| 826 | return result; |
| 827 | } else if (status.exited()) { |
| 828 | throw new BadExitStatusException(this, result, status.toString()); |
| 829 | } else { |
| 830 | throw new AbnormalTerminationException(this, result, status.toString()); |
| 831 | } |
| 832 | } |
| 833 | |
| 834 | private static TerminationStatus waitForProcess(Process process, |
| 835 | boolean killSubprocessOnInterrupt) { |
| 836 | boolean wasInterrupted = false; |
| 837 | try { |
| 838 | while (true) { |
| 839 | try { |
| 840 | return new TerminationStatus(process.waitFor()); |
| 841 | } catch (InterruptedException ie) { |
| 842 | wasInterrupted = true; |
| 843 | if (killSubprocessOnInterrupt) { |
| 844 | process.destroy(); |
| 845 | } |
| 846 | } |
| 847 | } |
| 848 | } finally { |
Philipp Wollermann | 28f08f1 | 2015-09-21 13:42:01 +0000 | [diff] [blame] | 849 | // Read this for detailed explanation: http://www.ibm.com/developerworks/library/j-jtp05236/ |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 850 | if (wasInterrupted) { |
| 851 | Thread.currentThread().interrupt(); // preserve interrupted status |
| 852 | } |
| 853 | } |
| 854 | } |
| 855 | |
| 856 | private void logCommand() { |
| 857 | if (!log.isLoggable(Level.FINE)) { |
| 858 | return; |
| 859 | } |
| 860 | log.fine(toDebugString()); |
| 861 | } |
| 862 | |
| 863 | /** |
| 864 | * A string representation of this command object which includes |
| 865 | * the arguments, the environment, and the working directory. Avoid |
| 866 | * relying on the specifics of this format. Note that the size |
| 867 | * of the result string will reflect the size of the command. |
| 868 | */ |
| 869 | public String toDebugString() { |
| 870 | StringBuilder message = new StringBuilder(128); |
| 871 | message.append("Executing (without brackets):"); |
| 872 | for (final String arg : processBuilder.command()) { |
| 873 | message.append(" ["); |
| 874 | message.append(arg); |
| 875 | message.append(']'); |
| 876 | } |
| 877 | message.append("; environment: "); |
Ulf Adams | 07dba94 | 2015-03-05 14:47:37 +0000 | [diff] [blame] | 878 | message.append(processBuilder.environment()); |
Han-Wen Nienhuys | d08b27f | 2015-02-25 16:45:20 +0100 | [diff] [blame] | 879 | final File workingDirectory = processBuilder.directory(); |
| 880 | message.append("; working dir: "); |
| 881 | message.append(workingDirectory == null ? |
| 882 | "(current)" : |
| 883 | workingDirectory.toString()); |
| 884 | return message.toString(); |
| 885 | } |
| 886 | |
| 887 | /** |
| 888 | * Close the <code>out</code> stream and log a warning if anything happens. |
| 889 | */ |
| 890 | private static void silentClose(final OutputStream out) { |
| 891 | try { |
| 892 | out.close(); |
| 893 | } catch (IOException ioe) { |
| 894 | String message = "Unexpected exception while closing output stream"; |
| 895 | log.log(Level.WARNING, message, ioe); |
| 896 | } |
| 897 | } |
| 898 | } |