Coherent4.2.10/coh.386/sys1.c

Compare this file to the similar file:
Show the results in this format:

/* $Header: /ker/coh.386/RCS/sys1.c,v 2.7 93/10/29 00:55:39 nigel Exp Locker: nigel $ */
/* (lgl-
 *	The information contained herein is a trade secret of Mark Williams
 *	Company, and  is confidential information.  It is provided  under a
 *	license agreement,  and may be  copied or disclosed  only under the
 *	terms of  that agreement.  Any  reproduction or disclosure  of this
 *	material without the express written authorization of Mark Williams
 *	Company or persuant to the license agreement is unlawful.
 *
 *	COHERENT Version 2.3.37
 *	Copyright (c) 1982, 1983, 1984.
 *	An unpublished work by Mark Williams Company, Chicago.
 *	All rights reserved.
 -lgl) */
/*
 * General system calls.
 *
 * $Log:	sys1.c,v $
 * Revision 2.7  93/10/29  00:55:39  nigel
 * R98 (aka 4.2 Beta) prior to removing System Global memory
 * 
 * Revision 2.6  93/09/02  18:08:16  nigel
 * Nigel's r85, minor edits only
 * 
 * Revision 2.5  93/08/25  12:32:16  nigel
 * Update wait () entry point for new flag system
 * 
 * Revision 2.4  93/08/19  03:26:50  nigel
 * Nigel's r83 (Stylistic cleanup)
 */

#include <common/_gregset.h>
#include <kernel/sig_lib.h>
#include <kernel/proc_lib.h>
#include <kernel/cred_lib.h>
#include <kernel/reg.h>
#include <sys/wait.h>
#include <sys/errno.h>
#include <sys/times.h>
#include <signal.h>
#include <sys/stat.h>
#include <stddef.h>
#include <unistd.h>

#define	_KERNEL		1

#include <kernel/_timers.h>
#include <kernel/trace.h>
#include <sys/uproc.h>
#include <sys/acct.h>
#include <sys/con.h>
#include <sys/proc.h>
#include <sys/sched.h>
#include <sys/seg.h>
#include <sys/cred.h>

/*
 * Send alarm signal to specified process - function timed by ualarm ()
 */

#if	__USE_PROTO__
void sigalrm (PROC * pp)
#else
void
sigalrm (pp)
PROC * pp;
#endif
{
	sendsig (SIGALRM, pp);
}


/*
 * Send a SIGALARM signal in `n' seconds.
 */

int
ualarm (n)
unsigned n;
{
	PROC * pp = SELF;
	unsigned s;

	/*
	 * Calculate time left before current alarm timeout.
	 */

	s = 0;
	if (pp->p_alrmtim.t_last != NULL)
		s = (pp->p_alrmtim.t_lbolt - lbolt + HZ - 1) / HZ;

	/*
	 * Cancel previous alarm [if any], start new alarm [if n != 0].
	 */

	timeout2 (& pp->p_alrmtim, (long) n * HZ, sigalrm, pp);

	/*
	 * Return time left before previous alarm timeout.
	 */

	return s;
}


/*
 * Change the size of our data segment.
 */

char *
ubrk (cp)
caddr_t cp;
{
	SEG * sp;
	caddr_t sb;
	SR	* stack_sr;
	caddr_t top_of_stack;

	/*
	 * Pick up the segment handle for our data segment.
	 */

	sp = SELF->p_segl [SIPDATA].sr_segp;


	/*
	 * Extract the starting virtual address for our data segment,
	 * as it is currently mapped into the memory space.
	 */

	sb = SELF->p_segl [SIPDATA].sr_base;


	/*
	 * We can not move the top of the data segment below the
	 * start of the data segment.
	 */

	if (cp < sb) {
		SET_U_ERROR (ENOMEM,
		    "Requested brk address is below start of data segment.");
		return -1;
	}


	/*
	 * Would the request cause a collision with the stack segment?
	 *
	 * Since the stack grows downward, its top is below its base :-).
	 */

	stack_sr = & SELF->p_segl [SISTACK];
	top_of_stack = stack_sr->sr_base - stack_sr->sr_size;

	if (btocru (cp) >= btocru (top_of_stack)) {
		SET_U_ERROR (ENOMEM,
		    "Requested brk address would collide with stack segment.");
 		return -1;
	}

	/*
	 * Attempt to establish the segment with the newly requested size.
	 */

	segsize (sp, cp - sb);


	/*
	 * Be sure to return the true new top of data segment.
	 */

	sb += sp->s_size;

	T_HAL (0x8000, printf ("=%x ", sb));
	return sb;
}


/*
 * Execute an l.out or COFF file.
 */

int
uexece (np, argp, envp, regsetp)
char	      *	np;
char	      *	argp [];
char	      *	envp [];
gregset_t     *	regsetp;
{
	return pexece (np, argp, envp, regsetp);
}


/*
 * Exit.
 */

void
uexit (s)
unsigned	s;
{
	pexit ((s & 0xFF) << 8);
}


/*
 * Fork.
 */

pid_t
ufork ()
{
	pid_t		child_pid;

	if ((child_pid = pfork ()) == 0) {
		/*
		 * Child.
		 */

		u.u_rval2 = SELF->p_pid;
		return 0;
	}

	/*
	 * Parent.
	 */

	u.u_rval2 = 0;
	return child_pid;
}


/*
 * Get group id.
 * Get effective group id.
 */

gid_t
ugetgid ()
{
	u.u_rval2 = SELF->p_credp->cr_gid;
	return SELF->p_credp->cr_rgid;
}


/*
 * Get user id.
 * Get effective user id.
 */

uid_t
ugetuid ()
{
	u.u_rval2 = SELF->p_credp->cr_uid;
	return SELF->p_credp->cr_ruid;
}


/*
 * Get process group.
 * Set the process group.
 *
 * This is System V type setpgrp ().
 * Set process group equal to process id (make process its own group leader).
 * If process was NOT already a group leader, lose its controlling terminal.
 */

int
upgrp (fl)
int	fl;
{
	if (fl) {
		if (SELF->p_group != SELF->p_pid)
			SELF->p_ttdev = NODEV;
		SELF->p_group = SELF->p_pid;
	}
	return SELF->p_group;
}


/*
 * Get process id.
 */

pid_t
ugetpid ()
{
	u.u_rval2 = SELF->p_ppid;
	return SELF->p_pid;
}


/*
 * See if we have permission to send the signal, `sig' to the process, `pp'.
 */

#if	__USE_PROTO__
__LOCAL__ int sigperm (int sig, __proc_t * procp)
#else
__LOCAL__ int
sigperm (sig, procp)
int sig;
__proc_t * procp;
#endif
{
	/*
	 * Note that when we support job control, SIGCONT requires only that
	 * the target process be in the same session.
	 */

#ifdef	_POSIX_SAVED_IDS
	if (SELF->p_credp->cr_suid == procp->p_credp->cr_suid ||
	    SELF->p_credp->cr_ruid == procp->p_credp->cr_ruid)
		return 1;
#else
	if (SELF->p_credp->cr_uid == procp->p_credp->cr_uid ||
	    SELF->p_credp->cr_ruid == procp->p_credp->cr_ruid)
		return 1;
#endif

#if	0
	/*
	 * NIGEL: This is wrong! I have moved the real-id match code above.
	 */

	if (SELF->p_credp->cr_ruid == procp->p_credp->cr_ruid &&
	    (sig == SIGHUP || sig == SIGINT || sig == SIGQUIT ||
	     sig == SIGTERM)) 
		return 1;
#endif

	if (SELF->p_credp->cr_uid == 0) {
		u.u_flag |= ASU;
		return 1;
	}
	return 0;
}


/*
 * Iteration function user below in ukill ().
 */

#if	__USE_PROTO__
int _kill_proc (__proc_t * procp, __VOID__ * arg)
#else
int
_kill_proc (procp, arg)
__proc_t      *	procp;
__VOID__      *	arg;
#endif
{
	int		sig;

	ASSERT (procp != NULL && arg != NULL);

	sig = * (int *) arg;

	if (sigperm (sig, procp)) {
		if (sig)
			sendsig (sig, procp);
	} else
		set_user_error (EPERM);

	return 0;	/* continue iterating */
}


/*
 * Send the signal `sig' to the process with id `pid'.
 */

int
ukill (pid, sig)
int pid;
unsigned sig;
{
	PROC	      *	procp;
	int sigflag;

	if (! __IN_RANGE (0, sig, _SIGNAL_MAX)) {
		set_user_error (EINVAL);
		return -1;
	}

	sigflag = 0;

	if (pid > 0) {	/* send to matching process */
		if ((procp = process_find_pid (pid)) != NULL &&
		    procp->p_state != PSDEAD) {
		    	/*
		    	 * Originally, this code didn't do the permissions
		    	 * checking if sig == 0.
		    	 */

			if (sigperm (sig, procp)) {
				if (sig)
					sendsig (sig, procp);
			} else
				set_user_error (EPERM);
			sigflag = 1;
		}
	} else if (pid < -1)
		sigflag = process_find_pgid (- pid, _kill_proc, & sig);
	else if (pid == 0)
		sigflag = process_find_pgid (SELF->p_group, _kill_proc, & sig);
	else if (pid == -1)
		sigflag = process_find_all (_kill_proc, & sig);

	if (sigflag == 0)
		set_user_error (ESRCH);

	return 0;
}


/*
 * Lock a process in core.
 */

int
ulock (f)
int f;
{
	if (super () == 0)
		return -1;
	if (f)
		SELF->p_flags |= PFLOCK;
	else
		SELF->p_flags &= ~ PFLOCK;
	return 0;
}


/*
 * Change priority by the given increment.
 */

int
unice (n)
int n;
{
	n += SELF->p_nice;
	if (n < MINNICE)
		n = MINNICE;
	if (n > MAXNICE)
		n = MAXNICE;
	if (n < SELF->p_nice && super () == 0)
		return -1;
	SELF->p_nice = n;
	return 0;
}


/*
 * Non existant system call.
 */

int
unone ()
{
	set_user_error (EFAULT);
	return -1;
}


/*
 * Pause.  Go to sleep on a channel that nobody will wakeup so that only
 * signals will wake us up.
 */

int
upause ()
{
	x_sleep ((char *) & u, prilo, slpriSigLjmp, "pause");
	return 0;
}


/*
 * Start / stop profiling.
 *
 * buff:	address in user data of an array of shorts
 * bufsiz:	number of bytes in the area at buff
 * offset:	address in user text of start of profiling area
 * scale:	0 or 1 - turn off profiling
 *		other - treat as 16 bit scale factor
 *
 * For purposes of compatibility with System 5, scale values work as follows:
 *	0xFFFF	profile buffer is same length as text being profiled.
 *	0x7FFF  profile buffer is half as long as text being profiled.
 *	0x4000	profile buffer is one fourth as long as text profiled.
 *		(each short in the buffer covers 8 bytes of text)
 *	...	...
 *	0x0002	each short in the buffer covers 64K bytes of text.
 *
 * Values 0xFFFF and 0x7FFF are used, for historical reasons, when 0x10000
 * and 0x8000, respectively, should be used.  To clean up the ensuing
 * arithmetic, there is an upward rounding kluge below.
 *
 * Each clock interrupt, take (pc - offset) * scale * (2 **-16) as a byte
 * offset into pbase.  Add 1 to the short at or below the given address
 * when profiling.
 */

int
uprofil (buff, bufsiz, offset, scale)
short * buff;
int bufsiz, offset, scale;
{
	u.u_pbase = (caddr_t) buff;
	u.u_pbend = (caddr_t) (buff + bufsiz);
	u.u_pofft = offset;
	u.u_pscale = scale & 0xffff;	/* scale is really unsigned short */

	/* round up kluge - see above */
	if ((scale & 0xfff) == 0xfff)
		u.u_pscale ++;
	return 0;
}


/*
 * Process trace.
 */

int
uptrace (req, pid, add, data)
int	req;
int * add;
pid_t		pid;
int		data;
{
	int ret;

#if	TRACER & TRACE_HAL
	int readChild = 0;	/* for debug, true if reading child memory */

	if (t_hal & 0x10000) {
		switch (req) {
		case 0:	/* init called by child */
			printf ("PSetup: child =%d  ", SELF->p_pid);
			break;
		case 1:	/* parent reads child text */
			printf ("PRdT: add =%x ", add);
			readChild = 1;
			break;
		case 2:	/* parent reads child data */
			printf ("PRdD: add =%x ", add);
			readChild = 1;
			break;
		case 3:	/* parent reads child u area */
			printf ("PRdU: add =%x ", add);
			readChild = 1;
			break;
		case 4:	/* parent writes child text */
			printf ("PWrT: add =%x data =%x  ", add, data);
			break;
		case 5:	/* parent writes child data */
			printf ("PWrD: add =%x data =%x  ", add, data);
			break;
		case 6:	/* parent writes child u area */
			printf ("PWrU: add =%x data =%x ", add, data);
			break;
		case 7:	/* resume child, maybe fake signal to child */
			printf ("PResume: sig =%d  ", data);
			break;
		case 8:	/* terminate child */
			printf ("PTerm: pid =%d  ", pid);
			break;
		case 9:	/* single-step child, maybe fake signal to child */
			printf ("PSStp: sig =%d  ", data);
			break;
		}
	}
#endif

	if (req == 0) {
		SELF->p_flags |= PFTRAC;
		ret = 0;
	} else
		ret = ptset (req, pid, add, data);

#if	TRACER & TRACE_HAL
	if (t_hal & 0x10000) {
		if (readChild)
			printf ("data =%x  ", ret);
	}
#endif

	return ret;
}


/*
 * Set group id.
 *
 * As in SVID issue 2:
 *
 * if effective gid is superuser
 *	set real, effective, and saved effective gid to argument "gid"
 * else if real gid is same as "gid"
 *	set effective gid to "gid"
 * else if saved effective gid is same as "gid"
 *	set effective gid to "gid"
 */

int
usetgid (gid)
o_gid_t gid;
{
	cred_t	      *	credp;

	if (super ()) {
		if ((credp = cred_newgid (SELF->p_credp, gid)) == NULL)
			set_user_error (EAGAIN);
		else
			SELF->p_credp = credp;
	} else {
		/* super () sets u_error when it fails */
		set_user_error (0);

		if (SELF->p_credp->cr_rgid == gid ||
		    SELF->p_credp->cr_sgid == gid) {
			if ((credp = cred_newegid (SELF->p_credp,
						   gid)) == NULL)
				set_user_error (EAGAIN);
			else
				SELF->p_credp = credp;
		} else
			SET_U_ERROR (EPERM, "Illegal gid");
	}
	return 0;
}


/*
 * Set user id.
 *
 * As in SVID issue 2:
 *
 * if effective uid is superuser
 *	set real, effective, and saved effective uid to argument "uid"
 * else if real uid is same as "uid"
 *	set effective uid to "uid"
 * else if saved effective uid is same as "uid"
 *	set effective uid to "uid"
 */

int
usetuid (uid)
o_uid_t uid;
{
	cred_t	      *	credp;

	if (super ()) {
		if ((credp = cred_newuid (SELF->p_credp, uid)) == NULL)
			set_user_error (EAGAIN);
		else
			SELF->p_credp = credp;
	} else {
		/* super () sets u_error when it fails */
		set_user_error (0);

		if (SELF->p_credp->cr_ruid == uid ||
		    SELF->p_credp->cr_suid == uid) {
			if ((credp = cred_neweuid (SELF->p_credp,
						   uid)) == NULL)
				set_user_error (EAGAIN);
			else
				SELF->p_credp = credp;
		} else
			SET_U_ERROR (EPERM, "Illegal uid");
	}
	return 0;
}


/*
 * Set time and date.
 *
 * Unlike the libc interface, this routine expects a time_t value
 * as an arg, not a time_t pointer.
 */

int
ustime (newtime)
time_t newtime;
{
	int s;

	if (super () == 0)
		return -1;

	s = sphi ();
	timer.t_time = newtime;
	spl (s);
	return 0;
}


/*
 * Return process times.
 */

int
utimes (tp)
struct tms * tp;
{
	struct tms tbuffer;

	if (tp != NULL) {
		tbuffer.tms_utime = SELF->p_utime;
		tbuffer.tms_stime = SELF->p_stime;
		tbuffer.tms_cutime = SELF->p_cutime;
		tbuffer.tms_cstime = SELF->p_cstime;
		kucopyS (& tbuffer, tp, sizeof (tbuffer));
	}
	return lbolt;
}


/*
 * Wait for a child to terminate. If this function is called from i286 code,
 * then we will have a "statp" argument, where we stash the exit status, but
 * for the i386 code we put a value in u.u_rval2.
 *
 * NIGEL: The idiom for looping over the process table must be changed to
 * work right ASAP.
 */

int
uwait (statp)
short	      *	statp;
{
	PROC * pp;
	PROC * ppp;
	PROC * cpp;
	int pid;

	/* Wait for a child to stop or die. */

	T_HAL (8, printf ("[%d]waits ", SELF->p_pid));
	ppp = SELF;

	for (;;) {
		/* Look at all processes. */
again:
		__GLOBAL_LOCK_PROCESS_TABLE ("uwait ()");
		cpp = NULL;
		pp = & procq;
		while ((pp = pp->p_nforw) != & procq) {

			/* Ignore the current process. */
			if (pp == ppp)
				continue;
			/*
			 * Ignore processes that aren't children of the
			 * current one.
			 */
			if (pp->p_ppid != ppp->p_pid ||
			    (pp->p_flags & PFSTOP) != 0)
				continue;

			/* Here is a child that hit a breakpoint. */
			if (pp->p_flags & PFWAIT) {
				pp->p_flags &= ~ PFWAIT;
				pp->p_flags |= PFSTOP;

				if (statp != NULL)
					putusd (statp, pp->p_exit);
				else
					u.u_rval2 = pp->p_exit;

				__GLOBAL_UNLOCK_PROCESS_TABLE ();
				T_HAL (8,
				       printf ("[%d]ends waiting, %d stopped ",
					       SELF->p_pid, pid));
				return pp->p_pid;
			}
			if (pp->p_state == PSDEAD) {
				ppp->p_cutime += pp->p_utime + pp->p_cutime;
				ppp->p_cstime += pp->p_stime + pp->p_cstime;

				if (statp != NULL)
					putusd (statp, pp->p_exit);
				else
					u.u_rval2 = pp->p_exit;
				pid = pp->p_pid;
				__GLOBAL_UNLOCK_PROCESS_TABLE ();
				relproc (pp);

				if ((proc_signal_misc (ppp) &
				     __SF_NOCLDWAIT) != 0)
					goto again;

				T_HAL (8, printf ("[%d]ends waiting, %d died ",
						  SELF->p_pid, pid));
				return pid;
			}
			cpp = pp;
		}
		__GLOBAL_UNLOCK_PROCESS_TABLE ();

		if (cpp == NULL) {
			T_HAL (8, printf ("[%d]ends waiting, no children ",
					  SELF->p_pid));
			set_user_error (ECHILD);
			return -1;
		}
		(void) x_sleep ((char *) ppp, prilo, slpriSigLjmp, "wait");
		/* Wait for a child to terminate.  */
	}
}


/*
 * waitpid () and wait () share the same system call number under BCS.
 *
 * pid argument:
 *	>  0	wait for child whose process matches pid
 *	=  0	wait for any child in current process group
 *	= -1	wait for any child - same as wait ()
 *	< -1	wait for any child in group given by -pid
 *
 * The only waitpid () options supported are WNOHANG and WUNTRACED.
 */

int
uwaitpid (opid, stat_loc, options)
pid_t opid;
int	* stat_loc, options;
{
	PROC * pp;
	PROC * ppp;
	PROC * cpp;
	int pid;

	if ((options & ~ (WUNTRACED | WNOHANG)) != 0) {
		cmn_err (CE_NOTE, "!waitpid (%d,0x%x,0x%x): unsupported", opid,
		  (unsigned) stat_loc, options);
		set_user_error (EINVAL);
		return -1;
	}

	/* Wait for a child to stop or die. */
	ppp = SELF;
	for (;;) {
		/* Look at all processes. */
again:
		__GLOBAL_LOCK_PROCESS_TABLE ("uwaitpid ()");
		cpp = NULL;
		pp = & procq;
		while ((pp = pp->p_nforw) != & procq) {

			/* Ignore the current process. */
			if (pp == ppp)
				continue;
			/*
			 * Ignore processes that aren't children of the
			 * current one.
			 */
			if (pp->p_ppid != ppp->p_pid ||
			    (pp->p_flags & PFSTOP) != 0)
				continue;

			/* If opid == 0 we want to match gids */
			/* If opid > 0, want to match opid to child pid */
			/* If opid <-1, want to match -opid to child gid */
			if ((opid == 0 && pp->p_group != ppp->p_group) ||
			    (opid > 0 && opid != pp->p_pid) ||
			    (opid < -1 && - opid != pp->p_group))
				continue;

			/* if opid == -1, then any child is acceptable */

			/* Here is an acceptable child that hit a breakpoint. */
			if (pp->p_flags & PFWAIT) {
				pp->p_flags &= ~ PFWAIT;
				pp->p_flags |= PFSTOP;
				u.u_rval2 = pp->p_exit;

				__GLOBAL_UNLOCK_PROCESS_TABLE ();
				return pp->p_pid;
			}

			/* Here is an acceptable child that is a zombie. */
			if (pp->p_state == PSDEAD) {
				ppp->p_cutime += pp->p_utime + pp->p_cutime;
				ppp->p_cstime += pp->p_stime + pp->p_cstime;
				u.u_rval2 = pp->p_exit;
				pid = pp->p_pid;
				__GLOBAL_UNLOCK_PROCESS_TABLE ();
				relproc (pp);

				if ((proc_signal_misc (ppp) &
				     __SF_NOCLDWAIT) != 0)
					goto again;

				return pid;
			}
			cpp = pp;
		}

		__GLOBAL_UNLOCK_PROCESS_TABLE ();

		if (cpp == NULL) {
			set_user_error (ECHILD);
			return -1;
		}

		if (options & WNOHANG) {
               		u.u_rval2 = 0;
			return 0;
		}

		/* Wait for a child to terminate. */
		(void) x_sleep ((char *) ppp, prilo, slpriSigLjmp,
				"waitpid");
	}
}


/*
 * Wait for a child to terminate.
 *
 * iBCS2 says the same system call number is wait () and waitpid (), the
 * distinction being in how the psw is set on entry.
 *
 * iBCS2 fails to mention that when wait () or waitpid () report status
 * by writing into the pointer supplied, the status is put into %edx by
 * the kernel, and moved from there into user space by the function in
 * libc.a.  uwait () and uwaitpid () specify a value for %edx by writing
 * to u.u_rval2.
 *
 * Do wait () unless (ZF | PF | SF | OF) (= WPMASK) are set in psw.
 */

#define	WPMASK	(__FLAG_MASK (1, __ZERO) | __FLAG_MASK (1, __PARITY) | \
		 __FLAG_MASK (1, __SIGN) | __FLAG_MASK (1, __OVERFLOW))

int
uwait386(arg1, arg2, arg3, regsetp)
unsigned long	arg1;
unsigned long	arg2;
unsigned long	arg3;
gregset_t     *	regsetp;
{
	return ((__flag_arith_t) __FLAG_REG (regsetp) & WPMASK) == WPMASK ?
			uwaitpid (arg1, arg2, arg3) : uwait (NULL);
}