blob: a29cb051b89e199bd2862fa4aa238c86236bd676 [file] [log] [blame]
/*
* 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://www.apache.org/licenses/LICENSE-2.0
*
* 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.netbeans.modules.nativeexecution;
import com.sun.jna.Pointer;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.io.SequenceInputStream;
import java.lang.reflect.Field;
import java.nio.charset.StandardCharsets;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.logging.Level;
import org.netbeans.modules.nativeexecution.api.ExecutionEnvironmentFactory;
import org.netbeans.modules.nativeexecution.api.HostInfo.OSFamily;
import org.netbeans.modules.nativeexecution.support.EnvWriter;
import org.netbeans.modules.nativeexecution.api.util.MacroMap;
import org.netbeans.modules.nativeexecution.api.util.UnbufferSupport;
import org.netbeans.modules.nativeexecution.api.util.WindowsSupport;
import org.netbeans.modules.nativeexecution.pty.PtyUtility;
import org.netbeans.modules.nativeexecution.support.Win32APISupport;
import org.openide.util.Exceptions;
import org.openide.util.NbBundle;
import org.openide.util.Utilities;
public final class LocalNativeProcess extends AbstractNativeProcess {
private Process process = null;
private PipedInputStream errorPipedInputStream = null;
private PipedOutputStream errorPipedOutputStream = null;
private boolean win1073741515added = false;
public LocalNativeProcess(NativeProcessInfo info) {
super(info);
}
@Override
protected void create() throws Throwable {
if (hostInfo.getOSFamily() == OSFamily.WINDOWS) {
createWin();
} else {
createNonWin();
}
}
private void createNonWin() throws IOException, InterruptedException {
final MacroMap env = info.getEnvironment().clone();
if (info.isUnbuffer()) {
UnbufferSupport.initUnbuffer(info.getExecutionEnvironment(), env);
}
final ProcessBuilder pb = new ProcessBuilder(hostInfo.getShell(), "-s"); // NOI18N
// Get working directory ....
String workingDirectory = info.getWorkingDirectory(true);
if (workingDirectory != null) {
File wd = new File(workingDirectory);
if (!wd.exists()) {
throw new FileNotFoundException(loc("NativeProcess.noSuchDirectoryError.text", wd.getAbsolutePath())); // NOI18N
}
pb.directory(wd);
}
if (isInterrupted()) {
throw new InterruptedException();
}
process = pb.start();
OutputStream toProcessStream = process.getOutputStream();
InputStream fromProcessStream = process.getInputStream();
setErrorStream(process.getErrorStream());
setInputStream(fromProcessStream);
setOutputStream(toProcessStream);
toProcessStream.write("echo $$\n".getBytes()); // NOI18N
toProcessStream.flush();
EnvWriter ew = new EnvWriter(toProcessStream, false);
ew.write(env);
if (info.getInitialSuspend()) {
toProcessStream.write("ITS_TIME_TO_START=\n".getBytes()); // NOI18N
toProcessStream.write("trap 'ITS_TIME_TO_START=1' CONT\n".getBytes()); // NOI18N
toProcessStream.write("while [ -z \"$ITS_TIME_TO_START\" ]; do sleep 1; done\n".getBytes()); // NOI18N
}
if (info.isRedirectError()) {
toProcessStream.write(("exec 2>&1\n").getBytes()); // NOI18N
}
toProcessStream.write(("exec " + info.getCommandLineForShell() + "\n").getBytes()); // NOI18N
toProcessStream.flush();
creation_ts = System.nanoTime();
readPID(fromProcessStream);
}
private void createWin() throws IOException, InterruptedException {
// Don't use shell wrapping on Windows...
// Mostly this is because exec works not as expected and we cannot
// control processes started with exec method....
// Suspend is not supported on Windows.
final ProcessBuilder pb = new ProcessBuilder(); // NOI18N
final MacroMap jointEnv = MacroMap.forExecEnv(ExecutionEnvironmentFactory.getLocal());
jointEnv.putAll(info.getEnvironment());
if (isInterrupted()) {
throw new InterruptedException();
}
if (info.isUnbuffer()) {
UnbufferSupport.initUnbuffer(info.getExecutionEnvironment(), jointEnv);
}
pb.environment().clear();
for (Entry<String, String> envEntry : jointEnv.entrySet()) {
pb.environment().put(envEntry.getKey(), envEntry.getValue());
}
pb.redirectErrorStream(info.isRedirectError());
pb.command(info.getCommand());
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest(String.format("Command: %s", info.getCommand())); // NOI18N
}
String wdir = info.getWorkingDirectory(true);
if (wdir != null) {
File wd = new File(wdir);
if (!wd.exists()) {
throw new FileNotFoundException(loc("NativeProcess.noSuchDirectoryError.text", wd.getAbsolutePath())); // NOI18N
}
pb.directory(wd);
if (LOG.isLoggable(Level.FINEST)) {
LOG.finest(String.format("Working directory: %s", wdir)); // NOI18N
}
}
process = pb.start();
creation_ts = System.nanoTime();
errorPipedOutputStream = new PipedOutputStream();
errorPipedInputStream = new PipedInputStream(errorPipedOutputStream);
setErrorStream(new SequenceInputStream(process.getErrorStream(), errorPipedInputStream));
setInputStream(process.getInputStream());
setOutputStream(process.getOutputStream());
int newPid = 12345;
try {
String className = process.getClass().getName();
if ("java.lang.Win32Process".equals(className) || "java.lang.ProcessImpl".equals(className)) { // NOI18N
Field f = process.getClass().getDeclaredField("handle"); // NOI18N
f.setAccessible(true);
long phandle = f.getLong(process);
Win32APISupport kernel = Win32APISupport.INSTANCE;
Win32APISupport.HANDLE handle = new Win32APISupport.HANDLE();
handle.setPointer(Pointer.createConstant(phandle));
newPid = kernel.GetProcessId(handle);
}
} catch (Throwable e) {
}
ByteArrayInputStream bis = new ByteArrayInputStream(("" + newPid).getBytes()); // NOI18N
readPID(bis);
}
@Override
public final int waitResult() throws InterruptedException {
if (process == null) {
return -1;
}
try {
int exitcode = process.waitFor();
finishing();
/*
* Bug 179555 - Qt application fails to run in case of default qt sdk installation
*/
if (exitcode == -1073741515 && Utilities.isWindows()) {
// This means Initialization error. May be the reason is that no required dll found
// Several threads may be here.
// Must be sure that message is added only once.
synchronized (this) {
if (!win1073741515added && errorPipedOutputStream != null) {
StringBuilder cmd = new StringBuilder();
Iterator<String> iterator = info.getCommand().iterator();
String exec;
if (info.isPtyMode()) {
exec = iterator.next();
String ptyUtilityPath = null;
try {
ptyUtilityPath = PtyUtility.getInstance().getPath(ExecutionEnvironmentFactory.getLocal());
} catch (IOException ex) {
}
if (ptyUtilityPath != null && exec.equals(ptyUtilityPath)) {
exec = iterator.next(); // quoted executable
exec = exec.substring(1, exec.length() - 1); // remove quotes before converting
// remove quotes before converting
exec = WindowsSupport.getInstance().convertToWindowsPath(exec);
}
} else {
exec = iterator.next();
}
if (exec.contains(" ")) { // NOI18N
cmd.append('"').append(exec).append('"').append(' '); // NOI18N
} else {
cmd.append(exec).append(' ');
}
while (iterator.hasNext()) {
cmd.append(iterator.next()).append(' ');
}
String errorMsg = loc("LocalNativeProcess.windowsProcessStartFailed.1073741515.text", cmd.toString()); // NOI18N
if (info.isPtyMode()) {
errorMsg = errorMsg.replace("\n", "\n\r"); // NOI18N
}
try {
errorPipedOutputStream.write(errorMsg.getBytes(StandardCharsets.UTF_8));
errorPipedOutputStream.flush();
} catch (IOException ex) {
Exceptions.printStackTrace(ex);
}
win1073741515added = true;
}
}
}
return exitcode;
} finally {
try {
if (errorPipedOutputStream != null) {
errorPipedOutputStream.close();
}
} catch (IOException ex) {
// Exceptions.printStackTrace(ex);
}
}
}
@Override
protected int destroyImpl() {
if (process != null) {
process.destroy();
return 1;
}
return 0;
}
private static String loc(String key, String... params) {
return NbBundle.getMessage(LocalNativeProcess.class, key, params);
}
}