#include <sys/param.h>
#include <sys/systm.h>
#include <sys/proc.h>
#include <sys/caps.h>
#include <sys/vnode.h>
#include <sys/ptrace.h>
#include <sys/signalvar.h>
#include <vfs/procfs/procfs.h>
#include <sys/signal2.h>
#include <sys/spinlock2.h>
#include <vm/vm.h>
#ifndef FIX_SSTEP
#define FIX_SSTEP(lp)
#endif
#define TRACE_WAIT_P(curp, p) \
(((p)->p_stat == SSTOP) && \
(p)->p_pptr == (curp) && \
((p)->p_flags & P_TRACED))
#define PROCFS_CTL_ATTACH 1
#define PROCFS_CTL_DETACH 2
#define PROCFS_CTL_STEP 3
#define PROCFS_CTL_RUN 4
#define PROCFS_CTL_WAIT 5
static vfs_namemap_t ctlnames[] = {
{ "attach", PROCFS_CTL_ATTACH },
{ "detach", PROCFS_CTL_DETACH },
{ "step", PROCFS_CTL_STEP },
{ "run", PROCFS_CTL_RUN },
{ "wait", PROCFS_CTL_WAIT },
{ 0 },
};
static vfs_namemap_t signames[] = {
{ "hup", SIGHUP }, { "int", SIGINT },
{ "quit", SIGQUIT }, { "ill", SIGILL },
{ "trap", SIGTRAP }, { "abrt", SIGABRT },
{ "iot", SIGIOT }, { "emt", SIGEMT },
{ "fpe", SIGFPE }, { "kill", SIGKILL },
{ "bus", SIGBUS }, { "segv", SIGSEGV },
{ "sys", SIGSYS }, { "pipe", SIGPIPE },
{ "alrm", SIGALRM }, { "term", SIGTERM },
{ "urg", SIGURG }, { "stop", SIGSTOP },
{ "tstp", SIGTSTP }, { "cont", SIGCONT },
{ "chld", SIGCHLD }, { "ttin", SIGTTIN },
{ "ttou", SIGTTOU }, { "io", SIGIO },
{ "xcpu", SIGXCPU }, { "xfsz", SIGXFSZ },
{ "vtalrm", SIGVTALRM }, { "prof", SIGPROF },
{ "winch", SIGWINCH }, { "info", SIGINFO },
{ "usr1", SIGUSR1 }, { "usr2", SIGUSR2 },
{ 0 },
};
static int procfs_control (struct proc *curp, struct lwp *lp, int op);
static int
procfs_control(struct proc *curp, struct lwp *lp, int op)
{
struct proc *p = lp->lwp_proc;
int error;
ASSERT_LWKT_TOKEN_HELD(&p->p_token);
if ((p->p_flags & P_INEXEC) != 0)
return EAGAIN;
if (op != PROCFS_CTL_DETACH) {
if (securelevel > 0 && p->p_pid == 1)
return (EPERM);
if (!CHECKIO(curp, p) || p_trespass(curp->p_ucred, p->p_ucred))
return (EPERM);
}
if (op == PROCFS_CTL_ATTACH) {
if (p->p_flags & P_TRACED)
return (EBUSY);
if (p->p_pid == curp->p_pid)
return (EINVAL);
p->p_flags |= P_TRACED;
p->p_xstat = 0;
if (p->p_pptr != curp) {
p->p_oppid = p->p_pptr->p_pid;
proc_reparent(p, curp);
}
proc_stop(p, SSTOP);
return (0);
}
switch (op) {
case PROCFS_CTL_DETACH:
case PROCFS_CTL_WAIT:
break;
default:
if (!TRACE_WAIT_P(curp, p))
return (EBUSY);
}
#ifdef FIX_SSTEP
FIX_SSTEP(lp);
#endif
p->p_xstat = 0;
switch (op) {
case PROCFS_CTL_DETACH:
if ((p->p_flags & P_TRACED) == 0)
return (0);
p->p_flags &= ~P_TRACED;
spin_lock(&lp->lwp_spin);
lwp_delsig(lp, SIGTRAP, 1);
spin_unlock(&lp->lwp_spin);
if (p->p_oppid != p->p_pptr->p_pid) {
struct proc *pp;
pp = pfs_pfind(p->p_oppid);
if (pp) {
proc_reparent(p, pp);
pfs_pdone(pp);
}
}
p->p_oppid = 0;
p->p_flags &= ~P_WAITED;
wakeup((caddr_t) curp);
break;
case PROCFS_CTL_STEP:
LWPHOLD(lp);
error = procfs_sstep(lp);
LWPRELE(lp);
if (error)
return (error);
break;
case PROCFS_CTL_RUN:
break;
case PROCFS_CTL_WAIT:
error = 0;
if (p->p_flags & P_TRACED) {
while (error == 0 &&
p->p_stat != SSTOP &&
(p->p_flags & P_TRACED) &&
(p->p_pptr == curp))
{
error = tsleep((caddr_t) p, PCATCH,
"procfsx", 0);
}
if (error == 0 && !TRACE_WAIT_P(curp, p))
error = EBUSY;
} else {
while (error == 0 && p->p_stat != SSTOP) {
error = tsleep((caddr_t) p, PCATCH,
"procfs", 0);
}
}
return (error);
default:
panic("procfs_control");
}
if (p->p_stat == SSTOP)
proc_unstop(p, SSTOP);
return (0);
}
int
procfs_doctl(struct proc *curp, struct lwp *lp, struct pfsnode *pfs,
struct uio *uio)
{
struct proc *p = lp->lwp_proc;
int xlen;
int error;
char msg[PROCFS_CTLLEN+1];
vfs_namemap_t *nm;
ASSERT_LWKT_TOKEN_HELD(&p->p_token);
if (uio->uio_rw != UIO_WRITE)
return (EOPNOTSUPP);
xlen = PROCFS_CTLLEN;
error = vfs_getuserstr(uio, msg, &xlen);
if (error)
return (error);
error = EOPNOTSUPP;
nm = vfs_findname(ctlnames, msg, xlen);
if (nm) {
error = procfs_control(curp, lp, nm->nm_val);
} else {
nm = vfs_findname(signames, msg, xlen);
if (nm) {
if (TRACE_WAIT_P(curp, p)) {
p->p_xstat = nm->nm_val;
#ifdef FIX_SSTEP
FIX_SSTEP(lp);
#endif
proc_unstop(p, SSTOP);
} else {
ksignal(p, nm->nm_val);
}
error = 0;
}
}
return (error);
}