Autopsy 4.22.1
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 */
19package org.sleuthkit.autopsy.coreutils;
20
21import java.io.BufferedReader;
22import java.io.IOException;
23import java.io.InputStream;
24import java.io.InputStreamReader;
25import java.io.Writer;
26import java.util.Date;
27import java.util.List;
28import java.util.concurrent.TimeUnit;
29import java.util.logging.Level;
30import org.apache.commons.lang3.SystemUtils;
31import org.sleuthkit.autopsy.core.UserPreferences;
32
37public 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
63 }
64
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
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
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
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
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();
523 }
524
525 if (outputWriterRedirect != null) {
526 outputWriterRedirect.stopRun();
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}
HybridTerminator(List< ProcessTerminator > terminators)
static void killProcess(Process process)
static int execute(ProcessBuilder processBuilder, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator)
ExecUtil.StreamToStringRedirect errorStringRedirect
ExecUtil.StreamToWriterRedirect outputWriterRedirect
static int execute(ProcessBuilder processBuilder, ProcessTerminator terminator)
synchronized void execute(final Writer stdoutWriter, final String aCommand, final String... params)
static final TimeUnit DEFAULT_TERMINATION_CHECK_INTERVAL_UNITS
Definition ExecUtil.java:41
static int waitForProcess(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator)
static final TimeUnit MAX_WAIT_FOR_TERMINATION_UNITS
Definition ExecUtil.java:43
ExecUtil.StreamToStringRedirect outputStringRedirect
static int execute(ProcessBuilder processBuilder)
synchronized String execute(final String aCommand, final String... params)
static int waitForTermination(String processName, Process process, long terminationCheckInterval, TimeUnit units, ProcessTerminator terminator)
static void terminateProcess(String processName, Process process)
static final long DEFAULT_TERMINATION_CHECK_INTERVAL
Definition ExecUtil.java:40
synchronized static Logger getLogger(String name)
Definition Logger.java:124

Copyright © 2012-2024 Sleuth Kit Labs. Generated on:
This work is licensed under a Creative Commons Attribution-Share Alike 3.0 United States License.