X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FUnixRuntime.java;h=051a50be063926a90b68c1f811487254a09ae4d2;hp=24a7967f0e598f0afc1d8a3cb4bb1fff044caf1b;hb=c59b7cfc7a6b67574d38c5c8eb7732bad37236b0;hpb=034a42fa65955289442614ef9914e5474fac62aa diff --git a/src/org/ibex/nestedvm/UnixRuntime.java b/src/org/ibex/nestedvm/UnixRuntime.java index 24a7967..051a50b 100644 --- a/src/org/ibex/nestedvm/UnixRuntime.java +++ b/src/org/ibex/nestedvm/UnixRuntime.java @@ -4,25 +4,19 @@ import org.ibex.nestedvm.util.*; import java.io.*; import java.util.*; +// FEATURE: Remove System.{out,err}.printlns and throw Errors where applicable + // FEATURE: BusyBox's ASH doesn't like \r\n at the end of lines // is ash just broken or are many apps like this? if so workaround in nestedvm -// FEATURE: Throw ErrnoException and catch in syscall whereever possible -// (only in cases where we've already paid the price for a throw) - public abstract class UnixRuntime extends Runtime implements Cloneable { /** The pid of this "process" */ private int pid; - private int ppid; + private UnixRuntime parent; protected int getPid() { return pid; } - /** processes filesystem */ - private FS fs; - public FS getFS() { return fs; } - public void setFS(FS fs) { - if(state != STOPPED) throw new IllegalStateException("Can't change fs while process is running"); - this.fs = fs; - } + private static final GlobalState defaultGD = new GlobalState(); + private GlobalState gd = defaultGD; /** proceses' current working directory - absolute path WITHOUT leading slash "" = root, "bin" = /bin "usr/bin" = /usr/bin */ @@ -30,40 +24,14 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { /** The runtime that should be run next when in state == EXECED */ private UnixRuntime execedRuntime; - - /* Static stuff */ - // FEATURE: Most of this is O(n) or worse - fix it - private final Object waitNotification = new Object(); - private final static int MAX_TASKS = 256; - private final static UnixRuntime[] tasks = new UnixRuntime[MAX_TASKS]; - private static int addTask(UnixRuntime rt) { - synchronized(tasks) { - for(int i=1;i= MAX_TASKS)) return -ECHILD; - for(;;) { - synchronized(tasks) { - UnixRuntime task = null; + if(pid == 0 || pid < -1) { + 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(children == null) return blocking ? -ECHILD : 0; + + UnixRuntime done = null; + + synchronized(children) { + for(;;) { if(pid == -1) { - for(int i=0;i 0) done = (UnixRuntime)exitedChildren.remove(exitedChildren.size() - 1); + } else if(pid > 0) { + UnixRuntime t = gd.tasks[pid]; + if(t.parent != this) return -ECHILD; + if(t.state == EXITED) { + if(!exitedChildren.remove(t)) throw new Error("should never happen"); + done = t; } - } else if(tasks[pid] != null && tasks[pid].ppid == this.pid && tasks[pid].state == EXITED) { - task = tasks[pid]; + } else { + // process group stuff, EINVAL returned above + throw new Error("should never happen"); } - - if(task != null) { - removeTask(task); - try { - if(statusAddr!=0) memWrite(statusAddr,task.exitStatus()<<8); - } catch(FaultException e) { - return -EFAULT; - } - - return task.pid; + if(done == null) { + if(!blocking) return 0; + try { children.wait(); } catch(InterruptedException e) {} + System.err.println("waitpid woke up: " + exitedChildren.size()); + } else { + gd.tasks[done.pid] = null; + break; } } - if((options&WNOHANG)!=0) return 0; - synchronized(waitNotification) { - try { waitNotification.wait(); } catch(InterruptedException e) { /* ignore */ } + } + if(statusAddr!=0) memWrite(statusAddr,done.exitStatus()<<8); + return done.pid; + } + + + protected void _exited() { + if(children != null) synchronized(children) { + for(Enumeration e = exitedChildren.elements(); e.hasMoreElements(); ) { + UnixRuntime child = (UnixRuntime) e.nextElement(); + gd.tasks[child.pid] = null; + } + exitedChildren.clear(); + for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) { + UnixRuntime child = (UnixRuntime) e.nextElement(); + child.parent = null; + } + activeChildren.clear(); + } + + UnixRuntime _parent = parent; + if(_parent == null) { + gd.tasks[pid] = null; + } else { + synchronized(_parent.children) { + if(parent == null) { + gd.tasks[pid] = null; + } else { + parent.activeChildren.remove(this); + parent.exitedChildren.add(this); + parent.children.notify(); + } } } } protected Object clone() throws CloneNotSupportedException { UnixRuntime r = (UnixRuntime) super.clone(); - r.pid = r.ppid = 0; + r.pid = 0; + r.parent = null; + r.children = null; + r.activeChildren = r.exitedChildren = null; return r; } @@ -232,29 +242,37 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { e.printStackTrace(); return -ENOMEM; } - - int childPID = addTask(r); - if(childPID < 0) return -ENOMEM; - - r.ppid = pid; + + r.parent = this; + + try { + r._started(); + } catch(ProcessTableFullExn e) { + return -ENOMEM; + } + + System.err.println("fork " + pid + " -> " + r.pid + " tasks[" + r.pid + "] = " + gd.tasks[r.pid]); + if(children == null) { + children = new Object(); + activeChildren = new Vector(); + exitedChildren = new Vector(); + } + activeChildren.add(r); state.r[V0] = 0; // return 0 to child state.pc += 4; // skip over syscall instruction r.setCPUState(state); r.state = PAUSED; - new Thread() { - public void run() { - try { - while(!r.execute()); - } catch(Exception e) { - System.err.println("Forked process threw exception: "); - e.printStackTrace(); - } - } - }.start(); + new ForkedProcess(r); - return childPID; + return r.pid; + } + + public static final class ForkedProcess extends Thread { + private final UnixRuntime initial; + public ForkedProcess(UnixRuntime initial) { this.initial = initial; start(); } + public void run() { UnixRuntime.executeAndExec(initial); } } public static int runAndExec(UnixRuntime r, String argv0, String[] rest) { return runAndExec(r,concatArgv(argv0,rest)); } @@ -279,63 +297,40 @@ public abstract class UnixRuntime extends Runtime implements Cloneable { return a; } - // FEATURE: call the syscall just "exec" - private int sys_execve(int cpath, int cargv, int cenvp) { - try { - return exec(normalizePath(cstring(cpath)),readStringArray(cargv),readStringArray(cenvp)); - } catch(FaultException e) { - return -EFAULT; - } + private int sys_exec(int cpath, int cargv, int cenvp) throws ErrnoException, FaultException { + return exec(normalizePath(cstring(cpath)),readStringArray(cargv),readStringArray(cenvp)); } - - private int exec(String path, String[] argv, String[] envp) { - final UnixRuntime r; - throw new Error("FIXME exec() not finished"); - //Class klass = fs.getClass(path); - //if(klass != null) return exec(klass,argv,envp); - - /*try { - Seekable s = fs.getSeekable(path); - boolean elf = false; - boolean script = false; - switch(s.read()) { - case '\177': elf = s.read() == 'E' && s.read() == 'L' && s.read() == 'F'; break; - case '#': script = s.read() == '!'; - } - s.close(); - if(script) { - // FIXME #! support - throw new Error("FIXME can't exec scripts yet"); - // append args, etc - // return exec(...) - } else if(elf) { - klass = fs.(path); - if(klass == null) return -ENOEXEC; - return exec(klass,argv,envp); - } else { - return -ENOEXEC; + private int exec(String normalizedPath, String[] argv, String[] envp) throws ErrnoException { + if(argv.length == 0) argv = new String[]{""}; + + Object o = gd.exec(this,normalizedPath); + if(o == null) return -ENOENT; + + if(o instanceof Class) { + Class c = (Class) o; + try { + return exec((UnixRuntime) c.newInstance(),argv,envp); + } catch(Exception e) { + e.printStackTrace(); + return -ENOEXEC; } - }*/ - // FEATURE: Way too much overlap in the handling of ioexeptions everhwere - /*catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { - if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES; - return -ENOENT; - } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; }*/ + } else { + String[] command = (String[]) o; + String[] newArgv = new String[argv.length + command[1] != null ? 2 : 1]; + int p = command[0].lastIndexOf('/'); + newArgv[0] = p == -1 ? command[0] : command[0].substring(p+1); + p = 1; + if(command[1] != null) newArgv[p++] = command[1]; + newArgv[p++] = "/" + normalizedPath; + for(int i=1;i= 0) return -EACCES; - return -ENOENT; - } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + private int sys_stat(int cstring, int addr) throws FaultException, ErrnoException { + FStat s = gd.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))); + if(s == null) return -ENOENT; + return stat(s,addr); + } - private int sys_mkdir(int cstring, int mode) { - try { - fs.mkdir(normalizePath(cstring(cstring))); - return 0; - } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { return -ENOENT; } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + private int sys_mkdir(int cstring, int mode) throws FaultException, ErrnoException { + gd.mkdir(this,normalizePath(cstring(cstring)),mode); + return 0; } - private int sys_getcwd(int addr, int size) { + private int sys_getcwd(int addr, int size) throws FaultException, ErrnoException { byte[] b = getBytes(cwd); if(size == 0) return -EINVAL; if(size < b.length+2) return -ERANGE; - try { - memset(addr,'/',1); - copyout(b,addr+1,b.length); - memset(addr+b.length+1,0,1); - return addr; - } catch(FaultException e) { - return -EFAULT; - } + memset(addr,'/',1); + copyout(b,addr+1,b.length); + memset(addr+b.length+1,0,1); + return addr; } - private int sys_chdir(int addr) { - try { - String path = normalizePath(cstring(addr)); - System.err.println("Chdir: " + cstring(addr) + " -> " + path + " pwd: " + cwd); - if(fs.stat(path).type() != FStat.S_IFDIR) return -ENOTDIR; - cwd = path; - System.err.println("Now: [" + cwd + "]"); - return 0; - } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { return -ENOENT; } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + 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; + cwd = path; + System.err.println("Now: [" + cwd + "]"); + return 0; } - - public void chdir(String dir) throws FileNotFoundException { - if(state != STOPPED) throw new IllegalStateException("Can't chdir while process is running"); - try { - dir = normalizePath(dir); - if(fs.stat(dir).type() != FStat.S_IFDIR) throw new FileNotFoundException(); - } catch(IOException e) { - throw new FileNotFoundException(); + + // FEATURE: Run through the fork/wait stuff one more time + public static class GlobalState { + protected static final int OPEN = 1; + protected static final int STAT = 2; + protected static final int LSTAT = 3; + protected static final int MKDIR = 4; + + // Make this protected (need to fix _exit) + private final UnixRuntime[] tasks; + private int nextPID = 1; + + private final MP[][] mps = new MP[128][]; + private FS root; + + public GlobalState() { this(255); } + public GlobalState(int maxProcs) { this(maxProcs,true); } + public GlobalState(int maxProcs, boolean defaultMounts) { + tasks = new UnixRuntime[maxProcs+1]; + if(defaultMounts) { + root = new HostFS(); + addMount("/dev",new DevFS()); + } } - cwd = dir; - } - public abstract static class FS { - protected FD _open(String path, int flags, int mode) throws IOException { return null; } - protected FStat _stat(String path) throws IOException { return null; } - protected void _mkdir(String path) throws IOException { throw new ErrnoException(EROFS); } + private static class MP { + public MP(String path, FS fs) { this.path = path; this.fs = fs; } + public String path; + public FS fs; + public int compareTo(Object o) { + if(!(o instanceof MP)) return 1; + return -path.compareTo(((MP)o).path); + } + } - protected static final int OPEN = 1; - protected static final int STAT = 2; - protected static final int MKDIR = 3; + public synchronized FS getMount(String path) { + if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /"); + if(path.equals("/")) return root; + path = path.substring(1); + int f = path.charAt(0) & 0x7f; + for(int i=0;mps[f] != null && i < mps[f].length;i++) + if(mps[f][i].path.equals(path)) return mps[f][i].fs; + return null; + } + + public synchronized void addMount(String path, FS fs) { + if(getMount(path) != null) throw new IllegalArgumentException("mount point already exists"); + if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /"); + if(path.equals("/")) { root = fs; return; } + path = path.substring(1); + int f = path.charAt(0) & 0x7f; + int oldLength = mps[f] == null ? 0 : mps[f].length; + MP[] newList = new MP[oldLength + 1]; + if(oldLength != 0) System.arraycopy(mps[f],0,newList,0,oldLength); + newList[oldLength] = new MP(path,fs); + Arrays.sort(newList); + mps[f] = newList; + } + + public synchronized void removeMount(String path) { + if(getMount(path) == null) throw new IllegalArgumentException("mount point doesn't exist"); + if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /"); + if(path.equals("/")) { root = null; return; } + path = path.substring(1); + int f = path.charAt(0) & 0x7f; + MP[] oldList = mps[f]; + MP[] newList = new MP[oldList.length - 1]; + int p = 0; + for(p=0;p " + new String(out,0,outp) + " (cwd: " + cwd + ")"); return new String(out,0,outp); } + + // FEATURE: override Runtime.hostFStat to add executable type checking + // hostFStat + /* try { + FileInputStream fis = new FileInputStream(f); + switch(fis.read()) { + case '\177': _executable = fis.read() == 'E' && fis.read() == 'L' && fis.read() == 'F'; break; + case '#': _executable = fis.read() == '!'; + } + fis.close(); + } catch(IOException e) { } + */ + + // FEATURE: inode stuff + FD hostFSDirFD(File f) { return FS.directoryFD(f.list(),f.hashCode()); } - public static class MountPointFS extends FS { - private static class MP { - public MP(String path, FS fs) { this.path = path; this.fs = fs; } - public String path; - public FS fs; - public int compareTo(Object o) { - if(!(o instanceof MP)) return 1; - return -path.compareTo(((MP)o).path); - } - } - private final MP[][] mps = new MP[128][]; - private final FS root; - public MountPointFS(FS root) { this.root = root; } - - private static String fixup(String path) { - if(!path.startsWith("/")) throw new IllegalArgumentException("Mount point doesn't start with a /"); - path = path.substring(1); - if(path.length() == 0) throw new IllegalArgumentException("Zero length mount point path"); - return path; - } - public synchronized FS get(String path) { - path = fixup(path); - int f = path.charAt(0) & 0x7f; - for(int i=0;mps[f] != null && i < mps[f].length;i++) - if(mps[f][i].path.equals(path)) return mps[f][i].fs; - return null; - } - - public synchronized void add(String path, FS fs) { - if(get(path) != null) throw new IllegalArgumentException("mount point already exists"); - path = fixup(path); - int f = path.charAt(0) & 0x7f; - int oldLength = mps[f] == null ? 0 : mps[f].length; - MP[] newList = new MP[oldLength + 1]; - if(oldLength != 0) System.arraycopy(mps[f],0,newList,0,oldLength); - newList[oldLength] = new MP(path,fs); - Arrays.sort(newList); - mps[f] = newList; - } - - public synchronized void remove(String path) { - path = fixup(path); - if(get(path) == null) throw new IllegalArgumentException("mount point doesn't exist"); - int f = path.charAt(0) & 0x7f; - MP[] oldList = mps[f]; - MP[] newList = new MP[oldList.length - 1]; - int p = 0; - for(p=0;p= OPEN_MAX) throw new FileNotFoundException(); - if(fds[n] == null) throw new FileNotFoundException(); - return fds[n].dup(); + if(n < 0 || n >= OPEN_MAX) return null; + if(r.fds[n] == null) return null; + return r.fds[n].dup(); } if(path.equals("fd")) { int count=0; - for(int i=0;i= OPEN_MAX) throw new FileNotFoundException(); - if(fds[n] == null) throw new FileNotFoundException(); - return fds[n].fstat(); + if(n < 0 || n >= OPEN_MAX) return null; + if(r.fds[n] == null) return null; + return r.fds[n].fstat(); } 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; }}; - throw new FileNotFoundException(); + return null; } - public void _mkdir(String path) throws IOException { throw new ErrnoException(EACCES); } + public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EACCES); } } }