blob: 3fd87118f92e45e2b52824c107e4191d72bb0eda [file] [log] [blame]
/*
* Copyright 2000-2013 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.intellij.execution.process;
import com.intellij.execution.ExecutionException;
import com.intellij.execution.configurations.GeneralCommandLine;
import com.intellij.openapi.application.PathManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.OutputStream;
import java.io.PrintWriter;
/**
* @author traff
*/
public class RunnerMediator {
public static final Logger LOG = Logger.getInstance("#com.intellij.execution.process.RunnerMediator");
private static final char IAC = (char)5;
private static final char BRK = (char)3;
private static final char C = (char)5;
private static final String STANDARD_RUNNERW = "runnerw.exe";
/**
* Creates default runner mediator
* @return
*/
public static RunnerMediator getInstance() {
return new RunnerMediator();
}
/**
* Sends sequence of two chars(codes 5 and {@code event}) to a process output stream
*/
private static void sendCtrlEventThroughStream(@NotNull final Process process, final char event) {
OutputStream os = process.getOutputStream();
@SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
PrintWriter pw = new PrintWriter(os);
pw.print(IAC);
pw.print(event);
pw.flush();
}
/**
* In case of windows creates process with runner mediator(runnerw.exe) injected to command line string, which adds a capability
* to terminate process tree gracefully with ctrl+break.
*
* Returns appropriate process handle, which in case of Unix is able to terminate whole process tree by sending sig_kill
*
*/
public ProcessHandler createProcess(@NotNull final GeneralCommandLine commandLine) throws ExecutionException {
return createProcess(commandLine, false);
}
public ProcessHandler createProcess(@NotNull final GeneralCommandLine commandLine, final boolean useSoftKill) throws ExecutionException {
if (SystemInfo.isWindows) {
injectRunnerCommand(commandLine);
}
Process process = commandLine.createProcess();
return new CustomDestroyProcessHandler(process, commandLine, useSoftKill);
}
@Nullable
private static String getRunnerPath() {
if (SystemInfo.isWindows) {
final String path = System.getenv("IDEA_RUNNERW");
if (path != null && new File(path).exists()) {
return path;
}
File runnerw = new File(PathManager.getBinPath(), STANDARD_RUNNERW);
if (runnerw.exists()) {
return runnerw.getPath();
}
return null;
}
else {
throw new IllegalStateException("There is no need of runner under unix based OS");
}
}
static void injectRunnerCommand(@NotNull GeneralCommandLine commandLine) {
final String path = getRunnerPath();
if (path != null) {
commandLine.getParametersList().addAt(0, commandLine.getExePath());
commandLine.setExePath(path);
}
}
/**
* Destroys process tree: in case of windows via imitating ctrl+break, in case of unix via sending sig_kill to every process in tree.
* @param process to kill with all sub-processes.
*/
public static boolean destroyProcess(@NotNull final Process process) {
return destroyProcess(process, false);
}
/**
* Destroys process tree: in case of windows via imitating ctrl+c, in case of unix via sending sig_int to every process in tree.
* @param process to kill with all sub-processes.
*/
static boolean destroyProcess(@NotNull final Process process, final boolean softKill) {
try {
if (SystemInfo.isWindows) {
sendCtrlEventThroughStream(process, softKill ? C : BRK);
return true;
}
else if (SystemInfo.isUnix) {
if (softKill) {
return UnixProcessManager.sendSigIntToProcessTree(process);
}
else {
return UnixProcessManager.sendSigKillToProcessTree(process);
}
}
else {
return false;
}
}
catch (Exception e) {
LOG.error("Couldn't terminate the process", e);
return false;
}
}
public static class CustomDestroyProcessHandler extends ColoredProcessHandler {
private final boolean mySoftKill;
public CustomDestroyProcessHandler(@NotNull Process process, @NotNull GeneralCommandLine commandLine) {
this(process, commandLine, false);
}
public CustomDestroyProcessHandler(@NotNull Process process, @NotNull GeneralCommandLine commandLine, final boolean softKill) {
super(process, commandLine.getCommandLineString());
mySoftKill = softKill;
}
protected boolean shouldDestroyProcessRecursively(){
return true;
}
@Override
protected void destroyProcessImpl() {
if (!RunnerMediator.destroyProcess(getProcess(), mySoftKill)) {
super.destroyProcessImpl();
}
}
}
}