X-Git-Url: http://git.megacz.com/?p=nestedvm.git;a=blobdiff_plain;f=src%2Forg%2Fibex%2Fnestedvm%2FUnixRuntime.java;h=306b70e5a4b09bfe2388ec0633f194d0049314bc;hp=3befb91aeaf744fe096c22a9630b73c0d631af36;hb=b11e7c6c29f2b5f7b0828bf93eb741c4a30ec411;hpb=c2b2704764af1ade923ba8f15d517b87f9d16189 diff --git a/src/org/ibex/nestedvm/UnixRuntime.java b/src/org/ibex/nestedvm/UnixRuntime.java index 3befb91..306b70e 100644 --- a/src/org/ibex/nestedvm/UnixRuntime.java +++ b/src/org/ibex/nestedvm/UnixRuntime.java @@ -1,78 +1,67 @@ +// Copyright 2000-2005 the Contributors, as shown in the revision logs. +// Licensed under the Apache License 2.0 ("the License"). +// You may not use this file except in compliance with the License. + package org.ibex.nestedvm; import org.ibex.nestedvm.util.*; import java.io.*; import java.util.*; +import java.net.*; +import java.lang.reflect.*; // For lazily linked RuntimeCompiler + +// FEATURE: vfork -public abstract class UnixRuntime extends Runtime { +public abstract class UnixRuntime extends Runtime implements Cloneable { /** The pid of this "process" */ private int pid; - private int ppid; - protected int getPid() { return pid; } + private UnixRuntime parent; + public final int getPid() { return pid; } - /** processes filesystem */ - private FS fs; - public FS getFS() { return fs; } - public void setFS(FS fs) { - if(state >= RUNNING) throw new IllegalStateException("Can't change fs while process is running"); - this.fs = fs; + private static final GlobalState defaultGS = new GlobalState(); + private GlobalState gs; + public void setGlobalState(GlobalState gs) { + if(state != STOPPED) throw new IllegalStateException("can't change GlobalState when running"); + if(gs == null) throw new NullPointerException("gs is null"); + this.gs = gs; } - /** proceses current working directory */ + /** proceses' current working directory - absolute path WITHOUT leading slash + "" = root, "bin" = /bin "usr/bin" = /usr/bin */ private String cwd; - /* 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 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(); } @@ -82,62 +71,145 @@ public abstract class UnixRuntime extends Runtime { return false; } - protected String[] createEnv(String[] extra) { - String[] defaults = new String[5]; + String[] createEnv(String[] extra) { + String[] defaults = new String[7]; 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.name") != null) - defaults[n++] = "HOME=" + getSystemProperty("user.home"); + String tmp; + if(!envHas("USER",extra) && Platform.getProperty("user.name") != null) + defaults[n++] = "USER=" + Platform.getProperty("user.name"); + if(!envHas("HOME",extra) && (tmp=Platform.getProperty("user.home")) != null && (tmp=gs.mapHostPath(tmp)) != null) + defaults[n++] = "HOME=" + tmp; + if(!envHas("TMPDIR",extra) && (tmp=Platform.getProperty("java.io.tmpdir")) != null && (tmp=gs.mapHostPath(tmp)) != null) + defaults[n++] = "TMPDIR=" + tmp; 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]; for(int i=0;i PATH_MAX) return -ERANGE; + copyout(b,outAddr,b.length); + return 0; + } - // FEATURE: Allow simple, broken signal delivery to other processes - // (check if a signal was delivered before and after syscalls) - // FEATURE: Implement raise() in terms of call("raise",...) - kinda cheap, but it keeps the complexity in newlib + // 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. @@ -153,132 +225,406 @@ public abstract class UnixRuntime extends Runtime { 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: { - String msg = "Terminating on signal: " + signal + "\n"; - exitStatus = 1; - state = DONE; - if(fds[2]==null) { - System.out.print(msg); - } else { - try { - byte[] b = getBytes(msg); - fds[2].write(b,0,b.length); - } - catch(IOException e) { /* ignore */ } - } - } + default: + exit(128+signal, true); } return 0; } - private int sys_waitpid(int pid, int statusAddr, int options) { + private int sys_waitpid(int pid, int statusAddr, int options) throws FaultException, ErrnoException { final int WNOHANG = 1; if((options & ~(WNOHANG)) != 0) return -EINVAL; - if(pid !=-1 && (pid <= 0 || pid >= MAX_TASKS)) return -ECHILD; - for(;;) { - synchronized(tasks) { - UnixRuntime task = null; + if(pid == 0 || pid < -1) { + 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 >= gs.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.elementAt(exitedChildren.size() - 1); + exitedChildren.removeElementAt(exitedChildren.size() - 1); } - } else if(tasks[pid] != null && tasks[pid].ppid == this.pid && tasks[pid].state == DONE) { - task = tasks[pid]; - } - - if(task != null) { - removeTask(task); - try { - if(statusAddr!=0) memWrite(statusAddr,task.exitStatus()<<8); - } catch(FaultException e) { - return -EFAULT; + } else if(pid > 0) { + if(pid >= gs.tasks.length) return -ECHILD; + UnixRuntime t = gs.tasks[pid]; + if(t.parent != this) return -ECHILD; + if(t.state == EXITED) { + if(!exitedChildren.removeElement(t)) throw new Error("should never happen"); + done = t; } - - return task.pid; + } else { + // process group stuff, EINVAL returned above + 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()); + } else { + gs.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; + } + + + void _exited() { + if(children != null) synchronized(children) { + for(Enumeration e = exitedChildren.elements(); e.hasMoreElements(); ) { + UnixRuntime child = (UnixRuntime) e.nextElement(); + gs.tasks[child.pid] = null; + } + exitedChildren.removeAllElements(); + for(Enumeration e = activeChildren.elements(); e.hasMoreElements(); ) { + UnixRuntime child = (UnixRuntime) e.nextElement(); + child.parent = null; + } + activeChildren.removeAllElements(); + } + + UnixRuntime _parent = parent; + if(_parent == null) { + gs.tasks[pid] = null; + } else { + synchronized(_parent.children) { + if(parent == null) { + gs.tasks[pid] = null; + } else { + if(!parent.activeChildren.removeElement(this)) throw new Error("should never happen _exited: pid: " + pid); + parent.exitedChildren.addElement(this); + parent.children.notify(); + } } } } - // Great ugliness lies within..... + protected Object clone() throws CloneNotSupportedException { + UnixRuntime r = (UnixRuntime) super.clone(); + r.pid = 0; + r.parent = null; + r.children = null; + r.activeChildren = r.exitedChildren = null; + return r; + } + private int sys_fork() { - CPUState state = getCPUState(); - int sp = state.r[SP]; final UnixRuntime r; + try { - r = (UnixRuntime) getClass().newInstance(); + r = (UnixRuntime) clone(); } catch(Exception e) { - System.err.println(e); + e.printStackTrace(); return -ENOMEM; } - int child_pid = addTask(r); - if(child_pid < 0) return -ENOMEM; - - r.ppid = pid; - r.brkAddr = brkAddr; - r.fds = new FD[OPEN_MAX]; - for(int i=0;i= sp-PAGE_SIZE*2) - System.arraycopy(writePages[i],0,r.writePages[i],0,PAGE_WORDS); - } else { - r.readPages[i] = r.readPages[i]; - } + + 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(); } - state.r[V0] = 0; - state.pc += 4; + activeChildren.addElement(r); + + CPUState state = new CPUState(); + getCPUState(state); + 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 child_pid; + 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)); } + public static int runAndExec(UnixRuntime r, String[] argv) { r.start(argv); return executeAndExec(r); } + + public static int executeAndExec(UnixRuntime r) { + for(;;) { + for(;;) { + if(r.execute()) break; + 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; + for(int p=addr;memRead(p) != 0;p+=4) count++; + String[] a = new String[count]; + for(int i=0,p=addr;i= 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 = 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 = 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 { + gs.mkdir(this,normalizePath(cstring(cstring)),mode); + return 0; + } + + private int sys_unlink(int cstring) throws FaultException, ErrnoException { + gs.unlink(this,normalizePath(cstring(cstring))); + return 0; + } + + 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; + memset(addr,'/',1); + copyout(b,addr+1,b.length); + memset(addr+b.length+1,0,1); + return addr; + } + + private int sys_chdir(int addr) throws ErrnoException, FaultException { + String path = normalizePath(cstring(addr)); + FStat st = gs.stat(this,path); + if(st == null) return -ENOENT; + if(st.type() != FStat.S_IFDIR) return -ENOTDIR; + cwd = path; + return 0; + } + + private int sys_getdents(int fdn, int addr, int count, int seekptr) throws FaultException, ErrnoException { + count = Math.min(count,MAX_CHUNK); + if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + byte[] buf = byteBuf(count); + int n = fds[fdn].getdents(buf,0,count); + copyout(buf,addr,n); + return n; + } + + void _preCloseFD(FD fd) { + // release all fcntl locks on this file + Seekable s = fd.seekable(); + if (s == null) return; + try { - String path = cleanupPath(cstring(cstring)); - return stat(fs.stat(path),addr); + for (int i=0; i < gs.locks.length; i++) { + Seekable.Lock l = gs.locks[i]; + if (l == null) continue; + if (s.equals(l.seekable()) && l.getOwner() == this) { + l.release(); + gs.locks[i] = null; + } + } + } catch (IOException e) { throw new RuntimeException(e); } + } + + void _postCloseFD(FD fd) { + if (fd.isMarkedForDeleteOnClose()) { + try { gs.unlink(this, fd.getNormalizedPath()); } + catch (Throwable t) {} + } + } + + /** Implements the F_GETLK and F_SETLK cases of fcntl syscall. + * If l_start = 0 and l_len = 0 the lock refers to the entire file. + * Uses GlobalState to ensure locking across processes in the same JVM. + struct flock { + short l_type; // lock type: F_UNLCK, F_RDLCK, F_WRLCK + short l_whence; // type of l_start: SEEK_SET, SEEK_CUR, SEEK_END + long l_start; // starting offset, bytes + long l_len; // len = 0 means until EOF + short l_pid; // lock owner + short l_xxx; // padding + }; + */ + private int sys_fcntl_lock(int fdn, int cmd, int arg) throws FaultException{ + if (cmd != F_GETLK && cmd != F_SETLK) return sys_fcntl(fdn, cmd, arg); + + if(fdn < 0 || fdn >= OPEN_MAX) return -EBADFD; + if(fds[fdn] == null) return -EBADFD; + FD fd = fds[fdn]; + + if (arg == 0) return -EINVAL; + int word = memRead(arg); + int l_start = memRead(arg+4); + int l_len = memRead(arg+8); + int l_type = word>>16; + int l_whence = word&0x00ff; + + Seekable.Lock[] locks = gs.locks; + Seekable s = fd.seekable(); + if (s == null) return -EINVAL; + + try { + + switch (l_whence) { + case SEEK_SET: break; + case SEEK_CUR: l_start += s.pos(); break; + case SEEK_END: l_start += s.length(); break; + default: return -1; + } + + if (cmd == F_GETLK) { + // The simple Java file locking below will happily return + // a lock that overlaps one already held by the JVM. Thus + // we must check over all the locks held by other Runtimes + for (int i=0; i < locks.length; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + if (!locks[i].overlaps(l_start, l_len)) + continue; + if (locks[i].getOwner() == this) + continue; + if (locks[i].isShared() && l_type == F_RDLCK) + continue; + + // overlapping lock held by another process + return 0; + } + + // check if an area is lockable by attempting to obtain a lock + Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); + + if (lock != null) { // no lock exists + memWrite(arg, SEEK_SET|(F_UNLCK<<16)); + lock.release(); + } + + return 0; + } + + // now processing F_SETLK + if (cmd != F_SETLK) return -EINVAL; + + if (l_type == F_UNLCK) { + // release all locks that fall within the boundaries given + for (int i=0; i < locks.length; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + if (locks[i].getOwner() != this) continue; + + int pos = (int)locks[i].position(); + if (pos < l_start) continue; + if (l_start != 0 && l_len != 0) // start/len 0 means unlock all + if (pos + locks[i].size() > l_start + l_len) + continue; + + locks[i].release(); + locks[i] = null; + } + return 0; + + } else if (l_type == F_RDLCK || l_type == F_WRLCK) { + // first see if a lock already exists + for (int i=0; i < locks.length; i++) { + if (locks[i] == null || !s.equals(locks[i].seekable())) + continue; + + if (locks[i].getOwner() == this) { + // if this Runtime owns an overlapping lock work with it + if (locks[i].contained(l_start, l_len)) { + locks[i].release(); + locks[i] = null; + } else if (locks[i].contains(l_start, l_len)) { + if (locks[i].isShared() == (l_type == F_RDLCK)) { + // return this more general lock + memWrite(arg+4, (int)locks[i].position()); + memWrite(arg+8, (int)locks[i].size()); + return 0; + } else { + locks[i].release(); + locks[i] = null; + } + } + } else { + // if another Runtime has an lock and it is exclusive or + // we want an exclusive lock then fail + if (locks[i].overlaps(l_start, l_len) + && (!locks[i].isShared() || l_type == F_WRLCK)) + return -EAGAIN; + } + } + + // create the lock + Seekable.Lock lock = s.lock(l_start, l_len, l_type == F_RDLCK); + if (lock == null) return -EAGAIN; + lock.setOwner(this); + + int i; + for (i=0; i < locks.length; i++) + if (locks[i] == null) break; + if (i == locks.length) return -ENOLCK; + locks[i] = lock; + return 0; + + } else { + return -EINVAL; + } + + } catch (IOException e) { throw new RuntimeException(e); } + } + + static class SocketFD extends FD { + public static final int TYPE_STREAM = 0; + public static final int TYPE_DGRAM = 1; + public static final int LISTEN = 2; + public int type() { return flags & 1; } + public boolean listen() { return (flags & 2) != 0; } + + int flags; + int options; + + Socket s; + ServerSocket ss; + DatagramSocket ds; + + InetAddress bindAddr; + int bindPort = -1; + InetAddress connectAddr; + int connectPort = -1; + + DatagramPacket dp; + InputStream is; + OutputStream os; + + private static final byte[] EMPTY = new byte[0]; + public SocketFD(int type) { + flags = type; + if(type == TYPE_DGRAM) + dp = new DatagramPacket(EMPTY,0); + } + + public void setOptions() { + try { + if(s != null && type() == TYPE_STREAM && !listen()) { + Platform.socketSetKeepAlive(s,(options & SO_KEEPALIVE) != 0); + } + } catch(SocketException e) { + if(STDERR_DIAG) e.printStackTrace(); + } + } + + public void _close() { + try { + if(s != null) s.close(); + if(ss != null) ss.close(); + if(ds != null) ds.close(); + } catch(IOException e) { + /* ignore */ + } } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { - if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) return -EACCES; - return -ENOENT; + + public int read(byte[] a, int off, int length) throws ErrnoException { + if(type() == TYPE_DGRAM) return recvfrom(a,off,length,null,null); + if(is == null) throw new ErrnoException(EPIPE); + try { + int n = is.read(a,off,length); + return n < 0 ? 0 : n; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + public int recvfrom(byte[] a, int off, int length, InetAddress[] sockAddr, int[] port) throws ErrnoException { + if(type() == TYPE_STREAM) return read(a,off,length); + + if(off != 0) throw new IllegalArgumentException("off must be 0"); + dp.setData(a); + dp.setLength(length); + try { + if(ds == null) ds = new DatagramSocket(); + ds.receive(dp); + } catch(IOException e) { + if(STDERR_DIAG) e.printStackTrace(); + throw new ErrnoException(EIO); + } + if(sockAddr != null) { + sockAddr[0] = dp.getAddress(); + port[0] = dp.getPort(); + } + return dp.getLength(); + } + + public int write(byte[] a, int off, int length) throws ErrnoException { + if(type() == TYPE_DGRAM) return sendto(a,off,length,null,-1); + + if(os == null) throw new ErrnoException(EPIPE); + try { + os.write(a,off,length); + return length; + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + public int sendto(byte[] a, int off, int length, InetAddress destAddr, int destPort) throws ErrnoException { + if(off != 0) throw new IllegalArgumentException("off must be 0"); + if(type() == TYPE_STREAM) return write(a,off,length); + + if(destAddr == null) { + destAddr = connectAddr; + destPort = connectPort; + + if(destAddr == null) throw new ErrnoException(ENOTCONN); + } + + dp.setAddress(destAddr); + dp.setPort(destPort); + dp.setData(a); + dp.setLength(length); + + try { + if(ds == null) ds = new DatagramSocket(); + ds.send(dp); + } catch(IOException e) { + if(STDERR_DIAG) e.printStackTrace(); + if("Network is unreachable".equals(e.getMessage())) throw new ErrnoException(EHOSTUNREACH); + throw new ErrnoException(EIO); + } + return dp.getLength(); } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + + public int flags() { return O_RDWR; } + public FStat _fstat() { return new SocketFStat(); } + } + + private int sys_socket(int domain, int type, int proto) { + if(domain != AF_INET || (type != SOCK_STREAM && type != SOCK_DGRAM)) return -EPROTONOSUPPORT; + return addFD(new SocketFD(type == SOCK_STREAM ? SocketFD.TYPE_STREAM : SocketFD.TYPE_DGRAM)); } + private SocketFD getSocketFD(int fdn) throws ErrnoException { + if(fdn < 0 || fdn >= OPEN_MAX) throw new ErrnoException(EBADFD); + if(fds[fdn] == null) throw new ErrnoException(EBADFD); + if(!(fds[fdn] instanceof SocketFD)) throw new ErrnoException(ENOTSOCK); + + return (SocketFD) fds[fdn]; + } - private int sys_mkdir(int cstring, int mode) { + private int sys_connect(int fdn, int addr, int namelen) throws ErrnoException, FaultException { + SocketFD fd = getSocketFD(fdn); + + if(fd.type() == SocketFD.TYPE_STREAM && (fd.s != null || fd.ss != null)) return -EISCONN; + int word1 = memRead(addr); + if( ((word1 >>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT; + int port = word1 & 0xffff; + byte[] ip = new byte[4]; + copyin(addr+4,ip,4); + + InetAddress inetAddr; try { - fs.mkdir(cleanupPath(cstring(cstring))); - return 0; + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + + fd.connectAddr = inetAddr; + fd.connectPort = port; + + try { + switch(fd.type()) { + case SocketFD.TYPE_STREAM: { + Socket s = new Socket(inetAddr,port); + fd.s = s; + fd.setOptions(); + fd.is = s.getInputStream(); + fd.os = s.getOutputStream(); + break; + } + case SocketFD.TYPE_DGRAM: + break; + default: + throw new Error("should never happen"); + } + } catch(IOException e) { + return -ECONNREFUSED; } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { return -ENOENT; } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + + return 0; } - - private int sys_getcwd(int addr, int size) { - byte[] b = getBytes(cwd); - if(size == 0) return -EINVAL; - if(size < b.length+1) return -ERANGE; - if(!new File(cwd).exists()) return -ENOENT; + private int sys_resolve_hostname(int chostname, int addr, int sizeAddr) throws FaultException { + String hostname = cstring(chostname); + int size = memRead(sizeAddr); + InetAddress[] inetAddrs; try { - copyout(b,addr,b.length); - memset(addr+b.length+1,0,1); - return addr; - } catch(FaultException e) { - return -EFAULT; + inetAddrs = InetAddress.getAllByName(hostname); + } catch(UnknownHostException e) { + return HOST_NOT_FOUND; + } + int count = min(size/4,inetAddrs.length); + for(int i=0;i>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT; + int port = word1 & 0xffff; + InetAddress inetAddr = null; + if(memRead(addr+4) != 0) { + byte[] ip = new byte[4]; + copyin(addr+4,ip,4); + + try { + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + } + + switch(fd.type()) { + case SocketFD.TYPE_STREAM: { + fd.bindAddr = inetAddr; + fd.bindPort = port; + return 0; + } + case SocketFD.TYPE_DGRAM: { + if(fd.ds != null) fd.ds.close(); + try { + fd.ds = inetAddr != null ? new DatagramSocket(port,inetAddr) : new DatagramSocket(port); + } catch(IOException e) { + return -EADDRINUSE; + } + return 0; + } + default: + throw new Error("should never happen"); } } - private int sys_chdir(int addr) { + private int sys_listen(int fdn, int backlog) throws ErrnoException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM) return -EOPNOTSUPP; + if(fd.ss != null || fd.s != null) return -EISCONN; + if(fd.bindPort < 0) return -EOPNOTSUPP; + try { - String path = cleanupPath(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); + fd.ss = new ServerSocket(fd.bindPort,backlog,fd.bindAddr); + fd.flags |= SocketFD.LISTEN; return 0; + } catch(IOException e) { + return -EADDRINUSE; } - catch(ErrnoException e) { return -e.errno; } - catch(FileNotFoundException e) { return -ENOENT; } - catch(IOException e) { return -EIO; } - catch(FaultException e) { return -EFAULT; } + } + + private int sys_accept(int fdn, int addr, int lenaddr) throws ErrnoException, FaultException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM) return -EOPNOTSUPP; + if(!fd.listen()) return -EOPNOTSUPP; - public void chdir(String dir) throws FileNotFoundException { - if(state >= RUNNING) throw new IllegalStateException("Can't chdir while process is running"); + int size = memRead(lenaddr); + + ServerSocket s = fd.ss; + Socket client; try { - dir = cleanupPath(dir); - if(fs.stat(dir).type() != FStat.S_IFDIR) throw new FileNotFoundException(); + client = s.accept(); } catch(IOException e) { - throw new FileNotFoundException(); + return -EIO; } - cwd = dir; - } - public abstract static class FS { - public FD open(String path, int flags, int mode) throws IOException { throw new FileNotFoundException(); } - public FStat stat(String path) throws IOException { throw new FileNotFoundException(); } - public void mkdir(String path) throws IOException { throw new ErrnoException(ENOTDIR); } - - public static FD directoryFD(String[] files, int hashCode) throws IOException { - ByteArrayOutputStream bos = new ByteArrayOutputStream(); - DataOutputStream dos = new DataOutputStream(bos); - for(int i=0;i= 8) { + memWrite(addr,(6 << 24) | (AF_INET << 16) | client.getPort()); + byte[] b = client.getInetAddress().getAddress(); + copyout(b,addr+4,4); + memWrite(lenaddr,8); } + + SocketFD clientFD = new SocketFD(SocketFD.TYPE_STREAM); + clientFD.s = client; + try { + clientFD.is = client.getInputStream(); + clientFD.os = client.getOutputStream(); + } catch(IOException e) { + return -EIO; + } + int n = addFD(clientFD); + if(n == -1) { clientFD.close(); return -ENFILE; } + return n; } + + private int sys_shutdown(int fdn, int how) throws ErrnoException { + SocketFD fd = getSocketFD(fdn); + if(fd.type() != SocketFD.TYPE_STREAM || fd.listen()) return -EOPNOTSUPP; + if(fd.s == null) return -ENOTCONN; - private static boolean needsCleanup(String path) { - if(path.indexOf("//") != -1) return true; - if(path.indexOf('.') != -1) { - if(path.length() == 1) return true; - if(path.equals("..")) return true; - if(path.startsWith("./") || path.indexOf("/./") != -1 || path.endsWith("/.")) return true; - if(path.startsWith("../") || path.indexOf("/../") != -1 || path.endsWith("/..")) return true; + Socket s = fd.s; + + try { + 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 false; + + return 0; } - // FIXME: This is probably still buggy - // FEATURE: Remove some of the "should never happen checks" - protected String cleanupPath(String p) throws ErrnoException { - if(p.length() == 0) throw new ErrnoException(ENOENT); - if(needsCleanup(p)) { - char[] in = p.toCharArray(); - char[] out; - int outp ; - if(in[0] == '/') { - out = new char[in.length]; - outp = 0; - } else { - out = new char[cwd.length() + in.length + 1]; - outp = cwd.length(); - for(int i=0;i 0 && out[outp] != '/'); - } else if(in[inp+1] == '/') { - inp++; - } else { - out[outp++] = '/'; - } - } else { - out[outp++] = '/'; - out[outp++] = in[inp++]; - } - } else { - out[outp++] = in[inp++]; + private int sys_sendto(int fdn, int addr, int count, int flags, int destAddr, int socklen) throws ErrnoException,ReadFaultException { + SocketFD fd = getSocketFD(fdn); + if(flags != 0) throw new ErrnoException(EINVAL); + + int word1 = memRead(destAddr); + if( ((word1 >>> 16)&0xff) != AF_INET) return -EAFNOSUPPORT; + int port = word1 & 0xffff; + InetAddress inetAddr; + byte[] ip = new byte[4]; + copyin(destAddr+4,ip,4); + try { + inetAddr = Platform.inetAddressFromBytes(ip); + } catch(UnknownHostException e) { + return -EADDRNOTAVAIL; + } + + count = Math.min(count,MAX_CHUNK); + byte[] buf = byteBuf(count); + copyin(addr,buf,count); + try { + return fd.sendto(buf,0,count,inetAddr,port); + } catch(ErrnoException e) { + if(e.errno == EPIPE) exit(128+13,true); + throw e; + } + } + + private int sys_recvfrom(int fdn, int addr, int count, int flags, int sourceAddr, int socklenAddr) throws ErrnoException, FaultException { + SocketFD fd = getSocketFD(fdn); + if(flags != 0) throw new ErrnoException(EINVAL); + + InetAddress[] inetAddr = sourceAddr == 0 ? null : new InetAddress[1]; + int[] port = sourceAddr == 0 ? null : new int[1]; + + count = Math.min(count,MAX_CHUNK); + byte[] buf = byteBuf(count); + int n = fd.recvfrom(buf,0,count,inetAddr,port); + copyout(buf,addr,n); + + if(sourceAddr != 0) { + memWrite(sourceAddr,(AF_INET << 16) | port[0]); + byte[] ip = inetAddr[0].getAddress(); + copyout(ip,sourceAddr+4,4); + } + + return n; + } + + private int sys_select(int n, int readFDs, int writeFDs, int exceptFDs, int timevalAddr) throws ReadFaultException, ErrnoException { + return -ENOSYS; + } + + private static String hostName() { + try { + return InetAddress.getLocalHost().getHostName(); + } catch(UnknownHostException e) { + return "darkstar"; + } + } + + private int sys_sysctl(int nameaddr, int namelen, int oldp, int oldlenaddr, int newp, int newlen) throws FaultException { + if(newp != 0) return -EPERM; + if(namelen == 0) return -ENOENT; + if(oldp == 0) return 0; + + Object o = null; + switch(memRead(nameaddr)) { + case CTL_KERN: + if(namelen != 2) break; + switch(memRead(nameaddr+4)) { + case KERN_OSTYPE: o = "NestedVM"; break; + case KERN_HOSTNAME: o = hostName(); break; + case KERN_OSRELEASE: o = VERSION; break; + case KERN_VERSION: o = "NestedVM Kernel Version " + VERSION; break; } - } - if(outp == 0) out[outp++] = '/'; - return new String(out,0,outp); + break; + case CTL_HW: + if(namelen != 2) break; + switch(memRead(nameaddr+4)) { + case HW_MACHINE: o = "NestedVM Virtual Machine"; break; + } + break; + } + if(o == null) return -ENOENT; + int len = memRead(oldlenaddr); + if(o instanceof String) { + byte[] b = getNullTerminatedBytes((String)o); + if(len < b.length) return -ENOMEM; + len = b.length; + copyout(b,oldp,len); + memWrite(oldlenaddr,len); + } else if(o instanceof Integer) { + if(len < 4) return -ENOMEM; + memWrite(oldp,((Integer)o).intValue()); } else { - if(p.startsWith("/")) return p; - StringBuffer sb = new StringBuffer(cwd); - if(!cwd.equals("/")) sb.append('/'); - return sb.append(p).toString(); + throw new Error("should never happen"); } + return 0; } - // FEATURE: Probably should make this more general - support mountpoints, etc - public class UnixOverlayFS extends FS { - private final FS root; - private final FS dev = new DevFS(); - public UnixOverlayFS(FS root) { - this.root = root; + public static final class GlobalState { + Hashtable execCache = new Hashtable(); + + final UnixRuntime[] tasks; + int nextPID = 1; + + /** Table of all current file locks held by this process. */ + Seekable.Lock[] locks = new Seekable.Lock[16]; + + private MP[] mps = new MP[0]; + 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) { + File root = null; + if(Platform.getProperty("nestedvm.root") != null) { + root = new File(Platform.getProperty("nestedvm.root")); + if(!root.isDirectory()) throw new IllegalArgumentException("nestedvm.root is not a directory"); + } else { + String cwd = Platform.getProperty("user.dir"); + root = Platform.getRoot(new File(cwd != null ? cwd : ".")); + } + + addMount("/",new HostFS(root)); + + if(Platform.getProperty("nestedvm.root") == null) { + File[] roots = Platform.listRoots(); + for(int i=0;i=0;i--) { + FS fs = i == mps.length ? root : mps[i].fs; + String path = i == mps.length ? "" : mps[i].path; + if(!(fs instanceof HostFS)) continue; + File fsroot = ((HostFS)fs).getRoot(); + if(!fsroot.isAbsolute()) fsroot = new File(fsroot.getAbsolutePath()); + if(f.getPath().startsWith(fsroot.getPath())) { + char sep = File.separatorChar; + String child = f.getPath().substring(fsroot.getPath().length()); + if(sep != '/') { + char[] child_ = child.toCharArray(); + for(int j=0;j 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++] = '.'; } + if(outp > 0 && out[outp-1] == '/') outp--; + //System.err.println("normalize: " + path + " -> " + new String(out,0,outp) + " (cwd: " + cwd + ")"); + int outStart = out[0] == '/' ? 1 : 0; + return new String(out,outStart,outp - outStart); + } + + FStat hostFStat(final File f, Object data) { + boolean e = false; + try { + FileInputStream fis = new FileInputStream(f); + switch(fis.read()) { + case '\177': e = fis.read() == 'E' && fis.read() == 'L' && fis.read() == 'F'; break; + case '#': e = fis.read() == '!'; + } + fis.close(); + } catch(IOException e2) { } + HostFS fs = (HostFS) data; + final int inode = fs.inodes.get(f.getAbsolutePath()); + final int devno = fs.devno; + return new HostFStat(f,e) { + public int inode() { return inode; } + public int dev() { return devno; } + }; + } + + FD hostFSDirFD(File f, Object _fs) { + HostFS fs = (HostFS) _fs; + return fs.new HostDirFD(f); } - // FIXME: This is totally broken on non-unix hosts - need to do some kind of cygwin type mapping public static class HostFS extends FS { - public static String fixPath(String path) throws FileNotFoundException { - return path; - } - - public String hostCWD() { - return getSystemProperty("user.dir"); - } - - // FEATURE: This shares a lot with Runtime.open - public FD open(String path, int flags, int mode) throws IOException { - path = fixPath(path); - final File f = new File(path); - // NOTE: createNewFile is a Java2 function - if((flags & (O_EXCL|O_CREAT)) == (O_EXCL|O_CREAT)) - if(!f.createNewFile()) throw new ErrnoException(EEXIST); - if(!f.exists() && (flags&O_CREAT) == 0) return null; - if(f.isDirectory()) { - if((flags&3)!=RD_ONLY) throw new ErrnoException(EACCES); - return directoryFD(f.list(),path.hashCode()); - } - final Seekable.File sf = new Seekable.File(path,(flags&3)!=RD_ONLY); - if((flags&O_TRUNC)!=0) sf.setLength(0); - return new SeekableFD(sf,mode) { - protected FStat _fstat() { return new HostFStat(f) { - public int size() { - try { return sf.length(); } catch(IOException e) { return 0; } + InodeCache inodes = new InodeCache(4000); + protected File root; + public File getRoot() { return root; } + + protected File hostFile(String path) { + char sep = File.separatorChar; + if(sep != '/') { + char buf[] = path.toCharArray(); + for(int i=0;i 'z' || path.charAt(1) != '/') + return null; + + path = drive + ":" + path.substring(1).replace('/', '\\'); + return new File(path); + } + + public CygdriveFS() { super("/"); } + } + + private static void putInt(byte[] buf, int off, int n) { + buf[off+0] = (byte)((n>>>24)&0xff); + buf[off+1] = (byte)((n>>>16)&0xff); + buf[off+2] = (byte)((n>>> 8)&0xff); + buf[off+3] = (byte)((n>>> 0)&0xff); } - private static class DevFStat extends FStat { - public int dev() { return 1; } - public int mode() { return 0666; } - public int type() { return S_IFCHR; } - public int nlink() { return 1; } - } - private static FD devZeroFD = new FD() { - public boolean readable() { return true; } - public boolean writable() { return true; } - public int read(byte[] a, int off, int length) { Arrays.fill(a,off,off+length,(byte)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(); } - }; - private static FD devNullFD = new FD() { - public boolean readable() { return true; } - public boolean writable() { return true; } - 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 class DevFS extends FS { - public FD open(String path, int mode, int flags) throws IOException { - if(path.equals("/null")) return devNullFD; - if(path.equals("/zero")) return devZeroFD; - if(path.startsWith("/fd/")) { + public static abstract class DirFD extends FD { + private int pos = -2; + + protected abstract int size(); + protected abstract String name(int n); + protected abstract int inode(int n); + protected abstract int myDev(); + 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; + int reclen; + OUTER: for(;len > 0 && pos < size();pos++){ + switch(pos) { + case -2: + case -1: + ino = pos == -1 ? parentInode() : myInode(); + if(ino == -1) continue; + reclen = 9 + (pos == -1 ? 2 : 1); + if(reclen > len) break OUTER; + buf[off+8] = '.'; + if(pos == -1) buf[off+9] = '.'; + break; + default: { + String f = name(pos); + byte[] fb = getBytes(f); + reclen = fb.length + 9; + if(reclen > len) break OUTER; + ino = inode(pos); + System.arraycopy(fb,0,buf,off+8,fb.length); + } + } + buf[off+reclen-1] = 0; // null terminate + reclen = (reclen + 3) & ~3; // add padding + putInt(buf,off,reclen); + putInt(buf,off+4,ino); + off += reclen; + len -= reclen; + } + return off-ooff; + } + + protected FStat _fstat() { + return new FStat() { + public int type() { return S_IFDIR; } + public int inode() { return myInode(); } + public int dev() { return myDev(); } + }; + } + } + + public static class DevFS extends FS { + private static final int ROOT_INODE = 1; + private static final int NULL_INODE = 2; + private static final int ZERO_INODE = 3; + private static final int FD_INODE = 4; + private static final int FD_INODES = 32; + + 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 { + public int myDev() { return devno; } + } + + private FD devZeroFD = new FD() { + public int read(byte[] a, int off, int length) { + /*Arrays.fill(a,off,off+length,(byte)0);*/ + for(int i=off;i= 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")) { + 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(); + 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; + } + + public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EROFS); } + public void unlink(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); } + } + + + public static class ResourceFS extends FS { + final InodeCache inodes = new InodeCache(500); + + public FStat lstat(UnixRuntime r, String path) throws ErrnoException { return stat(r,path); } + public void mkdir(UnixRuntime r, String path, int mode) throws ErrnoException { throw new ErrnoException(EROFS); } + public void unlink(UnixRuntime r, String path) throws ErrnoException { throw new ErrnoException(EROFS); } + + FStat connFStat(final URLConnection conn) { + return new FStat() { + public int type() { return S_IFREG; } + public int nlink() { return 1; } + public int mode() { return 0444; } + public int size() { return conn.getContentLength(); } + public int mtime() { return (int)(conn.getDate() / 1000); } + public int inode() { return inodes.get(conn.getURL().toString()); } + public int dev() { return devno; } + }; } - public void mkdir(String path) throws IOException { throw new ErrnoException(EACCES); } + public FStat stat(UnixRuntime r, String path) throws ErrnoException { + URL url = r.getClass().getResource("/" + path); + if(url == null) return null; + try { + return connFStat(url.openConnection()); + } catch(IOException e) { + throw new ErrnoException(EIO); + } + } + + public FD open(UnixRuntime r, String path, int flags, int mode) throws ErrnoException { + if((flags & ~3) != 0) { + if(STDERR_DIAG) + System.err.println("WARNING: Unsupported flags passed to ResourceFS.open(\"" + path + "\"): " + toHex(flags & ~3)); + throw new ErrnoException(ENOTSUP); + } + if((flags&3) != RD_ONLY) throw new ErrnoException(EROFS); + URL url = r.getClass().getResource("/" + path); + if(url == null) return null; + try { + final URLConnection conn = url.openConnection(); + Seekable.InputStream si = new Seekable.InputStream(conn.getInputStream()); + return new SeekableFD(si,flags) { protected FStat _fstat() { return connFStat(conn); } }; + } catch(FileNotFoundException e) { + if(e.getMessage() != null && e.getMessage().indexOf("Permission denied") >= 0) throw new ErrnoException(EACCES); + return null; + } catch(IOException e) { throw new ErrnoException(EIO); } + } } }