merge in vnode changes
[unix-history] / usr / src / sys / kern / kern_sig.c
/*
* Copyright (c) 1982, 1986, 1989 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms are permitted
* provided that the above copyright notice and this paragraph are
* duplicated in all such forms and that any documentation,
* advertising materials, and other materials related to such
* distribution and use acknowledge that the software was developed
* by the University of California, Berkeley. The name of the
* University may not be used to endorse or promote products derived
* from this software without specific prior written permission.
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*
* @(#)kern_sig.c 7.3.1.1 (Berkeley) %G%
*/
#include "param.h"
#include "systm.h"
#include "dir.h"
#include "ucred.h"
#include "user.h"
#include "vnode.h"
#include "proc.h"
#include "timeb.h"
#include "times.h"
#include "buf.h"
#include "mount.h"
#include "text.h"
#include "seg.h"
#include "vm.h"
#include "acct.h"
#include "uio.h"
#include "file.h"
#include "kernel.h"
#include "machine/reg.h"
#include "machine/pte.h"
#include "machine/psl.h"
#include "machine/mtpr.h"
#define cantmask (sigmask(SIGKILL)|sigmask(SIGCONT)|sigmask(SIGSTOP))
#define stopsigmask (sigmask(SIGSTOP)|sigmask(SIGTSTP)| \
sigmask(SIGTTIN)|sigmask(SIGTTOU))
/*
* Generalized interface signal handler.
*/
sigvec()
{
register struct a {
int signo;
struct sigvec *nsv;
struct sigvec *osv;
} *uap = (struct a *)u.u_ap;
struct sigvec vec;
register struct sigvec *sv;
register int sig;
int bit;
sig = uap->signo;
if (sig <= 0 || sig >= NSIG || sig == SIGKILL || sig == SIGSTOP) {
u.u_error = EINVAL;
return;
}
sv = &vec;
if (uap->osv) {
sv->sv_handler = u.u_signal[sig];
sv->sv_mask = u.u_sigmask[sig];
bit = sigmask(sig);
sv->sv_flags = 0;
if ((u.u_sigonstack & bit) != 0)
sv->sv_flags |= SV_ONSTACK;
if ((u.u_sigintr & bit) != 0)
sv->sv_flags |= SV_INTERRUPT;
u.u_error =
copyout((caddr_t)sv, (caddr_t)uap->osv, sizeof (vec));
if (u.u_error)
return;
}
if (uap->nsv) {
u.u_error =
copyin((caddr_t)uap->nsv, (caddr_t)sv, sizeof (vec));
if (u.u_error)
return;
if (sig == SIGCONT && sv->sv_handler == SIG_IGN) {
u.u_error = EINVAL;
return;
}
setsigvec(sig, sv);
}
}
setsigvec(sig, sv)
int sig;
register struct sigvec *sv;
{
register struct proc *p;
register int bit;
bit = sigmask(sig);
p = u.u_procp;
/*
* Change setting atomically.
*/
(void) splhigh();
u.u_signal[sig] = sv->sv_handler;
u.u_sigmask[sig] = sv->sv_mask &~ cantmask;
if (sv->sv_flags & SV_INTERRUPT)
u.u_sigintr |= bit;
else
u.u_sigintr &= ~bit;
if (sv->sv_flags & SV_ONSTACK)
u.u_sigonstack |= bit;
else
u.u_sigonstack &= ~bit;
if (sv->sv_handler == SIG_IGN) {
p->p_sig &= ~bit; /* never to be seen again */
p->p_sigignore |= bit;
p->p_sigcatch &= ~bit;
} else {
p->p_sigignore &= ~bit;
if (sv->sv_handler == SIG_DFL)
p->p_sigcatch &= ~bit;
else
p->p_sigcatch |= bit;
}
(void) spl0();
}
sigblock()
{
struct a {
int mask;
} *uap = (struct a *)u.u_ap;
register struct proc *p = u.u_procp;
(void) splhigh();
u.u_r.r_val1 = p->p_sigmask;
p->p_sigmask |= uap->mask &~ cantmask;
(void) spl0();
}
sigsetmask()
{
struct a {
int mask;
} *uap = (struct a *)u.u_ap;
register struct proc *p = u.u_procp;
(void) splhigh();
u.u_r.r_val1 = p->p_sigmask;
p->p_sigmask = uap->mask &~ cantmask;
(void) spl0();
}
sigpause()
{
struct a {
int mask;
} *uap = (struct a *)u.u_ap;
register struct proc *p = u.u_procp;
/*
* When returning from sigpause, we want
* the old mask to be restored after the
* signal handler has finished. Thus, we
* save it here and mark the proc structure
* to indicate this (should be in u.).
*/
u.u_oldmask = p->p_sigmask;
p->p_flag |= SOMASK;
p->p_sigmask = uap->mask &~ cantmask;
for (;;)
sleep((caddr_t)&u, PSLEP);
/*NOTREACHED*/
}
#undef cantmask
sigstack()
{
register struct a {
struct sigstack *nss;
struct sigstack *oss;
} *uap = (struct a *)u.u_ap;
struct sigstack ss;
if (uap->oss) {
u.u_error = copyout((caddr_t)&u.u_sigstack, (caddr_t)uap->oss,
sizeof (struct sigstack));
if (u.u_error)
return;
}
if (uap->nss) {
u.u_error =
copyin((caddr_t)uap->nss, (caddr_t)&ss, sizeof (ss));
if (u.u_error == 0)
u.u_sigstack = ss;
}
}
kill()
{
register struct a {
int pid;
int signo;
} *uap = (struct a *)u.u_ap;
register struct proc *p;
if (uap->signo < 0 || uap->signo > NSIG) {
u.u_error = EINVAL;
return;
}
if (uap->pid > 0) {
/* kill single process */
p = pfind(uap->pid);
if (p == 0) {
u.u_error = ESRCH;
return;
}
if (u.u_uid && u.u_uid != p->p_uid)
u.u_error = EPERM;
else if (uap->signo)
psignal(p, uap->signo);
return;
}
switch (uap->pid) {
case -1: /* broadcast signal */
u.u_error = killpg1(uap->signo, 0, 1);
break;
case 0: /* signal own process group */
u.u_error = killpg1(uap->signo, 0, 0);
break;
default: /* negative explicit process group */
u.u_error = killpg1(uap->signo, -uap->pid, 0);
break;
}
return;
}
killpg()
{
register struct a {
int pgid;
int signo;
} *uap = (struct a *)u.u_ap;
if (uap->signo < 0 || uap->signo > NSIG) {
u.u_error = EINVAL;
return;
}
u.u_error = killpg1(uap->signo, uap->pgid, 0);
}
/* KILL CODE SHOULDNT KNOW ABOUT PROCESS INTERNALS !?! */
killpg1(signo, pgid, all)
int signo, pgid, all;
{
register struct proc *p;
struct pgrp *pgrp;
int f = 0, error = 0;
if (all)
/*
* broadcast
*/
for (p = allproc; p != NULL; p = p->p_nxt) {
if (p->p_ppid == 0 || p->p_flag&SSYS ||
p == u.u_procp ||
(u.u_uid && u.u_uid != p->p_uid &&
!(signo == SIGCONT && inferior(p))))
continue;
f++;
if (signo)
psignal(p, signo);
}
else {
if (pgid == 0)
/*
* zero pgid means send to my process group.
*/
pgrp = u.u_procp->p_pgrp;
else {
pgrp = pgfind(pgid);
if (pgrp == NULL)
return(ESRCH);
}
if (!(pgrp->pg_jobc) &&
(signo==SIGTTIN || signo==SIGTTOU || signo==SIGTSTP))
return(EPERM);
for (p = pgrp->pg_mem; p != NULL; p = p->p_pgrpnxt) {
if (p->p_ppid == 0 || p->p_flag&SSYS)
continue;
if (u.u_uid && u.u_uid != p->p_uid &&
!(signo == SIGCONT && inferior(p))) {
error = EPERM;
continue;
}
f++;
if (signo)
psignal(p, signo);
}
}
return (error ? error : (f == 0 ? ESRCH : 0));
}
/*
* Send the specified signal to
* all processes with 'pgid' as
* process group.
*/
gsignal(pgid, sig)
{
register struct pgrp *pgrp;
register struct proc *p;
if (!pgid)
return;
if ((pgrp = pgfind(pgid)) == NULL)
return;
pgsignal(pgrp, sig);
}
pgsignal(pgrp, sig)
register struct pgrp *pgrp;
{
register struct proc *p;
if (!(pgrp->pg_jobc) &&
(sig==SIGTTIN || sig==SIGTTOU || sig==SIGTSTP))
return;
for (p = pgrp->pg_mem; p != NULL; p = p->p_pgrpnxt)
psignal(p, sig);
}
/*
* Send the specified signal to
* the specified process.
*/
psignal(p, sig)
register struct proc *p;
register int sig;
{
register int s;
register int (*action)();
int mask;
if ((unsigned)sig >= NSIG)
return;
mask = sigmask(sig);
/*
* If proc is traced, always give parent a chance.
*/
if (p->p_flag & STRC)
action = SIG_DFL;
else {
/*
* If the signal is being ignored,
* then we forget about it immediately.
*/
if (p->p_sigignore & mask)
return;
if (p->p_sigmask & mask)
action = SIG_HOLD;
else if (p->p_sigcatch & mask)
action = SIG_CATCH;
else
action = SIG_DFL;
}
if (sig) {
switch (sig) {
case SIGTERM:
if ((p->p_flag&STRC) || action != SIG_DFL)
break;
/* fall into ... */
case SIGKILL:
if (p->p_nice > NZERO)
p->p_nice = NZERO;
break;
case SIGCONT:
p->p_sig &= ~stopsigmask;
break;
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
/*FALLTHROUGH*/
case SIGSTOP:
p->p_sig &= ~sigmask(SIGCONT);
break;
}
p->p_sig |= mask;
}
/*
* Defer further processing for signals which are held.
*/
if (action == SIG_HOLD)
return;
s = splhigh();
switch (p->p_stat) {
case SSLEEP:
/*
* If process is sleeping at negative priority
* we can't interrupt the sleep... the signal will
* be noticed when the process returns through
* trap() or syscall().
*/
if (p->p_pri <= PZERO)
goto out;
/*
* Process is sleeping and traced... make it runnable
* so it can discover the signal in issig() and stop
* for the parent.
*/
if (p->p_flag&STRC)
goto run;
switch (sig) {
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
/*
* These are the signals which by default
* stop a process.
*/
if (action != SIG_DFL)
goto run;
/*
* If a child in vfork(), stopping could
* cause deadlock.
*/
if (p->p_flag&SVFORK)
goto out;
p->p_sig &= ~mask;
p->p_cursig = sig;
psignal(p->p_pptr, SIGCHLD);
stop(p);
goto out;
case SIGIO:
case SIGURG:
case SIGCHLD:
case SIGWINCH:
/*
* These signals are special in that they
* don't get propogated... if the process
* isn't interested, forget it.
*/
if (action != SIG_DFL)
goto run;
p->p_sig &= ~mask; /* take it away */
goto out;
default:
/*
* All other signals cause the process to run
*/
goto run;
}
/*NOTREACHED*/
case SSTOP:
/*
* If traced process is already stopped,
* then no further action is necessary.
*/
if (p->p_flag&STRC)
goto out;
switch (sig) {
case SIGKILL:
/*
* Kill signal always sets processes running.
*/
goto run;
case SIGCONT:
/*
* If the process catches SIGCONT, let it handle
* the signal itself. If it isn't waiting on
* an event, then it goes back to run state.
* Otherwise, process goes back to sleep state.
*/
if (action != SIG_DFL || p->p_wchan == 0)
goto run;
p->p_stat = SSLEEP;
goto out;
case SIGSTOP:
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
/*
* Already stopped, don't need to stop again.
* (If we did the shell could get confused.)
*/
p->p_sig &= ~mask; /* take it away */
goto out;
default:
/*
* If process is sleeping interruptibly, then
* unstick it so that when it is continued
* it can look at the signal.
* But don't setrun the process as its not to
* be unstopped by the signal alone.
*/
if (p->p_wchan && p->p_pri > PZERO)
unsleep(p);
goto out;
}
/*NOTREACHED*/
default:
/*
* SRUN, SIDL, SZOMB do nothing with the signal,
* other than kicking ourselves if we are running.
* It will either never be noticed, or noticed very soon.
*/
if (p == u.u_procp && !noproc)
aston();
goto out;
}
/*NOTREACHED*/
run:
/*
* Raise priority to at least PUSER.
*/
if (p->p_pri > PUSER)
p->p_pri = PUSER;
setrun(p);
out:
splx(s);
}
/*
* Returns true if the current
* process has a signal to process.
* The signal to process is put in p_cursig.
* This is asked at least once each time a process enters the
* system (though this can usually be done without actually
* calling issig by checking the pending signal masks.)
* A signal does not do anything
* directly to a process; it sets
* a flag that asks the process to
* do something to itself.
*/
issig()
{
register struct proc *p;
register int sig;
int sigbits, mask;
p = u.u_procp;
for (;;) {
sigbits = p->p_sig &~ p->p_sigmask;
if ((p->p_flag&STRC) == 0)
sigbits &= ~p->p_sigignore;
if (p->p_flag&SVFORK)
sigbits &= ~stopsigmask;
if (sigbits == 0)
break;
sig = ffs((long)sigbits);
mask = sigmask(sig);
p->p_sig &= ~mask; /* take the signal! */
p->p_cursig = sig;
if (p->p_flag&STRC && (p->p_flag&SVFORK) == 0) {
/*
* If traced, always stop, and stay
* stopped until released by the parent.
*/
psignal(p->p_pptr, SIGCHLD);
do {
stop(p);
swtch();
} while (!procxmt() && p->p_flag&STRC);
/*
* If the traced bit got turned off,
* then put the signal taken above back into p_sig
* and go back up to the top to rescan signals.
* This ensures that p_sig* and u_signal are consistent.
*/
if ((p->p_flag&STRC) == 0) {
p->p_sig |= mask;
continue;
}
/*
* If parent wants us to take the signal,
* then it will leave it in p->p_cursig;
* otherwise we just look for signals again.
*/
sig = p->p_cursig;
if (sig == 0)
continue;
/*
* If signal is being masked put it back
* into p_sig and look for other signals.
*/
mask = sigmask(sig);
if (p->p_sigmask & mask) {
p->p_sig |= mask;
continue;
}
}
switch ((int)u.u_signal[sig]) {
case SIG_DFL:
/*
* Don't take default actions on system processes.
*/
if (p->p_ppid == 0)
break;
switch (sig) {
case SIGTSTP:
case SIGTTIN:
case SIGTTOU:
case SIGSTOP:
if (p->p_flag&STRC)
continue;
psignal(p->p_pptr, SIGCHLD);
stop(p);
swtch();
continue;
case SIGCONT:
case SIGCHLD:
case SIGURG:
case SIGIO:
case SIGWINCH:
/*
* These signals are normally not
* sent if the action is the default.
*/
continue; /* == ignore */
default:
goto send;
}
/*NOTREACHED*/
case SIG_HOLD:
case SIG_IGN:
/*
* Masking above should prevent us
* ever trying to take action on a held
* or ignored signal, unless process is traced.
*/
if ((p->p_flag&STRC) == 0)
printf("issig\n");
continue;
default:
/*
* This signal has an action, let
* psig process it.
*/
goto send;
}
/*NOTREACHED*/
}
/*
* Didn't find a signal to send.
*/
p->p_cursig = 0;
return (0);
send:
/*
* Let psig process the signal.
*/
return (sig);
}
/*
* Put the argument process into the stopped
* state and notify the parent via wakeup.
* Signals are handled elsewhere.
*/
stop(p)
register struct proc *p;
{
p->p_stat = SSTOP;
p->p_flag &= ~SWTED;
wakeup((caddr_t)p->p_pptr);
}
/*
* Perform the action specified by
* the current signal.
* The usual sequence is:
* if (issig())
* psig();
* The signal bit has already been cleared by issig,
* and the current signal number stored in p->p_cursig.
*/
psig()
{
register struct proc *p = u.u_procp;
register int sig = p->p_cursig;
int mask = sigmask(sig), returnmask;
register int (*action)();
if (sig == 0)
panic("psig");
action = u.u_signal[sig];
if (action != SIG_DFL) {
if (action == SIG_IGN || (p->p_sigmask & mask))
panic("psig action");
u.u_error = 0;
/*
* Set the new mask value and also defer further
* occurences of this signal (unless we're simulating
* the old signal facilities).
*
* Special case: user has done a sigpause. Here the
* current mask is not of interest, but rather the
* mask from before the sigpause is what we want restored
* after the signal processing is completed.
*/
(void) splhigh();
if (p->p_flag & SOUSIG) {
if (sig != SIGILL && sig != SIGTRAP) {
u.u_signal[sig] = SIG_DFL;
p->p_sigcatch &= ~mask;
}
mask = 0;
}
if (p->p_flag & SOMASK) {
returnmask = u.u_oldmask;
p->p_flag &= ~SOMASK;
} else
returnmask = p->p_sigmask;
p->p_sigmask |= u.u_sigmask[sig] | mask;
(void) spl0();
u.u_ru.ru_nsignals++;
sendsig(action, sig, returnmask);
p->p_cursig = 0;
return;
}
u.u_acflag |= AXSIG;
switch (sig) {
case SIGILL:
case SIGIOT:
case SIGBUS:
case SIGQUIT:
case SIGTRAP:
case SIGEMT:
case SIGFPE:
case SIGSEGV:
case SIGSYS:
u.u_arg[0] = sig;
if (core() == 0)
sig += 0200;
}
exit(sig);
}
/*
* Create a core image on the file "core"
* If you are looking for protection glitches,
* there are probably a wealth of them here
* when this occurs to a suid command.
*
* It writes UPAGES block of the
* user.h area followed by the entire
* data+stack segments.
*/
core()
{
register struct vnode *vp, *dvp;
register struct nameidata *ndp = &u.u_nd;
struct vattr vattr;
int error;
if (u.u_uid != u.u_ruid || u.u_gid != u.u_rgid)
return (EFAULT);
if (ctob(UPAGES + u.u_dsize + u.u_ssize) >=
u.u_rlimit[RLIMIT_CORE].rlim_cur)
return (EFAULT);
if (u.u_procp->p_textp) {
vop_lock(u.u_procp->p_textp->x_vptr);
error = vn_access(u.u_procp->p_textp->x_vptr, VREAD, u.u_cred);
vop_unlock(u.u_procp->p_textp->x_vptr);
if (error)
return (EFAULT);
}
ndp->ni_segflg = UIO_SYSSPACE;
ndp->ni_dirp = "core";
if (error = vn_open(ndp, FCREAT|FWRITE, 0644))
return (error);
vp = ndp->ni_vp;
if (vp->v_type != VREG ||
vop_getattr(vp, &vattr, u.u_cred) ||
vattr.va_nlink != 1) {
error = EFAULT;
goto out;
}
#ifdef MMAP
{ register int fd;
/* unmasp funky devices in the user's address space */
for (fd = 0; fd < u.u_lastfile; fd++)
if (u.u_ofile[fd] && (u.u_pofile[fd] & UF_MAPPED))
munmapfd(fd);
}
#endif
vattr_null(&vattr);
vattr.va_size = 0;
vop_setattr(vp, &vattr, u.u_cred);
u.u_acflag |= ACORE;
error = vn_rdwr(UIO_WRITE, vp, (caddr_t)&u, ctob(UPAGES), (off_t)0,
UIO_SYSSPACE, IO_UNIT, (int *)0);
if (error == 0)
error = vn_rdwr(UIO_WRITE, vp,
(caddr_t)ctob(dptov(u.u_procp, 0)),
(int)ctob(u.u_dsize), (off_t)ctob(UPAGES),
UIO_USERSPACE, IO_UNIT, (int *)0);
if (error == 0)
error = vn_rdwr(UIO_WRITE, vp,
(caddr_t)ctob(sptov(u.u_procp, u.u_ssize - 1)),
(int)ctob(u.u_ssize),
(off_t)ctob(UPAGES) + ctob(u.u_dsize),
UIO_USERSPACE, IO_UNIT, (int *)0);
out:
if (vp)
vrele(vp);
return (error);
}