1 /* 2 * Copyright (c) 1995, 2010, 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 41 /* This class is for the exclusive use of ProcessBuilder.start() to 42 * create new processes. 43 * 44 * @author Martin Buchholz 45 * @since 1.5 46 */ 47 48 final class ProcessImpl extends Process { 49 private static final sun.misc.JavaIOFileDescriptorAccess fdAccess 50 = sun.misc.SharedSecrets.getJavaIOFileDescriptorAccess(); 51 52 /** 53 * Open a file for writing. If {@code append} is {@code true} then the file 54 * is opened for atomic append directly and a FileOutputStream constructed 55 * with the resulting handle. This is because a FileOutputStream created 56 * to append to a file does not open the file in a manner that guarantees 57 * that writes by the child process will be atomic. 58 */ 59 private static FileOutputStream newFileOutputStream(File f, boolean append) 60 throws IOException 61 { 62 if (append) { 63 SecurityManager sm = System.getSecurityManager(); 64 if (sm != null) 65 sm.checkWrite(f.getPath()); 66 long handle = openForAtomicAppend(f.getPath()); 67 final FileDescriptor fd = new FileDescriptor(); 68 fdAccess.setHandle(fd, handle); 69 return AccessController.doPrivileged( 70 new PrivilegedAction<FileOutputStream>() { 71 public FileOutputStream run() { 72 return new FileOutputStream(fd); 73 } 74 } 75 ); 76 } else { 77 return new FileOutputStream(f); 78 } 79 } 80 81 // System-dependent portion of ProcessBuilder.start() 82 static Process start(String cmdarray[], 83 java.util.Map<String,String> environment, 84 String dir, 85 ProcessBuilder.Redirect[] redirects, 86 boolean redirectErrorStream) 87 throws IOException 88 { 89 String envblock = ProcessEnvironment.toEnvironmentBlock(environment); 90 91 FileInputStream f0 = null; 92 FileOutputStream f1 = null; 93 FileOutputStream f2 = null; 94 95 try { 96 long[] stdHandles; 97 if (redirects == null) { 98 stdHandles = new long[] { -1L, -1L, -1L }; 99 } else { 100 stdHandles = new long[3]; 101 102 if (redirects[0] == Redirect.PIPE) 103 stdHandles[0] = -1L; 104 else if (redirects[0] == Redirect.INHERIT) 105 stdHandles[0] = fdAccess.getHandle(FileDescriptor.in); 106 else { 107 f0 = new FileInputStream(redirects[0].file()); 108 stdHandles[0] = fdAccess.getHandle(f0.getFD()); 109 } 110 111 if (redirects[1] == Redirect.PIPE) 112 stdHandles[1] = -1L; 113 else if (redirects[1] == Redirect.INHERIT) 114 stdHandles[1] = fdAccess.getHandle(FileDescriptor.out); 115 else { 116 f1 = newFileOutputStream(redirects[1].file(), 117 redirects[1].append()); 118 stdHandles[1] = fdAccess.getHandle(f1.getFD()); 119 } 120 121 if (redirects[2] == Redirect.PIPE) 122 stdHandles[2] = -1L; 123 else if (redirects[2] == Redirect.INHERIT) 124 stdHandles[2] = fdAccess.getHandle(FileDescriptor.err); 125 else { 126 f2 = newFileOutputStream(redirects[2].file(), 127 redirects[2].append()); 128 stdHandles[2] = fdAccess.getHandle(f2.getFD()); 129 } 130 } 131 132 return new ProcessImpl(cmdarray, envblock, dir, 133 stdHandles, redirectErrorStream); 134 } finally { 135 // In theory, close() can throw IOException 136 // (although it is rather unlikely to happen here) 137 try { if (f0 != null) f0.close(); } 138 finally { 139 try { if (f1 != null) f1.close(); } 140 finally { if (f2 != null) f2.close(); } 141 } 142 } 143 144 } 145 146 private long handle = 0; 147 private OutputStream stdin_stream; 148 private InputStream stdout_stream; 149 private InputStream stderr_stream; 150 151 private ProcessImpl(final String cmd[], 152 final String envblock, 153 final String path, 154 final long[] stdHandles, 155 final boolean redirectErrorStream) 156 throws IOException 157 { 158 // Win32 CreateProcess requires cmd[0] to be normalized 159 cmd[0] = new File(cmd[0]).getPath(); 160 161 StringBuilder cmdbuf = new StringBuilder(80); 162 for (int i = 0; i < cmd.length; i++) { 163 if (i > 0) { 164 cmdbuf.append(' '); 165 } 166 String s = cmd[i]; 167 if (s.indexOf(' ') >= 0 || s.indexOf('\t') >= 0) { 168 if (s.charAt(0) != '"') { 169 cmdbuf.append('"'); 170 cmdbuf.append(s); 171 if (s.endsWith("\\")) { 172 cmdbuf.append("\\"); 173 } 174 cmdbuf.append('"'); 175 } else if (s.endsWith("\"")) { 176 /* The argument has already been quoted. */ 177 cmdbuf.append(s); 178 } else { 179 /* Unmatched quote for the argument. */ 180 throw new IllegalArgumentException(); 181 } 182 } else { 183 cmdbuf.append(s); 184 } 185 } 186 String cmdstr = cmdbuf.toString(); 187 188 handle = create(cmdstr, envblock, path, 189 stdHandles, redirectErrorStream); 190 191 java.security.AccessController.doPrivileged( 192 new java.security.PrivilegedAction<Void>() { 193 public Void run() { 194 if (stdHandles[0] == -1L) 195 stdin_stream = ProcessBuilder.NullOutputStream.INSTANCE; 196 else { 197 FileDescriptor stdin_fd = new FileDescriptor(); 198 fdAccess.setHandle(stdin_fd, stdHandles[0]); 199 stdin_stream = new BufferedOutputStream( 200 new FileOutputStream(stdin_fd)); 201 } 202 203 if (stdHandles[1] == -1L) 204 stdout_stream = ProcessBuilder.NullInputStream.INSTANCE; 205 else { 206 FileDescriptor stdout_fd = new FileDescriptor(); 207 fdAccess.setHandle(stdout_fd, stdHandles[1]); 208 stdout_stream = new BufferedInputStream( 209 new FileInputStream(stdout_fd)); 210 } 211 212 if (stdHandles[2] == -1L) 213 stderr_stream = ProcessBuilder.NullInputStream.INSTANCE; 214 else { 215 FileDescriptor stderr_fd = new FileDescriptor(); 216 fdAccess.setHandle(stderr_fd, stdHandles[2]); 217 stderr_stream = new FileInputStream(stderr_fd); 218 } 219 220 return null; }}); 221 } 222 223 public OutputStream getOutputStream() { 224 return stdin_stream; 225 } 226 227 public InputStream getInputStream() { 228 return stdout_stream; 229 } 230 231 public InputStream getErrorStream() { 232 return stderr_stream; 233 } 234 235 public void finalize() { 236 closeHandle(handle); 237 } 238 239 private static final int STILL_ACTIVE = getStillActive(); 240 private static native int getStillActive(); 241 242 public int exitValue() { 243 int exitCode = getExitCodeProcess(handle); 244 if (exitCode == STILL_ACTIVE) 245 throw new IllegalThreadStateException("process has not exited"); 246 return exitCode; 247 } 248 private static native int getExitCodeProcess(long handle); 249 250 public int waitFor() throws InterruptedException { 251 waitForInterruptibly(handle); 252 if (Thread.interrupted()) 253 throw new InterruptedException(); 254 return exitValue(); 255 } 256 private static native void waitForInterruptibly(long handle); 257 258 public void destroy() { terminateProcess(handle); } 259 private static native void terminateProcess(long handle); 260 261 /** 262 * Create a process using the win32 function CreateProcess. 263 * 264 * @param cmdstr the Windows commandline 265 * @param envblock NUL-separated, double-NUL-terminated list of 266 * environment strings in VAR=VALUE form 267 * @param dir the working directory of the process, or null if 268 * inheriting the current directory from the parent process 269 * @param stdHandles array of windows HANDLEs. Indexes 0, 1, and 270 * 2 correspond to standard input, standard output and 271 * standard error, respectively. On input, a value of -1 272 * means to create a pipe to connect child and parent 273 * processes. On output, a value which is not -1 is the 274 * parent pipe handle corresponding to the pipe which has 275 * been created. An element of this array is -1 on input 276 * if and only if it is <em>not</em> -1 on output. 277 * @param redirectErrorStream redirectErrorStream attribute 278 * @return the native subprocess HANDLE returned by CreateProcess 279 */ 280 private static native long create(String cmdstr, 281 String envblock, 282 String dir, 283 long[] stdHandles, 284 boolean redirectErrorStream) 285 throws IOException; 286 287 /** 288 * Opens a file for atomic append. The file is created if it doesn't 289 * already exist. 290 * 291 * @param file the file to open or create 292 * @return the native HANDLE 293 */ 294 private static native long openForAtomicAppend(String path) 295 throws IOException; 296 297 private static native boolean closeHandle(long handle); 298 }