/*LINTLIBRARY*/
#include "unix.h"
#include "extern.h"


/* Getpid() */

_getpid()
{
    return(udata.u_ptab->p_pid);
}

/* Getppid() */

_getppid()
{
    return(udata.u_ptab->p_pptr->p_pid);
}


/* Getuid() */

_getuid()
{
    return(udata.u_ptab->p_uid);
}


_getgid()
{
    return(udata.u_gid);
}


/*********************************
setuid(uid)
int uid;
***********************************/

#define uid (int)udata.u_argn

_setuid()
{
    if (super() || udata.u_ptab->p_uid == uid)
    {
	udata.u_ptab->p_uid = uid;
	udata.u_euid = uid;
	return(0);
    }
    udata.u_error = EPERM;
    return(-1);
}

#undef uid



/*****************************************
setgid(gid)
int gid;
****************************************/

#define gid (int16)udata.u_argn

_setgid()
{
    if (super() || udata.u_gid == gid)
    {
	udata.u_gid = gid;
	udata.u_egid = gid;
	return(0);
    }
    udata.u_error = EPERM;
    return(-1);
}

#undef gid;



/***********************************
time(tvec)
int tvec[];
**************************************/

#define tvec (int *)udata.u_argn

_time()
{
    int svec[2];

    rdtime(svec);  /* In machdep.c */
    uput(svec, tvec, 2*sizeof(int));
    return(0);
}

#undef tvec


/**************************************    
stime(tvec)
int tvec[];
**********************************/

#define tvec (int *)udata.u_argn

_stime()
{
/*
    int svec[2];

    ifnot (super())
    {
	udata.u_error = EPERM;
	return(-1);
    }

    uget(tvec, svec, 2*sizeof(int));
    if (udata.u_error)
	return (-1);

    sttime(tvec);
    return(0);
*/

    udata.u_error = EPERM;
    return(-1);
}

#undef tvec



/********************************************
times(buf)
char *buf;
**********************************************/

#define buf (char *)udata.u_argn

_times()
{
    ifnot (valadr(buf,6*sizeof(time_t)))
	return(-1);

    di();
    uput(&udata.u_utime, buf, 4*sizeof(time_t));
    uput(&ticks, buf + 4*sizeof(time_t), sizeof(time_t));
    ei();
    return(0);
}

#undef buf



/* User's execve() call. All other flavors are library routines. */

/*****************************************
execve(name, argv, envp)
char *name;
char *argv[];
char *envp[];
*****************************************/

#define name (char *)udata.u_argn2
#define argv (char **)udata.u_argn1
#define envp (char **)udata.u_argn

_execve()
{
    register inoptr ino;
    char *buf;
    inoptr n_open();
    char *bread();
    blkno_t bmap();

    blkno_t blk;
    char **nargv;   /* In user space */
    char **nenvp;   /* In user space */
    struct s_argblk *abuf, *ebuf;
    int (**sigp)();
    int argc;
    char **rargs();
    char *progptr;
    blkno_t pblk;
    int j;

    /* Bit of code that is poked into user address space to help signal
	catchers return correctly.  It pops HL, then all regs, and returns */

    static char sigprog[] = { 0xe1, 0xf1, 0xc1, 0xd1, 0xe1, 0xdd,
		 0xe1, 0xfd, 0xe1, 0xc9 };

    ifnot (ino = n_open(name,NULLINOPTR))
	return(-1);

    if (ino->c_node.i_size.o_blkno >= ((uint16)PROGTOP-(uint16)PROGBASE)/512)
    {
	udata.u_error = ENOMEM;
	goto nogood;
    }

    ifnot ( (getperm(ino) & OTH_EX) &&
	    (ino->c_node.i_mode & F_REG) &&
            (ino->c_node.i_mode & (OWN_EX | OTH_EX | GRP_EX)) )
    {
	udata.u_error = EACCES;
	goto nogood;
    }

    setftime(ino, A_TIME);

    /* Gather the arguments, and put them in temporary buffers. */
    /* Put environment in another buffer. */
    abuf = (struct s_argblk *)tmpbuf();
    ebuf = (struct s_argblk *)tmpbuf();
    if (wargs(argv, abuf) || wargs(envp, ebuf))
	goto nogood;


    /* Read in the first block of the new program */
    buf = bread( ino->c_dev, bmap(ino, 0, 1), 0);

    if ((*buf & 0xff) != EMAGIC)
    {
	udata.u_error = ENOEXEC;
	goto nogood2;
    }

    /* Here, check the setuid stuff. No other changes need be made in
    the user data */
    if (ino->c_node.i_mode & SET_UID)
	udata.u_euid = ino->c_node.i_uid;

    if (ino->c_node.i_mode & SET_GID)
	udata.u_egid = ino->c_node.i_gid;

    /* At this point, we are definitely going to succeed with the exec,
    so we can start writing over the old program */

#ifdef MEMDEBUG
    kprintf("Execing process %d\n", udata.u_ptab->p_pid);
#endif

    uput(buf,PROGBASE,512);
    brelse(buf);


    /* Release all pages (except the first), and mark them as VIRGIN.
       After loading the program, and setting the stack,
       mark all still-virgin pages as illegal.
	The first page of the process is reused.  */

    for (j=1; j<16; ++j)
    {
	if (realpage(udata.u_page[j]))
	    pg_free(udata.u_page[j]);
	udata.u_page[j] = VIRGPAGE;
    }
    ldmmu(udata.u_page);

    /* Read in the rest of the program */
    progptr = PROGBASE+512;
    for (blk = 1; blk <= ino->c_node.i_size.o_blkno; ++blk)
    {
        pblk = bmap(ino, blk, 1);
	if (pblk != -1)
	{
            buf = bread( ino->c_dev, pblk, 0);
	    uput(buf, progptr, 512);
	    brelse(buf);
	}
	progptr += 512;
    }
    i_deref(ino);


    /* Zero out the free memory remaining in the last allocated page */
    /* TODO (only up to 512 bytes necessary) */

    udata.u_break = progptr;

    /* Poke in the special signal catcher program */
    uput(sigprog, PROGTOP-sizeof(sigprog), sizeof(sigprog));

    /* Read back the arguments and the environment */
    nargv = rargs(PROGTOP-sizeof(sigprog), abuf, &argc);
    nenvp = rargs((char *)nargv, ebuf, NULL);
    brelse((char *)abuf);
    brelse((char *)ebuf);

    /* Fill in udata.u_name */
    uget(ugetw(nargv),udata.u_name,8);

    /* Turn off caught signals */
    for (sigp= udata.u_sigvec; sigp < (udata.u_sigvec+NSIGS); ++sigp)
	if (*sigp != SIG_IGN)
	    *sigp = SIG_DFL;

    /* Shove argc and the address of nargv just below nenvp */
    uputw(argc, nenvp - 1);
    uputw(nargv, nenvp - 2);

    /* Mark pages above program and below stack as illegal */
    rdmmu(udata.u_page);
    for (j=1; j<16; ++j)
    {
	if (udata.u_page[j] == VIRGPAGE)
	    udata.u_page[j] = NOPAGE;
    }
    ldmmu(udata.u_page);

    /* Go jump into the program, first setting the stack */
    doexec((int16 *)(udata.u_isp = nenvp - 2));

nogood2:
    brelse(buf);
nogood:
    i_deref(ino);
    return(-1);

}

#undef name
#undef argv
#undef envp



wargs(argv,argbuf)   /* argv in user space */
char **argv;
struct s_argblk *argbuf;
{
    register char *ptr;    /* Address of base of arg strings in user space */
    register int n;
    int c;
    register char *bufp;
    char *bread();

    argbuf->a_argc = 0;  /* Store argc in argbuf. */
    bufp = argbuf->a_buf;

    while (ptr = ugetw(argv++))
    {
	++(argbuf->a_argc);  /* Store argc in argbuf. */
	do
	{
	    *bufp++ = c = ugetc(ptr++);
	    if (bufp >= argbuf->a_buf+500)
		udata.u_error = E2BIG;

	    if (udata.u_error)
	    {
		bawrite((char *)argbuf);
		return (1);
	    }
	}
	while (c);
    }

    argbuf->a_arglen = bufp - argbuf->a_buf;  /*Store total string size. */

    return (0);
}




char **
rargs(ptr,argbuf,cnt)
register char *ptr;
struct s_argblk *argbuf;
int *cnt;
{
    char **argv;  /* Address of users argv[], just below ptr */
    int argc, arglen;
    char **argbase;
    char *sptr;
    char *bread();

    sptr = argbuf->a_buf;

    /* Move them into the users address space, at the very top */
    ptr -= (arglen = argbuf->a_arglen);
    if (arglen)
        uput(sptr, ptr, arglen);

    /* Set argv to point below the argument strings */
    argc = argbuf->a_argc;
    argbase = argv = (char**)ptr - (argc + 1);
    if (cnt)
  	*cnt = argc;

    /* Set each element of argv[] to point to its argument string */

    while (argc--)
    {
        uputw(ptr, argv++);
	if (argc)
	{
	    do
		++ptr;
	    while(*sptr++);
        }
    }
    uputw(argv, NULL);

    return (argbase);
}



/**********************************
brk(addr)
char *addr;
************************************/

#define addr (char *)udata.u_argn

_brk()
{
    register j;

    /* Don't allow break to be set past user's stack pointer */
    if (addr >= (char *)udata.u_usp)
    {
	udata.u_error = ENOMEM;
	return (-1);
    }

    udata.u_break = addr;

    /* Make all unallocated pages up to and including the page with
    the break address virgin pages */

    for (j=0; j <= addrpage(udata.u_break); ++j)
    {
	if (udata.u_page[j] == NOPAGE)
	    udata.u_page[j] = VIRGPAGE;
    }

    /* TODO: If the break is reduced, free the pages above the break */

    return(0);
}

#undef addr



/************************************
sbrk(incr)
uint16 incr;
***************************************/

#define incr (uint16)udata.u_argn

_sbrk()
{
    register char *oldbrk;

    udata.u_argn += (oldbrk = udata.u_break);
    if (_brk())
	return(-1);

    return((int)oldbrk);
}

#undef incr



/**************************************
wait(statloc)
int *statloc;
****************************************/

#define statloc (int *)udata.u_argn

_wait()
{
    register ptptr p;
    register int retval;
    
    if (statloc && !valadr(statloc, sizeof(int)))
    {
	udata.u_error = EFAULT;
	return(-1);
    }

    di();
    /* See if we have any children. */
    for (p=ptab;p < ptab+PTABSIZE; ++p)
    {
	if (p->p_status && p->p_pptr == udata.u_ptab && p != udata.u_ptab)
	    goto ok;
    }
    udata.u_error = ECHILD;
    ei();
    return (-1);

ok:
    /* Search for an exited child; */
    for (;;)
    {
	chksigs();
        if (udata.u_cursig)
	{
	    udata.u_error = EINTR;
	    return(-1);
	}
	di();
	for(p=ptab;p < ptab+PTABSIZE; ++p)
	{
	    if (p->p_status == P_ZOMBIE && p->p_pptr == udata.u_ptab)
	    {
		if (statloc)
		    uputw(p->p_exitval, statloc);
		p->p_status = P_EMPTY;
		retval = p->p_pid;

	        /* Add in child's time info */
		/* It was stored on top of p_wait in the childs process
		table entry */
		addtick(&udata.u_cutime, &(p->p_wait));
	 	addtick(&udata.u_cstime, (char *)(&(p->p_wait)) +
				 sizeof(time_t));

		ei();
		return(retval);
	    }
	}
	/* Nothing yet, so wait */
	psleep(udata.u_ptab);
    }

}

#undef statloc



/**************************************
_exit(val)
int16 val;
**************************************/

#define val (int16)udata.u_argn

__exit()
{
    doexit(val,0);
}

#undef val



doexit(val,val2)
int16 val;
int16 val2;
{
    register int16 j;
    register ptptr p;
    

#ifdef MEMDEBUG
    kprintf("process %d exiting\n", udata.u_ptab->p_pid);
#endif

    for (j=0; j < UFTSIZE; ++j)
    {
	ifnot (udata.u_files[j] & 0x80)  /* Portable equivalent of == -1 */
	    doclose(j);
    }

/*    _sync();  /* Not necessary, but a good idea. */

    di();
    udata.u_ptab->p_exitval = (val<<8) | (val2 & 0xff);

    /* Set child's parents to init */
    for(p=ptab;p < ptab+PTABSIZE; ++p)
    {
	if (p->p_status && p->p_pptr == udata.u_ptab)
	    p->p_pptr = initproc;
    }
    i_deref(udata.u_cwd);

    /* Stash away child's execution tick counts in process table,
    overlaying some no longer necessary stuff. */
    addtick(&udata.u_utime,&udata.u_cutime);
    addtick(&udata.u_stime,&udata.u_cstime);
    bcopy(&udata.u_utime, &(udata.u_ptab->p_wait), 2 * sizeof(time_t));

    /* Wake up a waiting parent, if any. */
    if (udata.u_ptab != initproc)
	wakeup((char *)udata.u_ptab->p_pptr);
    udata.u_ptab->p_status = P_ZOMBIE;

    /* Free all pages */
    for (j=0; j < 16; ++j)
    {
	if (realpage(udata.u_page[j]))
	    pg_free(udata.u_page[j]);
    }

    brelse(udata.u_ptab->p_ublk);  /* Release udata storage block */


    ei();
    swapin(getproc());
    panic("doexit:won't exit");
}


_fork()
{
   return (dofork());
}



_pause()
{
    psleep(0);
    udata.u_error = EINTR; 
    return(-1);
}


/*************************************
signal(sig, func)
int16 sig;
int16 (*func)();
***************************************/

#define sig (int16)udata.u_argn1
#define func (int (*)())udata.u_argn

_signal()
{
    int retval;

    di();
    if (sig < 1 || sig == SIGKILL || sig >= NSIGS)
    {
	udata.u_error = EINVAL;
	goto nogood;
    }

    if (func == SIG_IGN)
	udata.u_ptab->p_ignored |= sigmask(sig);
    else
    {
	if (func != SIG_DFL && !valadr((char *)func, 1))
	{
	    udata.u_error = EFAULT;
	    goto nogood;
	}
	udata.u_ptab->p_ignored &= ~sigmask(sig);
    }
    retval = udata.u_sigvec[sig];
    udata.u_sigvec[sig] = func;
    ei();
    return(retval);

nogood:
    ei();
    return(-1);
}

#undef sig
#undef func


    
/**************************************
kill(pid, sig)
int16 pid;
int16 sig;
*****************************************/

#define pid (int16)udata.u_argn1
#define sig (int16)udata.u_argn

_kill()
{
    ptptr p;
    
    if (sig <= 0 || sig > 15)
    {
	udata.u_error = EINVAL;
	return(-1);
    }

    ifnot (pid)
    {
	sgrpsig(udata.u_ptab->p_tty, sig);
	return (0);
    }

    for (p=ptab; p < ptab+PTABSIZE; ++p)
    {
	if (p->p_pid == pid)
	{
	    ssig(p,sig);
	    return(0);
 	}
    }

}

#undef pid
#undef sig



/********************************
alarm(secs)
uint16 secs;
*********************************/

#define secs (int16)udata.u_argn

_alarm()
{
    int retval;

    di();
    retval = udata.u_ptab->p_alarm;
    udata.u_ptab->p_alarm = secs;
    ei();
    return(retval);
}

#undef secs
