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