#include <u.h>
#include <libc.h>
#include <ureg.h>
#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);
}
|