PageRenderTime 70ms CodeModel.GetById 32ms RepoModel.GetById 0ms app.codeStats 0ms

/luni/src/main/java/java/lang/ProcessManager.java

https://github.com/dbaeumges/android_platform_libcore
Java | 409 lines | 254 code | 56 blank | 99 comment | 39 complexity | 0d4812124ceec9d47f45e55255d0a864 MD5 | raw file
  1. /*
  2. * Copyright (C) 2007 The Android Open Source Project
  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. package java.lang;
  17. import java.io.File;
  18. import java.io.FileDescriptor;
  19. import java.io.FileInputStream;
  20. import java.io.FileOutputStream;
  21. import java.io.IOException;
  22. import java.io.InputStream;
  23. import java.io.OutputStream;
  24. import java.lang.ref.ReferenceQueue;
  25. import java.lang.ref.WeakReference;
  26. import java.util.Arrays;
  27. import java.util.HashMap;
  28. import java.util.Map;
  29. import java.util.logging.Level;
  30. import java.util.logging.Logger;
  31. import libcore.io.IoUtils;
  32. /**
  33. * Manages child processes.
  34. */
  35. final class ProcessManager {
  36. /**
  37. * constant communicated from native code indicating that a
  38. * child died, but it was unable to determine the status
  39. */
  40. private static final int WAIT_STATUS_UNKNOWN = -1;
  41. /**
  42. * constant communicated from native code indicating that there
  43. * are currently no children to wait for
  44. */
  45. private static final int WAIT_STATUS_NO_CHILDREN = -2;
  46. /**
  47. * constant communicated from native code indicating that a wait()
  48. * call returned -1 and set an undocumented (and hence unexpected) errno
  49. */
  50. private static final int WAIT_STATUS_STRANGE_ERRNO = -3;
  51. /**
  52. * Initializes native static state.
  53. */
  54. static native void staticInitialize();
  55. static {
  56. staticInitialize();
  57. }
  58. /**
  59. * Map from pid to Process. We keep weak references to the Process objects
  60. * and clean up the entries when no more external references are left. The
  61. * process objects themselves don't require much memory, but file
  62. * descriptors (associated with stdin/out/err in this case) can be
  63. * a scarce resource.
  64. */
  65. private final Map<Integer, ProcessReference> processReferences
  66. = new HashMap<Integer, ProcessReference>();
  67. /** Keeps track of garbage-collected Processes. */
  68. private final ProcessReferenceQueue referenceQueue
  69. = new ProcessReferenceQueue();
  70. private ProcessManager() {
  71. // Spawn a thread to listen for signals from child processes.
  72. Thread processThread = new Thread(ProcessManager.class.getName()) {
  73. @Override
  74. public void run() {
  75. watchChildren();
  76. }
  77. };
  78. processThread.setDaemon(true);
  79. processThread.start();
  80. }
  81. /**
  82. * Kills the process with the given ID.
  83. *
  84. * @parm pid ID of process to kill
  85. */
  86. private static native void kill(int pid) throws IOException;
  87. /**
  88. * Cleans up after garbage collected processes. Requires the lock on the
  89. * map.
  90. */
  91. void cleanUp() {
  92. ProcessReference reference;
  93. while ((reference = referenceQueue.poll()) != null) {
  94. synchronized (processReferences) {
  95. processReferences.remove(reference.processId);
  96. }
  97. }
  98. }
  99. /**
  100. * Listens for signals from processes and calls back to
  101. * {@link #onExit(int,int)}.
  102. */
  103. native void watchChildren();
  104. /**
  105. * Called by {@link #watchChildren()} when a child process exits.
  106. *
  107. * @param pid ID of process that exited
  108. * @param exitValue value the process returned upon exit
  109. */
  110. void onExit(int pid, int exitValue) {
  111. ProcessReference processReference = null;
  112. synchronized (processReferences) {
  113. cleanUp();
  114. if (pid >= 0) {
  115. processReference = processReferences.remove(pid);
  116. } else if (exitValue == WAIT_STATUS_NO_CHILDREN) {
  117. if (processReferences.isEmpty()) {
  118. /*
  119. * There are no eligible children; wait for one to be
  120. * added. The wait() will return due to the
  121. * notifyAll() call below.
  122. */
  123. try {
  124. processReferences.wait();
  125. } catch (InterruptedException ex) {
  126. // This should never happen.
  127. throw new AssertionError("unexpected interrupt");
  128. }
  129. } else {
  130. /*
  131. * A new child was spawned just before we entered
  132. * the synchronized block. We can just fall through
  133. * without doing anything special and land back in
  134. * the native wait().
  135. */
  136. }
  137. } else {
  138. // Something weird is happening; abort!
  139. throw new AssertionError("unexpected wait() behavior");
  140. }
  141. }
  142. if (processReference != null) {
  143. ProcessImpl process = processReference.get();
  144. if (process != null) {
  145. process.setExitValue(exitValue);
  146. }
  147. }
  148. }
  149. /**
  150. * Executes a native process. Fills in in, out, and err and returns the
  151. * new process ID upon success.
  152. */
  153. static native int exec(String[] command, String[] environment,
  154. String workingDirectory, FileDescriptor in, FileDescriptor out,
  155. FileDescriptor err, boolean redirectErrorStream) throws IOException;
  156. /**
  157. * Executes a process and returns an object representing it.
  158. */
  159. Process exec(String[] taintedCommand, String[] taintedEnvironment, File workingDirectory,
  160. boolean redirectErrorStream) throws IOException {
  161. // Make sure we throw the same exceptions as the RI.
  162. if (taintedCommand == null) {
  163. throw new NullPointerException();
  164. }
  165. if (taintedCommand.length == 0) {
  166. throw new IndexOutOfBoundsException();
  167. }
  168. // Handle security and safety by copying mutable inputs and checking them.
  169. String[] command = taintedCommand.clone();
  170. String[] environment = taintedEnvironment != null ? taintedEnvironment.clone() : null;
  171. SecurityManager securityManager = System.getSecurityManager();
  172. if (securityManager != null) {
  173. securityManager.checkExec(command[0]);
  174. }
  175. // Check we're not passing null Strings to the native exec.
  176. for (String arg : command) {
  177. if (arg == null) {
  178. throw new NullPointerException();
  179. }
  180. }
  181. // The environment is allowed to be null or empty, but no element may be null.
  182. if (environment != null) {
  183. for (String env : environment) {
  184. if (env == null) {
  185. throw new NullPointerException();
  186. }
  187. }
  188. }
  189. FileDescriptor in = new FileDescriptor();
  190. FileDescriptor out = new FileDescriptor();
  191. FileDescriptor err = new FileDescriptor();
  192. String workingPath = (workingDirectory == null)
  193. ? null
  194. : workingDirectory.getPath();
  195. // Ensure onExit() doesn't access the process map before we add our
  196. // entry.
  197. synchronized (processReferences) {
  198. int pid;
  199. try {
  200. pid = exec(command, environment, workingPath, in, out, err, redirectErrorStream);
  201. } catch (IOException e) {
  202. IOException wrapper = new IOException("Error running exec()."
  203. + " Command: " + Arrays.toString(command)
  204. + " Working Directory: " + workingDirectory
  205. + " Environment: " + Arrays.toString(environment));
  206. wrapper.initCause(e);
  207. throw wrapper;
  208. }
  209. ProcessImpl process = new ProcessImpl(pid, in, out, err);
  210. ProcessReference processReference
  211. = new ProcessReference(process, referenceQueue);
  212. processReferences.put(pid, processReference);
  213. /*
  214. * This will wake up the child monitor thread in case there
  215. * weren't previously any children to wait on.
  216. */
  217. processReferences.notifyAll();
  218. return process;
  219. }
  220. }
  221. static class ProcessImpl extends Process {
  222. /** Process ID. */
  223. final int id;
  224. final InputStream errorStream;
  225. /** Reads output from process. */
  226. final InputStream inputStream;
  227. /** Sends output to process. */
  228. final OutputStream outputStream;
  229. /** The process's exit value. */
  230. Integer exitValue = null;
  231. final Object exitValueMutex = new Object();
  232. ProcessImpl(int id, FileDescriptor in, FileDescriptor out,
  233. FileDescriptor err) {
  234. this.id = id;
  235. this.errorStream = new ProcessInputStream(err);
  236. this.inputStream = new ProcessInputStream(in);
  237. this.outputStream = new ProcessOutputStream(out);
  238. }
  239. public void destroy() {
  240. try {
  241. kill(this.id);
  242. } catch (IOException e) {
  243. Logger.getLogger(Runtime.class.getName()).log(Level.FINE,
  244. "Failed to destroy process " + id + ".", e);
  245. }
  246. }
  247. public int exitValue() {
  248. synchronized (exitValueMutex) {
  249. if (exitValue == null) {
  250. throw new IllegalThreadStateException(
  251. "Process has not yet terminated.");
  252. }
  253. return exitValue;
  254. }
  255. }
  256. public InputStream getErrorStream() {
  257. return this.errorStream;
  258. }
  259. public InputStream getInputStream() {
  260. return this.inputStream;
  261. }
  262. public OutputStream getOutputStream() {
  263. return this.outputStream;
  264. }
  265. public int waitFor() throws InterruptedException {
  266. synchronized (exitValueMutex) {
  267. while (exitValue == null) {
  268. exitValueMutex.wait();
  269. }
  270. return exitValue;
  271. }
  272. }
  273. void setExitValue(int exitValue) {
  274. synchronized (exitValueMutex) {
  275. this.exitValue = exitValue;
  276. exitValueMutex.notifyAll();
  277. }
  278. }
  279. @Override
  280. public String toString() {
  281. return "Process[id=" + id + "]";
  282. }
  283. }
  284. static class ProcessReference extends WeakReference<ProcessImpl> {
  285. final int processId;
  286. public ProcessReference(ProcessImpl referent,
  287. ProcessReferenceQueue referenceQueue) {
  288. super(referent, referenceQueue);
  289. this.processId = referent.id;
  290. }
  291. }
  292. static class ProcessReferenceQueue extends ReferenceQueue<ProcessImpl> {
  293. @Override
  294. public ProcessReference poll() {
  295. // Why couldn't they get the generics right on ReferenceQueue? :(
  296. Object reference = super.poll();
  297. return (ProcessReference) reference;
  298. }
  299. }
  300. static final ProcessManager instance = new ProcessManager();
  301. /** Gets the process manager. */
  302. static ProcessManager getInstance() {
  303. return instance;
  304. }
  305. /** Automatically closes fd when collected. */
  306. private static class ProcessInputStream extends FileInputStream {
  307. private FileDescriptor fd;
  308. private ProcessInputStream(FileDescriptor fd) {
  309. super(fd);
  310. this.fd = fd;
  311. }
  312. @Override
  313. public void close() throws IOException {
  314. try {
  315. super.close();
  316. } finally {
  317. synchronized (this) {
  318. if (fd != null && fd.valid()) {
  319. try {
  320. IoUtils.close(fd);
  321. } finally {
  322. fd = null;
  323. }
  324. }
  325. }
  326. }
  327. }
  328. }
  329. /** Automatically closes fd when collected. */
  330. private static class ProcessOutputStream extends FileOutputStream {
  331. private FileDescriptor fd;
  332. private ProcessOutputStream(FileDescriptor fd) {
  333. super(fd);
  334. this.fd = fd;
  335. }
  336. @Override
  337. public void close() throws IOException {
  338. try {
  339. super.close();
  340. } finally {
  341. synchronized (this) {
  342. if (fd != null && fd.valid()) {
  343. try {
  344. IoUtils.close(fd);
  345. } finally {
  346. fd = null;
  347. }
  348. }
  349. }
  350. }
  351. }
  352. }
  353. }