Autopsy  4.19.3
Graphical digital forensics platform for The Sleuth Kit and other tools.
ExecUtil.java
Go to the documentation of this file.
1 /*
2  * Autopsy Forensic Browser
3  *
4  * Copyright 2013-2020 Basis Technology Corp.
5  * Contact: carrier <at> sleuthkit <dot> org
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  * http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  */
19 package org.sleuthkit.autopsy.coreutils;
20 
21 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25 import java.io.Writer;
26 import java.util.Date;
27 import java.util.List;
28 import java.util.concurrent.TimeUnit;
29 import java.util.logging.Level;
30 import org.apache.commons.lang3.SystemUtils;
32 
37 public final class ExecUtil {
38 
39  private static final Logger logger = Logger.getLogger(ExecUtil.class.getName());
40  private static final long DEFAULT_TERMINATION_CHECK_INTERVAL = 5;
41  private static final TimeUnit DEFAULT_TERMINATION_CHECK_INTERVAL_UNITS = TimeUnit.SECONDS;
42  private static final long MAX_WAIT_FOR_TERMINATION = 1;
43  private static final TimeUnit MAX_WAIT_FOR_TERMINATION_UNITS = TimeUnit.MINUTES;
44 
54  public interface ProcessTerminator {
55 
62  boolean shouldTerminateProcess();
63  }
64 
69  public static class InterruptedThreadProcessTerminator implements ProcessTerminator {
70 
71  @Override
72  public boolean shouldTerminateProcess() {
73  return Thread.currentThread().isInterrupted();
74  }
75  }
76 
81  public static class TimedProcessTerminator implements ProcessTerminator {
82 
83  private final long startTimeInSeconds;
84  private final Long maxRunTimeInSeconds;
85 
92  public TimedProcessTerminator(long maxRunTimeInSeconds) {
93  this.maxRunTimeInSeconds = maxRunTimeInSeconds;
94  this.startTimeInSeconds = (new Date().getTime()) / 1000;
95  }
96 
105  this.maxRunTimeInSeconds = (long) UserPreferences.getProcessTimeOutHrs() * 3600;
106  } else {
107  this.maxRunTimeInSeconds = null;
108  }
109  this.startTimeInSeconds = (new Date().getTime()) / 1000;
110  }
111 
112  @Override
113  public boolean shouldTerminateProcess() {
114  if (maxRunTimeInSeconds != null) {
115  long currentTimeInSeconds = (new Date().getTime()) / 1000;
116  return (currentTimeInSeconds - this.startTimeInSeconds) > this.maxRunTimeInSeconds;
117  } else {
118  return false;
119  }
120  }
121  }
122 
127  public static class HybridTerminator implements ProcessTerminator {
128  private final List<ProcessTerminator> terminatorList;
129 
135  public HybridTerminator(List<ProcessTerminator> terminators) {
136  this.terminatorList = terminators;
137  }
138 
139  @Override
140  public boolean shouldTerminateProcess() {
141  for(ProcessTerminator terminator: terminatorList) {
142  if(terminator.shouldTerminateProcess()) {
143  return true;
144  }
145  }
146  return false;
147  }
148  }
149 
172  public static int execute(ProcessBuilder processBuilder) throws SecurityException, IOException {
173  return ExecUtil.execute(processBuilder, 30, TimeUnit.DAYS, new ProcessTerminator() {
174  @Override
175  public boolean shouldTerminateProcess() {
176  return false;
177  }
178  });
179  }
180 
203  public static int execute(ProcessBuilder processBuilder, ProcessTerminator terminator) throws SecurityException, IOException {
205  }
206 
234  public static int execute(ProcessBuilder processBuilder, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator) throws SecurityException, IOException {
235  return waitForTermination(processBuilder.command().get(0), processBuilder.start(), terminationCheckInterval, units, terminator);
236  }
237 
264  public static int waitForTermination(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator) throws IOException {
265  try {
266  return waitForProcess(processName, process, terminationCheckInterval, units, terminator);
267  } catch (InterruptedException ex) {
268  /*
269  * Reset the interrupted flag and wrap the exception in an
270  * IOException for backwards compatibility.
271  */
272  Thread.currentThread().interrupt();
273  throw new IOException(String.format("Interrupted executing %s", processName), ex); //NON-NLS
274  }
275  }
276 
298  private static int waitForProcess(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator) throws IOException, InterruptedException {
299  do {
300  try {
301  process.waitFor(terminationCheckInterval, units);
302  } catch (InterruptedException ex) {
303  logger.log(Level.WARNING, String.format("Interrupted executing %s", processName), ex); //NON-NLS
304  Thread.currentThread().interrupt();
305  terminateProcess(processName, process);
306  /*
307  * Note that if the preceding call to terminateProcess() throws
308  * an IOException, the caller will get that exception instead of
309  * this InterruptedException, which is arguably preferable. If
310  * terminateProcess() does not throw an IOException, then its
311  * call to waitFor() will throw a fresh InterruptedException,
312  * which is fine.
313  */
314  throw ex;
315  }
316  if (process.isAlive() && terminator.shouldTerminateProcess()) {
317  terminateProcess(processName, process);
318  }
319  } while (process.isAlive());
320 
321  /*
322  * Careful: Process.exitValue() throws an IllegalStateException if the
323  * process is still alive when the method is called. This code is set up
324  * so that the only way Process.exitValue() can be called is when it has
325  * not been bypassed by an exception and the preceding loop has
326  * terminated with Process.isAlive == false.
327  */
328  return process.exitValue();
329  }
330 
344  public static void killProcess(Process process) {
345  String processName = process.toString();
346  try {
347  terminateProcess(processName, process);
348  } catch (IOException ex) {
349  logger.log(Level.WARNING, String.format("Error occured executing %s", processName), ex); //NON-NLS
350  } catch (InterruptedException ex) {
351  logger.log(Level.WARNING, String.format("Interrupted executing %s", processName), ex); //NON-NLS
352  Thread.currentThread().interrupt();
353  }
354  }
355 
369  private static void terminateProcess(String processName, Process process) throws IOException, InterruptedException {
370  if (process == null || !process.isAlive()) {
371  return;
372  }
373 
374  if (SystemUtils.IS_OS_WINDOWS) {
375  try {
376  Win32Process parentProcess = new Win32Process(process);
377  List<Win32Process> children = parentProcess.getChildren();
378  children.stream().forEach((child) -> {
379  child.terminate();
380  });
381  parentProcess.terminate();
382  } catch (Exception ex) {
383  /*
384  * Wrap whatever exception was thrown from Windows in an
385  * exception that is appropriate for this API.
386  */
387  throw new IOException(String.format("Error occured terminating %s", processName), ex); //NON-NLS
388  }
389  } else {
390  process.destroyForcibly();
391  }
392 
393  if (!process.waitFor(MAX_WAIT_FOR_TERMINATION, MAX_WAIT_FOR_TERMINATION_UNITS)) {
394  throw new IOException(String.format("Failed to terminate %s after %d %s", processName, MAX_WAIT_FOR_TERMINATION, MAX_WAIT_FOR_TERMINATION_UNITS)); //NON-NLS
395  }
396  }
397 
398  /*
399  * Fields used by deprecated methods that require instantiation of an
400  * ExecUtil object.
401  */
402  private Process proc = null;
406  private int exitValue = -100;
407 
421  @Deprecated
422  public synchronized String execute(final String aCommand, final String... params) throws IOException, InterruptedException {
423  // build command array
424  String[] arrayCommand = new String[params.length + 1];
425  arrayCommand[0] = aCommand;
426 
427  StringBuilder arrayCommandToLog = new StringBuilder();
428  arrayCommandToLog.append(aCommand).append(" ");
429 
430  for (int i = 1; i < arrayCommand.length; i++) {
431  arrayCommand[i] = params[i - 1];
432  arrayCommandToLog.append(arrayCommand[i]).append(" ");
433  }
434 
435  final Runtime rt = Runtime.getRuntime();
436  logger.log(Level.INFO, "Executing {0}", arrayCommandToLog.toString()); //NON-NLS
437 
438  proc = rt.exec(arrayCommand);
439 
440  //stderr redirect
441  errorStringRedirect = new ExecUtil.StreamToStringRedirect(proc.getErrorStream(), "ERROR"); //NON-NLS
442  errorStringRedirect.start();
443 
444  //stdout redirect
445  outputStringRedirect = new ExecUtil.StreamToStringRedirect(proc.getInputStream(), "OUTPUT"); //NON-NLS
446  outputStringRedirect.start();
447 
448  //wait for process to complete and capture error core
449  this.exitValue = proc.waitFor();
450 
451  // wait for output redirectors to finish writing / reading
452  outputStringRedirect.join();
453  errorStringRedirect.join();
454 
455  return outputStringRedirect.getOutput();
456  }
457 
469  @Deprecated
470  public synchronized void execute(final Writer stdoutWriter, final String aCommand, final String... params) throws IOException, InterruptedException {
471 
472  // build command array
473  String[] arrayCommand = new String[params.length + 1];
474  arrayCommand[0] = aCommand;
475 
476  StringBuilder arrayCommandToLog = new StringBuilder();
477  arrayCommandToLog.append(aCommand).append(" ");
478 
479  for (int i = 1; i < arrayCommand.length; i++) {
480  arrayCommand[i] = params[i - 1];
481  arrayCommandToLog.append(arrayCommand[i]).append(" ");
482  }
483 
484  final Runtime rt = Runtime.getRuntime();
485  logger.log(Level.INFO, "Executing {0}", arrayCommandToLog.toString()); //NON-NLS
486 
487  proc = rt.exec(arrayCommand);
488 
489  //stderr redirect
490  errorStringRedirect = new ExecUtil.StreamToStringRedirect(proc.getErrorStream(), "ERROR"); //NON-NLS
491  errorStringRedirect.start();
492 
493  //stdout redirect
494  outputWriterRedirect = new ExecUtil.StreamToWriterRedirect(proc.getInputStream(), stdoutWriter);
495  outputWriterRedirect.start();
496 
497  //wait for process to complete and capture error core
498  this.exitValue = proc.waitFor();
499  logger.log(Level.INFO, "{0} exit value: {1}", new Object[]{aCommand, exitValue}); //NON-NLS
500 
501  // wait for them to finish writing / reading
502  outputWriterRedirect.join();
503  errorStringRedirect.join();
504 
505  //gc process with its streams
506  //proc = null;
507  }
508 
512  @Deprecated
513  public synchronized void stop() {
514 
515  if (errorStringRedirect != null) {
516  errorStringRedirect.stopRun();
517  errorStringRedirect = null;
518  }
519 
520  if (outputStringRedirect != null) {
521  outputStringRedirect.stopRun();
522  outputStringRedirect = null;
523  }
524 
525  if (outputWriterRedirect != null) {
526  outputWriterRedirect.stopRun();
527  outputWriterRedirect = null;
528  }
529 
530  if (proc != null) {
531  proc.destroy();
532  proc = null;
533  }
534  }
535 
542  @Deprecated
543  synchronized public int getExitValue() {
544  return this.exitValue;
545  }
546 
553  private static class StreamToStringRedirect extends Thread {
554 
555  private static final Logger logger = Logger.getLogger(StreamToStringRedirect.class.getName());
556  private final InputStream is;
557  private final StringBuffer output = new StringBuffer();
558  private volatile boolean doRun = false;
559 
560  StreamToStringRedirect(final InputStream anIs, final String aType) {
561  this.is = anIs;
562  this.doRun = true;
563  }
564 
571  @Override
572  public final void run() {
573  final String SEP = System.getProperty("line.separator");
574  InputStreamReader isr;
575  BufferedReader br = null;
576  try {
577  isr = new InputStreamReader(this.is);
578  br = new BufferedReader(isr);
579  String line = null;
580  while (doRun && (line = br.readLine()) != null) {
581  this.output.append(line).append(SEP);
582  }
583  } catch (final IOException ex) {
584  logger.log(Level.WARNING, "Error redirecting stream to string buffer", ex); //NON-NLS
585  } finally {
586  if (br != null) {
587  try {
588  br.close();
589  } catch (IOException ex) {
590  logger.log(Level.SEVERE, "Error closing stream reader", ex); //NON-NLS
591  }
592  }
593  }
594  }
595 
600  public void stopRun() {
601  doRun = false;
602  }
603 
610  public final String getOutput() {
611  return this.output.toString();
612  }
613  }
614 
623  private static class StreamToWriterRedirect extends Thread {
624 
625  private static final Logger logger = Logger.getLogger(StreamToStringRedirect.class.getName());
626  private final InputStream is;
627  private volatile boolean doRun = false;
628  private Writer writer = null;
629 
630  StreamToWriterRedirect(final InputStream anIs, final Writer writer) {
631  this.is = anIs;
632  this.writer = writer;
633  this.doRun = true;
634  }
635 
642  @Override
643  public final void run() {
644  final String SEP = System.getProperty("line.separator");
645  InputStreamReader isr;
646  BufferedReader br = null;
647  try {
648  isr = new InputStreamReader(this.is);
649  br = new BufferedReader(isr);
650  String line = null;
651  while (doRun && (line = br.readLine()) != null) {
652  writer.append(line).append(SEP);
653  }
654  } catch (final IOException ex) {
655  logger.log(Level.SEVERE, "Error reading output and writing to file writer", ex); //NON-NLS
656  } finally {
657  try {
658  if (doRun) {
659  writer.flush();
660  }
661  if (br != null) {
662  br.close();
663  }
664 
665  } catch (IOException ex) {
666  logger.log(Level.SEVERE, "Error flushing file writer", ex); //NON-NLS
667  }
668  }
669  }
670 
675  public void stopRun() {
676  doRun = false;
677  }
678  }
679 }
ExecUtil.StreamToStringRedirect errorStringRedirect
Definition: ExecUtil.java:403
static int execute(ProcessBuilder processBuilder, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator)
Definition: ExecUtil.java:234
static int execute(ProcessBuilder processBuilder, ProcessTerminator terminator)
Definition: ExecUtil.java:203
ExecUtil.StreamToWriterRedirect outputWriterRedirect
Definition: ExecUtil.java:405
static int execute(ProcessBuilder processBuilder)
Definition: ExecUtil.java:172
synchronized void execute(final Writer stdoutWriter, final String aCommand, final String...params)
Definition: ExecUtil.java:470
static int waitForTermination(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator)
Definition: ExecUtil.java:264
static void terminateProcess(String processName, Process process)
Definition: ExecUtil.java:369
ExecUtil.StreamToStringRedirect outputStringRedirect
Definition: ExecUtil.java:404
static void killProcess(Process process)
Definition: ExecUtil.java:344
HybridTerminator(List< ProcessTerminator > terminators)
Definition: ExecUtil.java:135
static final TimeUnit MAX_WAIT_FOR_TERMINATION_UNITS
Definition: ExecUtil.java:43
static int waitForProcess(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator)
Definition: ExecUtil.java:298
static final long MAX_WAIT_FOR_TERMINATION
Definition: ExecUtil.java:42
static final long DEFAULT_TERMINATION_CHECK_INTERVAL
Definition: ExecUtil.java:40
synchronized static Logger getLogger(String name)
Definition: Logger.java:124
static final TimeUnit DEFAULT_TERMINATION_CHECK_INTERVAL_UNITS
Definition: ExecUtil.java:41
synchronized String execute(final String aCommand, final String...params)
Definition: ExecUtil.java:422

Copyright © 2012-2022 Basis Technology. Generated on: Tue Jun 27 2023
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.