blob: 709330e4e26fffe41008e55aff893e08902faa71 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wst.jsdt.js.cli.internal.util;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import org.eclipse.wst.jsdt.js.cli.CLIPlugin;
import org.eclipse.wst.jsdt.js.cli.core.CLICommand;
/**
* @author "Adalberto Lopez Venegas (adalbert)"
* @author "Ilya Buziuk (ibuziuk)"
*/
public class ProcessUtil {
private ProcessUtil() {
}
public static boolean isWindows() {
String OS = System.getProperty("os.name", "unknown"); //$NON-NLS-1$//$NON-NLS-2$
return OS.toLowerCase().indexOf("win") > -1; //$NON-NLS-1$
}
/**
* Kills a process by its id.
* For windows it uses "taskkill" command, i.e.
* - Kill a process and its children processes forcefully:
* "taskkill /PID <pid> /T /F"
*
* - Kill a process normally without its children processes:
* "taskkill /PID <pid>"
*
* For Linux & Mac it uses "pkill" and "kill" command, i.e.
* - Kill a process and its children processes forcefully:
* "pkill -9 -P <pid>"
*
* NOTE: The <pid> needs to be alive in order to kill it's
* children processes.
*
* - Kill a process normally without its children processes:
* "kill <pid>"
*
* @param pid the process id to be kill.
* @param includeChildProcesses whether the process will be kill with it's
* children processes.
* @param terminateProcessForcefully whether the process will forcefully kill.
* @return the exit value of the command. By convention, the value 0 indicates
* normal termination.
*/
public static int terminateProcessById(String pid, Boolean includeChildProcesses, Boolean terminateProcessForcefully){
List<String> command = new ArrayList<String>();
if(isWindows()){
command.add("taskkill"); //$NON-NLS-1$
command.add("/PID"); //$NON-NLS-1$
command.add(pid);
if(includeChildProcesses){
command.add("/T"); //$NON-NLS-1$
}
if(terminateProcessForcefully){
command.add("/F"); //$NON-NLS-1$
}
} else {
if(includeChildProcesses){
command.add("pkill"); //$NON-NLS-1$
if(terminateProcessForcefully){
command.add("-9"); //$NON-NLS-1$
}
command.add("-P"); //$NON-NLS-1$
} else {
command.add("kill"); //$NON-NLS-1$
if(terminateProcessForcefully){
command.add("-9"); //$NON-NLS-1$
}
}
command.add(pid);
}
ProcessBuilder processBuilder = new ProcessBuilder(command);
int exit = 1;
try {
exit = processBuilder.start().waitFor();
} catch (InterruptedException | IOException e) {
exit = 1;
CLIPlugin.logError(e);
}
return exit;
}
/**
* Returns the PID of the process which matches certain parameters depending on OS
*
* @param CLICommand
* command that was used to launch the process
*
* @param path
* the absolute path of the location from where the process was started
*
* @return the pid matched or null if no match.
*/
public static String getPID(CLICommand command, String path) {
return isWindows() ? getPIDonWindows(command.getCommandName())
: getPIDonUnix(command.getToolName(), path);
}
/**
* Returns the PID of the node process with the matched CommandLine pattern
* (command name e.g gulp serve, grunt connect etc).
* On windows gulp / grunt processes are defined as node.js ones
* The next command, i.e. "wmic process where
* 'caption^=\"node.exe\" and CommandLine like \"<pattern>\"' get processid"
*
* @param commandName
* the name of command e.g. serve, connect
*
* @return the first pid matched or null if no match.
*/
private static String getPIDonWindows(String commandName) {
String pid = null;
String wmicCommand = "wmic process where 'caption^=\"node.exe\" and CommandLine like \"%" + commandName //$NON-NLS-1$
+ "%\"' get processid"; //$NON-NLS-1$
String[] command = new String[] { "cmd", "/c", //$NON-NLS-1$ //$NON-NLS-2$
"for /f \"usebackq skip=1\" %F in (`" + wmicCommand + "`) do @echo %F" }; //$NON-NLS-1$ //$NON-NLS-2$
ProcessBuilder processBuilder = new ProcessBuilder(command);
try {
Process process = processBuilder.start();
InputStream inputStream = process.getInputStream();
BufferedReader buffer = new BufferedReader(new InputStreamReader(inputStream));
int exit = process.waitFor();
if (exit == 0) {
String line = buffer.readLine();
if (line != null && !line.equals("ECHO is on.")) { //$NON-NLS-1$
pid = line;
}
}
} catch (IOException | InterruptedException e) {
CLIPlugin.logError(e);
}
return pid;
}
/**
* Returns the PID of the Node.js based tool (grunt / gulp) process with the matched path
*
* For Linux & Mac it uses the next command, i.e.
* ps axwww | grep -i <toolName>.* | grep -v grep | awk '{print $1}'
*
*
* @param toolName
* the name of Node.js based tool e.g. grunt / gulp
* @param path
* the absolute path of the location from where the process was started
*
* @return the first pid matched or null if no match.
*/
private static String getPIDonUnix(String toolName, String path) {
List<String> mathingPIDs = new ArrayList<String>();
String[] command = new String[] { "/bin/sh", "-c", //$NON-NLS-1$ //$NON-NLS-2$
"ps axwww | grep -i " + toolName + ".* | grep -v grep | awk '{print $1}'" }; //$NON-NLS-1$ //$NON-NLS-2$
ProcessBuilder processBuilder = new ProcessBuilder(command);
try {
Process process = processBuilder.start();
InputStream inputStream = process.getInputStream();
BufferedReader buffer = new BufferedReader(new InputStreamReader(inputStream));
int exit = process.waitFor();
if (exit == 0) {
String line;
while ((line = buffer.readLine()) != null && !line.equals("ECHO is on.")) { //$NON-NLS-1$
mathingPIDs.add(line);
}
}
} catch (IOException | InterruptedException e) {
CLIPlugin.logError(e);
}
// if there is only one match - just return it, otherwise need to perform path match
return (mathingPIDs.size() == 1) ? mathingPIDs.get(0) : findPID(mathingPIDs, path);
}
/*
* @return the first pid matched path or null if no match.
*/
private static String findPID(List<String> mathingPIDs, String path) {
String pid = null;
for (String p : mathingPIDs) {
ProcessBuilder processBuilder = new ProcessBuilder(new String[] { "pwdx", p }); //$NON-NLS-1$
try {
Process process = processBuilder.start();
InputStream inputStream = process.getInputStream();
BufferedReader buffer = new BufferedReader(new InputStreamReader(inputStream));
int exit = process.waitFor();
if (exit == 0) {
String line = buffer.readLine();
if (line != null && line.endsWith(path)) {
pid = p;
break;
}
}
} catch (IOException | InterruptedException e) {
CLIPlugin.logError(e);
}
}
return pid;
}
}