2 * Copyright 2000-2014 JetBrains s.r.o.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package com.intellij.execution.process;
18 import com.intellij.openapi.diagnostic.Logger;
19 import com.intellij.openapi.util.Ref;
20 import com.intellij.openapi.util.SystemInfo;
21 import com.intellij.util.Processor;
22 import com.intellij.util.ReflectionUtil;
23 import com.sun.jna.Library;
24 import com.sun.jna.Native;
25 import com.sun.jna.Platform;
27 import java.io.BufferedReader;
29 import java.io.IOException;
30 import java.io.InputStreamReader;
36 public class UnixProcessManager {
37 private static final Logger LOG = Logger.getInstance(UnixProcessManager.class);
39 public static final int SIGINT = 2;
40 public static final int SIGKILL = 9;
41 public static final int SIGTERM = 15;
42 public static final int SIGCONT = 19;
44 private static CLib C_LIB;
48 if (!Platform.isWindows()) {
49 C_LIB = ((CLib)Native.loadLibrary("c", CLib.class));
53 Logger log = Logger.getInstance(UnixProcessManager.class);
54 log.warn("Can't load c library", e);
59 private UnixProcessManager() { }
61 public static int getProcessPid(Process process) {
63 return ReflectionUtil.getField(process.getClass(), process, int.class, "pid");
66 throw new IllegalStateException("system is not unix", e);
70 public static void sendSignal(int pid, int signal) {
72 C_LIB.kill(pid, signal);
75 private static void checkCLib() {
77 throw new IllegalStateException("System is not unix(couldn't load c library)");
81 public static boolean sendSigIntToProcessTree(Process process) {
82 return sendSignalToProcessTree(process, SIGINT);
85 public static boolean sendSigKillToProcessTree(Process process) {
86 return sendSignalToProcessTree(process, SIGKILL);
90 * Sends signal to every child process of a tree root process
92 * @param process tree root process
94 public static boolean sendSignalToProcessTree(Process process, int signal) {
98 final int our_pid = C_LIB.getpid();
99 final int process_pid = getProcessPid(process);
101 final Ref<Integer> foundPid = new Ref<Integer>();
102 final ProcessInfo processInfo = new ProcessInfo();
103 final List<Integer> childrenPids = new ArrayList<Integer>();
105 findChildProcesses(our_pid, process_pid, foundPid, processInfo, childrenPids);
107 // result is true if signal was sent to at least one process
108 final boolean result;
109 if (!foundPid.isNull()) {
110 processInfo.killProcTree(foundPid.get(), signal, UNIX_KILLER);
114 for (Integer pid : childrenPids) {
115 processInfo.killProcTree(pid, signal, UNIX_KILLER);
117 result = !childrenPids.isEmpty(); //we've tried to kill at least one process
122 catch (Exception e) {
123 //If we fail somehow just return false
124 LOG.warn("Error killing the process", e);
129 private static void findChildProcesses(final int our_pid,
130 final int process_pid,
131 final Ref<Integer> foundPid,
132 final ProcessInfo processInfo, final List<Integer> childrenPids) {
133 final Ref<Boolean> ourPidFound = Ref.create(false);
134 processPSOutput(getPSCmd(false), new Processor<String>() {
136 public boolean process(String s) {
137 StringTokenizer st = new StringTokenizer(s, " ");
139 int parent_pid = Integer.parseInt(st.nextToken());
140 int pid = Integer.parseInt(st.nextToken());
142 processInfo.register(pid, parent_pid);
144 if (parent_pid == process_pid) {
145 childrenPids.add(pid);
148 if (pid == our_pid) {
149 ourPidFound.set(true);
151 else if (pid == process_pid) {
152 if (parent_pid == our_pid) {
156 throw new IllegalStateException("Process (pid=" + process_pid + ") is not our child(our pid = " + our_pid + ")");
162 if (!ourPidFound.get()) {
163 throw new IllegalStateException("IDE pid is not found in ps list(" + our_pid + ")");
167 public static void processPSOutput(String[] cmd, Processor<String> processor) {
168 processCommandOutput(cmd, processor, true, true);
171 public static void processCommandOutput(String[] cmd, Processor<String> processor, boolean skipFirstLine, boolean throwOnError) {
173 Process p = Runtime.getRuntime().exec(cmd);
174 processCommandOutput(p, processor, skipFirstLine, throwOnError);
176 catch (IOException e) {
177 throw new IllegalStateException(e);
181 private static void processCommandOutput(Process process, Processor<String> processor, boolean skipFirstLine, boolean throwOnError) throws IOException {
182 BufferedReader stdOutput = new BufferedReader(new InputStreamReader(process.getInputStream()));
184 BufferedReader stdError = new BufferedReader(new InputStreamReader(process.getErrorStream()));
187 stdOutput.readLine(); //ps output header
190 while ((s = stdOutput.readLine()) != null) {
191 processor.process(s);
194 StringBuilder errorStr = new StringBuilder();
195 while ((s = stdError.readLine()) != null) {
196 if (s.contains("environment variables being ignored")) { // PY-8160
199 errorStr.append(s).append("\n");
201 if (throwOnError && errorStr.length() > 0) {
202 throw new IOException("Error reading ps output:" + errorStr.toString());
214 public static String[] getPSCmd(boolean commandLineOnly) {
215 return getPSCmd(commandLineOnly, false);
218 public static String[] getPSCmd(boolean commandLineOnly, boolean isShortenCommand) {
219 String psCommand = "/bin/ps";
220 if (!new File(psCommand).isFile()) {
223 if (SystemInfo.isLinux) {
224 return new String[]{psCommand, "-e", "--format", commandLineOnly ? "%a" : "%P%p%a"};
226 else if (SystemInfo.isMac || SystemInfo.isFreeBSD) {
227 final String command = isShortenCommand ? "comm" : "command";
228 return new String[]{psCommand, "-ax", "-o", commandLineOnly ? command : "ppid,pid," + command};
231 throw new IllegalStateException(System.getProperty("os.name") + " is not supported.");
235 private interface CLib extends Library {
237 int kill(int pid, int signal);
240 public static class ProcessInfo {
241 private Map<Integer, List<Integer>> BY_PARENT = new TreeMap<Integer, List<Integer>>(); // pid -> list of children pids
243 public void register(Integer pid, Integer parentPid) {
244 List<Integer> children = BY_PARENT.get(parentPid);
245 if (children == null) children = new LinkedList<Integer>();
247 BY_PARENT.put(parentPid, children);
250 public void killProcTree(int pid, int signal, ProcessKiller killer) {
251 List<Integer> children = BY_PARENT.get(pid);
252 if (children != null) {
253 for (int child : children) killProcTree(child, signal, killer);
255 killer.kill(pid, signal);
259 public interface ProcessKiller {
260 void kill(int pid, int signal);
263 private static final ProcessKiller UNIX_KILLER = new ProcessKiller() {
265 public void kill(int pid, int signal) {
266 sendSignal(pid, signal);