--- /dev/null
+$(eval $(shell test -e Makefile.common || curl -O http://core.ibex.org/Makefile.common))
+include Makefile.common
+
+
--- /dev/null
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+import java.lang.reflect.*;
+
+public class Host extends Loader {
+
+ final String hostname;
+ public Host(String path, String hostname) { super(path); this.hostname = hostname; }
+
+ public void changed(Watched w) {
+ super.changed(w);
+ Log.error(this, "changed(" + w + ")");
+ try {
+ ClassLoader cl = getClassLoader();
+ if (cl == null) return;
+ Class c = cl.loadClass("Main");
+ if (c == null) return;
+ Method m = c.getMethod("main", new Class[] { });
+ m.invoke(null, new Object[] { });
+ } catch (Exception e) {
+ Log.warn(this, "nope");
+ }
+ }
+}
--- /dev/null
+package org.ibex.jinetd;
+import java.net.*;
+import org.ibex.net.*;
+import org.ibex.io.*;
+
+/**
+ * jinetd will scan /jinetd/port/<num>/*.jar for a
+ * class implementing this interface; instances must
+ * have public constructors
+ */
+public interface Listener {
+
+ public void accept(Connection c);
+
+}
--- /dev/null
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+import java.util.zip.*;
+
+/** represents a file or directory which is scanned for updates */
+public class Loader extends Watcher {
+
+ public Loader(String path) { super(path); }
+
+ private TreeClassLoader classloader = new TreeClassLoader();
+ public ClassLoader getClassLoader() {
+ ClassLoader classloader = this.classloader;
+ if (classloader == null) {
+ classloader = this.classloader = new TreeClassLoader();
+ Log.warn(this, "getting classloader...");
+ try {
+ compileSource();
+ } catch (Exception e) {
+ Log.error(this, e);
+ }
+ }
+ return classloader;
+ }
+
+ private void fill(Vec vec, File dir) {
+ if (!dir.exists()) return;
+ if (!dir.isDirectory()) {
+ if (!dir.getPath().endsWith(".java")) return;
+ vec.addElement(dir.getAbsolutePath());
+ } else {
+ String[] list = dir.list();
+ for(int i=0; i<list.length; i++)
+ fill(vec, new File(dir.getAbsolutePath() + File.separatorChar + list[i]));
+ }
+ }
+ private void compileSource() throws Exception {
+ File srcdir = new File(this.path + File.separatorChar + "SRC");
+ if (!srcdir.exists()) return;
+ if (new File("/usr/bin/jikes").exists()) {
+ File bindir = new File(this.path + File.separatorChar + "BIN"); bindir.mkdirs();
+ File libdir = new File(this.path + File.separatorChar + "LIB");
+ String classpath = System.getProperty("java.class.path");
+ String [] l = new File("/jinetd/LIB/").list();
+ for(int i=0; i<l.length; i++) {
+ if (!l[i].endsWith(".jar")) continue;
+ classpath += File.pathSeparatorChar;
+ classpath += "/jinetd/LIB/" + l[i];
+ }
+ String bootclasspath = System.getProperty("sun.boot.class.path", "");
+ Vec args = new Vec();
+ args.addElement("/usr/bin/jikes");
+ args.addElement("+E");
+ args.addElement("-nowarn");
+ args.addElement("-bootclasspath");
+ args.addElement(bootclasspath);
+ args.addElement("-extdirs");
+ args.addElement(libdir.getAbsolutePath());
+ args.addElement("-classpath");
+ args.addElement(classpath);
+ args.addElement("-sourcepath");
+ args.addElement(srcdir.getAbsolutePath());
+ args.addElement("-d");
+ args.addElement(bindir.getAbsolutePath());
+ fill(args, srcdir);
+ String[] all = new String[args.size()];
+ args.copyInto(all);
+ Log.info(this, "invoking jikes");
+ for(int i=0; i<all.length; i++) Log.info(this, " " + all[i]);
+ final Process jikes = Runtime.getRuntime().exec(all);
+ final BufferedReader out = new BufferedReader(new InputStreamReader(jikes.getInputStream()));
+ final BufferedReader err = new BufferedReader(new InputStreamReader(jikes.getErrorStream()));
+ new Thread() { public void run() {
+ try { for(String s = out.readLine(); s != null; s = out.readLine()) Log.info("jikes[stdout]", s); }
+ catch (Exception e) { Log.warn("jikes", e); } } }.start();
+ new Thread() { public void run() {
+ try { for(String s = err.readLine(); s != null; s = err.readLine()) Log.info("jikes[stderr]", s); }
+ catch (Exception e) { Log.warn("jikes", e); } } }.start();
+ jikes.waitFor();
+ } else {
+ Log.error(this, "ACK! jikes not found, javac not (yet) supported");
+ }
+ }
+
+ // only watch SRC and LIB for changes
+ public Watched slash(String path) {
+ return (path.equals("LIB") ||
+ path.equals("BIN") ||
+ path.equals("SRC") ||
+ path.endsWith(".jar") ) ? super.slash(path) : null; }
+
+
+ // dump the classloader if anything changes
+ public void changed(Watched w) {
+ if (w.path.indexOf("BIN") != -1) return;
+ if (classloader != null) {
+ Log.info(this, "Reloading all classes: " + path);
+ classloader = null;
+ }
+ }
+
+ private class TreeClassLoader extends ClassLoader {
+ private Hashtable cache = new Hashtable();
+
+ private synchronized Class defineClass(String name) {
+ // first see if it's just sitting there
+ File classFile = slash("BIN").slash(name.replace('.', File.separatorChar) + ".class");
+ if (classFile.exists()) {
+ try {
+ FileInputStream fis = new FileInputStream(classFile);
+ byte[] b = InputStreamToByteArray.convert(fis);
+ fis.close();
+ Log.debug(this, " loading " + name + " from " + classFile.getAbsolutePath());
+ return defineClass(b, 0, b.length);
+ } catch (Exception e) {
+ Log.warn(this, e);
+ }
+ }
+
+ // then scan the jarfiles for it
+ File lib = slash("LIB");
+ if (lib.exists() && lib.isDirectory()) {
+ try {
+ String[] paths = lib.list();
+ for(int i=0; i<paths.length; i++) {
+ if (paths[i].endsWith(".jar")) {
+ File f = new File(getAbsolutePath()+File.separatorChar+"LIB"+File.separatorChar+paths[i]);
+ //Log.debug(this, " scanning " + f.getAbsolutePath());
+ ZipFile zf = new ZipFile(f);
+ ZipEntry ze = zf.getEntry(name.replace('.', File.separatorChar) + ".class");
+ if (ze != null) {
+ byte[] b = InputStreamToByteArray.convert(zf.getInputStream(ze));
+ Log.debug(this, " loading " + name + " from " + f.getAbsolutePath());
+ zf.close();
+ return defineClass(b, 0, b.length);
+ }
+ zf.close();
+ }
+ }
+ } catch (Exception e) {
+ Log.warn(this, e);
+ }
+
+ // finally scan ourselves
+ try {
+ String[] paths = list();
+ for(int i=0; i<paths.length; i++) {
+ if (paths[i].endsWith(".jar")) {
+ File f = new File(getAbsolutePath()+File.separatorChar+paths[i]);
+ //Log.debug(this, " scanning " + f.getAbsolutePath());
+ ZipFile zf = new ZipFile(f);
+ ZipEntry ze = zf.getEntry(name.replace('.', File.separatorChar) + ".class");
+ if (ze != null) {
+ byte[] b = InputStreamToByteArray.convert(zf.getInputStream(ze));
+ Log.debug(this, " loading " + name + " from " + f.getAbsolutePath());
+ zf.close();
+ return defineClass(b, 0, b.length);
+ }
+ zf.close();
+ }
+ }
+ } catch (Exception e) {
+ Log.warn(this, e);
+ }
+
+ }
+
+ // finally, resort to compiling it if we have to
+ //File src = new File(getAbsolutePath() + File.separatorChar + "SRC");
+ // FIXME
+ //if (!sourcebuilt) buildSource();
+ return null;
+ }
+
+ public synchronized Class loadClass(String name, boolean resolve) throws ClassNotFoundException {
+ try {
+ Class c = findSystemClass(name);
+ if (c != null) { if (resolve) resolveClass(c); return c; }
+ } catch (ClassNotFoundException e) { /* DELIBERATE */ }
+ Class c = (Class)cache.get(name);
+ if (c == null) {
+ //Log.info(this, "looking for class " + name);
+ c = defineClass(name);
+ if (c == null) throw new ClassNotFoundException();
+ cache.put(name, c);
+ }
+ if (resolve) resolveClass(c);
+ return c;
+ }
+ }
+}
--- /dev/null
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+
+public class Main {
+
+ public static Root root = new Root(System.getProperty("jinetd.root", "/jinetd"));
+ public static void main(String[] s) throws Exception {
+ Log.color = true;
+ while(true) try {
+ Thread.sleep(1000);
+ //Log.info(Main.class, "scanning...");
+ if (root != null) root.scan();
+ } catch (Exception e) { Log.error(Main.class, e); }
+ }
+
+}
--- /dev/null
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import org.ibex.io.*;
+import java.io.*;
+import java.util.*;
+import java.net.*;
+import java.util.zip.*;
+
+public class Port extends Loader {
+
+ private final NetworkInterface iface;
+ private final int port;
+ private final Thread listener;
+
+ public Port(String path, NetworkInterface iface, int port) {
+ super(path);
+ this.iface = iface;
+ this.port = port;
+ this.listener = new PortThread();
+ listener.start();
+ }
+
+ public void changed(Watched w) {
+ //Log.warn(this, "Port: noticed change in " + w);
+ super.changed(w);
+ }
+
+ void dispatch(final Connection conn) throws Exception {
+ getClassLoader();
+ String[] list = list();
+ for(int i=0; i<list.length; i++) {
+ if (!list[i].endsWith(".jar")) continue;
+ //Log.warn(this, "checking " + (this.path + File.separatorChar + list[i]));
+ File f = new File(this.path + File.separatorChar + list[i]);
+ ZipInputStream zis = new ZipInputStream(new FileInputStream(f));
+ for(ZipEntry ze = zis.getNextEntry(); ze != null; ze = zis.getNextEntry()) {
+ String name = ze.getName();
+ if (name.endsWith(".class"))
+ dispatch(conn, name.substring(0, name.length() - ".class".length()).replace('/', '.'));
+ }
+ }
+ check(conn, new File(getAbsolutePath() + File.separatorChar + "BIN"));
+ }
+
+ void check(Connection conn, File f) throws Exception {
+ //Log.warn(this, "check(" + f.getAbsolutePath() + ")");
+ if (!f.exists()) return;
+ if (!f.isDirectory()) {
+ if (!f.getAbsolutePath().endsWith(".class")) return;
+ String name = f.getAbsolutePath().substring(getAbsolutePath().length() + 5);
+ name = name.substring(0, name.length() - ".class".length()).replace(File.separatorChar, '.');
+ dispatch(conn, name);
+ } else {
+ String[] list = f.list();
+ for(int i=0; i<list.length; i++) check(conn, new File(f.getAbsolutePath() + File.separatorChar + list[i]));
+ }
+ }
+ void dispatch(final Connection conn, String name) throws Exception {
+ //Log.info(this, "attempting class " + name);
+ try {
+ Class c = getClassLoader().loadClass(name);
+ if (c != null) {
+ if (Listener.class.isAssignableFrom(c)) {
+ Log.error(this, "dispatching connection on port " + port + " to " +
+ c.getName());
+ final Listener l = (Listener)c.newInstance();
+ new Thread() { public void run() {
+ Log.clearnotes();
+ try {
+ l.accept(conn);
+ } catch (Exception e) {
+ Log.error(l.getClass(), "Listener threw exception");
+ Log.error(l.getClass(), e);
+ } finally {
+ conn.close();
+ }
+ } }.start();
+ return;
+ }
+ }
+ } catch (Exception e) {
+ Log.error(this, e);
+ }
+ }
+
+ private class PortThread extends Thread {
+ public void run() {
+ try {
+ Log.warn(this, "Now listening on interface " + iface + ", port " + port);
+ ServerSocket ss = new ServerSocket(port);
+ for(Socket s = ss.accept(); ; s = ss.accept()) try {
+ Log.warn(this, "accepted connection on port " + port);
+ dispatch(new Connection(s, "megacz.com"));
+ Log.warn(this, "done searching for service on port " + port);
+ } catch (Exception e) { Log.warn(Port.class, e); }
+ } catch (Exception e) { Log.error(Port.class, e);
+ } catch (Throwable t) {
+ Log.error(this, "serious error, aborting VM");
+ Log.error(this, t);
+ Root.reboot();
+ }
+ }
+ }
+}
--- /dev/null
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+import java.net.*;
+
+public class Root extends Loader {
+
+ private final Host host;
+ private final Watched port;
+
+ public Root(String path) {
+ super(path);
+ host = new Host(path + File.separatorChar + "host", null);
+ port = new PortDir(path + File.separatorChar + "port");
+ }
+
+ public Watched slash(String part) {
+ if (part.equals("host")) return host;
+ if (part.equals("port")) return port;
+ if (part.equals("LIB")) return super.slash(part);
+ return null;
+ }
+
+ public static void reboot() {
+ Log.flush();
+ System.exit(0);
+ }
+
+ public void changed(Watched w) {
+ if (w.part.equals("host")) {
+ Log.warn(this, "/host changed");
+ } else if (w.part.equals("port")) {
+ Log.warn(this, "/port changed");
+ } else if (w.getAbsolutePath().startsWith("/jinetd/LIB/")) {
+ if (w.lastModifiedAtLastScan != -1) {
+ Log.error(this, "jinetd upgraded; bouncing the JVM....");
+ reboot();
+ }
+ } else {
+ Log.info(this, "unknown directory " + w.part + " changed");
+ }
+ }
+
+ private static class PortDir extends Watched {
+ public PortDir(String path) { super(path); }
+ public Watched slash(String part) {
+ String ipaddr = part.indexOf(':') == -1 ? null : part.substring(0, part.indexOf(':'));
+ String portnum = part.indexOf(':') == -1 ? part : part.substring(part.indexOf(':') + 1);
+ try {
+ return new Port(this.path + File.separatorChar + part,
+ ipaddr == null ? null : NetworkInterface.getByInetAddress(InetAddress.getByName(ipaddr)),
+ portnum.equals("*") ? 0 : Integer.parseInt(portnum));
+ } catch (UnknownHostException e) { Log.warn(this, "can't resolve host for port directory: " + part);
+ } catch (NumberFormatException e) { Log.warn(this, "invalid port directory: " + part);
+ } catch (Exception e) { Log.warn(this, "error instantiating Port: " + part);
+ }
+ return null;
+ }
+ }
+
+}
--- /dev/null
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+
+public class Watched extends File {
+
+ // Instance //////////////////////////////////////////////////////////////////////////////
+
+ private Hashtable cache = new Hashtable();
+ long lastModifiedAtLastScan = -1;
+ public final String path;
+ public final String part;
+
+ public Watcher watcher() { return ((Watched)all.get(getParent())).watcher(); }
+ public Watched slash(String part) { return get(this.path + File.separatorChar + part); }
+ public void scan() throws IOException {
+ if (!exists()) { return; }
+ if (lastModifiedAtLastScan != lastModified()) { watcher().changed(this); lastModifiedAtLastScan = lastModified(); }
+ if (!isDirectory()) { return; }
+ Vec removals = new Vec();
+ for(Iterator i = cache.values().iterator(); i.hasNext();) {
+ Watched w = ((Watched)i.next());
+ if (w.exists()) w.scan();
+ else { watcher().changed(w); removals.addElement(w.path.substring(this.path.length() + 1)); }
+ }
+ for(int i=0; i<removals.size(); i++) cache.remove(removals.elementAt(i));
+ String[] kids = list();
+ if (kids == null) return;
+ for(int i=0; i<kids.length; i++) {
+ if (cache.get(kids[i]) != null) continue;
+ Watched kid = slash(kids[i]);
+ if (kid == null) continue;
+ cache.put(kids[i], kid);
+ watcher().changed(kid);
+ }
+ }
+
+
+ // Pooling //////////////////////////////////////////////////////////////////////////////
+
+ private static WeakHashMap all = new WeakHashMap();
+ protected Watched(String path) {
+ super(path);
+ this.path = path;
+ this.part = path.substring(path.lastIndexOf(File.separatorChar) + 1);
+ all.put(path, this);
+ }
+ private static Watched get(String path) {
+ Watched ret = (Watched)all.get(path);
+ if (ret == null) ret = new Watched(path);
+ return ret;
+ }
+}
--- /dev/null
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import java.io.*;
+import java.util.*;
+
+public abstract class Watcher extends Watched {
+ protected Watcher(String path) { super(path); }
+ public abstract void changed(Watched w);
+ public Watcher watcher() { return this; }
+}
--- /dev/null
+package org.ibex.jinetd;
+import org.ibex.util.*;
+import org.ibex.io.*;
+import org.ibex.net.*;
+import java.net.*;
+import java.io.*;
+import java.util.*;
+
+public interface Worker {
+ public void handleRequest(Connection c);
+}
+
+
+
+