|
@@ -1,818 +0,0 @@
|
|
|
-
|
|
|
- * Licensed to the Apache Software Foundation (ASF) under one or more
|
|
|
- * contributor license agreements. See the NOTICE file distributed with
|
|
|
- * this work for additional information regarding copyright ownership.
|
|
|
- * The ASF licenses this file to You under the Apache License, Version 2.0
|
|
|
- * (the "License"); you may not use this file except in compliance with
|
|
|
- * the License. You may obtain a copy of the License at
|
|
|
- *
|
|
|
- * http:
|
|
|
- *
|
|
|
- * Unless required by applicable law or agreed to in writing, software
|
|
|
- * distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
- * See the License for the specific language governing permissions and
|
|
|
- * limitations under the License.
|
|
|
- */
|
|
|
-package org.apache.dolphinscheduler.common.utils.process;
|
|
|
-
|
|
|
-import com.sun.jna.Pointer;
|
|
|
-import com.sun.jna.platform.win32.*;
|
|
|
-import com.sun.jna.ptr.IntByReference;
|
|
|
-import java.lang.reflect.Field;
|
|
|
-import org.apache.dolphinscheduler.common.utils.OSUtils;
|
|
|
-import sun.security.action.GetPropertyAction;
|
|
|
-
|
|
|
-import java.io.*;
|
|
|
-import java.security.AccessController;
|
|
|
-import java.security.PrivilegedAction;
|
|
|
-import java.util.ArrayList;
|
|
|
-import java.util.Locale;
|
|
|
-import java.util.concurrent.TimeUnit;
|
|
|
-import java.util.regex.Matcher;
|
|
|
-import java.util.regex.Pattern;
|
|
|
-
|
|
|
-import static com.sun.jna.platform.win32.WinBase.INVALID_HANDLE_VALUE;
|
|
|
-import static com.sun.jna.platform.win32.WinBase.STILL_ACTIVE;
|
|
|
-import static java.util.Objects.requireNonNull;
|
|
|
-
|
|
|
-public class ProcessImplForWin32 extends Process {
|
|
|
-
|
|
|
- private static final Field FD_HANDLE;
|
|
|
-
|
|
|
- static {
|
|
|
- if (!OSUtils.isWindows()) {
|
|
|
- throw new RuntimeException("ProcessImplForWin32 can be only initialized in " +
|
|
|
- "Windows environment, but current OS is " + OSUtils.getOSName());
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- FD_HANDLE = requireNonNull(FileDescriptor.class.getDeclaredField("handle"));
|
|
|
- FD_HANDLE.setAccessible(true);
|
|
|
- } catch (NoSuchFieldException e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static final int PIPE_SIZE = 4096 + 24;
|
|
|
-
|
|
|
- private static final int HANDLE_STORAGE_SIZE = 6;
|
|
|
-
|
|
|
- private static final int OFFSET_READ = 0;
|
|
|
-
|
|
|
- private static final int OFFSET_WRITE = 1;
|
|
|
-
|
|
|
- private static final WinNT.HANDLE JAVA_INVALID_HANDLE_VALUE = new WinNT.HANDLE(Pointer.createConstant(-1));
|
|
|
-
|
|
|
- private static void setHandle(FileDescriptor obj, long handle) {
|
|
|
- try {
|
|
|
- FD_HANDLE.set(obj, handle);
|
|
|
- } catch (IllegalAccessException e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static long getHandle(FileDescriptor obj) {
|
|
|
- try {
|
|
|
- return (Long) FD_HANDLE.get(obj);
|
|
|
- } catch (IllegalAccessException e) {
|
|
|
- throw new RuntimeException(e);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * Open a file for writing. If {@code append} is {@code true} then the file
|
|
|
- * is opened for atomic append directly and a FileOutputStream constructed
|
|
|
- * with the resulting handle. This is because a FileOutputStream created
|
|
|
- * to append to a file does not open the file in a manner that guarantees
|
|
|
- * that writes by the child process will be atomic.
|
|
|
- */
|
|
|
- private static FileOutputStream newFileOutputStream(File f, boolean append)
|
|
|
- throws IOException
|
|
|
- {
|
|
|
- if (append) {
|
|
|
- String path = f.getPath();
|
|
|
- SecurityManager sm = System.getSecurityManager();
|
|
|
- if (sm != null) {
|
|
|
- sm.checkWrite(path);
|
|
|
- }
|
|
|
- long handle = openForAtomicAppend(path);
|
|
|
- final FileDescriptor fd = new FileDescriptor();
|
|
|
- setHandle(fd, handle);
|
|
|
- return AccessController.doPrivileged(
|
|
|
- new PrivilegedAction<FileOutputStream>() {
|
|
|
- @Override
|
|
|
- public FileOutputStream run() {
|
|
|
- return new FileOutputStream(fd);
|
|
|
- }
|
|
|
- }
|
|
|
- );
|
|
|
- } else {
|
|
|
- return new FileOutputStream(f);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- static Process start(String username,
|
|
|
- String password,
|
|
|
- String[] cmdarray,
|
|
|
- java.util.Map<String,String> environment,
|
|
|
- String dir,
|
|
|
- ProcessBuilderForWin32.Redirect[] redirects,
|
|
|
- boolean redirectErrorStream)
|
|
|
- throws IOException
|
|
|
- {
|
|
|
- String envblock = ProcessEnvironmentForWin32.toEnvironmentBlock(environment);
|
|
|
-
|
|
|
- FileInputStream f0 = null;
|
|
|
- FileOutputStream f1 = null;
|
|
|
- FileOutputStream f2 = null;
|
|
|
-
|
|
|
- try {
|
|
|
- long[] stdHandles;
|
|
|
- if (redirects == null) {
|
|
|
- stdHandles = new long[] { -1L, -1L, -1L };
|
|
|
- } else {
|
|
|
- stdHandles = new long[3];
|
|
|
-
|
|
|
- if (redirects[0] == ProcessBuilderForWin32.Redirect.PIPE) {
|
|
|
- stdHandles[0] = -1L;
|
|
|
- } else if (redirects[0] == ProcessBuilderForWin32.Redirect.INHERIT) {
|
|
|
- stdHandles[0] = getHandle(FileDescriptor.in);
|
|
|
- } else {
|
|
|
- f0 = new FileInputStream(redirects[0].file());
|
|
|
- stdHandles[0] = getHandle(f0.getFD());
|
|
|
- }
|
|
|
-
|
|
|
- if (redirects[1] == ProcessBuilderForWin32.Redirect.PIPE) {
|
|
|
- stdHandles[1] = -1L;
|
|
|
- } else if (redirects[1] == ProcessBuilderForWin32.Redirect.INHERIT) {
|
|
|
- stdHandles[1] = getHandle(FileDescriptor.out);
|
|
|
- } else {
|
|
|
- f1 = newFileOutputStream(redirects[1].file(),
|
|
|
- redirects[1].append());
|
|
|
- stdHandles[1] = getHandle(f1.getFD());
|
|
|
- }
|
|
|
-
|
|
|
- if (redirects[2] == ProcessBuilderForWin32.Redirect.PIPE) {
|
|
|
- stdHandles[2] = -1L;
|
|
|
- } else if (redirects[2] == ProcessBuilderForWin32.Redirect.INHERIT) {
|
|
|
- stdHandles[2] = getHandle(FileDescriptor.err);
|
|
|
- } else {
|
|
|
- f2 = newFileOutputStream(redirects[2].file(),
|
|
|
- redirects[2].append());
|
|
|
- stdHandles[2] = getHandle(f2.getFD());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return new ProcessImplForWin32(username, password, cmdarray, envblock, dir, stdHandles, redirectErrorStream);
|
|
|
- } finally {
|
|
|
-
|
|
|
-
|
|
|
- try { if (f0 != null) {
|
|
|
- f0.close();
|
|
|
- }
|
|
|
- }
|
|
|
- finally {
|
|
|
- try { if (f1 != null) {
|
|
|
- f1.close();
|
|
|
- }
|
|
|
- }
|
|
|
- finally { if (f2 != null) {
|
|
|
- f2.close();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- private static class LazyPattern {
|
|
|
-
|
|
|
-
|
|
|
- private static final Pattern PATTERN =
|
|
|
- Pattern.compile("[^\\s\"]+|\"[^\"]*\"");
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * program arguments.
|
|
|
- *
|
|
|
- * The command string is broken into tokens. The token separator is a space
|
|
|
- * or quota character. The space inside quotation is not a token separator.
|
|
|
- * There are no escape sequences.
|
|
|
- */
|
|
|
- private static String[] getTokensFromCommand(String command) {
|
|
|
- ArrayList<String> matchList = new ArrayList<>(8);
|
|
|
- Matcher regexMatcher = ProcessImplForWin32.LazyPattern.PATTERN.matcher(command);
|
|
|
- while (regexMatcher.find()) {
|
|
|
- matchList.add(regexMatcher.group());
|
|
|
- }
|
|
|
- return matchList.toArray(new String[matchList.size()]);
|
|
|
- }
|
|
|
-
|
|
|
- private static final int VERIFICATION_CMD_BAT = 0;
|
|
|
- private static final int VERIFICATION_WIN32 = 1;
|
|
|
- private static final int VERIFICATION_WIN32_SAFE = 2;
|
|
|
- private static final int VERIFICATION_LEGACY = 3;
|
|
|
-
|
|
|
-
|
|
|
- private static final char[][] ESCAPE_VERIFICATION = {
|
|
|
-
|
|
|
-
|
|
|
- {' ', '\t', '<', '>', '&', '|', '^'},
|
|
|
- {' ', '\t', '<', '>'},
|
|
|
- {' ', '\t', '<', '>'},
|
|
|
- {' ', '\t'}
|
|
|
- };
|
|
|
-
|
|
|
- private static String createCommandLine(int verificationType,
|
|
|
- final String executablePath,
|
|
|
- final String[] cmd)
|
|
|
- {
|
|
|
- StringBuilder cmdbuf = new StringBuilder(80);
|
|
|
-
|
|
|
- cmdbuf.append(executablePath);
|
|
|
-
|
|
|
- for (int i = 1; i < cmd.length; ++i) {
|
|
|
- cmdbuf.append(' ');
|
|
|
- String s = cmd[i];
|
|
|
- if (needsEscaping(verificationType, s)) {
|
|
|
- cmdbuf.append('"');
|
|
|
-
|
|
|
- if (verificationType == VERIFICATION_WIN32_SAFE) {
|
|
|
-
|
|
|
- int length = s.length();
|
|
|
- for (int j = 0; j < length; j++) {
|
|
|
- char c = s.charAt(j);
|
|
|
- if (c == DOUBLEQUOTE) {
|
|
|
- int count = countLeadingBackslash(verificationType, s, j);
|
|
|
- while (count-- > 0) {
|
|
|
- cmdbuf.append(BACKSLASH);
|
|
|
- }
|
|
|
- cmdbuf.append(BACKSLASH);
|
|
|
- }
|
|
|
- cmdbuf.append(c);
|
|
|
- }
|
|
|
- } else {
|
|
|
- cmdbuf.append(s);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- int count = countLeadingBackslash(verificationType, s, s.length());
|
|
|
- while (count-- > 0) {
|
|
|
- cmdbuf.append(BACKSLASH);
|
|
|
- }
|
|
|
- cmdbuf.append('"');
|
|
|
- } else {
|
|
|
- cmdbuf.append(s);
|
|
|
- }
|
|
|
- }
|
|
|
- return cmdbuf.toString();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * Return the argument without quotes (1st and last) if present, else the arg.
|
|
|
- * @param str a string
|
|
|
- * @return the string without 1st and last quotes
|
|
|
- */
|
|
|
- private static String unQuote(String str) {
|
|
|
- int len = str.length();
|
|
|
- return (len >= 2 && str.charAt(0) == DOUBLEQUOTE && str.charAt(len - 1) == DOUBLEQUOTE)
|
|
|
- ? str.substring(1, len - 1)
|
|
|
- : str;
|
|
|
- }
|
|
|
-
|
|
|
- private static boolean needsEscaping(int verificationType, String arg) {
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- String unquotedArg = unQuote(arg);
|
|
|
- boolean argIsQuoted = !arg.equals(unquotedArg);
|
|
|
- boolean embeddedQuote = unquotedArg.indexOf(DOUBLEQUOTE) >= 0;
|
|
|
-
|
|
|
- switch (verificationType) {
|
|
|
- case VERIFICATION_CMD_BAT:
|
|
|
- if (embeddedQuote) {
|
|
|
- throw new IllegalArgumentException("Argument has embedded quote, " +
|
|
|
- "use the explicit CMD.EXE call.");
|
|
|
- }
|
|
|
- break;
|
|
|
- case VERIFICATION_WIN32_SAFE:
|
|
|
- if (argIsQuoted && embeddedQuote) {
|
|
|
- throw new IllegalArgumentException("Malformed argument has embedded quote: "
|
|
|
- + unquotedArg);
|
|
|
- }
|
|
|
- break;
|
|
|
- default:
|
|
|
- break;
|
|
|
- }
|
|
|
-
|
|
|
- if (!argIsQuoted) {
|
|
|
- char[] testEscape = ESCAPE_VERIFICATION[verificationType];
|
|
|
- for (int i = 0; i < testEscape.length; ++i) {
|
|
|
- if (arg.indexOf(testEscape[i]) >= 0) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- private static String getExecutablePath(String path)
|
|
|
- throws IOException
|
|
|
- {
|
|
|
- String name = unQuote(path);
|
|
|
- if (name.indexOf(DOUBLEQUOTE) >= 0) {
|
|
|
- throw new IllegalArgumentException("Executable name has embedded quote, " +
|
|
|
- "split the arguments: " + name);
|
|
|
- }
|
|
|
-
|
|
|
- File fileToRun = new File(name);
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- return fileToRun.getPath();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * An executable is any program that is an EXE or does not have an extension
|
|
|
- * and the Windows createProcess will be looking for .exe.
|
|
|
- * The comparison is case insensitive based on the name.
|
|
|
- * @param executablePath the executable file
|
|
|
- * @return true if the path ends in .exe or does not have an extension.
|
|
|
- */
|
|
|
- private boolean isExe(String executablePath) {
|
|
|
- File file = new File(executablePath);
|
|
|
- String upName = file.getName().toUpperCase(Locale.ROOT);
|
|
|
- return (upName.endsWith(".EXE") || upName.indexOf('.') < 0);
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- private boolean isShellFile(String executablePath) {
|
|
|
- String upPath = executablePath.toUpperCase();
|
|
|
- return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT"));
|
|
|
- }
|
|
|
-
|
|
|
- private String quoteString(String arg) {
|
|
|
- StringBuilder argbuf = new StringBuilder(arg.length() + 2);
|
|
|
- return argbuf.append('"').append(arg).append('"').toString();
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- private static int countLeadingBackslash(int verificationType,
|
|
|
- CharSequence input, int start) {
|
|
|
- if (verificationType == VERIFICATION_CMD_BAT) {
|
|
|
- return 0;
|
|
|
- }
|
|
|
- int j;
|
|
|
- for (j = start - 1; j >= 0 && input.charAt(j) == BACKSLASH; j--) {
|
|
|
-
|
|
|
- }
|
|
|
- return (start - 1) - j;
|
|
|
- }
|
|
|
-
|
|
|
- private static final char DOUBLEQUOTE = '\"';
|
|
|
- private static final char BACKSLASH = '\\';
|
|
|
-
|
|
|
- private WinNT.HANDLE handle;
|
|
|
- private OutputStream stdinStream;
|
|
|
- private InputStream stdoutStream;
|
|
|
- private InputStream stderrStream;
|
|
|
-
|
|
|
- private ProcessImplForWin32(
|
|
|
- String username,
|
|
|
- String password,
|
|
|
- String[] cmd,
|
|
|
- final String envblock,
|
|
|
- final String path,
|
|
|
- final long[] stdHandles,
|
|
|
- final boolean redirectErrorStream)
|
|
|
- throws IOException
|
|
|
- {
|
|
|
- String cmdstr;
|
|
|
- final SecurityManager security = System.getSecurityManager();
|
|
|
- GetPropertyAction action = new GetPropertyAction("jdk.lang.Process.allowAmbiguousCommands",
|
|
|
- (security == null) ? "true" : "false");
|
|
|
- final boolean allowAmbiguousCommands = !"false".equalsIgnoreCase(action.run());
|
|
|
- if (allowAmbiguousCommands && security == null) {
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- String executablePath = new File(cmd[0]).getPath();
|
|
|
-
|
|
|
-
|
|
|
- if (needsEscaping(VERIFICATION_LEGACY, executablePath) ) {
|
|
|
- executablePath = quoteString(executablePath);
|
|
|
- }
|
|
|
-
|
|
|
- cmdstr = createCommandLine(
|
|
|
-
|
|
|
- VERIFICATION_LEGACY,
|
|
|
- executablePath,
|
|
|
- cmd);
|
|
|
- } else {
|
|
|
- String executablePath;
|
|
|
- try {
|
|
|
- executablePath = getExecutablePath(cmd[0]);
|
|
|
- } catch (IllegalArgumentException e) {
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- StringBuilder join = new StringBuilder();
|
|
|
-
|
|
|
- for (String s : cmd) {
|
|
|
- join.append(s).append(' ');
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- cmd = getTokensFromCommand(join.toString());
|
|
|
- executablePath = getExecutablePath(cmd[0]);
|
|
|
-
|
|
|
-
|
|
|
- if (security != null) {
|
|
|
- security.checkExec(executablePath);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- boolean isShell = allowAmbiguousCommands ? isShellFile(executablePath)
|
|
|
- : !isExe(executablePath);
|
|
|
- cmdstr = createCommandLine(
|
|
|
-
|
|
|
- isShell ? VERIFICATION_CMD_BAT
|
|
|
- : (allowAmbiguousCommands ? VERIFICATION_WIN32 : VERIFICATION_WIN32_SAFE),
|
|
|
- quoteString(executablePath),
|
|
|
- cmd);
|
|
|
- }
|
|
|
-
|
|
|
- handle = create(username, password, cmdstr, envblock, path, stdHandles, redirectErrorStream);
|
|
|
-
|
|
|
- AccessController.doPrivileged(
|
|
|
- new PrivilegedAction<Void>() {
|
|
|
- @Override
|
|
|
- public Void run() {
|
|
|
- if (stdHandles[0] == -1L) {
|
|
|
- stdinStream = ProcessBuilderForWin32.NullOutputStream.INSTANCE;
|
|
|
- } else {
|
|
|
- FileDescriptor stdinFd = new FileDescriptor();
|
|
|
- setHandle(stdinFd, stdHandles[0]);
|
|
|
- stdinStream = new BufferedOutputStream(
|
|
|
- new FileOutputStream(stdinFd));
|
|
|
- }
|
|
|
-
|
|
|
- if (stdHandles[1] == -1L) {
|
|
|
- stdoutStream = ProcessBuilderForWin32.NullInputStream.INSTANCE;
|
|
|
- } else {
|
|
|
- FileDescriptor stdoutFd = new FileDescriptor();
|
|
|
- setHandle(stdoutFd, stdHandles[1]);
|
|
|
- stdoutStream = new BufferedInputStream(
|
|
|
- new FileInputStream(stdoutFd));
|
|
|
- }
|
|
|
-
|
|
|
- if (stdHandles[2] == -1L) {
|
|
|
- stderrStream = ProcessBuilderForWin32.NullInputStream.INSTANCE;
|
|
|
- } else {
|
|
|
- FileDescriptor stderrFd = new FileDescriptor();
|
|
|
- setHandle(stderrFd, stdHandles[2]);
|
|
|
- stderrStream = new FileInputStream(stderrFd);
|
|
|
- }
|
|
|
-
|
|
|
- return null; }});
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public OutputStream getOutputStream() {
|
|
|
- return stdinStream;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public InputStream getInputStream() {
|
|
|
- return stdoutStream;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public InputStream getErrorStream() {
|
|
|
- return stderrStream;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- protected void finalize() {
|
|
|
- closeHandle(handle);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int exitValue() {
|
|
|
- int exitCode = getExitCodeProcess(handle);
|
|
|
- if (exitCode == STILL_ACTIVE) {
|
|
|
- throw new IllegalThreadStateException("process has not exited");
|
|
|
- }
|
|
|
- return exitCode;
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public int waitFor() throws InterruptedException {
|
|
|
- waitForInterruptibly(handle);
|
|
|
- if (Thread.interrupted()) {
|
|
|
- throw new InterruptedException();
|
|
|
- }
|
|
|
- return exitValue();
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public boolean waitFor(long timeout, TimeUnit unit)
|
|
|
- throws InterruptedException
|
|
|
- {
|
|
|
- if (getExitCodeProcess(handle) != STILL_ACTIVE) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- if (timeout <= 0) {
|
|
|
- return false;
|
|
|
- }
|
|
|
-
|
|
|
- long remainingNanos = unit.toNanos(timeout);
|
|
|
- long deadline = System.nanoTime() + remainingNanos ;
|
|
|
-
|
|
|
- do {
|
|
|
-
|
|
|
- long msTimeout = TimeUnit.NANOSECONDS.toMillis(remainingNanos + 999_999L);
|
|
|
- waitForTimeoutInterruptibly(handle, msTimeout);
|
|
|
- if (Thread.interrupted()) {
|
|
|
- throw new InterruptedException();
|
|
|
- }
|
|
|
- if (getExitCodeProcess(handle) != STILL_ACTIVE) {
|
|
|
- return true;
|
|
|
- }
|
|
|
- remainingNanos = deadline - System.nanoTime();
|
|
|
- } while (remainingNanos > 0);
|
|
|
-
|
|
|
- return (getExitCodeProcess(handle) != STILL_ACTIVE);
|
|
|
- }
|
|
|
-
|
|
|
- @Override
|
|
|
- public void destroy() { terminateProcess(handle); }
|
|
|
-
|
|
|
- @Override
|
|
|
- public Process destroyForcibly() {
|
|
|
- destroy();
|
|
|
- return this;
|
|
|
- }
|
|
|
- @Override
|
|
|
- public boolean isAlive() {
|
|
|
- return isProcessAlive(handle);
|
|
|
- }
|
|
|
-
|
|
|
- private static boolean initHolder(WinNT.HANDLEByReference pjhandles,
|
|
|
- WinNT.HANDLEByReference[] pipe,
|
|
|
- int offset,
|
|
|
- WinNT.HANDLEByReference phStd) {
|
|
|
- if (!pjhandles.getValue().equals(JAVA_INVALID_HANDLE_VALUE)) {
|
|
|
- phStd.setValue(pjhandles.getValue());
|
|
|
- pjhandles.setValue(JAVA_INVALID_HANDLE_VALUE);
|
|
|
- } else {
|
|
|
- if (!Kernel32.INSTANCE.CreatePipe(pipe[0], pipe[1], null, PIPE_SIZE)) {
|
|
|
- throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
|
|
|
- } else {
|
|
|
- WinNT.HANDLE thisProcessEnd = offset == OFFSET_READ ? pipe[1].getValue() : pipe[0].getValue();
|
|
|
- phStd.setValue(pipe[offset].getValue());
|
|
|
- pjhandles.setValue(thisProcessEnd);
|
|
|
- }
|
|
|
- }
|
|
|
- Kernel32.INSTANCE.SetHandleInformation(phStd.getValue(), WinBase.HANDLE_FLAG_INHERIT, WinBase.HANDLE_FLAG_INHERIT);
|
|
|
- return true;
|
|
|
- }
|
|
|
-
|
|
|
- private static void releaseHolder(boolean complete, WinNT.HANDLEByReference[] pipe, int offset) {
|
|
|
- closeHandle(pipe[offset].getValue());
|
|
|
- if (complete) {
|
|
|
- closeHandle(pipe[offset == OFFSET_READ ? OFFSET_WRITE : OFFSET_READ].getValue());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void prepareIOEHandleState(WinNT.HANDLE[] stdIOE, Boolean[] inherit) {
|
|
|
- for(int i = 0; i < HANDLE_STORAGE_SIZE; ++i) {
|
|
|
- WinNT.HANDLE hstd = stdIOE[i];
|
|
|
- if (!WinBase.INVALID_HANDLE_VALUE.equals(hstd)) {
|
|
|
- inherit[i] = Boolean.TRUE;
|
|
|
- Kernel32.INSTANCE.SetHandleInformation(hstd, WinBase.HANDLE_FLAG_INHERIT, 0);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void restoreIOEHandleState(WinNT.HANDLE[] stdIOE, Boolean[] inherit) {
|
|
|
- for (int i = HANDLE_STORAGE_SIZE - 1; i >= 0; --i) {
|
|
|
- if (!WinBase.INVALID_HANDLE_VALUE.equals(stdIOE[i])) {
|
|
|
- Kernel32.INSTANCE.SetHandleInformation(stdIOE[i], WinBase.HANDLE_FLAG_INHERIT, Boolean.TRUE.equals(inherit[i]) ? WinBase.HANDLE_FLAG_INHERIT : 0);
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static WinNT.HANDLE processCreate(String username,
|
|
|
- String password,
|
|
|
- String cmd,
|
|
|
- final String envblock,
|
|
|
- final String path,
|
|
|
- final WinNT.HANDLEByReference[] stdHandles,
|
|
|
- final boolean redirectErrorStream) {
|
|
|
- WinNT.HANDLE ret = new WinNT.HANDLE(Pointer.createConstant(0));
|
|
|
-
|
|
|
- WinNT.HANDLE[] stdIOE = new WinNT.HANDLE[] {
|
|
|
- WinBase.INVALID_HANDLE_VALUE, WinBase.INVALID_HANDLE_VALUE, WinBase.INVALID_HANDLE_VALUE,
|
|
|
- stdHandles[0].getValue(), stdHandles[1].getValue(), stdHandles[2].getValue()
|
|
|
- };
|
|
|
- stdIOE[0] = Kernel32.INSTANCE.GetStdHandle(Wincon.STD_INPUT_HANDLE);
|
|
|
- stdIOE[1] = Kernel32.INSTANCE.GetStdHandle(Wincon.STD_OUTPUT_HANDLE);
|
|
|
- stdIOE[2] = Kernel32.INSTANCE.GetStdHandle(Wincon.STD_ERROR_HANDLE);
|
|
|
-
|
|
|
- Boolean[] inherit = new Boolean[] {
|
|
|
- Boolean.FALSE, Boolean.FALSE, Boolean.FALSE,
|
|
|
- Boolean.FALSE, Boolean.FALSE, Boolean.FALSE
|
|
|
- };
|
|
|
-
|
|
|
- prepareIOEHandleState(stdIOE, inherit);
|
|
|
-
|
|
|
-
|
|
|
- WinNT.HANDLEByReference hStdInput = new WinNT.HANDLEByReference();
|
|
|
- WinNT.HANDLEByReference[] pipeIn = new WinNT.HANDLEByReference[] {
|
|
|
- new WinNT.HANDLEByReference(WinBase.INVALID_HANDLE_VALUE), new WinNT.HANDLEByReference(WinBase.INVALID_HANDLE_VALUE) };
|
|
|
-
|
|
|
-
|
|
|
- WinNT.HANDLEByReference hStdOutput = new WinNT.HANDLEByReference();
|
|
|
- WinNT.HANDLEByReference[] pipeOut = new WinNT.HANDLEByReference[] {
|
|
|
- new WinNT.HANDLEByReference(WinBase.INVALID_HANDLE_VALUE), new WinNT.HANDLEByReference(WinBase.INVALID_HANDLE_VALUE) };
|
|
|
-
|
|
|
-
|
|
|
- WinNT.HANDLEByReference hStdError = new WinNT.HANDLEByReference();
|
|
|
- WinNT.HANDLEByReference[] pipeError = new WinNT.HANDLEByReference[] {
|
|
|
- new WinNT.HANDLEByReference(WinBase.INVALID_HANDLE_VALUE), new WinNT.HANDLEByReference(WinBase.INVALID_HANDLE_VALUE) };
|
|
|
-
|
|
|
- boolean success;
|
|
|
- if (initHolder(stdHandles[0], pipeIn, OFFSET_READ, hStdInput)) {
|
|
|
- if (initHolder(stdHandles[1], pipeOut, OFFSET_WRITE, hStdOutput)) {
|
|
|
- WinBase.STARTUPINFO si = new WinBase.STARTUPINFO();
|
|
|
- si.hStdInput = hStdInput.getValue();
|
|
|
- si.hStdOutput = hStdOutput.getValue();
|
|
|
-
|
|
|
- if (redirectErrorStream) {
|
|
|
- si.hStdError = si.hStdOutput;
|
|
|
- stdHandles[2].setValue(JAVA_INVALID_HANDLE_VALUE);
|
|
|
- success = true;
|
|
|
- } else {
|
|
|
- success = initHolder(stdHandles[2], pipeError, OFFSET_WRITE, hStdError);
|
|
|
- si.hStdError = hStdError.getValue();
|
|
|
- }
|
|
|
-
|
|
|
- if (success) {
|
|
|
- WTypes.LPSTR lpEnvironment = envblock == null ? new WTypes.LPSTR() : new WTypes.LPSTR(envblock);
|
|
|
- WinBase.PROCESS_INFORMATION pi = new WinBase.PROCESS_INFORMATION();
|
|
|
- si.dwFlags = WinBase.STARTF_USESTDHANDLES;
|
|
|
- if (!Advapi32.INSTANCE.CreateProcessWithLogonW(
|
|
|
- username
|
|
|
- , null
|
|
|
- , password
|
|
|
- , Advapi32.LOGON_WITH_PROFILE
|
|
|
- , null
|
|
|
- , cmd
|
|
|
- , WinBase.CREATE_NO_WINDOW
|
|
|
- , lpEnvironment.getPointer()
|
|
|
- , path
|
|
|
- , si
|
|
|
- , pi)) {
|
|
|
- throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
|
|
|
- } else {
|
|
|
- closeHandle(pi.hThread);
|
|
|
- ret = pi.hProcess;
|
|
|
- }
|
|
|
- }
|
|
|
- releaseHolder(ret.getPointer().equals(Pointer.createConstant(0)), pipeError, OFFSET_WRITE);
|
|
|
- releaseHolder(ret.getPointer().equals(Pointer.createConstant(0)), pipeOut, OFFSET_WRITE);
|
|
|
- }
|
|
|
- releaseHolder(ret.getPointer().equals(Pointer.createConstant(0)), pipeIn, OFFSET_READ);
|
|
|
- }
|
|
|
- restoreIOEHandleState(stdIOE, inherit);
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- private static synchronized WinNT.HANDLE create(String username,
|
|
|
- String password,
|
|
|
- String cmd,
|
|
|
- final String envblock,
|
|
|
- final String path,
|
|
|
- final long[] stdHandles,
|
|
|
- final boolean redirectErrorStream) {
|
|
|
- WinNT.HANDLE ret = new WinNT.HANDLE(Pointer.createConstant(0));
|
|
|
- WinNT.HANDLEByReference[] handles = new WinNT.HANDLEByReference[stdHandles.length];
|
|
|
- for (int i = 0; i < stdHandles.length; i++) {
|
|
|
- handles[i] = new WinNT.HANDLEByReference(new WinNT.HANDLE(Pointer.createConstant(stdHandles[i])));
|
|
|
- }
|
|
|
-
|
|
|
- if (cmd != null && username != null && password != null) {
|
|
|
- ret = processCreate(username, password, cmd, envblock, path, handles, redirectErrorStream);
|
|
|
- }
|
|
|
-
|
|
|
- for (int i = 0; i < stdHandles.length; i++) {
|
|
|
- stdHandles[i] = handles[i].getPointer().getLong(0);
|
|
|
- }
|
|
|
-
|
|
|
- return ret;
|
|
|
- }
|
|
|
-
|
|
|
- private static int getExitCodeProcess(WinNT.HANDLE handle) {
|
|
|
- IntByReference exitStatus = new IntByReference();
|
|
|
- if (!Kernel32.INSTANCE.GetExitCodeProcess(handle, exitStatus)) {
|
|
|
- throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
|
|
|
- }
|
|
|
- return exitStatus.getValue();
|
|
|
- }
|
|
|
-
|
|
|
- private static void terminateProcess(WinNT.HANDLE handle) {
|
|
|
- Kernel32.INSTANCE.TerminateProcess(handle, 1);
|
|
|
- }
|
|
|
-
|
|
|
- private static boolean isProcessAlive(WinNT.HANDLE handle) {
|
|
|
- IntByReference exitStatus = new IntByReference();
|
|
|
- Kernel32.INSTANCE.GetExitCodeProcess(handle, exitStatus);
|
|
|
- return exitStatus.getValue() == STILL_ACTIVE;
|
|
|
- }
|
|
|
-
|
|
|
- private static void closeHandle(WinNT.HANDLE handle) {
|
|
|
- if (!handle.equals(INVALID_HANDLE_VALUE)) {
|
|
|
- Kernel32Util.closeHandle(handle);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- * Opens a file for atomic append. The file is created if it doesn't
|
|
|
- * already exist.
|
|
|
- *
|
|
|
- * @param path the file to open or create
|
|
|
- * @return the native HANDLE
|
|
|
- */
|
|
|
- private static long openForAtomicAppend(String path) throws IOException {
|
|
|
- int access = WinNT.GENERIC_READ | WinNT.GENERIC_WRITE;
|
|
|
- int sharing = WinNT.FILE_SHARE_READ | WinNT.FILE_SHARE_WRITE;
|
|
|
- int disposition = WinNT.OPEN_ALWAYS;
|
|
|
- int flagsAndAttributes = WinNT.FILE_ATTRIBUTE_NORMAL;
|
|
|
- if (path == null || path.isEmpty()) {
|
|
|
- return -1;
|
|
|
- } else {
|
|
|
- WinNT.HANDLE handle = Kernel32.INSTANCE.CreateFile(path, access, sharing, null, disposition, flagsAndAttributes, null);
|
|
|
- if (handle == WinBase.INVALID_HANDLE_VALUE) {
|
|
|
- throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
|
|
|
- }
|
|
|
- return handle.getPointer().getLong(0);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void waitForInterruptibly(WinNT.HANDLE handle) {
|
|
|
- int result = Kernel32.INSTANCE.WaitForMultipleObjects(1, new WinNT.HANDLE[]{handle}, false, WinBase.INFINITE);
|
|
|
- if (result == WinBase.WAIT_FAILED) {
|
|
|
- throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void waitForTimeoutInterruptibly(WinNT.HANDLE handle, long timeout) {
|
|
|
- int result = Kernel32.INSTANCE.WaitForMultipleObjects(1, new WinNT.HANDLE[]{handle}, false, (int) timeout);
|
|
|
- if (result == WinBase.WAIT_FAILED) {
|
|
|
- throw new Win32Exception(Kernel32.INSTANCE.GetLastError());
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-}
|