/* * set things up for doing i/o on * the pfsnode (vp). (vp) is locked * on entry, and should be left locked * on exit. * * for procfs we don't need to do anything * in particular for i/o. all that is done * is to support exclusive open on process * memory images. * * procfs_open(struct vnode *a_vp, int a_mode, struct ucred *a_cred, * struct file *a_fp) */ static int procfs_open(struct vop_open_args *ap) { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *p1, *p2; int error; p2 = pfs_pfind(pfs->pfs_pid); if (p2 == NULL) return (ENOENT); if (pfs->pfs_pid && !PRISON_CHECK(ap->a_cred, p2->p_ucred)) { error = ENOENT; goto done; } switch (pfs->pfs_type) { case Pmem: if (((pfs->pfs_flags & FWRITE) && (ap->a_mode & O_EXCL)) || ((pfs->pfs_flags & O_EXCL) && (ap->a_mode & FWRITE))) { error = EBUSY; goto done; } p1 = curproc; KKASSERT(p1); /* Can't trace a process that's currently exec'ing. */ if ((p2->p_flags & P_INEXEC) != 0) { error = EAGAIN; goto done; } if (!CHECKIO(p1, p2) || p_trespass(ap->a_cred, p2->p_ucred)) { error = EPERM; goto done; } if (ap->a_mode & FWRITE) pfs->pfs_flags = ap->a_mode & (FWRITE|O_EXCL); break; default: break; } error = vop_stdopen(ap); done: pfs_pdone(p2); return error; }
/* * close the pfsnode (vp) after doing i/o. * (vp) is not locked on entry or exit. * * nothing to do for procfs other than undo * any exclusive open flag (see _open above). * * procfs_close(struct vnode *a_vp, int a_fflag, struct ucred *a_cred) */ static int procfs_close(struct vop_close_args *ap) { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *p; switch (pfs->pfs_type) { case Pmem: if ((ap->a_fflag & FWRITE) && (pfs->pfs_flags & O_EXCL)) pfs->pfs_flags &= ~(FWRITE|O_EXCL); /* * v_opencount determines the last real close on the vnode. * * If this is the last close, then it checks to see if * the target process has PF_LINGER set in p_pfsflags, * if this is *not* the case, then the process' stop flags * are cleared, and the process is woken up. This is * to help prevent the case where a process has been * told to stop on an event, but then the requesting process * has gone away or forgotten about it. */ p = NULL; if ((ap->a_vp->v_opencount < 2) && ((p = pfs_pfind(pfs->pfs_pid)) != NULL || (p = pfs_zpfind(pfs->pfs_pid)) != NULL) && !(p->p_pfsflags & PF_LINGER)) { spin_lock(&p->p_spin); p->p_stops = 0; p->p_step = 0; spin_unlock(&p->p_spin); wakeup(&p->p_stype); wakeup(&p->p_step); } pfs_pdone(p); break; default: break; } return (vop_stdclose(ap)); }
/* * do an ioctl operation on a pfsnode (vp). * (vp) is not locked on entry or exit. */ static int procfs_ioctl(struct vop_ioctl_args *ap) { struct pfsnode *pfs = VTOPFS(ap->a_vp); struct proc *procp; struct proc *p; int error; int signo; struct procfs_status *psp; unsigned char flags; procp = pfs_pfind(pfs->pfs_pid); if (procp == NULL) return ENOTTY; p = curproc; if (p == NULL) { error = EINVAL; goto done; } /* Can't trace a process that's currently exec'ing. */ if ((procp->p_flags & P_INEXEC) != 0) { error = EAGAIN; goto done; } if (!CHECKIO(p, procp) || p_trespass(ap->a_cred, procp->p_ucred)) { error = EPERM; goto done; } switch (ap->a_command) { case PIOCBIS: spin_lock(&procp->p_spin); procp->p_stops |= *(unsigned int*)ap->a_data; spin_unlock(&procp->p_spin); break; case PIOCBIC: spin_lock(&procp->p_spin); procp->p_stops &= ~*(unsigned int*)ap->a_data; spin_unlock(&procp->p_spin); break; case PIOCSFL: /* * NFLAGS is "non-suser_xxx flags" -- currently, only * PFS_ISUGID ("ignore set u/g id"); */ #define NFLAGS (PF_ISUGID) flags = (unsigned char)*(unsigned int*)ap->a_data; if (flags & NFLAGS && (error = priv_check_cred(ap->a_cred, PRIV_ROOT, 0))) goto done; procp->p_pfsflags = flags; break; case PIOCGFL: *(unsigned int*)ap->a_data = (unsigned int)procp->p_pfsflags; break; case PIOCSTATUS: /* * NOTE: syscall entry deals with stopevents and may run without * the MP lock. */ psp = (struct procfs_status *)ap->a_data; psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; spin_lock(&procp->p_spin); if (procp->p_step) { psp->state = 0; psp->why = procp->p_stype; psp->val = procp->p_xstat; spin_unlock(&procp->p_spin); } else { psp->state = 1; spin_unlock(&procp->p_spin); psp->why = 0; /* Not defined values */ psp->val = 0; /* Not defined values */ } break; case PIOCWAIT: /* * NOTE: syscall entry deals with stopevents and may run without * the MP lock. */ psp = (struct procfs_status *)ap->a_data; spin_lock(&procp->p_spin); while (procp->p_step == 0) { tsleep_interlock(&procp->p_stype, PCATCH); spin_unlock(&procp->p_spin); if (procp->p_stops == 0) { error = EINVAL; goto done; } if (procp->p_flags & P_POSTEXIT) { error = EINVAL; goto done; } if (procp->p_flags & P_INEXEC) { error = EAGAIN; goto done; } error = tsleep(&procp->p_stype, PCATCH | PINTERLOCKED, "piocwait", 0); if (error) goto done; spin_lock(&procp->p_spin); } spin_unlock(&procp->p_spin); psp->state = 1; /* It stopped */ psp->flags = procp->p_pfsflags; psp->events = procp->p_stops; psp->why = procp->p_stype; /* why it stopped */ psp->val = procp->p_xstat; /* any extra info */ break; case PIOCCONT: /* Restart a proc */ /* * NOTE: syscall entry deals with stopevents and may run without * the MP lock. However, the caller is presumably interlocked * by having waited. */ if (procp->p_step == 0) { error = EINVAL; /* Can only start a stopped process */ goto done; } if ((signo = *(int*)ap->a_data) != 0) { if (signo >= NSIG || signo <= 0) { error = EINVAL; goto done; } ksignal(procp, signo); } procp->p_step = 0; wakeup(&procp->p_step); break; default: error = ENOTTY; goto done; } error = 0; done: pfs_pdone(procp); return 0; }
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); ASSERT_LWKT_TOKEN_HELD(&proc_token); /* Can't trace a process that's currently exec'ing. */ if ((p->p_flags & P_INEXEC) != 0) return EAGAIN; /* * Authorization check: rely on normal debugging protection, except * allow processes to disengage debugging on a process onto which * they have previously attached, but no longer have permission to * debug. */ 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); } /* * Attach - attaches the target process for debugging * by the calling process. */ if (op == PROCFS_CTL_ATTACH) { /* check whether already being traced */ if (p->p_flags & P_TRACED) return (EBUSY); /* can't trace yourself! */ if (p->p_pid == curp->p_pid) return (EINVAL); /* * Go ahead and set the trace flag. * Save the old parent (it's reset in * _DETACH, and also in kern_exit.c:wait4() * Reparent the process so that the tracing * proc gets to see all the action. * Stop the target. */ p->p_flags |= P_TRACED; faultin(p); p->p_xstat = 0; /* XXX ? */ if (p->p_pptr != curp) { p->p_oppid = p->p_pptr->p_pid; proc_reparent(p, curp); } proc_stop(p); return (0); } /* * Target process must be stopped, owned by (curp) and * be set up for tracing (P_TRACED flag set). * Allow DETACH to take place at any time for sanity. * Allow WAIT any time, of course. */ switch (op) { case PROCFS_CTL_DETACH: case PROCFS_CTL_WAIT: break; default: if (!TRACE_WAIT_P(curp, p)) return (EBUSY); } #ifdef FIX_SSTEP /* * do single-step fixup if needed */ FIX_SSTEP(lp); #endif /* * Don't deliver any signal by default. * To continue with a signal, just send * the signal name to the ctl file */ p->p_xstat = 0; switch (op) { /* * Detach. Cleans up the target process, reparent it if possible * and set it running once more. */ case PROCFS_CTL_DETACH: /* if not being traced, then this is a painless no-op */ if ((p->p_flags & P_TRACED) == 0) return (0); /* not being traced any more */ p->p_flags &= ~P_TRACED; /* remove pending SIGTRAP, else the process will die */ spin_lock(&lp->lwp_spin); lwp_delsig(lp, SIGTRAP); spin_unlock(&lp->lwp_spin); /* give process back to original parent */ 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; /* XXX ? */ wakeup((caddr_t) curp); /* XXX for CTL_WAIT below ? */ break; /* * Step. Let the target process execute a single instruction. */ case PROCFS_CTL_STEP: LWPHOLD(lp); error = procfs_sstep(lp); LWPRELE(lp); if (error) return (error); break; /* * Run. Let the target process continue running until a breakpoint * or some other trap. */ case PROCFS_CTL_RUN: break; /* * Wait for the target process to stop. * If the target is not being traced then just wait * to enter */ 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 the process is in a stopped state, make it runnable again. * Do not set LWP_MP_BREAKTSLEEP - that is, do not break a tsleep * that might be in progress. */ if (p->p_stat == SSTOP) proc_unstop(p); return (0); }