initial checkin
[org.ibex.nanogoat.git] / upstream / mips / org / xwt / mips / UnixRuntime.java
1 package org.xwt.mips;
2
3 import java.io.*;
4 import java.util.TimeZone;
5
6 public abstract class UnixRuntime extends Runtime {
7     /** The pid of this "process" */
8     private int pid;
9     private int ppid;
10     public int getPid() { return pid; }
11     
12     /* Static stuff */
13     private final Object waitNotification = new Object();
14     private final static int MAX_TASKS = 256;
15     private final static UnixRuntime[] tasks = new UnixRuntime[MAX_TASKS];
16     private static int addTask(UnixRuntime rt) {
17         synchronized(tasks) {
18             for(int i=1;i<MAX_TASKS;i++) {
19                 if(tasks[i] == null) {
20                     tasks[i] = rt;
21                     rt.pid = i;
22                     return i;
23                 }
24             }
25             return -1;
26         }
27     }
28     private static void removeTask(UnixRuntime rt) {
29         synchronized(tasks) {
30             for(int i=1;i<MAX_TASKS;i++)
31                 if(tasks[i] == rt) { tasks[i] = null; break; }
32         }
33     }
34     
35     public UnixRuntime(int pageSize, int totalPages, boolean allowEmptyPages) {
36         super(pageSize,totalPages,allowEmptyPages);
37     }
38     
39     private static String posixTZ() {
40         StringBuffer sb = new StringBuffer();
41         TimeZone zone = TimeZone.getDefault();
42         int off = zone.getRawOffset() / 1000;
43         sb.append(zone.getDisplayName(false,TimeZone.SHORT));
44         if(off > 0) sb.append("-");
45         else off = -off;
46         sb.append(off/3600); off = off%3600;
47         if(off > 0) sb.append(":").append(off/60); off=off%60;
48         if(off > 0) sb.append(":").append(off);
49         if(zone.useDaylightTime())
50             sb.append(zone.getDisplayName(true,TimeZone.SHORT));
51         return sb.toString();
52     }
53     
54     private static boolean envHas(String key,String[] environ) {
55         for(int i=0;i<environ.length;i++)
56             if(environ[i]!=null && environ[i].startsWith(key + "=")) return true;
57         return false;
58     }
59     
60     protected String[] createEnv(String[] extra) {
61         String[] defaults = new String[5];
62         int n=0;
63         if(extra == null) extra = new String[0];
64         if(!envHas("USER",extra) && getSystemProperty("user.name") != null)
65             defaults[n++] = "USER=" + getSystemProperty("user.name");
66         if(!envHas("HOME",extra) && getSystemProperty("user.name") != null)
67             defaults[n++] = "HOME=" + getSystemProperty("user.home");
68         if(!envHas("SHELL",extra)) defaults[n++] = "SHELL=/bin/sh";
69         if(!envHas("TERM",extra))  defaults[n++] = "TERM=vt100";
70         if(!envHas("TZ",extra))    defaults[n++] = "TZ=" + posixTZ();
71         String[] env = new String[extra.length+n];
72         for(int i=0;i<n;i++) env[i] = defaults[i];
73         for(int i=0;i<extra.length;i++) env[n++] = extra[i];
74         return env;
75     }
76     
77     protected void _start() {
78         if(addTask(this) < 0) throw new Error("Task list full");
79     }
80     
81     protected void _exit() {
82         synchronized(tasks) {
83             if(ppid == 0) removeTask(this);
84             for(int i=0;i<MAX_TASKS;i++) {
85                 if(tasks[i] != null && tasks[i].ppid == pid) {
86                     if(tasks[i].state == DONE) removeTask(tasks[i]);
87                     else tasks[i].ppid = 0;
88                 }
89             }
90             state = DONE;
91             if(ppid != 0) synchronized(tasks[ppid].waitNotification) { tasks[ppid].waitNotification.notify(); }
92         }
93     }
94
95     protected int syscall(int syscall, int a, int b, int c, int d) {
96         switch(syscall) {
97             case SYS_kill: return sys_kill(a,b);
98             case SYS_fork: return sys_fork();
99             case SYS_pipe: return sys_pipe(a);
100             case SYS_dup2: return sys_dup2(a,b);
101             case SYS_waitpid: return sys_waitpid(a,b,c);
102             default: return super.syscall(syscall,a,b,c,d);
103         }
104     }
105
106     /** The kill syscall.
107        SIGSTOP, SIGTSTO, SIGTTIN, and SIGTTOUT pause the process.
108        SIGCONT, SIGCHLD, SIGIO, and SIGWINCH are ignored.
109        Anything else terminates the process. */
110     private int sys_kill(int pid, int signal) {
111         // This will only be called by raise() in newlib to invoke the default handler
112         // We don't have to worry about actually delivering the signal
113         if(pid != pid) return -ESRCH;
114         if(signal < 0 || signal >= 32) return -EINVAL;
115         switch(signal) {
116             case 0: return 0;
117             case 17: // SIGSTOP
118             case 18: // SIGTSTP
119             case 21: // SIGTTIN
120             case 22: // SIGTTOU
121                 state = PAUSED;
122                 break;
123             case 19: // SIGCONT
124             case 20: // SIGCHLD
125             case 23: // SIGIO
126             case 28: // SIGWINCH
127                 break;
128             default: {
129                 String msg = "Terminating on signal: " + signal + "\n";
130                 exitStatus = 1;
131                 state = DONE;
132                 if(fds[2]==null) {
133                     System.out.print(msg);
134                 } else {
135                     try {
136                         byte[] b = msg.getBytes("US-ASCII"); 
137                         fds[2].write(b,0,b.length);
138                     }
139                     catch(UnsupportedEncodingException e){ throw new Error(e.getMessage()); }
140                     catch(IOException e) { }
141                 }
142             }
143         }
144         return 0;
145     }
146
147     private int sys_waitpid(int pid, int statusAddr, int options) {
148         final int WNOHANG = 1;
149         if((options & ~(WNOHANG)) != 0) return -EINVAL;
150         if(pid !=-1 && (pid <= 0 || pid >= MAX_TASKS)) return -ECHILD;
151         for(;;) {
152             synchronized(tasks) {
153                 UnixRuntime task = null;
154                 if(pid == -1) {
155                     for(int i=0;i<MAX_TASKS;i++) {
156                         if(tasks[i] != null && tasks[i].ppid == this.pid && tasks[i].state == DONE) {
157                             task = tasks[i];
158                             break;
159                         }
160                     }
161                 } else if(tasks[pid] != null && tasks[pid].ppid == this.pid && tasks[pid].state == DONE) {
162                     task = tasks[pid];
163                 }
164                 
165                 if(task != null) {
166                     removeTask(task);
167                     try {
168                         if(statusAddr!=0) memWrite(statusAddr,task.exitStatus()<<8);
169                     } catch(FaultException e) {
170                         return -EFAULT;
171                     }
172
173                     return task.pid;
174                 }
175             }
176             if((options&WNOHANG)!=0) return 0;
177             synchronized(waitNotification) {
178                 try { waitNotification.wait(); } catch(InterruptedException e) { throw new Error(e); }
179             }
180         }
181     }
182     
183     // Great ugliness lies within.....
184     private int sys_fork() {
185         CPUState state = getCPUState();
186         int sp = state.r[SP];
187         final UnixRuntime r;
188         try {
189             r = (UnixRuntime) getClass().newInstance();
190         } catch(Exception e) {
191             System.err.println(e);
192             return -ENOMEM;
193         }
194         int child_pid = addTask(r);
195         if(child_pid < 0) return -ENOMEM;
196         
197         r.ppid = pid;
198         r.brkAddr = brkAddr;
199         r.fds = new FD[OPEN_MAX];
200         for(int i=0;i<OPEN_MAX;i++) if(fds[i] != null) r.fds[i] = fds[i].dup();
201         r.cwd = cwd;
202         for(int i=0;i<TOTAL_PAGES;i++) {
203             if(readPages[i] == null) continue;
204             if(isEmptyPage(writePages[i])) {
205                 r.readPages[i] = r.writePages[i] = writePages[i];
206             } else if(writePages[i] != null) {
207                 r.readPages[i] = r.writePages[i] = new int[PAGE_WORDS];
208                 if(STACK_BOTTOM == 0 || i*PAGE_SIZE < STACK_BOTTOM || i*PAGE_SIZE >= sp-PAGE_SIZE*2)
209                     System.arraycopy(writePages[i],0,r.writePages[i],0,PAGE_WORDS);
210             } else {
211                 r.readPages[i] = r.readPages[i];
212             }
213         }
214         state.r[V0] = 0;
215         state.pc += 4;
216         r.setCPUState(state);
217         r.state = PAUSED;
218         
219         new Thread() {
220             public void run() {
221                 try {
222                     while(!r.execute());
223                 } catch(Exception e) {
224                     System.err.println("Forked process threw exception: ");
225                     e.printStackTrace();
226                 }
227             }
228         }.start();
229         
230         return child_pid;        
231     }
232             
233     private int sys_pipe(int addr) {
234         PipedOutputStream writerStream = new PipedOutputStream();
235         PipedInputStream readerStream;
236         try {
237              readerStream = new PipedInputStream(writerStream);
238         } catch(IOException e) {
239             return -EIO;
240         }
241         FD reader = new InputStreamFD(readerStream);
242         FD writer = new OutputStreamFD(writerStream);
243         int fd1 = allocFDEnt(reader);
244         if(fd1 < 0) return -ENFILE;
245         int fd2 = allocFDEnt(writer);
246         if(fd2 < 0) { closeFDEnt(fd1); return -ENFILE; }
247         try {
248             memWrite(addr,fd1);
249             memWrite(addr+4,fd2);
250         } catch(FaultException e) {
251             closeFDEnt(fd1);
252             closeFDEnt(fd2);
253             return -EFAULT;
254         }
255         return 0;
256     }
257     
258     private int sys_dup2(int oldd, int newd) {
259         if(oldd == newd) return 0;
260         FD oldFD;
261         try {
262             oldFD = fds[oldd];
263             if(oldFD == null) return -EBADFD;
264         } catch(ArrayIndexOutOfBoundsException e) {
265             return -EBADFD;
266         }
267         try {
268             if(fds[newd] != null) fds[newd].close();
269             fds[newd] = oldFD.dup();
270         } catch(ArrayIndexOutOfBoundsException e) {
271             return -EBADFD;
272         }
273         return 0;
274     }
275 }