1 /* 2 * Copyright (c) 1995, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package java.lang; 27 28 import java.io.IOException; 29 import java.io.File; 30 import java.io.InputStream; 31 import java.io.OutputStream; 32 import java.io.FileInputStream; 33 import java.io.FileOutputStream; 34 import java.io.FileDescriptor; 35 import java.io.BufferedInputStream; 36 import java.io.BufferedOutputStream; 37 import java.lang.ProcessBuilder.Redirect; 38 import java.security.AccessController; 39 import java.security.PrivilegedAction; 40 import java.util.concurrent.TimeUnit; 41 42 /* This class is for the exclusive use of ProcessBuilder.start() to 43 * create new processes. 44 * 45 * @author Martin Buchholz 46 * @since 1.5 47 */ 48 49 final class ProcessImpl extends Process { 50 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess 51 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess(); 52 53 /** 54 * Open a file for writing. If {@code append} is {@code true} then the file 55 * is opened for atomic append directly and a FileOutputStream constructed 56 * with the resulting handle. This is because a FileOutputStream created 57 * to append to a file does not open the file in a manner that guarantees 58 * that writes by the child process will be atomic. 59 */ 60 private static FileOutputStream newFileOutputStream(File f, boolean append) 61 throws IOException 62 { 63 if (append) { 64 String path = f.getPath(); 65 SecurityManager sm = System.getSecurityManager(); 66 if (sm != null) 67 sm.checkWrite(path); 68 long handle = openForAtomicAppend(path); 69 final FileDescriptor fd = new FileDescriptor(); 70 fdAccess.setHandle(fd, handle); 71 return AccessController.doPrivileged( 72 new PrivilegedAction<FileOutputStream>() { 73 public FileOutputStream run() { 74 return new FileOutputStream(fd); 75 } 76 } 77 ); 78 } else { 79 return new FileOutputStream(f); 80 } 81 } 82 83 // System-dependent portion of ProcessBuilder.start() 84 static Process start(String cmdarray[], 85 java.util.Map<String,String> environment, 86 String dir, 87 ProcessBuilder.Redirect[] redirects, 88 boolean redirectErrorStream) 89 throws IOException 90 { 91 String envblock = ProcessEnvironment.toEnvironmentBlock(environment); 92 93 FileInputStream f0 = null; 94 FileOutputStream f1 = null; 95 FileOutputStream f2 = null; 96 97 try { 98 long[] stdHandles; 99 if (redirects == null) { 100 stdHandles = new long[] { -1L, -1L, -1L }; 101 } else { 102 stdHandles = new long[3]; 103 104 if (redirects[0] == Redirect.PIPE) 105 stdHandles[0] = -1L; 106 else if (redirects[0] == Redirect.INHERIT) 107 stdHandles[0] = fdAccess.getHandle(FileDescriptor.in); 108 else { 109 f0 = new FileInputStream(redirects[0].file()); 110 stdHandles[0] = fdAccess.getHandle(f0.getFD()); 111 } 112 113 if (redirects[1] == Redirect.PIPE) 114 stdHandles[1] = -1L; 115 else if (redirects[1] == Redirect.INHERIT) 116 stdHandles[1] = fdAccess.getHandle(FileDescriptor.out); 117 else { 118 f1 = newFileOutputStream(redirects[1].file(), 119 redirects[1].append()); 120 stdHandles[1] = fdAccess.getHandle(f1.getFD()); 121 } 122 123 if (redirects[2] == Redirect.PIPE) 124 stdHandles[2] = -1L; 125 else if (redirects[2] == Redirect.INHERIT) 126 stdHandles[2] = fdAccess.getHandle(FileDescriptor.err); 127 else { 128 f2 = newFileOutputStream(redirects[2].file(), 129 redirects[2].append()); 130 stdHandles[2] = fdAccess.getHandle(f2.getFD()); 131 } 132 } 133 134 return new ProcessImpl(cmdarray, envblock, dir, 135 stdHandles, redirectErrorStream); 136 } finally { 137 // In theory, close() can throw IOException 138 // (although it is rather unlikely to happen here) 139 try { if (f0 != null) f0.close(); } 140 finally { 141 try { if (f1 != null) f1.close(); } 142 finally { if (f2 != null) f2.close(); } 143 } 144 } 145 146 } 147 148 private long handle = 0; 149 private OutputStream stdin_stream; 150 private InputStream stdout_stream; 151 private InputStream stderr_stream; 152 153 private ProcessImpl(final String cmd[], 154 final String envblock, 155 final String path, 156 final long[] stdHandles, 157 final boolean redirectErrorStream) 158 throws IOException 159 { 160 // Win32 CreateProcess requires cmd[0] to be normalized 161 cmd[0] = new File(cmd[0]).getPath(); 162 163 StringBuilder cmdbuf = new StringBuilder(80); 164 for (int i = 0; i < cmd.length; i++) { 165 if (i > 0) { 166 cmdbuf.append(' '); 167 } 168 String s = cmd[i]; 169 if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0) { 170 if (s.charAt(0) != '"') { 171 cmdbuf.append('"'); 172 cmdbuf.append(s); 173 if (s.endsWith("\\")) { 174 cmdbuf.append("\\"); 175 } 176 cmdbuf.append('"'); 177 } else if (s.endsWith("\"")) { 178 /* The argument has already been quoted. */ 179 cmdbuf.append(s); 180 } else { 181 /* Unmatched quote for the argument. */ 182 throw new IllegalArgumentException(); 183 } 184 } else { 185 cmdbuf.append(s); 186 } 187 } 188 String cmdstr = cmdbuf.toString(); 189 190 handle = create(cmdstr, envblock, path, 191 stdHandles, redirectErrorStream); 192 193 java.security.AccessController.doPrivileged( 194 new java.security.PrivilegedAction<Void>() { 195 public Void run() { 196 if (stdHandles[0] == -1L) 197 stdin_stream = ProcessBuilder.NullOutputStream.INSTANCE; 198 else { 199 FileDescriptor stdin_fd = new FileDescriptor(); 200 fdAccess.setHandle(stdin_fd, stdHandles[0]); 201 stdin_stream = new BufferedOutputStream( 202 new FileOutputStream(stdin_fd)); 203 } 204 205 if (stdHandles[1] == -1L) 206 stdout_stream = ProcessBuilder.NullInputStream.INSTANCE; 207 else { 208 FileDescriptor stdout_fd = new FileDescriptor(); 209 fdAccess.setHandle(stdout_fd, stdHandles[1]); 210 stdout_stream = new BufferedInputStream( 211 new FileInputStream(stdout_fd)); 212 } 213 214 if (stdHandles[2] == -1L) 215 stderr_stream = ProcessBuilder.NullInputStream.INSTANCE; 216 else { 217 FileDescriptor stderr_fd = new FileDescriptor(); 218 fdAccess.setHandle(stderr_fd, stdHandles[2]); 219 stderr_stream = new FileInputStream(stderr_fd); 220 } 221 222 return null; }}); 223 } 224 225 public OutputStream getOutputStream() { 226 return stdin_stream; 227 } 228 229 public InputStream getInputStream() { 230 return stdout_stream; 231 } 232 233 public InputStream getErrorStream() { 234 return stderr_stream; 235 } 236 237 public void finalize() { 238 closeHandle(handle); 239 } 240 241 private static final int STILL_ACTIVE = getStillActive(); 242 private static native int getStillActive(); 243 244 public int exitValue() { 245 int exitCode = getExitCodeProcess(handle); 246 if (exitCode == STILL_ACTIVE) 247 throw new IllegalThreadStateException("process has not exited"); 248 return exitCode; 249 } 250 private static native int getExitCodeProcess(long handle); 251 252 public int waitFor() throws InterruptedException { 253 waitForInterruptibly(handle); 254 if (Thread.interrupted()) 255 throw new InterruptedException(); 256 return exitValue(); 257 } 258 private static native void waitForInterruptibly(long handle); 259 @Override 260 public boolean waitFor(long timeout, TimeUnit unit) 261 throws InterruptedException { 262 if (getExitCodeProcess(handle) != STILL_ACTIVE) return true; 263 if (timeout <= 0) return false; 264 265 waitForTimeoutInterruptibly(handle, unit.toMillis(timeout)); 266 if (Thread.interrupted()) 267 throw new InterruptedException(); 268 return (getExitCodeProcess(handle) != STILL_ACTIVE); 269 } 270 private static native void waitForTimeoutInterruptibly( 271 long handle, long timeout); 272 273 public void destroy() { terminateProcess(handle); } 274 @Override 275 public Process destroyForcibly() { 276 destroy(); 277 return this; 278 } 279 private static native void terminateProcess(long handle); 280 281 @Override 282 public boolean isAlive() { 283 return isProcessAlive(handle); 284 } 285 private static native boolean isProcessAlive(long handle); 286 287 /** 288 * Create a process using the win32 function CreateProcess. 289 * 290 * @param cmdstr the Windows commandline 291 * @param envblock NUL-separated, double-NUL-terminated list of 292 * environment strings in VAR=VALUE form 293 * @param dir the working directory of the process, or null if 294 * inheriting the current directory from the parent process 295 * @param stdHandles array of windows HANDLEs. Indexes 0, 1, and 296 * 2 correspond to standard input, standard output and 297 * standard error, respectively. On input, a value of -1 298 * means to create a pipe to connect child and parent 299 * processes. On output, a value which is not -1 is the 300 * parent pipe handle corresponding to the pipe which has 301 * been created. An element of this array is -1 on input 302 * if and only if it is <em>not</em> -1 on output. 303 * @param redirectErrorStream redirectErrorStream attribute 304 * @return the native subprocess HANDLE returned by CreateProcess 305 */ 306 private static native long create(String cmdstr, 307 String envblock, 308 String dir, 309 long[] stdHandles, 310 boolean redirectErrorStream) 311 throws IOException; 312 313 /** 314 * Opens a file for atomic append. The file is created if it doesn't 315 * already exist. 316 * 317 * @param file the file to open or create 318 * @return the native HANDLE 319 */ 320 private static native long openForAtomicAppend(String path) 321 throws IOException; 322 323 private static native boolean closeHandle(long handle); 324 } 325