46
47 /**
48 * java.lang.Process subclass in the UNIX environment.
49 *
50 * @author Mario Wolczko and Ross Knippel.
51 * @author Konstantin Kladko (ported to Bsd)
52 * @author Martin Buchholz
53 */
54 final class UNIXProcess extends Process {
55 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
56 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
57
58 private final int pid;
59 private int exitcode;
60 private boolean hasExited;
61
62 private /* final */ OutputStream stdin;
63 private /* final */ InputStream stdout;
64 private /* final */ InputStream stderr;
65
66 /* this is for the reaping thread */
67 private native int waitForProcessExit(int pid);
68
69 /**
70 * Create a process using fork(2) and exec(2).
71 *
72 * @param fds an array of three file descriptors.
73 * Indexes 0, 1, and 2 correspond to standard input,
74 * standard output and standard error, respectively. On
75 * input, a value of -1 means to create a pipe to connect
76 * child and parent processes. On output, a value which
77 * is not -1 is the parent pipe fd corresponding to the
78 * pipe which has been created. An element of this array
79 * is -1 on input if and only if it is <em>not</em> -1 on
80 * output.
81 * @return the pid of the subprocess
82 */
83 private native int forkAndExec(byte[] prog,
84 byte[] argBlock, int argc,
85 byte[] envBlock, int envc,
86 byte[] dir,
87 int[] fds,
88 boolean redirectErrorStream)
89 throws IOException;
90
91 /**
92 * The thread factory used to create "process reaper" daemon threads.
93 */
94 private static class ProcessReaperThreadFactory implements ThreadFactory {
95 private final static ThreadGroup group = getRootThreadGroup();
96
97 private static ThreadGroup getRootThreadGroup() {
98 return doPrivileged(new PrivilegedAction<ThreadGroup> () {
99 public ThreadGroup run() {
100 ThreadGroup root = Thread.currentThread().getThreadGroup();
101 while (root.getParent() != null)
102 root = root.getParent();
103 return root;
115 }
116
117 /**
118 * The thread pool of "process reaper" daemon threads.
119 */
120 private static final Executor processReaperExecutor =
121 doPrivileged(new PrivilegedAction<Executor>() {
122 public Executor run() {
123 return Executors.newCachedThreadPool
124 (new ProcessReaperThreadFactory());
125 }});
126
127 UNIXProcess(final byte[] prog,
128 final byte[] argBlock, final int argc,
129 final byte[] envBlock, final int envc,
130 final byte[] dir,
131 final int[] fds,
132 final boolean redirectErrorStream)
133 throws IOException {
134
135 pid = forkAndExec(prog,
136 argBlock, argc,
137 envBlock, envc,
138 dir,
139 fds,
140 redirectErrorStream);
141
142 try {
143 doPrivileged(new PrivilegedExceptionAction<Void>() {
144 public Void run() throws IOException {
145 initStreams(fds);
146 return null;
147 }});
148 } catch (PrivilegedActionException ex) {
149 throw (IOException) ex.getException();
150 }
151 }
152
153 static FileDescriptor newFileDescriptor(int fd) {
154 FileDescriptor fileDescriptor = new FileDescriptor();
155 fdAccess.set(fileDescriptor, fd);
219 return exitcode;
220 }
221
222 private static native void destroyProcess(int pid);
223 public void destroy() {
224 // There is a risk that pid will be recycled, causing us to
225 // kill the wrong process! So we only terminate processes
226 // that appear to still be running. Even with this check,
227 // there is an unavoidable race condition here, but the window
228 // is very small, and OSes try hard to not recycle pids too
229 // soon, so this is quite safe.
230 synchronized (this) {
231 if (!hasExited)
232 destroyProcess(pid);
233 }
234 try { stdin.close(); } catch (IOException ignored) {}
235 try { stdout.close(); } catch (IOException ignored) {}
236 try { stderr.close(); } catch (IOException ignored) {}
237 }
238
239 /* This routine initializes JNI field offsets for the class */
240 private static native void initIDs();
241
242 static {
243 initIDs();
244 }
245
246 /**
247 * A buffered input stream for a subprocess pipe file descriptor
248 * that allows the underlying file descriptor to be reclaimed when
249 * the process exits, via the processExited hook.
250 *
251 * This is tricky because we do not want the user-level InputStream to be
252 * closed until the user invokes close(), and we need to continue to be
253 * able to read any buffered data lingering in the OS pipe buffer.
254 */
255 static class ProcessPipeInputStream extends BufferedInputStream {
256 ProcessPipeInputStream(int fd) {
257 super(new FileInputStream(newFileDescriptor(fd)));
258 }
259
260 private static byte[] drainInputStream(InputStream in)
261 throws IOException {
262 if (in == null) return null;
263 int n = 0;
|
46
47 /**
48 * java.lang.Process subclass in the UNIX environment.
49 *
50 * @author Mario Wolczko and Ross Knippel.
51 * @author Konstantin Kladko (ported to Bsd)
52 * @author Martin Buchholz
53 */
54 final class UNIXProcess extends Process {
55 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess
56 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess();
57
58 private final int pid;
59 private int exitcode;
60 private boolean hasExited;
61
62 private /* final */ OutputStream stdin;
63 private /* final */ InputStream stdout;
64 private /* final */ InputStream stderr;
65
66 private static enum LaunchMechanism {
67 FORK(1),
68 POSIX_SPAWN(2);
69
70 private int value;
71 LaunchMechanism(int x) {value = x;}
72 };
73
74 /* On BSD, the default is to spawn */
75 private static final LaunchMechanism launchMechanism;
76 private static byte[] helperpath;
77
78 private static byte[] toCString(String s) {
79 if (s == null)
80 return null;
81 byte[] bytes = s.getBytes();
82 byte[] result = new byte[bytes.length + 1];
83 System.arraycopy(bytes, 0,
84 result, 0,
85 bytes.length);
86 result[result.length-1] = (byte)0;
87 return result;
88 }
89
90 static {
91 launchMechanism = AccessController.doPrivileged(
92 new PrivilegedAction<LaunchMechanism>()
93 {
94 public LaunchMechanism run() {
95 String javahome = System.getProperty("java.home");
96
97 helperpath = toCString(javahome + "/lib/jspawnhelper");
98 String s = System.getProperty(
99 "jdk.lang.Process.launchMechanism", "posix_spawn");
100
101 try {
102 return LaunchMechanism.valueOf(s.toUpperCase());
103 } catch (IllegalArgumentException e) {
104 throw new Error(s + " is not a supported " +
105 "process launch mechanism on this platform.");
106 }
107 }
108 });
109 }
110
111 /* this is for the reaping thread */
112 private native int waitForProcessExit(int pid);
113
114 /**
115 * Create a process. Depending on the mode flag, this is done by
116 * one of the following mechanisms.
117 * - fork(2) and exec(2)
118 * - posix_spawn(2)
119 *
120 * @param fds an array of three file descriptors.
121 * Indexes 0, 1, and 2 correspond to standard input,
122 * standard output and standard error, respectively. On
123 * input, a value of -1 means to create a pipe to connect
124 * child and parent processes. On output, a value which
125 * is not -1 is the parent pipe fd corresponding to the
126 * pipe which has been created. An element of this array
127 * is -1 on input if and only if it is <em>not</em> -1 on
128 * output.
129 * @return the pid of the subprocess
130 */
131 private native int forkAndExec(int mode, byte[] helperpath,
132 byte[] prog,
133 byte[] argBlock, int argc,
134 byte[] envBlock, int envc,
135 byte[] dir,
136 int[] fds,
137 boolean redirectErrorStream)
138 throws IOException;
139
140 /**
141 * The thread factory used to create "process reaper" daemon threads.
142 */
143 private static class ProcessReaperThreadFactory implements ThreadFactory {
144 private final static ThreadGroup group = getRootThreadGroup();
145
146 private static ThreadGroup getRootThreadGroup() {
147 return doPrivileged(new PrivilegedAction<ThreadGroup> () {
148 public ThreadGroup run() {
149 ThreadGroup root = Thread.currentThread().getThreadGroup();
150 while (root.getParent() != null)
151 root = root.getParent();
152 return root;
164 }
165
166 /**
167 * The thread pool of "process reaper" daemon threads.
168 */
169 private static final Executor processReaperExecutor =
170 doPrivileged(new PrivilegedAction<Executor>() {
171 public Executor run() {
172 return Executors.newCachedThreadPool
173 (new ProcessReaperThreadFactory());
174 }});
175
176 UNIXProcess(final byte[] prog,
177 final byte[] argBlock, final int argc,
178 final byte[] envBlock, final int envc,
179 final byte[] dir,
180 final int[] fds,
181 final boolean redirectErrorStream)
182 throws IOException {
183
184 pid = forkAndExec(launchMechanism.value,
185 helperpath,
186 prog,
187 argBlock, argc,
188 envBlock, envc,
189 dir,
190 fds,
191 redirectErrorStream);
192
193 try {
194 doPrivileged(new PrivilegedExceptionAction<Void>() {
195 public Void run() throws IOException {
196 initStreams(fds);
197 return null;
198 }});
199 } catch (PrivilegedActionException ex) {
200 throw (IOException) ex.getException();
201 }
202 }
203
204 static FileDescriptor newFileDescriptor(int fd) {
205 FileDescriptor fileDescriptor = new FileDescriptor();
206 fdAccess.set(fileDescriptor, fd);
270 return exitcode;
271 }
272
273 private static native void destroyProcess(int pid);
274 public void destroy() {
275 // There is a risk that pid will be recycled, causing us to
276 // kill the wrong process! So we only terminate processes
277 // that appear to still be running. Even with this check,
278 // there is an unavoidable race condition here, but the window
279 // is very small, and OSes try hard to not recycle pids too
280 // soon, so this is quite safe.
281 synchronized (this) {
282 if (!hasExited)
283 destroyProcess(pid);
284 }
285 try { stdin.close(); } catch (IOException ignored) {}
286 try { stdout.close(); } catch (IOException ignored) {}
287 try { stderr.close(); } catch (IOException ignored) {}
288 }
289
290 private static native void init();
291
292 static {
293 init();
294 }
295
296 /**
297 * A buffered input stream for a subprocess pipe file descriptor
298 * that allows the underlying file descriptor to be reclaimed when
299 * the process exits, via the processExited hook.
300 *
301 * This is tricky because we do not want the user-level InputStream to be
302 * closed until the user invokes close(), and we need to continue to be
303 * able to read any buffered data lingering in the OS pipe buffer.
304 */
305 static class ProcessPipeInputStream extends BufferedInputStream {
306 ProcessPipeInputStream(int fd) {
307 super(new FileInputStream(newFileDescriptor(fd)));
308 }
309
310 private static byte[] drainInputStream(InputStream in)
311 throws IOException {
312 if (in == null) return null;
313 int n = 0;
|