/*******************************************************************************
* Copyright (c) 2009, 2012 Wind River Systems, Inc. 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
*
* Contributors:
* Wind River Systems - initial API and implementation
*******************************************************************************/
package org.eclipse.tcf.internal.debug.launch;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.FileLocator;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.tcf.internal.debug.Activator;
import org.eclipse.tcf.protocol.IPeer;
import org.eclipse.tcf.protocol.Protocol;
import org.eclipse.tcf.services.ILocator;
import org.eclipse.tcf.util.TCFTask;
import org.osgi.framework.Bundle;
/**
* This class checks that TCF Agent is running on the local host,
* and starts a new instance of the agent if it cannot be located.
*/
public class TCFLocalAgent {
public static final String
LOCAL_HOST = "127.0.0.1",
AGENT_NAME = "agent",
SERVER_NAME = "server";
private static final Map<String,String> ports = new HashMap<String,String>();
private static final Map<String,Process> agents = new HashMap<String,Process>();
static {
ports.put(AGENT_NAME, "1534");
ports.put(SERVER_NAME, "1535");
}
private static boolean destroed;
private static String getSysName() {
String os = System.getProperty("os.name");
String arch = System.getProperty("os.arch");
if (arch.equals("x86")) arch = "i386";
if (arch.equals("i686")) arch = "i386";
if (os.startsWith("Windows")) {
os = "Windows";
}
if (os.equals("Linux")) os = "GNU/Linux";
return os + "/" + arch;
}
private static Path getDevelAgentFileName(String nm) {
try {
String fnm = nm;
String sys = getSysName();
if (sys.startsWith("Windows")) {
sys = "MSVC/Win32";
fnm += ".exe";
}
Bundle bundle = Platform.getBundle(Activator.PLUGIN_ID);
File plugin = FileLocator.getBundleFile(bundle);
File agent = new File(plugin, "../../../org.eclipse.tcf.agent/" + nm + "/obj/" + sys + "/Debug/" + fnm);
if (!agent.exists()) return null;
return new Path(agent.getAbsolutePath());
}
catch (Exception x) {
Activator.log("Cannot find bundle location", x);
}
return null;
}
private static Path getAgentFileName(String fnm) {
String sys = getSysName();
if (sys.startsWith("Windows")) fnm += ".exe";
return new Path("agent/" + sys + "/" + fnm);
}
public static synchronized String runLocalAgent(final String nm) throws CoreException {
if (destroed) return null;
String id = getLocalAgentID(nm);
if (id != null) return id;
if (agents.containsKey(nm)) {
agents.remove(nm).destroy();
}
Path fnm = getDevelAgentFileName(nm);
if (fnm == null) fnm = getAgentFileName(nm);
try {
if (!fnm.isAbsolute()) {
Bundle bundle = Platform.getBundle(Activator.PLUGIN_ID);
URL url = FileLocator.find(bundle, fnm, null);
if (url != null) {
URLConnection ucn = url.openConnection();
ucn.setRequestProperty("Method", "HEAD");
ucn.connect();
long mtime = ucn.getLastModified();
File f = Activator.getDefault().getStateLocation().append(fnm).toFile();
if (!f.exists() || mtime != f.lastModified()) {
f.getParentFile().mkdirs();
InputStream inp = url.openStream();
OutputStream out = new FileOutputStream(f);
byte[] buf = new byte[0x1000];
for (;;) {
int len = inp.read(buf);
if (len < 0) break;
out.write(buf, 0, len);
}
out.close();
inp.close();
if (!"exe".equals(fnm.getFileExtension())) {
String[] cmd = {
"chmod",
"a+x",
f.getAbsolutePath()
};
Runtime.getRuntime().exec(cmd).waitFor();
}
f.setLastModified(mtime);
fnm = new Path(f.getAbsolutePath());
}
}
}
String[] cmd = {
fnm.toOSString(),
"-s",
"TCP:" + LOCAL_HOST + ":" + ports.get(nm)
};
final Process prs = Runtime.getRuntime().exec(cmd);
agents.put(nm, prs);
final TCFTask<String> waiting = waitAgentReady(nm);
Thread t = new Thread() {
public void run() {
try {
final int n = prs.waitFor();
final StringBuffer sbf = new StringBuffer();
if (n != 0) {
char cbf[] = new char[256];
InputStreamReader r = new InputStreamReader(prs.getErrorStream());
for (;;) {
try {
int rd = r.read(cbf);
if (rd < 0) break;
sbf.append(cbf, 0, rd);
}
catch (IOException x) {
break;
}
}
try {
r.close();
}
catch (IOException x) {
}
sbf.append("TCF " + nm + " exited with code ");
sbf.append(n);
Protocol.invokeLater(new Runnable() {
public void run() {
if (waiting.isDone()) return;
waiting.error(new IOException(sbf.toString()));
}
});
}
synchronized (TCFLocalAgent.class) {
if (agents.get(nm) == prs) {
if (n != 0 && !destroed) {
Activator.log(sbf.toString(), null);
}
agents.remove(nm);
}
}
}
catch (InterruptedException x) {
Activator.log("TCF " + nm + " monitor interrupted", x);
}
}
};
t.setDaemon(true);
t.setName("TCF Agent Monitor");
t.start();
return waiting.getIO();
}
catch (Throwable x) {
agents.remove(nm);
throw new CoreException(new Status(IStatus.ERROR,
Activator.PLUGIN_ID, 0,
"Cannot start local TCF " + nm + ".",
x));
}
}
private static boolean isLocalAgent(IPeer p, String nm) {
String prot = p.getTransportName();
if (prot.equals("PIPE")) return true;
if (prot.equals("UNIX")) {
String port = p.getAttributes().get(IPeer.ATTR_IP_PORT);
return ports.get(nm).equals(port);
}
String host = p.getAttributes().get(IPeer.ATTR_IP_HOST);
String port = p.getAttributes().get(IPeer.ATTR_IP_PORT);
return LOCAL_HOST.equals(host) && ports.get(nm).equals(port);
}
public static synchronized String getLocalAgentID(final String nm) {
return new TCFTask<String>() {
int cnt;
public void run() {
final ILocator locator = Protocol.getLocator();
for (IPeer p : locator.getPeers().values()) {
if (isLocalAgent(p, nm)) {
done(p.getID());
return;
}
}
if (cnt++ < 10) {
Protocol.invokeLater(100, this);
}
else {
done(null);
}
}
}.getE();
}
private static TCFTask<String> waitAgentReady(final String nm) {
return new TCFTask<String>() {
public void run() {
final ILocator locator = Protocol.getLocator();
for (IPeer p : locator.getPeers().values()) {
if (isLocalAgent(p, nm)) {
done(p.getID());
return;
}
}
final ILocator.LocatorListener listener = new ILocator.LocatorListener() {
public void peerAdded(IPeer p) {
if (!isDone() && isLocalAgent(p, nm)) {
done(p.getID());
locator.removeListener(this);
}
}
public void peerChanged(IPeer peer) {
}
public void peerHeartBeat(String id) {
}
public void peerRemoved(String id) {
}
};
locator.addListener(listener);
Protocol.invokeLater(30000, new Runnable() {
public void run() {
if (!isDone()) {
error(new Exception("Timeout waiting for TCF Agent to start"));
locator.removeListener(listener);
}
}
});
}
};
}
public static synchronized void destroy() {
destroed = true;
for (Process prs : agents.values()) prs.destroy();
agents.clear();
}
}