#include #include #include #include "linuxsys.h" #include "linux.h" SYSCALL(sys_exit) { int n = ARG1; char buf[12]; DPRINT("exit(%d)...\n", n); if(n == 0) exits(0); sprint(buf, "%d", n); exits(buf); abort(); } SYSCALL(sys_exit_group) { int n = ARG1; char buf[12]; DPRINT("exit_group(%d)...\n", n); if(n == 0) exits(0); sprint(buf, "%d", n); exits(buf); abort(); } enum { CLONE_VM =0x00000100, CLONE_FS =0x00000200, CLONE_FILES =0x00000400, CLONE_SIGHAND =0x00000800, CLONE_PTRACE =0x00002000, CLONE_VFORK =0x00004000, CLONE_PARENT =0x00008000, CLONE_THREAD =0x00010000, CLONE_NEWNS =0x00020000, CLONE_SYSVSEM =0x00040000, CLONE_SETTLS =0x00080000, CLONE_PARENT_SETTID =0x00100000, CLONE_CHILD_CLEARTID=0x00200000, CLONE_DETACHED =0x00400000, CLONE_UNTRACED =0x00800000, CLONE_CHILD_SETTID =0x01000000, CLONE_STOPPED =0x02000000, }; static void finishproc(void) { if(threadp->exitsig){ _kill(threadp->ptid, threadp->exitsig); } if(threadp->cleartidaddr) *threadp->cleartidaddr = 0; } int _kill(int pid, int sig) { int fd; char buf[80]; char *msg; DPRINT("kill(%d, %d)... from pid %d\n", pid, sig, threadp->tid); if((msg = sigstring(sig)) == nil){ return -EINVAL; } snprint(buf, sizeof(buf), "/proc/%d/note", pid); if((fd = open(buf, OWRITE)) < 0) return -ESRCH; write(fd, msg, strlen(msg)); close(fd); return 0; } typedef struct Ureg Ureg; typedef struct Cloneargs Cloneargs; struct Cloneargs { Ureg *ureg; ulong cflags; ulong newstack; int *parenttidptr; int *childtidptr; }; static int _clone(void *aux) { int r; int rflags; Cloneargs a, *x; Ureg u; x = aux; DPRINT("_clone: my stack is 0x%p\n", &r); memcpy(&a, x, sizeof(a)); memcpy(&u, x->ureg, sizeof(u)); rflags = RFREND|RFFDG|RFPROC|RFENVG; if(a.cflags & CLONE_FILES) rflags &= ~RFFDG; if(a.cflags & CLONE_VM){ rflags |= RFMEM; rflags &= ~RFREND; } if(a.cflags & CLONE_SETTLS){ fprint(2, "clone(): TLS is not supported!\n"); abort(); } if((a.cflags & CLONE_VM) && !(a.cflags & CLONE_FILES)){ fprint(2, "clone(): thread will share memory but not filedescriptors!\n"); abort(); } DPRINT("clone(): do rfork(0x%x)...\n", rflags); r = rfork(rflags); DPRINT("clone(): rfork(0x%x) -> %d in pid %d\n", rflags, r, getpid()); if(r < 0){ r = mkerror(); fprint(2, "clone(): rfork failed: %r\n"); return r; } if(r==0){ threadp->tid = getpid(); threadp->exitsig = a.cflags & 0xFF; sigclearall(&threadp->ss); threadp->ss.level = 0; DPRINT("clone(): thread/proc with tid %d starting...", threadp->tid); if(!(a.cflags & CLONE_THREAD)) threadp->tgid = threadp->tid; if(!(a.cflags & CLONE_THREAD) || !(a.cflags & CLONE_PARENT)) threadp->ptid = getppid(); threadp->cleartidaddr = nil; if(a.cflags & CLONE_CHILD_CLEARTID) threadp->cleartidaddr = a.childtidptr; if(a.cflags & CLONE_CHILD_SETTID){ assert(a.childtidptr!=nil); *a.childtidptr = threadp->tid; } if(!(a.cflags & CLONE_VM)) forkallfdtags(); atexit(finishproc); if(a.newstack){ u.sp = a.newstack; } /* child returns directly to proc */ u.pc += 2; u.ax = r; jumpureg(&u); abort(); } if(a.cflags & CLONE_PARENT_SETTID){ assert(a.parenttidptr!=nil); *a.parenttidptr = r; } return r; } static int clone(struct Ureg *ureg, ulong cflags, ulong newstack, int *parenttidptr, int *childtidptr) { Cloneargs a; DPRINT("clone()..."); a.ureg = ureg; a.cflags = cflags; a.newstack = newstack; a.parenttidptr = parenttidptr; a.childtidptr = childtidptr; return do9stack(_clone, &a); } SYSCALL(sys_fork) { int r; DPRINT("fork()..."); r = clone(ureg, 0, 0, nil, nil); RETURN(r); } SYSCALL(sys_clone) { int r; ulong cflags; ulong newstack; int *parenttidptr; int *childtidptr; cflags = (int)ARG1; newstack = (ulong)ARG2; parenttidptr = (int*)ARG3; childtidptr = (int*)ARG5; DPRINT("clone(0x%lux, 0x%lux, 0x%p, ..., 0x%p)...", cflags, newstack, parenttidptr, childtidptr); if(!newstack) newstack = ureg->sp; r = clone(ureg, cflags, newstack, parenttidptr, childtidptr); RETURN(r); } SYSCALL(sys_set_tid_address) { int r; int *tidaddr; tidaddr = (int*)ARG1; DPRINT("set_tid_address(0x%p)...", tidaddr); threadp->cleartidaddr = tidaddr; r = threadp->tgid; RETURN(r); } struct timeval { long tv_sec; /* seconds */ long tv_usec; /* microseconds */ }; struct rusage { struct timeval ru_utime; /* user time used */ struct timeval ru_stime; /* system time used */ long ru_maxrss; /* maximum resident set size */ long ru_ixrss; /* integral shared memory size */ long ru_idrss; /* integral unshared data size */ long ru_isrss; /* integral unshared stack size */ long ru_minflt; /* page reclaims */ long ru_majflt; /* page faults */ long ru_nswap; /* swaps */ long ru_inblock; /* block input operations */ long ru_oublock; /* block output operations */ long ru_msgsnd; /* messages sent */ long ru_msgrcv; /* messages received */ long ru_nsignals; /* signals received */ long ru_nvcsw; /* voluntary context switches */ long ru_nivcsw; /* involuntary " */ }; static int waitstatus(Waitmsg *w) { int r, t; char *bp, *ep; r = 0; t = 0; if(w->msg[0]){ /* message is 'prog pid:string' */ bp = w->msg; while(*bp){ if(*bp++ == ':') break; } if(*bp == 0) bp = w->msg; r = strtol(bp, &ep, 10); if(*ep == 0){ if(r < 0 || r >= 256) r = 1; }else{ t = stringsig(bp); if(t == 0) r = 1; } } return (r<<8) | t; } static void waitresource(struct rusage *ru, Waitmsg *w) { memset(ru, 0, sizeof(*ru)); ru->ru_utime.tv_sec = w->time[0]/1000; ru->ru_utime.tv_usec = (w->time[0]%1000)*1000; ru->ru_stime.tv_sec = w->time[1]/1000; ru->ru_stime.tv_usec = (w->time[1]%1000)*1000; } typedef struct Waited Waited; struct Waited { Waited *next; Waitmsg *w; }; static int wait4(int wpid, int *status, int options, struct rusage *ru) { static Waited *wl; static Lock wllock; Waited **i; Waitmsg *w; // fprint(2, "wait4: %d\n", getpid()); w = nil; lock(&wllock); for(i=&wl; *i; i=&((*i)->next)){ if(wpid <= 0 || (*i)->w->pid == wpid){ Waited *t; t = (*i); w = t->w; *i = t->next; free(t); break; } } unlock(&wllock); if(w == nil){ if(options & WNOHANG){ char pname[50]; Dir *d; snprint(pname, sizeof(pname), "/proc/%d/wait", getpid()); d = dirstat(pname); if(d != nil && d->length == 0){ free(d); return 0; } free(d); } for(;;){ Waited *e; w = wait(); if(w == nil){ return -1; } if(wpid <= 0 || w->pid == wpid) break; e = malloc(sizeof(Waited)); e->w = w; lock(&wllock); e->next = wl; wl = e; unlock(&wllock); } } if(ru != nil) waitresource(ru, w); if(status != nil) *status = waitstatus(w); wpid = w->pid; free(w); return wpid; } SYSCALL(sys_wait4) { int pid; int *status; int opt; struct rusage *ru; pid = (int)ARG1; status = (int*)ARG2; opt = (int)ARG3; ru = (struct rusage*)ARG4; DPRINT("wait4(%d, 0x%p, %d, 0x%p)...", pid, status, opt, ru); RETURN(wait4(pid, status, opt, ru)); } SYSCALL(sys_waitpid) { int pid; int *status; int opt; pid = (int)ARG1; status = (int*)ARG2; opt = (int)ARG3; DPRINT("waitpid(%d, 0x%p, %d)...", pid, status, opt); RETURN(wait4(pid, status, opt, nil)); } static int _exec(void *aux) { char **eargv; eargv = aux; mmapexit(); exec(eargv[0], eargv); return mkerror(); } SYSCALL(sys_execve) { char *name; char **argv; char **envp; char **eargv; int i, j; Dir *d; name = (char*)ARG1; argv = (char**)ARG2; envp = (char**)ARG3; DPRINT("execve(%s, %p, %p)...", name, argv, envp); if((d = dirstat(name)) == nil){ RETURN(mkerror()); } free(d); /* * what we do is to cat the emu arg's and the requested * together and exec() it. for example: execve("/bin/ls", ["ls"], ...) * becomes exec("/bin/linuxemu", ["/bin/linuxemu", "-d", "/bin/ls"]). * then we let the emulator figure out what kind the executable * is. */ for(j=0; emuargv[j]; j++) ; for(i=0; argv[i]; i++) ; eargv = malloc(sizeof(char*)*(i+j+1)); for(i=0; emuargv[i]; i++) eargv[i] = strdup(emuargv[i]); for(i=0; argv[i]; i++) eargv[i+j] = strdup(argv[i]); eargv[j] = strdup(name); eargv[i+j] = nil; if(envp) writeenv(envp); RETURN(do9stack(_exec, eargv)); } SYSCALL(sys_getpid) { DPRINT("getpid()..."); RETURN(threadp->pid); } SYSCALL(sys_getppid) { DPRINT("getppid()..."); RETURN(threadp->ptid); } SYSCALL(sys_gettid) { DPRINT("gettid()..."); RETURN(threadp->tid); } SYSCALL(sys_getpgrp) { RETURN(threadp->gid); } SYSCALL(sys_newgetuid) { RETURN(threadp->uid); } SYSCALL(sys_newgetgid) { RETURN(threadp->gid); } SYSCALL(sys_newgetegid) { RETURN(threadp->gid); } SYSCALL(sys_newgeteuid) { RETURN(threadp->uid); }