import java.io.*;
import java.util.*;
-// FIXME: Make plain old "mips-unknown-elf-gcc -o foo foo.c" work (modify spec file or whatever)
-
-// FEATURE: Remove System.{out,err}.printlns and throw Errors where applicable
-
public abstract class UnixRuntime extends Runtime implements Cloneable {
/** The pid of this "process" */
private int pid;
private UnixRuntime parent;
public final int getPid() { return pid; }
- private static final GlobalState defaultGD = new GlobalState();
- private GlobalState gd = defaultGD;
+ private static final GlobalState defaultGS = new GlobalState();
+ private GlobalState gs = defaultGS;
+ public void setGlobalState(GlobalState gs) {
+ if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running");
+ this.gs = gs;
+ }
/** proceses' current working directory - absolute path WITHOUT leading slash
"" = root, "bin" = /bin "usr/bin" = /usr/bin */
// FEATURE: Do the proper mangling for non-unix hosts
String userdir = getSystemProperty("user.dir");
- cwd = userdir != null && userdir.startsWith("/") && File.separatorChar == '/' ? userdir.substring(1) : "";
+ cwd =
+ userdir != null && userdir.startsWith("/") && File.separatorChar == '/' && HostFS.hostRootDir().getParent() == null
+ ? userdir.substring(1) : "";
}
// NOTE: getDisplayName() is a Java2 function
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.name") != null)
+ if(!envHas("HOME",extra) && getSystemProperty("user.home") != null)
defaults[n++] = "HOME=" + getSystemProperty("user.home");
if(!envHas("SHELL",extra)) defaults[n++] = "SHELL=/bin/sh";
if(!envHas("TERM",extra)) defaults[n++] = "TERM=vt100";
private static class ProcessTableFullExn extends RuntimeException { }
void _started() {
- UnixRuntime[] tasks = gd.tasks;
- synchronized(gd) {
+ UnixRuntime[] tasks = gs.tasks;
+ synchronized(gs) {
if(pid != 0) {
if(tasks[pid] == null || tasks[pid].pid != pid) throw new Error("should never happen");
} else {
int newpid = -1;
- int nextPID = gd.nextPID;
- for(int i=nextPID;i<tasks.length;i++) if(tasks[i] == null) { newpid = i; break; }
- if(newpid == -1) for(int i=1;i<nextPID;i++) if(tasks[i] == null) { newpid = i; break; }
- if(newpid == -1) throw new ProcessTableFullExn();
- pid = newpid;
- gd.nextPID = newpid + 1;
+ int nextPID = gs.nextPID;
+ for(int i=nextPID;i<tasks.length;i++) if(tasks[i] == null) { newpid = i; break; }
+ if(newpid == -1) for(int i=1;i<nextPID;i++) if(tasks[i] == null) { newpid = i; break; }
+ if(newpid == -1) throw new ProcessTableFullExn();
+ pid = newpid;
+ gs.nextPID = newpid + 1;
}
tasks[pid] = this;
}
case SYS_fork: return sys_fork();
case SYS_pipe: return sys_pipe(a);
case SYS_dup2: return sys_dup2(a,b);
+ case SYS_dup: return sys_dup(a);
case SYS_waitpid: return sys_waitpid(a,b,c);
case SYS_stat: return sys_stat(a,b);
case SYS_lstat: return sys_lstat(a,b);
}
FD _open(String path, int flags, int mode) throws ErrnoException {
- return gd.open(this,normalizePath(path),flags,mode);
+ return gs.open(this,normalizePath(path),flags,mode);
}
/** The kill syscall.
}
private int sys_waitpid(int pid, int statusAddr, int options) throws FaultException, ErrnoException {
- System.err.println("PID: " + this.pid + " is waiting on " + pid);
final int WNOHANG = 1;
if((options & ~(WNOHANG)) != 0) return -EINVAL;
if(pid == 0 || pid < -1) {
- System.err.println("WARNING: waitpid called with a pid of " + pid);
+ if(STDERR_DIAG) System.err.println("WARNING: waitpid called with a pid of " + pid);
return -ECHILD;
}
boolean blocking = (options&WNOHANG)==0;
- if(pid !=-1 && (pid <= 0 || pid >= gd.tasks.length)) return -ECHILD;
+ if(pid !=-1 && (pid <= 0 || pid >= gs.tasks.length)) return -ECHILD;
if(children == null) return blocking ? -ECHILD : 0;
UnixRuntime done = null;
if(pid == -1) {
if(exitedChildren.size() > 0) done = (UnixRuntime)exitedChildren.remove(exitedChildren.size() - 1);
} else if(pid > 0) {
- UnixRuntime t = gd.tasks[pid];
+ 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");
}
} else {
// process group stuff, EINVAL returned above
- throw new Error("should never happen");
+ throw new Error("should never happen");
}
if(done == null) {
if(!blocking) return 0;
try { children.wait(); } catch(InterruptedException e) {}
- System.err.println("waitpid woke up: " + exitedChildren.size());
+ //System.err.println("waitpid woke up: " + exitedChildren.size());
} else {
- gd.tasks[done.pid] = null;
+ gs.tasks[done.pid] = null;
break;
}
}
if(children != null) synchronized(children) {
for(Enumeration e = exitedChildren.elements(); e.hasMoreElements(); ) {
UnixRuntime child = (UnixRuntime) e.nextElement();
- gd.tasks[child.pid] = null;
+ gs.tasks[child.pid] = null;
}
exitedChildren.clear();
for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) {
UnixRuntime _parent = parent;
if(_parent == null) {
- gd.tasks[pid] = null;
+ gs.tasks[pid] = null;
} else {
synchronized(_parent.children) {
if(parent == null) {
- gd.tasks[pid] = null;
+ gs.tasks[pid] = null;
} else {
parent.activeChildren.remove(this);
parent.exitedChildren.add(this);
}
protected Object clone() throws CloneNotSupportedException {
- UnixRuntime r = (UnixRuntime) super.clone();
+ UnixRuntime r = (UnixRuntime) super.clone();
r.pid = 0;
r.parent = null;
r.children = null;
try {
r._started();
} catch(ProcessTableFullExn e) {
- return -ENOMEM;
+ return -ENOMEM;
}
- System.err.println("fork " + pid + " -> " + r.pid + " tasks[" + r.pid + "] = " + gd.tasks[r.pid]);
+ //System.err.println("fork " + pid + " -> " + r.pid + " tasks[" + r.pid + "] = " + gd.tasks[r.pid]);
if(children == null) {
children = new Object();
activeChildren = new Vector();
public static int runAndExec(UnixRuntime r, String[] argv) { r.start(argv); return executeAndExec(r); }
public static int executeAndExec(UnixRuntime r) {
- for(;;) {
+ for(;;) {
for(;;) {
if(r.execute()) break;
- System.err.println("WARNING: Pause requested while executing runAndExec()");
+ if(STDERR_DIAG) System.err.println("WARNING: Pause requested while executing runAndExec()");
}
if(r.state != EXECED) return r.exitStatus();
r = r.execedRuntime;
}
private String[] readStringArray(int addr) throws ReadFaultException {
- int count = 0;
+ int count = 0;
for(int p=addr;memRead(p) != 0;p+=4) count++;
String[] a = new String[count];
for(int i=0,p=addr;i<count;i++,p+=4) a[i] = cstring(memRead(p));
}
private int sys_exec(int cpath, int cargv, int cenvp) throws ErrnoException, FaultException {
- return exec(normalizePath(cstring(cpath)),readStringArray(cargv),readStringArray(cenvp));
+ return exec(normalizePath(cstring(cpath)),readStringArray(cargv),readStringArray(cenvp));
}
private int exec(String normalizedPath, String[] argv, String[] envp) throws ErrnoException {
if(argv.length == 0) argv = new String[]{""};
- Object o = gd.exec(this,normalizedPath);
+ Object o = gs.exec(this,normalizedPath);
if(o == null) return -ENOENT;
if(o instanceof Class) {
Class c = (Class) o;
try {
- return exec((UnixRuntime) c.newInstance(),argv,envp);
+ return exec((UnixRuntime) c.newInstance(),argv,envp);
} catch(Exception e) {
- e.printStackTrace();
+ e.printStackTrace();
return -ENOEXEC;
}
} else {
private int exec(UnixRuntime r, String[] argv, String[] envp) {
- System.err.println("Execing " + r);
+ //System.err.println("Execing " + r);
for(int i=0;i<OPEN_MAX;i++) if(closeOnExec[i]) closeFD(i);
r.fds = fds;
r.closeOnExec = closeOnExec;
fds = null;
closeOnExec = null;
- r.gd = gd;
+ r.gs = gs;
r.sm = sm;
r.cwd = cwd;
r.pid = pid;
return 0;
}
- // FEATURE: Use custom PipeFD - be sure to support PIPE_BUF of data
- private int sys_pipe(int addr) throws FaultException {
- PipedOutputStream writerStream = new PipedOutputStream();
- PipedInputStream readerStream;
- try {
- readerStream = new PipedInputStream(writerStream);
- } catch(IOException e) {
- return -EIO;
+ // 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 {
+ private final byte[] pipebuf = new byte[PIPE_BUF*4];
+ private int readPos;
+ private int writePos;
+
+ public final FD reader = new Reader();
+ public final FD writer = new Writer();
+
+ public class Reader extends FD {
+ protected FStat _fstat() { return new FStat(); }
+ public int read(byte[] buf, int off, int len) throws ErrnoException {
+ if(len == 0) return 0;
+ synchronized(Pipe.this) {
+ while(writePos != -1 && readPos == writePos) {
+ try { Pipe.this.wait(); } catch(InterruptedException e) { /* ignore */ }
+ }
+ if(writePos == -1) return 0; // eof
+ len = Math.min(len,writePos-readPos);
+ System.arraycopy(pipebuf,readPos,buf,off,len);
+ readPos += len;
+ if(readPos == writePos) Pipe.this.notify();
+ return len;
+ }
+ }
+ public void _close() { synchronized(Pipe.this) { readPos = -1; Pipe.this.notify(); } }
}
- FD reader = new InputStreamFD(readerStream);
- FD writer = new OutputStreamFD(writerStream);
- int fd1 = addFD(reader);
+
+ public class Writer extends FD {
+ protected FStat _fstat() { return new FStat(); }
+ public int write(byte[] buf, int off, int len) throws ErrnoException {
+ if(len == 0) return 0;
+ synchronized(Pipe.this) {
+ if(readPos == -1) throw new ErrnoException(EPIPE);
+ if(pipebuf.length - writePos < Math.min(len,PIPE_BUF)) {
+ // not enough space to atomicly write the data
+ while(readPos != -1 && readPos != writePos) {
+ try { Pipe.this.wait(); } catch(InterruptedException e) { /* ignore */ }
+ }
+ if(readPos == -1) throw new ErrnoException(EPIPE);
+ readPos = writePos = 0;
+ }
+ len = Math.min(len,pipebuf.length - writePos);
+ System.arraycopy(buf,off,pipebuf,writePos,len);
+ if(readPos == writePos) Pipe.this.notify();
+ writePos += len;
+ return len;
+ }
+ }
+ public void _close() { synchronized(Pipe.this) { writePos = -1; Pipe.this.notify(); } }
+ }
+ }
+
+ private int sys_pipe(int addr) {
+ Pipe pipe = new Pipe();
+
+ int fd1 = addFD(pipe.reader);
if(fd1 < 0) return -ENFILE;
- int fd2 = addFD(writer);
+ int fd2 = addFD(pipe.writer);
if(fd2 < 0) { closeFD(fd1); return -ENFILE; }
+
try {
memWrite(addr,fd1);
memWrite(addr+4,fd2);
return 0;
}
+ private int sys_dup(int oldd) {
+ if(oldd < 0 || oldd >= OPEN_MAX) return -EBADFD;
+ if(fds[oldd] == null) return -EBADFD;
+ FD fd = fds[oldd].dup();
+ int newd = addFD(fd);
+ if(newd < 0) { fd.close(); return -ENFILE; }
+ return newd;
+ }
+
private int sys_stat(int cstring, int addr) throws FaultException, ErrnoException {
- FStat s = gd.stat(this,normalizePath(cstring(cstring)));
+ FStat s = gs.stat(this,normalizePath(cstring(cstring)));
if(s == null) return -ENOENT;
return stat(s,addr);
}
private int sys_lstat(int cstring, int addr) throws FaultException, ErrnoException {
- FStat s = gd.lstat(this,normalizePath(cstring(cstring)));
+ FStat s = gs.lstat(this,normalizePath(cstring(cstring)));
if(s == null) return -ENOENT;
return stat(s,addr);
}
private int sys_mkdir(int cstring, int mode) throws FaultException, ErrnoException {
- gd.mkdir(this,normalizePath(cstring(cstring)),mode);
+ gs.mkdir(this,normalizePath(cstring(cstring)),mode);
return 0;
}
private int sys_chdir(int addr) throws ErrnoException, FaultException {
String path = normalizePath(cstring(addr));
- System.err.println("Chdir: " + cstring(addr) + " -> " + path + " pwd: " + cwd);
- if(gd.stat(this,path).type() != FStat.S_IFDIR) return -ENOTDIR;
+ //System.err.println("Chdir: " + cstring(addr) + " -> " + path + " pwd: " + cwd);
+ if(gs.stat(this,path).type() != FStat.S_IFDIR) return -ENOTDIR;
cwd = path;
- System.err.println("Now: [" + cwd + "]");
+ //System.err.println("Now: [" + cwd + "]");
return 0;
}
public GlobalState(int maxProcs, boolean defaultMounts) {
tasks = new UnixRuntime[maxProcs+1];
if(defaultMounts) {
- addMount("/",new HostFS());
+ addMount("/",new HostFS());
addMount("/dev",new DevFS());
}
}
public synchronized void removeMount(String path) {
if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /");
if(path.equals("/")) {
- removeMount(-1);
+ removeMount(-1);
} else {
path = path.substring(1);
int p;
for(p=0;p<mps.length;p++) if(mps[p].path.equals(path)) break;
- if(p == mps.length) throw new Error("mount point doesn't exist");
- removeMount(p);
+ if(p == mps.length) throw new IllegalArgumentException("mount point doesn't exist");
+ removeMount(p);
}
}
private Object fsop(int op, UnixRuntime r, String normalizedPath, int arg1, int arg2) throws ErrnoException {
int pl = normalizedPath.length();
if(pl != 0) {
- MP[] list;
+ MP[] list;
synchronized(this) { list = mps; }
for(int i=0;i<list.length;i++) {
- MP mp = list[i];
- int mpl = mp.path.length();
+ MP mp = list[i];
+ int mpl = mp.path.length();
if(normalizedPath.startsWith(mp.path) && (pl == mpl || (pl < mpl && normalizedPath.charAt(mpl) == '/')))
- return dispatch(mp.fs,op,r,pl == mpl ? "" : normalizedPath.substring(mpl+1),arg1,arg2);
+ return dispatch(mp.fs,op,r,pl == mpl ? "" : normalizedPath.substring(mpl+1),arg1,arg2);
}
}
return dispatch(root,op,r,normalizedPath,arg1,arg2);
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);
+ case OPEN: return fs.open(r,path,arg1,arg2);
case STAT: return fs.stat(r,path);
case LSTAT: return fs.lstat(r,path);
case MKDIR: fs.mkdir(r,path,arg1); return null;
long size = fstat.size();
CacheEnt ent = (CacheEnt) execCache.get(path);
if(ent != null) {
- System.err.println("Found cached entry for " + path);
- if(ent.time == mtime && ent.size == size) return ent.o;
- System.err.println("Cache was out of date");
+ //System.err.println("Found cached entry for " + path);
+ if(ent.time == mtime && ent.size == size) return ent.o;
+ //System.err.println("Cache was out of date");
execCache.remove(path);
}
FD fd = open(r,path,RD_ONLY,0);
try {
int n = s.read(buf,0,buf.length);
- if(n == -1) throw new Error("zero length file");
+ if(n == -1) throw new ErrnoException(ENOEXEC);
switch(buf[0]) {
case '\177': // possible ELF
break;
case '#':
if(n == 1) {
- int n2 = s.read(buf,1,buf.length-1);
+ int n2 = s.read(buf,1,buf.length-1);
if(n2 == -1) throw new ErrnoException(ENOEXEC);
n += n2;
}
int p = 2;
n -= 2;
OUTER: for(;;) {
- for(int i=p;i<p+n;i++) if(buf[i] == '\n') { p = i; break OUTER; }
+ for(int i=p;i<p+n;i++) if(buf[i] == '\n') { p = i; break OUTER; }
p += n;
if(p == buf.length) break OUTER;
n = s.read(buf,p,buf.length-p);
for(arg=2;arg<p;arg++) if(buf[arg] == ' ') break;
if(arg < p) {
int cmdEnd = arg;
- while(arg < p && buf[arg] == ' ') arg++;
+ while(arg < p && buf[arg] == ' ') arg++;
command[0] = new String(buf,2,cmdEnd);
command[1] = arg < p ? new String(buf,arg,p-arg) : null;
} else {
- command[0] = new String(buf,2,p-2);
+ command[0] = new String(buf,2,p-2);
}
- System.err.println("command[0]: " + command[0] + " command[1]: " + command[1]);
+ //System.err.println("command[0]: " + command[0] + " command[1]: " + command[1]);
break;
default:
throw new ErrnoException(ENOEXEC);
}
} catch(IOException e) {
fd.close();
- throw new ErrnoException(EIO);
+ throw new ErrnoException(EIO);
}
if(command == null) {
// its an elf binary
try {
s.seek(0);
- Class c = RuntimeCompiler.compile(s);
- System.err.println("Compile succeeded: " + c);
+ Class c = RuntimeCompiler.compile(s);
+ //System.err.println("Compile succeeded: " + c);
ent = new CacheEnt(mtime,size,c);
} catch(Compiler.Exn e) {
+ if(STDERR_DIAG) e.printStackTrace();
throw new ErrnoException(ENOEXEC);
} catch(IOException e) {
- throw new ErrnoException(EIO);
+ if(STDERR_DIAG) e.printStackTrace();
+ throw new ErrnoException(EIO);
}
} else {
- ent = new CacheEnt(mtime,size,command);
+ ent = new CacheEnt(mtime,size,command);
}
fd.close();
if(absolute) {
do { inp++; } while(in[inp] == '/');
} else if(cwdl != 0) {
- cwd.getChars(0,cwdl,out,0);
- outp = cwdl;
+ cwd.getChars(0,cwdl,out,0);
+ outp = cwdl;
}
path.getChars(0,path.length(),in,0);
while(in[inp] != 0) {
if(inp != 0) {
- if(in[inp] != '/') { out[outp++] = in[inp++]; continue; }
- while(in[inp] == '/') inp++;
+ if(in[inp] != '/') { out[outp++] = in[inp++]; continue; }
+ while(in[inp] == '/') inp++;
}
if(in[inp] == '\0') continue;
if(in[inp] != '.') { out[outp++] = '/'; out[outp++] = in[inp++]; continue; }
inp += 2;
if(outp > 0) outp--;
while(outp > 0 && out[outp] != '/') outp--;
- System.err.println("After ..: " + new String(out,0,outp));
+ //System.err.println("After ..: " + new String(out,0,outp));
continue;
}
inp++;
}
public static class HostFS extends FS {
- InodeCache inodes = new InodeCache(4096);
+ InodeCache inodes = new InodeCache(4000);
protected File root;
public File getRoot() { return root; }
private static File hostRootDir() {
+ if(getSystemProperty("nestedvm.root") != null) {
+ File f = new File(getSystemProperty("nestedvm.root"));
+ if(f.isDirectory()) return f;
+ // fall through to case below
+ }
String cwd = getSystemProperty("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());
return f;
if(sep != '/') {
char buf[] = path.toCharArray();
for(int i=0;i<buf.length;i++) {
- char c = buf[i];
+ char c = buf[i];
if(c == '/') buf[i] = sep;
else if(c == sep) buf[i] = '/';
}
int reclen;
OUTER: for(;len > 0 && pos < size();pos++){
switch(pos) {
- case -2:
+ case -2:
case -1:
- ino = pos == -1 ? parentInode() : myInode();
+ ino = pos == -1 ? parentInode() : myInode();
if(ino == -1) continue;
reclen = 9 + (pos == -1 ? 2 : 1);
if(reclen > len) break OUTER;
public String name(int n) {
switch(n) {
- case 0: return "null";
+ case 0: return "null";
case 1: return "zero";
case 2: return "fd";
default: return null;