CPP-3088 Kill CMake processes when exiting IDE
[idea/community.git] / platform / platform-impl / src / com / intellij / execution / process / ProcessUtils.java
1 /*
2  * Copyright 2000-2015 JetBrains s.r.o.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  */
16
17 /******************************************************************************
18  * Copyright (C) 2013  Fabio Zadrozny
19  *
20  * All rights reserved. This program and the accompanying materials
21  * are made available under the terms of the Eclipse Public License v1.0
22  * which accompanies this distribution, and is available at
23  * http://www.eclipse.org/legal/epl-v10.html
24  *
25  * Contributors:
26  *     Fabio Zadrozny <fabiofz@gmail.com> - initial API and implementation
27  ******************************************************************************/
28 package com.intellij.execution.process;
29
30 import com.intellij.openapi.diagnostic.Logger;
31 import com.intellij.openapi.util.SystemInfo;
32 import com.intellij.openapi.util.text.StringUtil;
33 import com.intellij.util.ArrayUtil;
34 import org.jetbrains.annotations.NotNull;
35 import org.jetbrains.annotations.Nullable;
36 import org.jvnet.winp.WinProcess;
37 import org.jvnet.winp.WinpException;
38
39 import java.io.*;
40 import java.util.ArrayList;
41 import java.util.Arrays;
42 import java.util.List;
43
44 public class ProcessUtils {
45   private static final Logger LOG = Logger.getInstance(ProcessUtils.class);
46
47   /**
48    * Passes the commands directly to Runtime.exec (with the passed envp)
49    */
50   @NotNull
51   public static Process createProcess(String[] cmdarray, String[] envp, File workingDir) throws IOException {
52     return Runtime.getRuntime().exec(getWithoutEmptyParams(cmdarray), getWithoutEmptyParams(envp), workingDir);
53   }
54
55   /**
56    * @return a new array without any null/empty elements originally contained in the array.
57    */
58   @Nullable
59   private static String[] getWithoutEmptyParams(@Nullable String[] cmdarray) {
60     if (cmdarray == null) {
61       return null;
62     }
63     ArrayList<String> list = new ArrayList<String>();
64     for (String string : cmdarray) {
65       if (string != null && string.length() > 0) {
66         list.add(string);
67       }
68     }
69     return ArrayUtil.toStringArray(list);
70   }
71
72   @NotNull
73   public static IProcessList getProcessList(@NotNull String helpersRoot) {
74     if (SystemInfo.isWindows) {
75       return new ProcessListWin32(helpersRoot);
76     }
77     if (SystemInfo.isLinux) {
78       return new ProcessListLinux();
79     }
80     if (SystemInfo.isMac) {
81       return new ProcessListMac();
82     }
83
84     LOG.error("Unexpected platform. Unable to list processes.");
85     return new IProcessList() {
86
87       @Override
88       public ProcessInfo[] getProcessList() {
89         return new ProcessInfo[0];
90       }
91     };
92   }
93
94   @NotNull
95   public static char[] loadFileText(@NotNull File file, @Nullable String encoding) throws IOException {
96     InputStream stream = new FileInputStream(file);
97     @SuppressWarnings("IOResourceOpenedButNotSafelyClosed")
98     Reader reader = encoding == null ? new InputStreamReader(stream) : new InputStreamReader(stream, encoding);
99     try {
100       return loadText(reader);
101     }
102     finally {
103       reader.close();
104     }
105   }
106
107   @NotNull
108   public static char[] loadText(@NotNull Reader reader) throws IOException {
109     //fill a buffer with the contents
110     int BUFFER_SIZE = 2 * 1024;
111
112     char[] readBuffer = new char[BUFFER_SIZE];
113     int n = reader.read(readBuffer);
114
115     int DEFAULT_FILE_SIZE = 8 * BUFFER_SIZE;
116
117     StringBuilder builder = new StringBuilder(DEFAULT_FILE_SIZE);
118
119     while (n > 0) {
120       builder.append(readBuffer, 0, n);
121       n = reader.read(readBuffer);
122     }
123
124     return builder.toString().toCharArray();
125   }
126
127   public static boolean killProcessTree(@NotNull Process process) {
128     if (SystemInfo.isWindows) {
129       try {
130         WinProcess winProcess = createWinProcess(process);
131         winProcess.killRecursively();
132         return true;
133       }
134       catch (Throwable e) {
135         LOG.info("Cannot kill process tree", e);
136       }
137     }
138     else if (SystemInfo.isUnix) {
139       return UnixProcessManager.sendSigKillToProcessTree(process);
140     }
141     return false;
142   }
143
144   public static void killProcess(@NotNull Process process) {
145     if (SystemInfo.isWindows) {
146       try {
147         WinProcess winProcess = createWinProcess(process);
148         winProcess.kill();
149       }
150       catch (Throwable e) {
151         LOG.info("Cannot kill process", e);
152       }
153     }
154     else if (SystemInfo.isUnix) {
155       UnixProcessManager.sendSignal(UnixProcessManager.getProcessPid(process), UnixProcessManager.SIGKILL);
156     }
157   }
158   
159   public static int getProcessID(@NotNull Process process) {
160     if (SystemInfo.isWindows) {
161       try {
162         return createWinProcess(process).getPid();
163       }
164       catch (Throwable e) {
165         LOG.info("Cannot get process id", e);
166         return -1;
167       }
168     }
169     else if (SystemInfo.isUnix) {
170       return UnixProcessManager.getProcessPid(process);
171     }
172     throw new IllegalStateException("Unknown OS: "  + SystemInfo.OS_NAME);
173   }
174
175   @SuppressWarnings("deprecation")
176   @NotNull
177   private static WinProcess createWinProcess(@NotNull Process process) {
178     if (process instanceof RunnerWinProcess) process = ((RunnerWinProcess)process).getOriginalProcess();
179     return new WinProcess(process);
180   }
181   
182   @Nullable
183   public static List<String> getCommandLinesOfRunningProcesses() {
184     try {
185       if (SystemInfo.isWindows) {
186         List<String> commandLines = new ArrayList<String>();
187         for (WinProcess process : WinProcess.all()) {
188           try {
189             commandLines.add(process.getCommandLine());
190           }
191           catch (WinpException ignored) { }
192         }
193         return commandLines;
194       }
195       else {
196         String[] cmd = UnixProcessManager.getPSCmd(true);
197         Process process = Runtime.getRuntime().exec(cmd);
198         List<String> outputLines = readLines(process.getInputStream(), false);
199         List<String> errorLines = readLines(process.getErrorStream(), false);
200         if (!errorLines.isEmpty()) {
201           throw new IOException(Arrays.toString(cmd) + " failed: " + StringUtil.join(errorLines, "\n"));
202         }
203
204         //trim 'ps' output header
205         return outputLines.subList(1, outputLines.size());
206       }
207     }
208     catch (Throwable e) {
209       LOG.info("Cannot collect command lines");
210       LOG.info(e);
211       return null;
212     }
213   }
214
215   @NotNull
216   private static List<String> readLines(@NotNull InputStream inputStream, boolean includeEmpty) throws IOException {
217     BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
218     try {
219       List<String> lines = new ArrayList<String>();
220       String line;
221       while ((line = reader.readLine()) != null) {
222         if (includeEmpty || !line.isEmpty()) {
223           lines.add(line);
224         }
225       }
226       return lines;
227     }
228     finally {
229       reader.close();
230     }
231   }
232 }