package org.ibex.nestedvm;
import org.ibex.nestedvm.util.*;
+// FEATURE: This is ugly, this stuff needs to be in org.ibex.util or something
+import org.ibex.classgen.util.Sort;
import java.io.*;
import java.util.*;
import java.net.*;
public final int getPid() { return pid; }
private static final GlobalState defaultGS = new GlobalState();
- private GlobalState gs = defaultGS;
+ private GlobalState gs;
public void setGlobalState(GlobalState gs) {
if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running");
this.gs = gs;
private Vector activeChildren;
private Vector exitedChildren;
- protected UnixRuntime(int pageSize, int totalPages) {
- super(pageSize,totalPages);
+ protected UnixRuntime(int pageSize, int totalPages) { this(pageSize,totalPages,false); }
+ protected UnixRuntime(int pageSize, int totalPages, boolean exec) {
+ super(pageSize,totalPages,exec);
- // FEATURE: Do the proper mangling for non-unix hosts
- String userdir = getSystemProperty("user.dir");
- cwd =
- userdir != null && userdir.startsWith("/") && File.separatorChar == '/' && getSystemProperty("nestedvm.root") == null
- ? userdir.substring(1) : "";
+ if(!exec) {
+ gs = defaultGS;
+ String userdir = Platform.getProperty("user.dir");
+ String nvroot = Platform.getProperty("nestedvm.root");
+ cwd = "";
+ if(userdir != null && nvroot == null) {
+ if(userdir.startsWith("/") && File.separatorChar == '/') {
+ cwd = userdir.substring(1);
+ } else {
+ Vector vec = new Vector();
+ File root = HostFS.hostRootDir();
+ String s = new File(userdir).getAbsolutePath();
+ File d = new File(s);
+ System.err.println(s);
+ System.err.println(d);
+ while(!d.equals(root)) {
+ System.err.println("Got " + d.getName());
+ vec.addElement(d.getName());
+ if((s = d.getParent()) == null) break;
+ d = new File(s);
+ }
+ if(s != null)
+ for(int i=vec.size()-1;i>=0;i--) cwd += (String) vec.elementAt(i) + (i==0?"":"/");
+ }
+ }
+ }
}
- // NOTE: getDisplayName() is a Java2 function
private static String posixTZ() {
StringBuffer sb = new StringBuffer();
TimeZone zone = TimeZone.getDefault();
int off = zone.getRawOffset() / 1000;
- sb.append(zone.getDisplayName(false,TimeZone.SHORT));
+ sb.append(Platform.timeZoneGetDisplayName(zone,false,false));
if(off > 0) sb.append("-");
else off = -off;
sb.append(off/3600); off = off%3600;
if(off > 0) sb.append(":").append(off/60); off=off%60;
if(off > 0) sb.append(":").append(off);
if(zone.useDaylightTime())
- sb.append(zone.getDisplayName(true,TimeZone.SHORT));
+ sb.append(Platform.timeZoneGetDisplayName(zone,true,false));
return sb.toString();
}
String[] defaults = new String[6];
int n=0;
if(extra == null) extra = new String[0];
- if(!envHas("USER",extra) && getSystemProperty("user.name") != null)
- defaults[n++] = "USER=" + getSystemProperty("user.name");
- if(!envHas("HOME",extra) && getSystemProperty("user.home") != null)
- defaults[n++] = "HOME=" + getSystemProperty("user.home");
+ if(!envHas("USER",extra) && Platform.getProperty("user.name") != null)
+ defaults[n++] = "USER=" + Platform.getProperty("user.name");
+ if(!envHas("HOME",extra) && Platform.getProperty("user.home") != null)
+ defaults[n++] = "HOME=" + Platform.getProperty("user.home");
if(!envHas("SHELL",extra)) defaults[n++] = "SHELL=/bin/sh";
- if(!envHas("TERM",extra)) defaults[n++] = "TERM=vt100";
+ if(!envHas("TERM",extra) && !win32Hacks) defaults[n++] = "TERM=vt100";
if(!envHas("TZ",extra)) defaults[n++] = "TZ=" + posixTZ();
if(!envHas("PATH",extra)) defaults[n++] = "PATH=/usr/local/bin:/usr/bin:/bin:/usr/local/sbin:/usr/sbin:/sbin";
String[] env = new String[extra.length+n];
synchronized(parent.children) {
int i = parent.activeChildren.indexOf(prev);
if(i == -1) throw new Error("should never happen");
- parent.activeChildren.set(i,this);
+ parent.activeChildren.setElementAt(this,i);
}
} else {
int newpid = -1;
return parent == null ? 1 : parent.pid;
}
+ // FEATURE: Signal handling
+ // check flag only on backwards jumps to basic blocks without compulsatory checks
+ // (see A Portable Research Framework for the Execution of Java Bytecode - Etienne Gagnon, Chapter 2)
+
/** The kill syscall.
SIGSTOP, SIGTSTO, SIGTTIN, and SIGTTOUT pause the process.
SIGCONT, SIGCHLD, SIGIO, and SIGWINCH are ignored.
case 18: // SIGTSTP
case 21: // SIGTTIN
case 22: // SIGTTOU
- state = PAUSED;
- break;
case 19: // SIGCONT
case 20: // SIGCHLD
case 23: // SIGIO
case 28: // SIGWINCH
break;
default:
- // FEATURE: This is ugly, make a clean interface to sys_exit
- return syscall(SYS_exit,128+signal,0,0,0,0,0);
+ exit(128+signal, true);
}
return 0;
}
synchronized(children) {
for(;;) {
if(pid == -1) {
- if(exitedChildren.size() > 0) done = (UnixRuntime)exitedChildren.remove(exitedChildren.size() - 1);
+ if(exitedChildren.size() > 0) {
+ done = (UnixRuntime)exitedChildren.elementAt(exitedChildren.size() - 1);
+ exitedChildren.removeElementAt(exitedChildren.size() - 1);
+ }
} else if(pid > 0) {
UnixRuntime t = gs.tasks[pid];
if(t.parent != this) return -ECHILD;
if(t.state == EXITED) {
- if(!exitedChildren.remove(t)) throw new Error("should never happen");
+ if(!exitedChildren.removeElement(t)) throw new Error("should never happen");
done = t;
}
} else {
UnixRuntime child = (UnixRuntime) e.nextElement();
gs.tasks[child.pid] = null;
}
- exitedChildren.clear();
+ exitedChildren.removeAllElements();
for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) {
UnixRuntime child = (UnixRuntime) e.nextElement();
child.parent = null;
}
- activeChildren.clear();
+ activeChildren.removeAllElements();
}
UnixRuntime _parent = parent;
if(parent == null) {
gs.tasks[pid] = null;
} else {
- if(!parent.activeChildren.remove(this)) throw new Error("should never happen _exited: pid: " + pid);
- parent.exitedChildren.add(this);
+ if(!parent.activeChildren.removeElement(this)) throw new Error("should never happen _exited: pid: " + pid);
+ parent.exitedChildren.addElement(this);
parent.children.notify();
}
}
activeChildren = new Vector();
exitedChildren = new Vector();
}
- activeChildren.add(r);
+ activeChildren.addElement(r);
state.r[V0] = 0; // return 0 to child
state.pc += 4; // skip over syscall instruction
if(o instanceof Class) {
Class c = (Class) o;
try {
- return exec((UnixRuntime) c.newInstance(),argv,envp);
+ UnixRuntime r = (UnixRuntime) c.getDeclaredConstructor(new Class[]{Boolean.TYPE}).newInstance(new Object[]{Boolean.TRUE});
+ return exec(r,argv,envp);
} catch(Exception e) {
e.printStackTrace();
return -ENOEXEC;
return 0;
}
- // FEATURE: Make sure fstat info is correct
- // FEATURE: This could be faster if we did direct copies from each process' memory
- // FEATURE: Check this logic one more time
- public static class Pipe {
+ static class Pipe {
private final byte[] pipebuf = new byte[PIPE_BUF*4];
private int readPos;
private int writePos;
public final FD writer = new Writer();
public class Reader extends FD {
- protected FStat _fstat() { return new FStat(); }
+ protected FStat _fstat() { return new SocketFStat(); }
public int read(byte[] buf, int off, int len) throws ErrnoException {
if(len == 0) return 0;
synchronized(Pipe.this) {
return len;
}
}
+ public int flags() { return O_RDONLY; }
public void _close() { synchronized(Pipe.this) { readPos = -1; Pipe.this.notify(); } }
}
public class Writer extends FD {
- protected FStat _fstat() { return new FStat(); }
+ protected FStat _fstat() { return new SocketFStat(); }
public int write(byte[] buf, int off, int len) throws ErrnoException {
if(len == 0) return 0;
synchronized(Pipe.this) {
return len;
}
}
+ public int flags() { return O_WRONLY; }
public void _close() { synchronized(Pipe.this) { writePos = -1; Pipe.this.notify(); } }
}
}
return n;
}
+ // FEATURE: UDP is totally broken
+
static class SocketFD extends FD {
public static final int TYPE_STREAM = 0;
public static final int TYPE_DGRAM = 1;
public void setOptions() {
try {
if(o != null && type() == TYPE_STREAM && !listen()) {
- ((Socket)o).setKeepAlive((options & SO_KEEPALIVE) != 0);
+ Platform.socketSetKeepAlive((Socket)o,(options & SO_KEEPALIVE) != 0);
}
} catch(SocketException e) {
if(STDERR_DIAG) e.printStackTrace();
throw new ErrnoException(EIO);
}
} else {
+ if(off != 0) throw new IllegalArgumentException("off must be 0");
DatagramSocket ds = (DatagramSocket) o;
- dp.setData(a,off,length);
+ dp.setData(a);
+ dp.setLength(length);
try {
ds.receive(dp);
} catch(IOException e) {
throw new ErrnoException(EIO);
}
} else {
+ if(off != 0) throw new IllegalArgumentException("off must be 0");
DatagramSocket ds = (DatagramSocket) o;
- dp.setData(a,off,length);
+ dp.setData(a);
+ dp.setLength(length);
try {
ds.send(dp);
} catch(IOException e) {
}
}
- // FEATURE: Check that these are correct
- public int flags() {
- if(is != null && os != null) return O_RDWR;
- if(is != null) return O_RDONLY;
- if(os != null) return O_WRONLY;
- return 0;
- }
-
- // FEATURE: Populate this properly
- public FStat _fstat() { return new FStat(); }
+ public int flags() { return O_RDWR; }
+ public FStat _fstat() { return new SocketFStat(); }
}
private int sys_socket(int domain, int type, int proto) {
InetAddress inetAddr;
try {
- inetAddr = InetAddress.getByAddress(ip);
+ inetAddr = Platform.inetAddressFromBytes(ip);
} catch(UnknownHostException e) {
return -EADDRNOTAVAIL;
}
break;
}
case SocketFD.TYPE_DGRAM: {
- DatagramSocket s = (DatagramSocket) fd.o;
- if(s == null) s = new DatagramSocket();
- s.connect(inetAddr,port);
+ if(fd.dp == null) fd.dp = new DatagramPacket(null,0);
+ fd.dp.setAddress(inetAddr);
+ fd.dp.setPort(port);
break;
}
default:
copyin(addr+4,ip,4);
try {
- inetAddr = InetAddress.getByAddress(ip);
+ inetAddr = Platform.inetAddressFromBytes(ip);
} catch(UnknownHostException e) {
return -EADDRNOTAVAIL;
}
Socket s = (Socket) fd.o;
try {
- if(how == SHUT_RD || how == SHUT_RDWR) s.shutdownInput();
- if(how == SHUT_WR || how == SHUT_RDWR) s.shutdownOutput();
+ if(how == SHUT_RD || how == SHUT_RDWR) Platform.socketHalfClose(s,false);
+ if(how == SHUT_WR || how == SHUT_RDWR) Platform.socketHalfClose(s,true);
} catch(IOException e) {
return -EIO;
}
}
return 0;
}
-
-
- /*public int sys_opensocket(int cstring, int port) throws FaultException, ErrnoException {
- String hostname = cstring(cstring);
- try {
- FD fd = new SocketFD(new Socket(hostname,port));
- int n = addFD(fd);
- if(n == -1) fd.close();
- return n;
- } catch(IOException e) {
- return -EIO;
- }
- }
-
- private static class ListenSocketFD extends FD {
- ServerSocket s;
- public ListenSocketFD(ServerSocket s) { this.s = s; }
- public int flags() { return 0; }
- // FEATURE: What should these be?
- public FStat _fstat() { return new FStat(); }
- public void _close() { try { s.close(); } catch(IOException e) { } }
- }
-
- public int sys_listensocket(int port) {
- try {
- ListenSocketFD fd = new ListenSocketFD(new ServerSocket(port));
- int n = addFD(fd);
- if(n == -1) fd.close();
- return n;
- } catch(IOException e) {
- return -EIO;
- }
- }
-
- public int sys_accept(int fdn) {
- if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD;
- if(fds[fdn] == null) return -EBADFD;
- if(!(fds[fdn] instanceof ListenSocketFD)) return -EBADFD;
- try {
- ServerSocket s = ((ListenSocketFD)fds[fdn]).s;
- SocketFD fd = new SocketFD(s.accept());
- int n = addFD(fd);
- if(n == -1) fd.close();
- return n;
- } catch(IOException e) {
- return -EIO;
- }
- }*/
// FEATURE: Run through the fork/wait stuff one more time
public static class GlobalState {
}
}
- private static class MP implements Comparable {
+ private static class MP implements Sort.Comparable {
public MP(String path, FS fs) { this.path = path; this.fs = fs; }
public String path;
public FS fs;
MP[] newMPS = new MP[oldLength + 1];
if(oldLength != 0) System.arraycopy(mps,0,newMPS,0,oldLength);
newMPS[oldLength] = new MP(path,fs);
- Arrays.sort(newMPS);
+ Sort.sort(newMPS);
mps = newMPS;
int highdevno = 0;
for(int i=0;i<mps.length;i++) highdevno = max(highdevno,mps[i].fs.devno);
mps = newMPS;
}
+ // FEATURE: We shouldn't need to special case the root dir, it should work in the MP array
private Object fsop(int op, UnixRuntime r, String normalizedPath, int arg1, int arg2) throws ErrnoException {
int pl = normalizedPath.length();
if(pl != 0) {
for(int i=0;i<list.length;i++) {
MP mp = list[i];
int mpl = mp.path.length();
- if(normalizedPath.startsWith(mp.path) && (pl == mpl || (pl < mpl && normalizedPath.charAt(mpl) == '/')))
+ if(normalizedPath.startsWith(mp.path) && (pl == mpl || normalizedPath.charAt(mpl) == '/'))
return dispatch(mp.fs,op,r,pl == mpl ? "" : normalizedPath.substring(mpl+1),arg1,arg2);
}
}
return dispatch(root,op,r,normalizedPath,arg1,arg2);
}
+ // FEATURE: move this into FS so some filesystem can override it directly (devfs)
private static Object dispatch(FS fs, int op, UnixRuntime r, String path, int arg1, int arg2) throws ErrnoException {
switch(op) {
case OPEN: return fs.open(r,path,arg1,arg2);
}
public synchronized Object exec(UnixRuntime r, String path) throws ErrnoException {
- // FIXME: Hideous hack to make a standalone busybox possible
- if(path.equals("bin/busybox") && Boolean.valueOf(getSystemProperty("nestedvm.busyboxhack")).booleanValue())
- return r.getClass();
+ // HACK: Hideous hack to make a standalone busybox possible
+ if(path.equals("bin/busybox") && r.getClass().getName().endsWith("BusyBox")) return r.getClass();
+
FStat fstat = stat(r,path);
if(fstat == null) return null;
long mtime = fstat.mtime();
// its an elf binary
try {
s.seek(0);
- Class c = RuntimeCompiler.compile(s);
+ Class c = RuntimeCompiler.compile(s,"unixruntime");
//System.err.println("Compile succeeded: " + c);
ent = new CacheEnt(mtime,size,c);
} catch(Compiler.Exn e) {
public abstract void unlink(UnixRuntime r, String path) throws ErrnoException;
}
- // FEATURE: chroot support in here
+ // chroot support should go in here if it is ever implemented chroot support in here
private String normalizePath(String path) {
boolean absolute = path.startsWith("/");
int cwdl = cwd.length();
+
// NOTE: This isn't just a fast path, it handles cases the code below doesn't
if(!path.startsWith(".") && path.indexOf("./") == -1 && path.indexOf("//") == -1 && !path.endsWith("."))
return absolute ? path.substring(1) : cwdl == 0 ? path : path.length() == 0 ? cwd : cwd + "/" + path;
cwd.getChars(0,cwdl,out,0);
outp = cwdl;
}
-
+
path.getChars(0,path.length(),in,0);
while(in[inp] != 0) {
- if(inp != 0 || cwdl==0) {
- if(in[inp] != '/') { out[outp++] = in[inp++]; continue; }
+ if(inp != 0) {
+ while(in[inp] != 0 && in[inp] != '/') { out[outp++] = in[inp++]; }
+ if(in[inp] == '\0') break;
while(in[inp] == '/') inp++;
}
- if(in[inp] == '\0') continue;
+
+ // Just read a /
+ if(in[inp] == '\0') break;
if(in[inp] != '.') { out[outp++] = '/'; out[outp++] = in[inp++]; continue; }
+ // Just read a /.
if(in[inp+1] == '\0' || in[inp+1] == '/') { inp++; continue; }
if(in[inp+1] == '.' && (in[inp+2] == '\0' || in[inp+2] == '/')) { // ..
+ // Just read a /..{$,/}
inp += 2;
if(outp > 0) outp--;
while(outp > 0 && out[outp] != '/') outp--;
//System.err.println("After ..: " + new String(out,0,outp));
continue;
}
+ // Just read a /.[^.] or /..[^/$]
inp++;
out[outp++] = '/';
out[outp++] = '.';
protected File root;
public File getRoot() { return root; }
- private static File hostRootDir() {
- if(getSystemProperty("nestedvm.root") != null) {
- File f = new File(getSystemProperty("nestedvm.root"));
+ static File hostRootDir() {
+ if(Platform.getProperty("nestedvm.root") != null) {
+ File f = new File(Platform.getProperty("nestedvm.root"));
if(f.isDirectory()) return f;
// fall through to case below
}
- String cwd = getSystemProperty("user.dir");
+ String cwd = Platform.getProperty("user.dir");
File f = new File(cwd != null ? cwd : ".");
if(!f.exists()) throw new Error("Couldn't get File for cwd");
f = new File(f.getAbsolutePath());
while(f.getParent() != null) f = new File(f.getParent());
+ // HACK: This works around a bug in some versions of ClassPath
+ if(f.getPath().length() == 0) f = new File("/");
return f;
}
public FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException {
- // FIXME: horrendous, ugly hack needed by TeX... sorry Brian...
- path = path.trim();
final File f = hostFile(path);
return r.hostFSOpen(f,flags,mode,this);
}
if(r.sm != null && !r.sm.allowWrite(f)) throw new ErrnoException(EACCES);
if(f.exists() && f.isDirectory()) throw new ErrnoException(EEXIST);
if(f.exists()) throw new ErrnoException(ENOTDIR);
- File parent = f.getParentFile();
+ File parent = getParentFile(f);
if(parent!=null && (!parent.exists() || !parent.isDirectory())) throw new ErrnoException(ENOTDIR);
if(!f.mkdir()) throw new ErrnoException(EIO);
}
+ private static File getParentFile(File f) {
+ String p = f.getParent();
+ return p == null ? null : new File(f,p);
+ }
+
public class HostDirFD extends DirFD {
private final File f;
private final File[] children;
- public HostDirFD(File f) { this.f = f; children = f.listFiles(); }
+ public HostDirFD(File f) {
+ this.f = f;
+ String[] l = f.list();
+ children = new File[l.length];
+ for(int i=0;i<l.length;i++) children[i] = new File(f,l[i]);
+ }
public int size() { return children.length; }
public String name(int n) { return children[n].getName(); }
public int inode(int n) { return inodes.get(children[n].getAbsolutePath()); }
public int parentInode() {
- File parent = f.getParentFile();
+ File parent = getParentFile(f);
return parent == null ? -1 : inodes.get(parent.getAbsolutePath());
}
public int myInode() { return inodes.get(f.getAbsolutePath()); }
protected abstract String name(int n);
protected abstract int inode(int n);
protected abstract int myDev();
- protected int parentInode() { return -1; }
- protected int myInode() { return -1; }
-
+ protected abstract int parentInode();
+ protected abstract int myInode();
+ public int flags() { return O_RDONLY; }
+
public int getdents(byte[] buf, int off, int len) {
int ooff = off;
int ino;
private static final int FD_INODE = 4;
private static final int FD_INODES = 32;
- private class DevFStat extends FStat {
+ private abstract class DevFStat extends FStat {
public int dev() { return devno; }
public int mode() { return 0666; }
public int type() { return S_IFCHR; }
public int nlink() { return 1; }
+ public abstract int inode();
}
private abstract class DevDirFD extends DirFD {
}
private FD devZeroFD = new FD() {
- public int read(byte[] a, int off, int length) { Arrays.fill(a,off,off+length,(byte)0); return length; }
+ public int read(byte[] a, int off, int length) {
+ /*Arrays.fill(a,off,off+length,(byte)0);*/
+ for(int i=off;i<off+length;i++) a[i] = 0;
+ return length;
+ }
public int write(byte[] a, int off, int length) { return length; }
public int seek(int n, int whence) { return 0; }
public FStat _fstat() { return new DevFStat(){ public int inode() { return ZERO_INODE; } }; }
+ public int flags() { return O_RDWR; }
};
private FD devNullFD = new FD() {
public int read(byte[] a, int off, int length) { return 0; }
public int write(byte[] a, int off, int length) { return length; }
public int seek(int n, int whence) { return 0; }
public FStat _fstat() { return new DevFStat(){ public int inode() { return NULL_INODE; } }; }
+ public int flags() { return O_RDWR; }
};
public FD open(UnixRuntime r, String path, int mode, int flags) throws ErrnoException {
if(path.startsWith("fd/")) {
int n;
try {
- n = Integer.parseInt(path.substring(4));
+ n = Integer.parseInt(path.substring(3));
} catch(NumberFormatException e) {
return null;
}
if(r.fds[n] == null) return null;
return r.fds[n].fstat();
}
- // FEATURE: inode stuff
- if(path.equals("fd")) return new FStat() { public int type() { return S_IFDIR; } public int mode() { return 0444; }};
- if(path.equals("")) return new FStat() { public int type() { return S_IFDIR; } public int mode() { return 0444; }};
+ if(path.equals("fd")) return new FStat() { public int inode() { return FD_INODE; } public int dev() { return devno; } public int type() { return S_IFDIR; } public int mode() { return 0444; }};
+ if(path.equals("")) return new FStat() { public int inode() { return ROOT_INODE; } public int dev() { return devno; } public int type() { return S_IFDIR; } public int mode() { return 0444; }};
return null;
}