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 259 private static native void waitForInterruptibly(long handle); 260 261 @Override 262 public boolean waitFor(long timeout, TimeUnit unit) 263 throws InterruptedException 264 { 265 if (getExitCodeProcess(handle) != STILL_ACTIVE) return true; 266 if (timeout <= 0) return false; 267 268 long msTimeout = unit.toMillis(timeout); 269 270 waitForTimeoutInterruptibly(handle, msTimeout); 271 if (Thread.interrupted()) 272 throw new InterruptedException(); 273 return (getExitCodeProcess(handle) != STILL_ACTIVE); 274 } 275 276 private static native void waitForTimeoutInterruptibly( 277 long handle, long timeout); 278 279 public void destroy() { terminateProcess(handle); } 280 281 @Override 282 public Process destroyForcibly() { 283 destroy(); 284 return this; 285 } 286 287 private static native void terminateProcess(long handle); 288 289 @Override 290 public boolean isAlive() { 291 return isProcessAlive(handle); 292 } 293 294 private static native boolean isProcessAlive(long handle); 295 296 /** 297 * Create a process using the win32 function CreateProcess. 298 * 299 * @param cmdstr the Windows commandline 300 * @param envblock NUL-separated, double-NUL-terminated list of 301 * environment strings in VAR=VALUE form 302 * @param dir the working directory of the process, or null if 303 * inheriting the current directory from the parent process 304 * @param stdHandles array of windows HANDLEs. Indexes 0, 1, and 305 * 2 correspond to standard input, standard output and 306 * standard error, respectively. On input, a value of -1 307 * means to create a pipe to connect child and parent 308 * processes. On output, a value which is not -1 is the 309 * parent pipe handle corresponding to the pipe which has 310 * been created. An element of this array is -1 on input 311 * if and only if it is <em>not</em> -1 on output. 312 * @param redirectErrorStream redirectErrorStream attribute 313 * @return the native subprocess HANDLE returned by CreateProcess 314 */ 315 private static native long create(String cmdstr, 316 String envblock, 317 String dir, 318 long[] stdHandles, 319 boolean redirectErrorStream) 320 throws IOException; 321 322 /** 323 * Opens a file for atomic append. The file is created if it doesn't 324 * already exist. 325 * 326 * @param file the file to open or create 327 * @return the native HANDLE 328 */ 329 private static native long openForAtomicAppend(String path) 330 throws IOException; 331 332 private static native boolean closeHandle(long handle); 333 334 private static native int getProcessId(long handle); 335 private static native String getModuleBaseName(int pid); 336 337 @Override 338 public int getPid() { 339 if (!isAlive()) { 340 return -1; 341 } else { 342 return getProcessId(handle); 343 } 344 } 345 346 @Override 347 public String getProcessName(int pid) { 348 return getModuleBaseName(pid); 349 } 350 351 }