| /* |
| * Copyright 2000-2011 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.openapi.util.Ref; |
| import com.intellij.openapi.util.SystemInfo; |
| import com.intellij.util.Processor; |
| import com.sun.jna.Library; |
| import com.sun.jna.Native; |
| import com.sun.jna.Platform; |
| import org.jetbrains.annotations.NotNull; |
| |
| import java.io.*; |
| import java.lang.reflect.Field; |
| import java.util.*; |
| |
| public class UnixProcessManager { |
| public static final int SIGINT = 2; |
| public static final int SIGKILL = 9; |
| public static final int SIGCONT = 19; |
| |
| private static CLib C_LIB; |
| |
| static { |
| try { |
| if (!Platform.isWindows()) { |
| C_LIB = ((CLib)Native.loadLibrary("c", CLib.class)); |
| } |
| } |
| catch (Exception e) { |
| C_LIB = null; |
| } |
| } |
| |
| private UnixProcessManager() { |
| } |
| |
| public static int getProcessPid() { |
| checkCLib(); |
| return C_LIB.getpid(); |
| } |
| |
| public static int getProcessPid(Process process) { |
| try { |
| Field f = process.getClass().getDeclaredField("pid"); |
| f.setAccessible(true); |
| return ((Number)f.get(process)).intValue(); |
| } |
| catch (NoSuchFieldException e) { |
| throw new IllegalStateException("system is not unix", e); |
| } |
| catch (IllegalAccessException e) { |
| throw new IllegalStateException("system is not unix", e); |
| } |
| } |
| |
| public static void sendSignal(Process process, int signal) { |
| int process_pid = getProcessPid(process); |
| sendSignal(process_pid, signal); |
| } |
| |
| public static void sendSignal(int pid, int signal) { |
| checkCLib(); |
| C_LIB.kill(pid, signal); |
| } |
| |
| private static void checkCLib() { |
| if (C_LIB == null) { |
| throw new IllegalStateException("System is not unix(couldn't load c library)"); |
| } |
| } |
| |
| public static boolean sendSigIntToProcessTree(Process process) { |
| return sendSignalToProcessTree(process, SIGINT); |
| } |
| |
| public static boolean sendSigKillToProcessTree(Process process) { |
| return sendSignalToProcessTree(process, SIGKILL); |
| } |
| |
| /** |
| * Sends signal to every child process of a tree root process |
| * |
| * @param process tree root process |
| */ |
| public static boolean sendSignalToProcessTree(Process process, int signal) { |
| checkCLib(); |
| |
| final int our_pid = C_LIB.getpid(); |
| final int process_pid = getProcessPid(process); |
| final Ref<Integer> foundPid = new Ref<Integer>(); |
| final ProcessInfo processInfo = new ProcessInfo(); |
| final List<Integer> childrenPids = new ArrayList<Integer>(); |
| |
| processPSOutput(new Processor<String>() { |
| @Override |
| public boolean process(String s) { |
| StringTokenizer st = new StringTokenizer(s, " "); |
| |
| int parent_pid = Integer.parseInt(st.nextToken()); |
| int pid = Integer.parseInt(st.nextToken()); |
| |
| processInfo.register(pid, parent_pid); |
| |
| if (parent_pid == process_pid) { |
| childrenPids.add(pid); |
| } |
| |
| if (pid == process_pid) { |
| if (parent_pid == our_pid) { |
| foundPid.set(pid); |
| } |
| else { |
| throw new IllegalStateException("process is not our child"); |
| } |
| } |
| return false; |
| } |
| }, getPSCmd(false)); |
| |
| boolean result; |
| if (!foundPid.isNull()) { |
| processInfo.killProcTree(foundPid.get(), signal); |
| result = true; |
| } |
| else { |
| for (Integer pid : childrenPids) { |
| processInfo.killProcTree(pid, signal); |
| } |
| result = false; |
| } |
| return result; |
| } |
| |
| public static void processPSOutput(Processor<String> processor, String[] cmd) { |
| try { |
| Process p = Runtime.getRuntime().exec(cmd); |
| |
| @SuppressWarnings({"IOResourceOpenedButNotSafelyClosed"}) |
| BufferedReader stdOutput = new BufferedReader(new |
| InputStreamReader(p.getInputStream())); |
| BufferedReader stdError = new BufferedReader(new |
| InputStreamReader(p.getErrorStream())); |
| |
| try { |
| String s; |
| stdOutput.readLine(); //ps output header |
| while ((s = stdOutput.readLine()) != null) { |
| processor.process(s); |
| } |
| |
| StringBuilder errorStr = new StringBuilder(); |
| while ((s = stdError.readLine()) != null) { |
| errorStr.append(s).append("\n"); |
| } |
| if (errorStr.length() > 0) { |
| throw new IllegalStateException("error:" + errorStr.toString()); |
| } |
| } |
| finally { |
| stdOutput.close(); |
| stdError.close(); |
| } |
| } |
| catch (IOException e) { |
| throw new IllegalStateException(e); |
| } |
| } |
| |
| public static String[] getPSCmd(boolean commandLineOnly) { |
| String psCommand = "/bin/ps"; |
| if (!new File(psCommand).isFile()) { |
| psCommand = "ps"; |
| } |
| if (SystemInfo.isLinux) { |
| return new String[]{psCommand, "-e", "--format", commandLineOnly ? "%a" : "%P%p%a"}; |
| } |
| <<<<<<< HEAD |
| else if (SystemInfo.isMac || SystemInfo.isFreeBSD) { |
| return new String[]{psCommand, "-ax", "-o", commandLineOnly ? "command" : "ppid,pid,command"}; |
| ======= |
| else if (SystemInfo.isMac) { |
| return new String[]{"ps", "-ax", "-E", "-o", commandLineOnly ? "command" : "ppid,pid,comm"}; |
| >>>>>>> OC-2904 Debugger: Attach to already running local process action |
| } |
| else { |
| throw new IllegalStateException(System.getProperty("os.name") + " is not supported."); |
| } |
| } |
| |
| @NotNull |
| public static String readProcEnviron(int child_pid) throws FileNotFoundException { |
| StringBuffer res = new StringBuffer(); |
| Scanner s = new Scanner(new File("/proc/" + child_pid + "/environ")); |
| while (s.hasNextLine()) { |
| res.append(s).append("\n"); |
| } |
| return res.toString(); |
| } |
| |
| |
| public interface CLib extends Library { |
| int getpid(); |
| |
| int kill(int pid, int signal); |
| } |
| |
| private static class ProcessInfo { |
| private Map<Integer, List<Integer>> BY_PARENT = new TreeMap<Integer, List<Integer>>(); // pid -> list of children pids |
| |
| public void register(Integer pid, Integer parentPid) { |
| List<Integer> children = BY_PARENT.get(parentPid); |
| if (children == null) children = new LinkedList<Integer>(); |
| children.add(pid); |
| BY_PARENT.put(parentPid, children); |
| } |
| |
| void killProcTree(int pid, int signal) { |
| List<Integer> children = BY_PARENT.get(pid); |
| if (children != null) { |
| for (int child : children) killProcTree(child, signal); |
| } |
| sendSignal(pid, signal); |
| } |
| } |
| } |