165521Spendry /*
265521Spendry * Copyright (c) 1993 Jan-Simon Pendry
365808Sbostic * Copyright (c) 1993
465808Sbostic * The Regents of the University of California. All rights reserved.
565521Spendry *
665521Spendry * This code is derived from software contributed to Berkeley by
765521Spendry * Jan-Simon Pendry.
865521Spendry *
965521Spendry * %sccs.include.redist.c%
1065521Spendry *
11*67389Spendry * @(#)procfs_ctl.c 8.4 (Berkeley) 06/15/94
1265521Spendry *
1365521Spendry * From:
1465521Spendry * $Id: procfs_ctl.c,v 3.2 1993/12/15 09:40:17 jsp Exp $
1565521Spendry */
1665521Spendry
1765521Spendry #include <sys/param.h>
1865521Spendry #include <sys/systm.h>
1965521Spendry #include <sys/time.h>
2065521Spendry #include <sys/kernel.h>
2165521Spendry #include <sys/proc.h>
2265521Spendry #include <sys/vnode.h>
2365521Spendry #include <sys/ioctl.h>
2465521Spendry #include <sys/tty.h>
2565521Spendry #include <sys/resource.h>
2665521Spendry #include <sys/resourcevar.h>
27*67389Spendry #include <sys/ptrace.h>
2865521Spendry #include <miscfs/procfs/procfs.h>
2965521Spendry
30*67389Spendry #ifndef FIX_SSTEP
31*67389Spendry #define FIX_SSTEP(p)
32*67389Spendry #endif
33*67389Spendry
34*67389Spendry
3565521Spendry /*
3665521Spendry * True iff process (p) is in trace wait state
3765521Spendry * relative to process (curp)
3865521Spendry */
3965521Spendry #define TRACE_WAIT_P(curp, p) \
4065521Spendry ((p)->p_stat == SSTOP && \
4165521Spendry (p)->p_pptr == (curp) && \
4265521Spendry ((p)->p_flag & P_TRACED))
4365521Spendry
4465521Spendry #define PROCFS_CTL_ATTACH 1
4565521Spendry #define PROCFS_CTL_DETACH 2
4665521Spendry #define PROCFS_CTL_STEP 3
4765521Spendry #define PROCFS_CTL_RUN 4
4865521Spendry #define PROCFS_CTL_WAIT 5
4965521Spendry
5065521Spendry static vfs_namemap_t ctlnames[] = {
5165521Spendry /* special /proc commands */
5265521Spendry { "attach", PROCFS_CTL_ATTACH },
5365521Spendry { "detach", PROCFS_CTL_DETACH },
5465521Spendry { "step", PROCFS_CTL_STEP },
5565521Spendry { "run", PROCFS_CTL_RUN },
5665521Spendry { "wait", PROCFS_CTL_WAIT },
5765521Spendry { 0 },
5865521Spendry };
5965521Spendry
6065521Spendry static vfs_namemap_t signames[] = {
6165521Spendry /* regular signal names */
6265521Spendry { "hup", SIGHUP }, { "int", SIGINT },
6365521Spendry { "quit", SIGQUIT }, { "ill", SIGILL },
6465521Spendry { "trap", SIGTRAP }, { "abrt", SIGABRT },
6565521Spendry { "iot", SIGIOT }, { "emt", SIGEMT },
6665521Spendry { "fpe", SIGFPE }, { "kill", SIGKILL },
6765521Spendry { "bus", SIGBUS }, { "segv", SIGSEGV },
6865521Spendry { "sys", SIGSYS }, { "pipe", SIGPIPE },
6965521Spendry { "alrm", SIGALRM }, { "term", SIGTERM },
7065521Spendry { "urg", SIGURG }, { "stop", SIGSTOP },
7165521Spendry { "tstp", SIGTSTP }, { "cont", SIGCONT },
7265521Spendry { "chld", SIGCHLD }, { "ttin", SIGTTIN },
7365521Spendry { "ttou", SIGTTOU }, { "io", SIGIO },
7465521Spendry { "xcpu", SIGXCPU }, { "xfsz", SIGXFSZ },
7565521Spendry { "vtalrm", SIGVTALRM }, { "prof", SIGPROF },
7665521Spendry { "winch", SIGWINCH }, { "info", SIGINFO },
7765521Spendry { "usr1", SIGUSR1 }, { "usr2", SIGUSR2 },
7865521Spendry { 0 },
7965521Spendry };
8065521Spendry
8165521Spendry static int
procfs_control(curp,p,op)8265521Spendry procfs_control(curp, p, op)
8365521Spendry struct proc *curp;
8465521Spendry struct proc *p;
8565521Spendry int op;
8665521Spendry {
8765521Spendry int error;
8865521Spendry
8965521Spendry /*
9065521Spendry * Attach - attaches the target process for debugging
9165521Spendry * by the calling process.
9265521Spendry */
9365521Spendry if (op == PROCFS_CTL_ATTACH) {
9465521Spendry /* check whether already being traced */
9565521Spendry if (p->p_flag & P_TRACED)
9665521Spendry return (EBUSY);
9765521Spendry
9865521Spendry /* can't trace yourself! */
9965521Spendry if (p->p_pid == curp->p_pid)
10065521Spendry return (EINVAL);
10165521Spendry
10265521Spendry /*
10365521Spendry * Go ahead and set the trace flag.
10465521Spendry * Save the old parent (it's reset in
10565521Spendry * _DETACH, and also in kern_exit.c:wait4()
10665521Spendry * Reparent the process so that the tracing
10765521Spendry * proc gets to see all the action.
10865521Spendry * Stop the target.
10965521Spendry */
11065521Spendry p->p_flag |= P_TRACED;
11165521Spendry p->p_xstat = 0; /* XXX ? */
11265521Spendry if (p->p_pptr != curp) {
11365521Spendry p->p_oppid = p->p_pptr->p_pid;
11465521Spendry proc_reparent(p, curp);
11565521Spendry }
11665521Spendry psignal(p, SIGSTOP);
11765521Spendry return (0);
11865521Spendry }
11965521Spendry
12065521Spendry /*
12165521Spendry * Target process must be stopped, owned by (curp) and
12265521Spendry * be set up for tracing (P_TRACED flag set).
12365521Spendry * Allow DETACH to take place at any time for sanity.
12465521Spendry * Allow WAIT any time, of course.
12565521Spendry */
12665521Spendry switch (op) {
12765521Spendry case PROCFS_CTL_DETACH:
12865521Spendry case PROCFS_CTL_WAIT:
12965521Spendry break;
13065521Spendry
13165521Spendry default:
13265521Spendry if (!TRACE_WAIT_P(curp, p))
13365521Spendry return (EBUSY);
13465521Spendry }
13565521Spendry
13665521Spendry /*
13765521Spendry * do single-step fixup if needed
13865521Spendry */
13965521Spendry FIX_SSTEP(p);
14065521Spendry
14165521Spendry /*
14265521Spendry * Don't deliver any signal by default.
14365521Spendry * To continue with a signal, just send
14465521Spendry * the signal name to the ctl file
14565521Spendry */
14665521Spendry p->p_xstat = 0;
14765521Spendry
14865521Spendry switch (op) {
14965521Spendry /*
15065521Spendry * Detach. Cleans up the target process, reparent it if possible
15165521Spendry * and set it running once more.
15265521Spendry */
15365521Spendry case PROCFS_CTL_DETACH:
15465521Spendry /* if not being traced, then this is a painless no-op */
15565521Spendry if ((p->p_flag & P_TRACED) == 0)
15665521Spendry return (0);
15765521Spendry
15865521Spendry /* not being traced any more */
15965521Spendry p->p_flag &= ~P_TRACED;
16065521Spendry
16165521Spendry /* give process back to original parent */
16265521Spendry if (p->p_oppid != p->p_pptr->p_pid) {
16365521Spendry struct proc *pp;
16465521Spendry
16565521Spendry pp = pfind(p->p_oppid);
16665521Spendry if (pp)
16765521Spendry proc_reparent(p, pp);
16865521Spendry }
16965521Spendry
17065521Spendry p->p_oppid = 0;
17165521Spendry p->p_flag &= ~P_WAITED; /* XXX ? */
17265521Spendry wakeup((caddr_t) curp); /* XXX for CTL_WAIT below ? */
17365521Spendry
17465521Spendry break;
17565521Spendry
17665521Spendry /*
17765521Spendry * Step. Let the target process execute a single instruction.
17865521Spendry */
17965521Spendry case PROCFS_CTL_STEP:
180*67389Spendry if (error = procfs_sstep(p, 1))
181*67389Spendry return (error);
18265521Spendry break;
18365521Spendry
18465521Spendry /*
18565521Spendry * Run. Let the target process continue running until a breakpoint
18665521Spendry * or some other trap.
18765521Spendry */
18865521Spendry case PROCFS_CTL_RUN:
18965521Spendry break;
19065521Spendry
19165521Spendry /*
19265521Spendry * Wait for the target process to stop.
19365521Spendry * If the target is not being traced then just wait
19465521Spendry * to enter
19565521Spendry */
19665521Spendry case PROCFS_CTL_WAIT:
19765521Spendry error = 0;
19865521Spendry if (p->p_flag & P_TRACED) {
19965521Spendry while (error == 0 &&
20065521Spendry (p->p_stat != SSTOP) &&
20165521Spendry (p->p_flag & P_TRACED) &&
20265521Spendry (p->p_pptr == curp)) {
20365521Spendry error = tsleep((caddr_t) p,
20465521Spendry PWAIT|PCATCH, "procfsx", 0);
20565521Spendry }
20665521Spendry if (error == 0 && !TRACE_WAIT_P(curp, p))
20765521Spendry error = EBUSY;
20865521Spendry } else {
20965521Spendry while (error == 0 && p->p_stat != SSTOP) {
21065521Spendry error = tsleep((caddr_t) p,
21165521Spendry PWAIT|PCATCH, "procfs", 0);
21265521Spendry }
21365521Spendry }
21465521Spendry return (error);
21565521Spendry
21665521Spendry default:
21765521Spendry panic("procfs_control");
21865521Spendry }
21965521Spendry
22065521Spendry if (p->p_stat == SSTOP)
22165521Spendry setrunnable(p);
22265521Spendry return (0);
22365521Spendry }
22465521Spendry
22565521Spendry int
procfs_doctl(curp,p,pfs,uio)22665521Spendry procfs_doctl(curp, p, pfs, uio)
22765521Spendry struct proc *curp;
22865521Spendry struct pfsnode *pfs;
22965521Spendry struct uio *uio;
23065521Spendry struct proc *p;
23165521Spendry {
23265521Spendry int xlen;
23365521Spendry int error;
23465521Spendry char msg[PROCFS_CTLLEN+1];
23565521Spendry vfs_namemap_t *nm;
23665521Spendry
23765521Spendry if (uio->uio_rw != UIO_WRITE)
23865521Spendry return (EOPNOTSUPP);
23965521Spendry
24065521Spendry xlen = PROCFS_CTLLEN;
24165521Spendry error = vfs_getuserstr(uio, msg, &xlen);
24265521Spendry if (error)
24365521Spendry return (error);
24465521Spendry
24565521Spendry /*
24665521Spendry * Map signal names into signal generation
24765521Spendry * or debug control. Unknown commands and/or signals
24865521Spendry * return EOPNOTSUPP.
24965521Spendry *
25065521Spendry * Sending a signal while the process is being debugged
25165521Spendry * also has the side effect of letting the target continue
25265521Spendry * to run. There is no way to single-step a signal delivery.
25365521Spendry */
25465521Spendry error = EOPNOTSUPP;
25565521Spendry
25665521Spendry nm = vfs_findname(ctlnames, msg, xlen);
25765521Spendry if (nm) {
25865521Spendry error = procfs_control(curp, p, nm->nm_val);
25965521Spendry } else {
26065521Spendry nm = vfs_findname(signames, msg, xlen);
26165521Spendry if (nm) {
26265521Spendry if (TRACE_WAIT_P(curp, p)) {
26365521Spendry p->p_xstat = nm->nm_val;
26465521Spendry FIX_SSTEP(p);
26565521Spendry setrunnable(p);
26665521Spendry } else {
26765521Spendry psignal(p, nm->nm_val);
26865521Spendry }
26965521Spendry error = 0;
27065521Spendry }
27165521Spendry }
27265521Spendry
27365521Spendry return (error);
27465521Spendry }
275