support Windows soft kills using runnerw.exe in KillableColoredProcessHandler

This commit is contained in:
Sergey Simonchik
2013-11-29 19:09:34 +04:00
parent b5675816ba
commit 77e0f1a978
4 changed files with 147 additions and 20 deletions
@@ -46,6 +46,10 @@ public class KillableColoredProcessHandler extends ColoredProcessHandler impleme
super(process, commandLine);
}
/**
* Sets whether the process will be terminated gracefully.
* @param shouldKillProcessSoftly true, if graceful process termination should be attempted first (i.e. soft kill)
*/
public void setShouldKillProcessSoftly(boolean shouldKillProcessSoftly) {
myShouldKillProcessSoftly = shouldKillProcessSoftly;
}
@@ -56,30 +60,42 @@ public class KillableColoredProcessHandler extends ColoredProcessHandler impleme
* @return
*/
private boolean canKillProcessSoftly() {
// soft-kill works on Unix systems
return SystemInfo.isUnix && processCanBeKilledByOS(getProcess());
if (processCanBeKilledByOS(myProcess)) {
if (SystemInfo.isWindows) {
// runnerw.exe can send Ctrl+C events to a wrapped process
return myProcess instanceof RunnerWinProcess;
}
else if (SystemInfo.isUnix) {
// 'kill -SIGINT <pid>' will be executed
return true;
}
}
return false;
}
@Override
protected void doDestroyProcess() {
boolean gracefulTerminationAttempted = false;
if (canKillProcessSoftly() && shouldKillProcessSoftly()) {
// Unix: [soft-kill] at first send INT signal:
final Process process = getProcess();
if (UnixProcessManager.sendSigIntToProcessTree(process)) {
return;
if (SystemInfo.isWindows) {
if (myProcess instanceof RunnerWinProcess) {
RunnerWinProcess runnerWinProcess = (RunnerWinProcess) myProcess;
runnerWinProcess.destroyGracefully(true);
gracefulTerminationAttempted = true;
}
}
else if (SystemInfo.isUnix) {
gracefulTerminationAttempted = UnixProcessManager.sendSigIntToProcessTree(myProcess);
}
}
// if soft kill isn't supported - use default implementation
super.doDestroyProcess();
// else IDE will suggest 'terminate dialog'
if (!gracefulTerminationAttempted) {
// execute default process destroy
super.doDestroyProcess();
}
}
/**
* This method should be overridden by children if the process shouldn't be killed softly (e.g. by kill -2)
*
* @return
* @return true, if graceful process termination should be attempted first
*/
protected boolean shouldKillProcessSoftly() {
return myShouldKillProcessSoftly;
@@ -92,7 +108,22 @@ public class KillableColoredProcessHandler extends ColoredProcessHandler impleme
@Override
public void killProcess() {
// kill -9
// execute 'kill -SIGKILL <pid>' on Unix
killProcessTree(getProcess());
}
@NotNull
public static KillableColoredProcessHandler create(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
final Process process;
if (SystemInfo.isWindows) {
process = RunnerWinProcess.create(commandLine);
}
else {
process = commandLine.createProcess();
}
return new KillableColoredProcessHandler(process,
commandLine.getCommandLineString(),
commandLine.getCharset());
}
}
@@ -101,7 +101,7 @@ public class RunnerMediator {
}
}
private static void injectRunnerCommand(@NotNull GeneralCommandLine commandLine) {
static void injectRunnerCommand(@NotNull GeneralCommandLine commandLine) {
final String path = getRunnerPath();
if (path != null) {
commandLine.getParametersList().addAt(0, commandLine.getExePath());
@@ -121,7 +121,7 @@ public class RunnerMediator {
* 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.
*/
private static boolean destroyProcess(@NotNull final Process process, final boolean softKill) {
static boolean destroyProcess(@NotNull final Process process, final boolean softKill) {
try {
if (SystemInfo.isWindows) {
sendCtrlEventThroughStream(process, softKill ? C : BRK);
@@ -0,0 +1,86 @@
/*
* 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.util.SystemInfo;
import org.jetbrains.annotations.NotNull;
import java.io.InputStream;
import java.io.OutputStream;
public class RunnerWinProcess extends Process {
private final Process myOriginalProcess;
private RunnerWinProcess(@NotNull Process originalProcess) {
myOriginalProcess = originalProcess;
}
@Override
public OutputStream getOutputStream() {
return myOriginalProcess.getOutputStream();
}
@Override
public InputStream getInputStream() {
return myOriginalProcess.getInputStream();
}
@Override
public InputStream getErrorStream() {
return myOriginalProcess.getErrorStream();
}
@Override
public int waitFor() throws InterruptedException {
return myOriginalProcess.waitFor();
}
@Override
public int exitValue() {
return myOriginalProcess.exitValue();
}
@Override
public void destroy() {
myOriginalProcess.destroy();
}
public Process getOriginalProcess() {
return myOriginalProcess;
}
/**
* Sends Ctrl+C or Ctrl+Break event to the process.
* @param softKill if true, Ctrl+C event will be sent (otherwise, Ctrl+Break)
*/
public void destroyGracefully(boolean softKill) {
RunnerMediator.destroyProcess(this, softKill);
}
@NotNull
public static RunnerWinProcess create(@NotNull GeneralCommandLine commandLine) throws ExecutionException {
if (!SystemInfo.isWindows) {
throw new RuntimeException(RunnerWinProcess.class.getSimpleName() + " works on Windows only!");
}
RunnerMediator.injectRunnerCommand(commandLine);
Process process = commandLine.createProcess();
return new RunnerWinProcess(process);
}
}
@@ -16,6 +16,7 @@
package com.intellij.execution.process.impl;
import com.intellij.execution.process.OSProcessManager;
import com.intellij.execution.process.RunnerWinProcess;
import com.intellij.execution.process.UnixProcessManager;
import com.intellij.openapi.diagnostic.Logger;
import com.intellij.openapi.util.SystemInfo;
@@ -42,12 +43,12 @@ public class OSProcessManagerImpl extends OSProcessManager {
public boolean killProcessTree(@NotNull Process process) {
if (SystemInfo.isWindows) {
try {
new WinProcess(process).killRecursively();
WinProcess winProcess = createWinProcess(process);
winProcess.killRecursively();
return true;
}
catch (Throwable e) {
LOG.info("Cannot kill process tree");
LOG.info(e);
LOG.info("Cannot kill process tree", e);
}
}
else if (SystemInfo.isUnix) {
@@ -56,6 +57,15 @@ public class OSProcessManagerImpl extends OSProcessManager {
return false;
}
@NotNull
private static WinProcess createWinProcess(@NotNull Process process) {
if (process instanceof RunnerWinProcess) {
RunnerWinProcess runnerWinProcess = (RunnerWinProcess) process;
return new WinProcess(runnerWinProcess.getOriginalProcess());
}
return new WinProcess(process);
}
@Override
public List<String> getCommandLinesOfRunningProcesses() {
try {