blob: b52aa0ac2774a7632c0488d765636196a94b0c36 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2016 Oak Ridge National Laboratory and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*******************************************************************************/
package org.eclipse.remote.internal.proxy.server.commands;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Map;
import org.eclipse.remote.proxy.core.MultiplexedChannel;
import org.eclipse.remote.proxy.core.exceptions.ProxyException;
/**
* TODO: Fix hang if command fails...
*
*/
public class ServerExecCommand extends AbstractServerCommand {
private final List<String> command;
private final Map<String, String> env;
private final boolean redirect;
private final boolean appendEnv;
private final String directory;
private final InputStream stdin;
private final OutputStream stdout;
private final OutputStream stderr;
private final DataOutputStream result;
private final DataInputStream cmd;
private Process proc;
private class CommandRunner implements Runnable {
@Override
public void run() {
ProcessBuilder builder = new ProcessBuilder(command);
try {
if (!appendEnv) {
builder.environment().clear();
builder.environment().putAll(env);
} else {
for (Map.Entry<String, String> entry : env.entrySet()) {
String val = builder.environment().get(entry.getKey());
if (val == null || !val.equals(entry.getValue())) {
builder.environment().put(entry.getKey(), entry.getValue());
}
}
}
} catch (UnsupportedOperationException | IllegalArgumentException e) {
// Leave environment untouched
}
File dir = new File(directory);
if (dir.exists() && dir.isAbsolute()) {
builder.directory(dir);
}
builder.redirectErrorStream(redirect);
try {
proc = builder.start();
startForwarder("stdout", proc.getInputStream(), stdout);
startForwarder("stderr", proc.getErrorStream(), stderr);
startForwarder("stdin", stdin, proc.getOutputStream());
System.err.println("waiting for proc");
new Thread(new ProcMonitor(), "process monitor").start();
int exit = proc.waitFor();
System.err.println("exit status="+exit);
result.writeInt(exit);
result.flush();
} catch (IOException | InterruptedException e) {
// Ignore?
}
}
}
private class ProcMonitor implements Runnable {
@Override
public void run() {
try {
cmd.readByte();
if (proc.isAlive()) {
proc.destroyForcibly();
}
} catch (IOException e) {
// Finish
}
}
}
private class Forwarder implements Runnable {
private final InputStream in;
private final OutputStream out;
public Forwarder(InputStream in, OutputStream out) {
this.in = in;
this.out = out;
}
@Override
public void run() {
byte[] buf = new byte[8192];
int n;
try {
while ((n = in.read(buf)) >= 0) {
if (n > 0) {
out.write(buf, 0, n);
out.flush();
}
}
} catch (IOException e) {
// Finish
}
}
}
public ServerExecCommand(List<String> command, Map<String, String> env, String directory, boolean redirect, boolean appendEnv, MultiplexedChannel chanA, MultiplexedChannel chanB, MultiplexedChannel chanC) {
this.command = command;
this.env = env;
this.directory = directory;
this.redirect = redirect;
this.appendEnv = appendEnv;
this.stdin = chanA.getInputStream();
this.stdout = chanA.getOutputStream();
this.stderr = chanB.getOutputStream();
this.result = new DataOutputStream(chanC.getOutputStream());
this.cmd = new DataInputStream(chanC.getInputStream());
}
public void exec() throws ProxyException {
new Thread(new CommandRunner(), command.get(0)).start();
}
private void startForwarder(String name, InputStream in, OutputStream out) {
Forwarder forwarder = new Forwarder(in, out);
new Thread(forwarder, command.get(0) + " " + name).start();
}
}